diff --git a/GitPullRequest.Services/AzureDevOpsRepository.cs b/GitPullRequest.Services/AzureDevOpsRepository.cs index c866ba8..fc895a9 100644 --- a/GitPullRequest.Services/AzureDevOpsRepository.cs +++ b/GitPullRequest.Services/AzureDevOpsRepository.cs @@ -27,21 +27,46 @@ public override int FindPullRequestForCanonicalName(string canonicalName) protected override IDictionary GetReferences(IRepository repo, string remoteName) { // for Azure DevOps we need to fetch PR branches so we can explore their history and get the commit before the automatic merge commit that is done on the server - GitService.Fetch(repo, remoteName, $"+refs/pull/*/merge:refs/remotes/{remoteName}/pull/*/merge"); + // because we save this in our our own refs/pulls namespace we might not need to fetch if nothing has changed on the server - return base.GetReferences(repo, remoteName); - } + // Get remote references the normal way + var remoteRefs = base.GetReferences(repo, remoteName); - protected override string GetTipForReference(IRepository repo, string canonicalName, string targetIdentifier) - { - if (FindPullRequestForCanonicalName(canonicalName) != -1) + var needsFetch = false; + foreach (var kvp in remoteRefs) + { + var pr = FindPullRequestForCanonicalName(kvp.Key); + if (pr != -1) + { + var localRef = repo.Refs[GetPullRequestRefName(remoteName, pr.ToString())]; + // If we don't have a local ref for this PR branch, or we do but its not up to date with the server, we need to fetch + if (localRef == null || !localRef.TargetIdentifier.Equals(kvp.Value, StringComparison.OrdinalIgnoreCase)) + { + needsFetch = true; + break; + } + } + } + if (needsFetch) + { + GitService.Fetch(repo, remoteName, new[] { $"+refs/pull/*/merge:{GetPullRequestRefName(remoteName, "*")}" }, false); + } + + // Now that we know the data is available we can go through and reset our PR refs to point to one commit before the merge commit + var pullRequestRefs = remoteRefs.Where(k => FindPullRequestForCanonicalName(k.Key) != -1).ToList(); + foreach (var kvp in pullRequestRefs) { // Get the commit at HEAD^1 as Azure DevOps automatically adds a merge commit on the server - var commit = repo.Commits.QueryBy(new CommitFilter() { IncludeReachableFrom = targetIdentifier }).Skip(1).FirstOrDefault(); - return commit.Sha; + var commit = repo.Commits.QueryBy(new CommitFilter() { IncludeReachableFrom = kvp.Value }).Skip(1).FirstOrDefault(); + remoteRefs[kvp.Key] = commit.Sha; } - return targetIdentifier; + return remoteRefs; + } + + private static string GetPullRequestRefName(string remoteName, string pr) + { + return $"refs/pulls/{remoteName}/pull/{pr}/merge"; } public override string GetPullRequestUrl(int number) diff --git a/GitPullRequest.Services/IGitService.cs b/GitPullRequest.Services/IGitService.cs index 4b6e364..51ec76c 100644 --- a/GitPullRequest.Services/IGitService.cs +++ b/GitPullRequest.Services/IGitService.cs @@ -5,7 +5,7 @@ namespace GitPullRequest.Services { public interface IGitService { - void Fetch(IRepository repo, string remoteName, string refSpec); + void Fetch(IRepository repo, string remoteName, string[] refSpecs, bool prune); IDictionary ListReferences(IRepository repo, string remoteName); } } \ No newline at end of file diff --git a/GitPullRequest.Services/LibGitService.cs b/GitPullRequest.Services/LibGitService.cs index 13c8655..1447af6 100644 --- a/GitPullRequest.Services/LibGitService.cs +++ b/GitPullRequest.Services/LibGitService.cs @@ -24,7 +24,7 @@ public IDictionary ListReferences(IRepository repo, string remot return dictionary; } - public void Fetch(IRepository repo, string remoteName, string refSpec) + public void Fetch(IRepository repo, string remoteName, string[] refSpecs, bool prune) { var credentialsHandler = CreateCredentialsHandler(repo, remoteName); ProgressHandler progressHandler = text => @@ -33,10 +33,11 @@ public void Fetch(IRepository repo, string remoteName, string refSpec) return true; }; - repo.Network.Fetch(remoteName, new[] { refSpec }, new FetchOptions + repo.Network.Fetch(remoteName, refSpecs, new FetchOptions { CredentialsProvider = credentialsHandler, - OnProgress = progressHandler + OnProgress = progressHandler, + Prune = prune }); } diff --git a/GitPullRequest.Services/RemoteRepository.cs b/GitPullRequest.Services/RemoteRepository.cs index aa4a2ae..26d5241 100644 --- a/GitPullRequest.Services/RemoteRepository.cs +++ b/GitPullRequest.Services/RemoteRepository.cs @@ -37,15 +37,10 @@ protected virtual IDictionary GetReferences(IRepository repo, st foreach (var reference in refs) { var (targetIdentifier, canonicalName) = (reference.Value, reference.Key); - dictionary[canonicalName] = GetTipForReference(repo, canonicalName, targetIdentifier); + dictionary[canonicalName] = targetIdentifier; } return dictionary; } - - protected virtual string GetTipForReference(IRepository repo, string canonicalName, string targetIdentifier) - { - return targetIdentifier; - } } } diff --git a/GitPullRequest.Services/ShellGitService.cs b/GitPullRequest.Services/ShellGitService.cs index 9c0a5e9..2fd1340 100644 --- a/GitPullRequest.Services/ShellGitService.cs +++ b/GitPullRequest.Services/ShellGitService.cs @@ -40,12 +40,12 @@ public IDictionary ListReferences(IRepository repo, string remot return dictionary; } - public void Fetch(IRepository repo, string remoteName, string refSpec) + public void Fetch(IRepository repo, string remoteName, string[] refSpecs, bool prune) { var startInfo = new ProcessStartInfo { FileName = "git", - Arguments = $"fetch {remoteName} {refSpec}", + Arguments = $"fetch {remoteName} {string.Join(" ", refSpecs)}" + (prune ? " --prune" : ""), WorkingDirectory = repo.Info.WorkingDirectory, RedirectStandardOutput = true, UseShellExecute = false,