Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 91e349d

Browse files
author
Meaghan Lewis
authored
Merge pull request #1212 from github/fixes/1190-find-merge-base
Find merge base when external PR branch has been deleted
2 parents ad67d7f + 7672f71 commit 91e349d

File tree

6 files changed

+46
-46
lines changed

6 files changed

+46
-46
lines changed

src/GitHub.App/Services/GitClient.cs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -380,32 +380,34 @@ public Task<bool> IsModified(IRepository repository, string path, byte[] content
380380
}
381381

382382
public async Task<string> GetPullRequestMergeBase(IRepository repo,
383-
UriString baseCloneUrl, UriString headCloneUrl, string baseSha, string headSha, string baseRef, string headRef)
383+
UriString targetCloneUrl, string baseSha, string headSha, string baseRef, int pullNumber)
384384
{
385385
Guard.ArgumentNotNull(repo, nameof(repo));
386-
Guard.ArgumentNotNull(baseCloneUrl, nameof(baseCloneUrl));
387-
Guard.ArgumentNotNull(headCloneUrl, nameof(headCloneUrl));
386+
Guard.ArgumentNotNull(targetCloneUrl, nameof(targetCloneUrl));
388387
Guard.ArgumentNotEmptyString(baseRef, nameof(baseRef));
389388

390-
var baseCommit = repo.Lookup<Commit>(baseSha);
391-
if (baseCommit == null)
389+
var headCommit = repo.Lookup<Commit>(headSha);
390+
if (headCommit == null)
392391
{
393-
await Fetch(repo, baseCloneUrl, baseRef);
394-
baseCommit = repo.Lookup<Commit>(baseSha);
395-
if (baseCommit == null)
392+
// The PR base branch might no longer exist, so we fetch using `refs/pull/<PR>/head` first.
393+
// This will often fetch the base commits, even when the base branch no longer exists.
394+
var headRef = $"refs/pull/{pullNumber}/head";
395+
await Fetch(repo, targetCloneUrl, headRef);
396+
headCommit = repo.Lookup<Commit>(headSha);
397+
if (headCommit == null)
396398
{
397-
throw new NotFoundException($"Couldn't find {baseSha} after fetching from {baseCloneUrl}:{baseRef}.");
399+
throw new NotFoundException($"Couldn't find {headSha} after fetching from {targetCloneUrl}:{headRef}.");
398400
}
399401
}
400402

401-
var headCommit = repo.Lookup<Commit>(headSha);
402-
if (headCommit == null)
403+
var baseCommit = repo.Lookup<Commit>(baseSha);
404+
if (baseCommit == null)
403405
{
404-
await Fetch(repo, headCloneUrl, headRef);
405-
headCommit = repo.Lookup<Commit>(headSha);
406-
if (headCommit == null)
406+
await Fetch(repo, targetCloneUrl, baseRef);
407+
baseCommit = repo.Lookup<Commit>(baseSha);
408+
if (baseCommit == null)
407409
{
408-
throw new NotFoundException($"Couldn't find {headSha} after fetching from {headCloneUrl}:{headRef}.");
410+
throw new NotFoundException($"Couldn't find {baseSha} after fetching from {targetCloneUrl}:{baseRef}.");
409411
}
410412
}
411413

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,10 @@ public IObservable<string> ExtractFile(
313313
sha = await gitClient.GetPullRequestMergeBase(
314314
repo,
315315
pullRequest.Base.RepositoryCloneUrl,
316-
pullRequest.Head.RepositoryCloneUrl,
317316
pullRequest.Base.Sha,
318317
pullRequest.Head.Sha,
319318
pullRequest.Base.Ref,
320-
pullRequest.Head.Ref);
319+
pullRequest.Number);
321320
}
322321
catch (NotFoundException ex)
323322
{

src/GitHub.Exports.Reactive/Services/IGitClient.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ public interface IGitClient
188188
/// Find the merge base SHA between two commits.
189189
/// </summary>
190190
/// <param name="repository">The repository.</param>
191-
/// <param name="baseCloneUrl">The clone url of the PR base.</param>
192-
/// <param name="headCloneUrl">The clone url of the PR head.</param>
191+
/// <param name="targetCloneUrl">The clone url of the PR target repo.</param>
193192
/// <param name="baseSha">The PR base SHA.</param>
194193
/// <param name="headSha">The PR head SHA.</param>
195194
/// <param name="baseRef">The PR base ref (e.g. 'master').</param>
@@ -198,7 +197,7 @@ public interface IGitClient
198197
/// The merge base SHA or null.
199198
/// </returns>
200199
/// <exception cref="LibGit2Sharp.NotFoundException">Thrown when the merge base can't be found.</exception>
201-
Task<string> GetPullRequestMergeBase(IRepository repo, UriString baseCloneUrl, UriString headCloneUrl, string baseSha, string headSha, string baseRef, string headRef);
200+
Task<string> GetPullRequestMergeBase(IRepository repo, UriString targetCloneUrl, string baseSha, string headSha, string baseRef, int pullNumber);
202201

203202
/// Checks whether the current head is pushed to its remote tracking branch.
204203
/// </summary>

src/GitHub.InlineReviews/Services/PullRequestSessionService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,13 @@ public async Task<string> GetPullRequestMergeBase(ILocalRepositoryModel reposito
122122
}
123123

124124
var repo = await GetRepository(repository);
125-
var baseUrl = pullRequest.Base.RepositoryCloneUrl;
125+
var targetUrl = pullRequest.Base.RepositoryCloneUrl;
126126
var headUrl = pullRequest.Head.RepositoryCloneUrl;
127127
var baseRef = pullRequest.Base.Ref;
128-
var headRef = pullRequest.Head.Ref;
128+
var pullNumber = pullRequest.Number;
129129
try
130130
{
131-
mergeBase = await gitClient.GetPullRequestMergeBase(repo, baseUrl, headUrl, baseSha, headSha, baseRef, headRef);
131+
mergeBase = await gitClient.GetPullRequestMergeBase(repo, targetUrl, baseSha, headSha, baseRef, pullNumber);
132132
}
133133
catch (NotFoundException ex)
134134
{

src/UnitTests/GitHub.App/Services/GitClientTests.cs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -162,61 +162,61 @@ public class TheGetPullRequestMergeBaseMethod : TestBaseClass
162162
[Fact]
163163
public async Task LocalBaseHeadAndMergeBase_DontFetch()
164164
{
165-
var baseUrl = new UriString("https://github.com/owner/repo");
166-
var headUrl = new UriString("https://github.com/owner/repo");
165+
var targetCloneUrl = new UriString("https://github.com/owner/repo");
167166
var baseSha = "baseSha";
168167
var headSha = "headSha";
169168
var expectMergeBaseSha = "mergeBaseSha";
170169
var baseRef = "master";
171-
var headRef = "headRef";
170+
var pullNumber = 0;
172171
var repo = MockRepo(baseSha, headSha, expectMergeBaseSha);
173172
var gitClient = new GitClient(Substitute.For<IGitHubCredentialProvider>());
174173

175-
var mergeBaseSha = await gitClient.GetPullRequestMergeBase(repo, baseUrl, headUrl, baseSha, headSha, baseRef, headRef);
174+
var mergeBaseSha = await gitClient.GetPullRequestMergeBase(repo, targetCloneUrl, baseSha, headSha, baseRef, pullNumber);
176175

177176
repo.Network.DidNotReceiveWithAnyArgs().Fetch(null as Remote, null, null as FetchOptions);
178177
Assert.Equal(expectMergeBaseSha, mergeBaseSha);
179178
}
180179

181180
[Theory]
182-
[InlineData("https://github.com/owner/repo", "https://github.com/owner/repo", "baseSha", "headSha", "mergeBaseSha", 0)]
183-
[InlineData("https://github.com/owner/repo", "https://github.com/owner/repo", null, "headSha", "mergeBaseSha", 1)]
184-
[InlineData("https://github.com/owner/repo", "https://github.com/owner/repo", "baseSha", null, "mergeBaseSha", 1)]
185-
[InlineData("https://github.com/owner/repo", "https://github.com/owner/repo", "baseSha", "headSha", null, 0)]
186-
public async Task WhenToFetch(string baseUrl, string headUrl, string baseSha, string headSha, string mergeBaseSha, int receivedFetch)
181+
[InlineData("https://github.com/owner/repo", "baseSha", "headSha", "mergeBaseSha", 0)]
182+
[InlineData("https://github.com/owner/repo", null, "headSha", "mergeBaseSha", 1)]
183+
[InlineData("https://github.com/owner/repo", "baseSha", null, "mergeBaseSha", 1)]
184+
[InlineData("https://github.com/owner/repo", "baseSha", "headSha", null, 0)]
185+
public async Task WhenToFetch(string targetCloneUrl, string baseSha, string headSha, string mergeBaseSha, int receivedFetch)
187186
{
188-
var baseUri = new UriString(baseUrl);
189-
var headUri = new UriString(headUrl);
187+
var targetCloneUri = new UriString("https://github.com/owner/repo");
190188
var baseRef = "master";
191-
var headRef = "headRef";
189+
var pullNumber = 0;
192190
var repo = MockRepo(baseSha, headSha, mergeBaseSha);
193191
var remote = Substitute.For<Remote>();
194192
repo.Network.Remotes.Add(null, null).ReturnsForAnyArgs(remote);
195193
var gitClient = new GitClient(Substitute.For<IGitHubCredentialProvider>());
196194

197195
try
198196
{
199-
await gitClient.GetPullRequestMergeBase(repo, baseUri, headUri, baseSha, headSha, baseRef, headRef);
197+
await gitClient.GetPullRequestMergeBase(repo, targetCloneUri, baseSha, headSha, baseRef, pullNumber);
200198
}
201199
catch (NotFoundException) { /* We're interested in calls to Fetch even if it throws */ }
202200

203201
repo.Network.Received(receivedFetch).Fetch(Arg.Any<Remote>(), Arg.Any<string[]>(), Arg.Any<FetchOptions>());
204202
}
205203

206204
[Theory]
207-
[InlineData("baseSha", null, "mergeBaseSha", "baseRef", "headRef", "headRef")]
208-
[InlineData(null, "headSha", "mergeBaseSha", "baseRef", "headRef", "baseRef")]
209-
public async Task WhatToFetch(string baseSha, string headSha, string mergeBaseSha, string baseRef, string headRef,
205+
[InlineData("baseSha", null, "mergeBaseSha", "baseRef", 777, "refs/pull/777/head")]
206+
[InlineData(null, "headSha", "mergeBaseSha", "baseRef", 777, "baseRef")]
207+
208+
// PR base might not exist, so we must fetch `refs/pull/<PR>/head` first.
209+
[InlineData(null, null, "mergeBaseSha", "baseRef", 777, "refs/pull/777/head")]
210+
public async Task WhatToFetch(string baseSha, string headSha, string mergeBaseSha, string baseRef, int pullNumber,
210211
string expectRefSpec)
211212
{
212213
var repo = MockRepo(baseSha, headSha, mergeBaseSha);
213-
var baseUrl = new UriString("https://github.com/owner/repo");
214-
var headUrl = new UriString("https://github.com/owner/repo");
214+
var targetCloneUri = new UriString("https://github.com/owner/repo");
215215
var gitClient = new GitClient(Substitute.For<IGitHubCredentialProvider>());
216216

217217
try
218218
{
219-
await gitClient.GetPullRequestMergeBase(repo, baseUrl, headUrl, baseSha, headSha, baseRef, headRef);
219+
await gitClient.GetPullRequestMergeBase(repo, targetCloneUri, baseSha, headSha, baseRef, pullNumber);
220220
}
221221
catch (NotFoundException) { /* We're interested in calls to Fetch even if it throws */ }
222222

@@ -233,12 +233,12 @@ static IRepository MockRepo(string baseSha, string headSha, string mergeBaseSha)
233233

234234
if (baseSha != null)
235235
{
236-
repo.Lookup<Commit>(baseSha).Returns(baseCommit);
236+
repo.Lookup<Commit>(baseSha).Returns(baseSha != null ? baseCommit : null);
237237
}
238238

239239
if (headSha != null)
240240
{
241-
repo.Lookup<Commit>(headSha).Returns(headCommit);
241+
repo.Lookup<Commit>(headSha).Returns(headSha != null ? headCommit : null);
242242
}
243243

244244
repo.ObjectDatabase.FindMergeBase(baseCommit, headCommit).Returns(mergeBaseCommit);

src/UnitTests/GitHub.App/Services/PullRequestServiceTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,11 @@ static async Task<string> ExtractFile(
168168

169169
if (mergeBaseException == null)
170170
{
171-
gitClient.GetPullRequestMergeBase(Arg.Any<IRepository>(), Arg.Any<UriString>(), Arg.Any<UriString>(), baseSha, headSha, baseRef, headRef).ReturnsForAnyArgs(Task.FromResult(mergeBaseSha));
171+
gitClient.GetPullRequestMergeBase(Arg.Any<IRepository>(), Arg.Any<UriString>(), baseSha, headSha, baseRef, pullNumber).ReturnsForAnyArgs(Task.FromResult(mergeBaseSha));
172172
}
173173
else
174174
{
175-
gitClient.GetPullRequestMergeBase(Arg.Any<IRepository>(), Arg.Any<UriString>(), Arg.Any<UriString>(), baseSha, headSha, baseRef, headRef).ReturnsForAnyArgs(Task.FromException<string>(mergeBaseException));
175+
gitClient.GetPullRequestMergeBase(Arg.Any<IRepository>(), Arg.Any<UriString>(), baseSha, headSha, baseRef, pullNumber).ReturnsForAnyArgs(Task.FromException<string>(mergeBaseException));
176176
}
177177

178178
gitClient.ExtractFile(Arg.Any<IRepository>(), mergeBaseSha, fileName).Returns(GetFileTask(mergeBaseFileContent));

0 commit comments

Comments
 (0)