From 15fbe8e9cb92662c3fef1464cefd5de34ed573e6 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Thu, 23 Jul 2020 16:58:42 -0400 Subject: [PATCH 1/2] Checkout branch looks to remote tracking branches as fallback --- LibGit2Sharp.Tests/CheckoutFixture.cs | 46 +++++++++++++++++++++++++++ LibGit2Sharp/Commands/Checkout.cs | 40 ++++++++++++++++++++--- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index f0c2c36ed..8962cbc1d 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -1028,6 +1028,52 @@ public void CanCheckoutPathFromCurrentBranch(string fileName) } } + [Theory] + [InlineData("br2", "origin")] + [InlineData("unique/branch", "another/remote")] + public void CheckoutBranchTriesRemoteTrackingBranchAsFallbackAndSucceedsIfOnlyOne(string branchName, string expectedRemoteName) + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + ResetAndCleanWorkingDirectory(repo); + + // Define another remote + var otherRemote = "another/remote"; + repo.Network.Remotes.Add(otherRemote, "https://github.com/libgit2/TestGitRepository"); + + // Define an extra remote tracking branch that does not conflict + repo.Refs.Add($"refs/remotes/{otherRemote}/unique/branch", repo.Head.Tip.Sha); + + Branch branch = Commands.Checkout(repo, branchName); + + Assert.NotNull(branch); + Assert.True(branch.IsTracking); + Assert.Equal($"refs/remotes/{expectedRemoteName}/{branchName}", branch.TrackedBranch.CanonicalName); + } + } + + [Fact] + public void CheckoutBranchTriesRemoteTrackingBranchAsFallbackAndThrowsIfMoreThanOne() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + ResetAndCleanWorkingDirectory(repo); + + // Define another remote + var otherRemote = "another/remote"; + repo.Network.Remotes.Add(otherRemote, "https://github.com/libgit2/TestGitRepository"); + + // Define remote tracking branches that conflict + var branchName = "conflicting/branch"; + repo.Refs.Add($"refs/remotes/origin/{branchName}", repo.Head.Tip.Sha); + repo.Refs.Add($"refs/remotes/{otherRemote}/{branchName}", repo.Head.Tip.Sha); + + Assert.Throws(() => Commands.Checkout(repo, branchName)); + } + } + /// /// Helper method to populate a simple repository with /// a single file and two branches. diff --git a/LibGit2Sharp/Commands/Checkout.cs b/LibGit2Sharp/Commands/Checkout.cs index bcbd29616..17f3c3f9f 100644 --- a/LibGit2Sharp/Commands/Checkout.cs +++ b/LibGit2Sharp/Commands/Checkout.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -37,18 +38,47 @@ public static Branch Checkout(IRepository repository, string committishOrBranchS Ensure.ArgumentNotNullOrEmptyString(committishOrBranchSpec, "committishOrBranchSpec"); Ensure.ArgumentNotNull(options, "options"); - Reference reference; - GitObject obj; + Reference reference = null; + GitObject obj = null; + Branch branch = null; + + try + { + repository.RevParse(committishOrBranchSpec, out reference, out obj); + } + catch (NotFoundException) + { + // If committishOrBranchSpec is not a local branch but matches a tracking branch + // in exactly one remote, use it. This is the "git checkout" command's default behavior. + // https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt-emgitcheckoutemltbranchgt + var remoteBranches = repository.Network.Remotes + .SelectMany(r => repository.Branches.Where(b => + b.IsRemote && + b.CanonicalName == $"refs/remotes/{r.Name}/{committishOrBranchSpec}")) + .ToList(); + + if (remoteBranches.Count == 1) + { + branch = repository.CreateBranch(committishOrBranchSpec, remoteBranches[0].Tip); + repository.Branches.Update(branch, b => b.TrackedBranch = remoteBranches[0].CanonicalName); + return Checkout(repository, branch, options); + } + + if (remoteBranches.Count > 1) + throw new AmbiguousSpecificationException( + $"'{committishOrBranchSpec}' matched multiple ({remoteBranches.Count}) remote tracking branches"); + + throw; + } - repository.RevParse(committishOrBranchSpec, out reference, out obj); if (reference != null && reference.IsLocalBranch) { - Branch branch = repository.Branches[reference.CanonicalName]; + branch = repository.Branches[reference.CanonicalName]; return Checkout(repository, branch, options); } Commit commit = obj.Peel(true); - Checkout(repository, commit.Tree, options, committishOrBranchSpec); + Checkout(repository, commit.Tree, options, committishOrBranchSpec); return repository.Head; } From 316da3ba9daf21e2aa8af16e9f5693b256220382 Mon Sep 17 00:00:00 2001 From: Brandon Ording Date: Wed, 29 Dec 2021 18:56:11 -0500 Subject: [PATCH 2/2] Formatting tweaks --- LibGit2Sharp/Commands/Checkout.cs | 6 ++++-- LibGit2Sharp/Commands/Remove.cs | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/LibGit2Sharp/Commands/Checkout.cs b/LibGit2Sharp/Commands/Checkout.cs index 17f3c3f9f..46d456be1 100644 --- a/LibGit2Sharp/Commands/Checkout.cs +++ b/LibGit2Sharp/Commands/Checkout.cs @@ -61,12 +61,14 @@ public static Branch Checkout(IRepository repository, string committishOrBranchS { branch = repository.CreateBranch(committishOrBranchSpec, remoteBranches[0].Tip); repository.Branches.Update(branch, b => b.TrackedBranch = remoteBranches[0].CanonicalName); + return Checkout(repository, branch, options); } if (remoteBranches.Count > 1) - throw new AmbiguousSpecificationException( - $"'{committishOrBranchSpec}' matched multiple ({remoteBranches.Count}) remote tracking branches"); + { + throw new AmbiguousSpecificationException($"'{committishOrBranchSpec}' matched multiple ({remoteBranches.Count}) remote tracking branches"); + } throw; } diff --git a/LibGit2Sharp/Commands/Remove.cs b/LibGit2Sharp/Commands/Remove.cs index 939c427d1..f96339c12 100644 --- a/LibGit2Sharp/Commands/Remove.cs +++ b/LibGit2Sharp/Commands/Remove.cs @@ -1,7 +1,6 @@ -using System.Linq; +using System.Collections.Generic; using System.IO; -using System.Collections.Generic; -using LibGit2Sharp; +using System.Linq; using LibGit2Sharp.Core; namespace LibGit2Sharp