From 1fb6221777ed17bbc181cf926b3096db49e8740b Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Tue, 8 Jan 2013 20:12:13 -0500 Subject: [PATCH 1/4] Re-enable skipped hard reset test --- LibGit2Sharp.Tests/ResetHeadFixture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibGit2Sharp.Tests/ResetHeadFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index d757633cd..f9b46c6c0 100644 --- a/LibGit2Sharp.Tests/ResetHeadFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -180,7 +180,7 @@ public void HardResetInABareRepositoryThrows() } } - [Fact(Skip = "Not working against current libgit2 version")] + [Fact] public void HardResetUpdatesTheContentOfTheWorkingDirectory() { var clone = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); From d1e8913fa8f1bf6cecad2ba2a3c9ccd52c2febb7 Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Tue, 8 Jan 2013 19:59:23 -0500 Subject: [PATCH 2/4] Update checkout in response to libgit2 checkout changes --- LibGit2Sharp.Tests/CheckoutFixture.cs | 53 ++++++++++++++-------- LibGit2Sharp/Repository.cs | 64 +++++++++++---------------- 2 files changed, 61 insertions(+), 56 deletions(-) diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index cb28d8085..082b7d7dc 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -10,6 +10,7 @@ public class CheckoutFixture : BaseFixture { private static readonly string originalFilePath = "a.txt"; private static readonly string originalFileContent = "Hello"; + private static readonly string conflictingFileContent = "There"; private static readonly string otherBranchName = "other"; [Theory] @@ -190,8 +191,16 @@ public void CheckoutUpdatesModifiedFilesInWorkingDirectory() } [Fact] - public void CanForcefullyCheckoutWithStagedChanges() + public void CanForcefullyCheckoutWithConflictingStagedChanges() { + // This test will check that we can checkout a branch that results + // in a conflict. Here is the high level steps of the test: + // 1) Create branch otherBranch from current commit in master, + // 2) Commit change to master + // 3) Switch to otherBranch + // 4) Create conflicting change + // 5) Forcefully checkout master + TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); using (var repo = new Repository(path.RepositoryPath)) @@ -200,31 +209,39 @@ public void CanForcefullyCheckoutWithStagedChanges() Branch master = repo.Branches["master"]; Assert.True(master.IsCurrentRepositoryHead); - // Set the working directory to the current head + // Set the working directory to the current head. ResetAndCleanWorkingDirectory(repo); Assert.False(repo.Index.RetrieveStatus().IsDirty); - // Add local change + // Create otherBranch from current Head. + repo.Branches.Add(otherBranchName, master.Tip); + + // Add change to master. string fullPath = Path.Combine(repo.Info.WorkingDirectory, fileFullPath); File.WriteAllText(fullPath, originalFileContent); repo.Index.Stage(fullPath); + repo.Commit("change in master", Constants.Signature, Constants.Signature); - // Verify working directory is now dirty - Assert.True(repo.Index.RetrieveStatus().IsDirty); + // Checkout otherBranch. + repo.Checkout(otherBranchName); - // And that the new file exists - Assert.True(File.Exists(fileFullPath)); + // Add change to otherBranch. + File.WriteAllText(fullPath, conflictingFileContent); + repo.Index.Stage(fullPath); + + // Assert that normal checkout throws exception + // for the conflict. + Assert.Throws(() => master.Checkout()); - // Checkout with the force option - Branch targetBranch = repo.Branches["i-do-numbers"]; - targetBranch.Checkout(CheckoutOptions.Force, null); + // Checkout with force option should succeed. + master.Checkout(CheckoutOptions.Force, null); - // Assert that target branch is checked out - Assert.True(targetBranch.IsCurrentRepositoryHead); + // Assert that master branch is checked out. + Assert.True(repo.Branches["master"].IsCurrentRepositoryHead); - // And that staged change (add) is no longer preset - Assert.False(File.Exists(fileFullPath)); + // And that the current index is not dirty. + Assert.False(repo.Index.RetrieveStatus().IsDirty); } } @@ -254,11 +271,11 @@ public void CheckingOutWithMergeConflictsThrows() // Assert that checking out master throws // when there are unstaged commits - Assert.Throws(() => repo.Checkout("master")); + Assert.Throws(() => repo.Checkout("master")); // And when there are staged commits repo.Index.Stage(fullPath); - Assert.Throws(() => repo.Checkout("master")); + Assert.Throws(() => repo.Checkout("master")); } } @@ -267,8 +284,8 @@ public void CheckingOutInABareRepoThrows() { using (var repo = new Repository(BareTestRepoPath)) { - Assert.Throws(() => repo.Checkout(repo.Branches["refs/heads/test"])); - Assert.Throws(() => repo.Checkout("refs/heads/test")); + Assert.Throws(() => repo.Checkout(repo.Branches["refs/heads/test"])); + Assert.Throws(() => repo.Checkout("refs/heads/test")); } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 3b9fa492e..d44798b19 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -491,11 +491,16 @@ public Branch Checkout(string committishOrBranchSpec, CheckoutOptions checkoutOp if (branch != null) { - return CheckoutInternal(branch.CanonicalName, checkoutOptions, onCheckoutProgress); + return Checkout(branch, checkoutOptions, onCheckoutProgress); } - var commitId = LookupCommit(committishOrBranchSpec).Id; - return CheckoutInternal(commitId.Sha, checkoutOptions, onCheckoutProgress); + Commit commit = LookupCommit(committishOrBranchSpec); + CheckoutTree(commit.Tree, checkoutOptions, onCheckoutProgress); + + // Update HEAD. + Refs.UpdateTarget("HEAD", commit.Id.Sha); + + return Head; } /// @@ -509,7 +514,16 @@ public Branch Checkout(Branch branch, CheckoutOptions checkoutOptions, CheckoutP { Ensure.ArgumentNotNull(branch, "branch"); - return CheckoutInternal(branch.CanonicalName, checkoutOptions, onCheckoutProgress); + // Get the current tip commit of the branch, instead of + // relying on branch.Tip, which references the commit at + // the time the passed in branch object was created. + Commit commit = LookupCommit(branch.CanonicalName); + CheckoutTree(commit.Tree, checkoutOptions, onCheckoutProgress); + + // Update HEAD. + Refs.UpdateTarget("HEAD", branch.CanonicalName); + + return Head; } /// @@ -520,47 +534,21 @@ public Branch Checkout(Branch branch, CheckoutOptions checkoutOptions, CheckoutP /// controlling checkout behavior. /// that checkout progress is reported through. /// The that was checked out. - private Branch CheckoutInternal(string commitIdOrCanonicalBranchName, CheckoutOptions checkoutOptions, CheckoutProgressHandler onCheckoutProgress) - { - if (Info.IsBare) - { - throw new InvalidOperationException("Checkout is not allowed in a bare repository."); - } - - // Unless the Force option is specified, - // check if the current index / working tree is in a state - // where we can checkout a new branch. - if ((checkoutOptions & CheckoutOptions.Force) != CheckoutOptions.Force) - { - RepositoryStatus repositoryStatus = Index.RetrieveStatus(); - if (repositoryStatus.IsDirty) - { - throw new MergeConflictException( - "There are changes to files in the working directory that would be overwritten by a checkout." + - "Please commit your changes before you switch branches."); - } - } - - // Update HEAD - Refs.UpdateTarget("HEAD", commitIdOrCanonicalBranchName); - - // Update the working directory - CheckoutHeadForce(onCheckoutProgress); - - return Head; - } - - private void CheckoutHeadForce(CheckoutProgressHandler onCheckoutProgress) + private void CheckoutTree(Tree tree, CheckoutOptions checkoutOptions, CheckoutProgressHandler onCheckoutProgress) { GitCheckoutOpts options = new GitCheckoutOpts { version = 1, - checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_FORCE | - CheckoutStrategy.GIT_CHECKOUT_REMOVE_UNTRACKED, + checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_SAFE, progress_cb = CheckoutCallbacks.GenerateCheckoutCallbacks(onCheckoutProgress) }; - Proxy.git_checkout_head(this.Handle, ref options); + if (checkoutOptions.HasFlag(CheckoutOptions.Force)) + { + options.checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_FORCE; + } + + Proxy.git_checkout_tree(this.Handle, tree.Id, ref options); } /// From 58ead54cdd15daea60f5486204d1773b98fb3ded Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Thu, 10 Jan 2013 12:06:55 -0500 Subject: [PATCH 3/4] Add checkout tests --- LibGit2Sharp.Tests/CheckoutFixture.cs | 174 +++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 3 deletions(-) diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index 082b7d7dc..3bde9559a 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; using Xunit.Extensions; @@ -10,7 +11,7 @@ public class CheckoutFixture : BaseFixture { private static readonly string originalFilePath = "a.txt"; private static readonly string originalFileContent = "Hello"; - private static readonly string conflictingFileContent = "There"; + private static readonly string alternateFileContent = "There again"; private static readonly string otherBranchName = "other"; [Theory] @@ -227,7 +228,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() repo.Checkout(otherBranchName); // Add change to otherBranch. - File.WriteAllText(fullPath, conflictingFileContent); + File.WriteAllText(fullPath, alternateFileContent); repo.Index.Stage(fullPath); // Assert that normal checkout throws exception @@ -342,6 +343,167 @@ public void CheckingOutThroughRepositoryCallsCheckoutProgress() } } + [Fact] + public void CheckoutRetainsUntrackedChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Generate an unstaged change. + string fullPathFileB = Path.Combine(repo.Info.WorkingDirectory, "b.txt"); + File.WriteAllText(fullPathFileB, alternateFileContent); + + // Verify that there is an untracked entry. + Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + + repo.Checkout(otherBranchName); + + // Verify untracked entry still exists. + Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + } + } + + [Fact] + public void ForceCheckoutRetainsUntrackedChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Generate an unstaged change. + string fullPathFileB = Path.Combine(repo.Info.WorkingDirectory, "b.txt"); + File.WriteAllText(fullPathFileB, alternateFileContent); + + // Verify that there is an untracked entry. + Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + + repo.Checkout(otherBranchName, CheckoutOptions.Force, null); + + // Verify untracked entry still exists. + Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + } + } + + [Fact] + public void CheckoutRetainsUnstagedChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Generate an unstaged change. + string fullPathFileA = Path.Combine(repo.Info.WorkingDirectory, originalFilePath); + File.WriteAllText(fullPathFileA, alternateFileContent); + + // Verify that there is a modified entry. + Assert.Equal(1, repo.Index.RetrieveStatus().Modified.Count()); + Assert.Equal(FileStatus.Modified, repo.Index.RetrieveStatus(fullPathFileA)); + + repo.Checkout(otherBranchName); + + // Verify modified entry still exists. + Assert.Equal(1, repo.Index.RetrieveStatus().Modified.Count()); + Assert.Equal(FileStatus.Modified, repo.Index.RetrieveStatus(fullPathFileA)); + } + } + + [Fact] + public void CheckoutRetainsStagedChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Generate a staged change. + string fullPathFileA = Path.Combine(repo.Info.WorkingDirectory, originalFilePath); + File.WriteAllText(fullPathFileA, alternateFileContent); + repo.Index.Stage(fullPathFileA); + + // Verify that there is a staged entry. + Assert.Equal(1, repo.Index.RetrieveStatus().Staged.Count()); + Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus(fullPathFileA)); + + repo.Checkout(otherBranchName); + + // Verify staged entry still exists. + Assert.Equal(1, repo.Index.RetrieveStatus().Staged.Count()); + Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus(fullPathFileA)); + } + } + + [Fact] + public void CheckoutRetainsIgnoredChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Create a bin directory. + string ignoredDirectoryPath = Path.Combine(repo.Info.WorkingDirectory, "bin"); + Directory.CreateDirectory(ignoredDirectoryPath); + + // Create file in ignored bin directory. + string ignoredFilePath = Path.Combine(repo.Info.WorkingDirectory, Path.Combine("bin", "some_ignored_file.txt")); + File.WriteAllText(ignoredFilePath, "hello from this ignored file."); + + // The following check does not report ignored entries... + // Assert.Equal(1, repo.Index.RetrieveStatus().Ignored.Count()); + + Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + + repo.Checkout(otherBranchName); + + // Verify that the ignored file still exists. + Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.True(File.Exists(ignoredFilePath)); + } + } + + [Fact] + public void ForceCheckoutRetainsIgnoredChanges() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Create a bin directory. + string ignoredDirectoryPath = Path.Combine(repo.Info.WorkingDirectory, "bin"); + Directory.CreateDirectory(ignoredDirectoryPath); + + // Create file in ignored bin directory. + string ignoredFilePath = Path.Combine(repo.Info.WorkingDirectory, Path.Combine("bin", "some_ignored_file.txt")); + File.WriteAllText(ignoredFilePath, "hello from this ignored file."); + + // The following check does not report ignored entries... + // Assert.Equal(1, repo.Index.RetrieveStatus().Ignored.Count()); + + Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + + repo.Checkout(otherBranchName, CheckoutOptions.Force, null); + + // Verify that the ignored file still exists. + Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.True(File.Exists(ignoredFilePath)); + } + } + /// /// Helper method to populate a simple repository with /// a single file and two branches. @@ -349,9 +511,15 @@ public void CheckingOutThroughRepositoryCallsCheckoutProgress() /// Repository to populate private void PopulateBasicRepository(Repository repo) { - string fullPathFileA = Path.Combine(repo.Info.WorkingDirectory, "a.txt"); + // Generate a .gitignore file. + string gitIgnoreFilePath = Path.Combine(repo.Info.WorkingDirectory, ".gitignore"); + File.WriteAllText(gitIgnoreFilePath, "bin"); + repo.Index.Stage(gitIgnoreFilePath); + + string fullPathFileA = Path.Combine(repo.Info.WorkingDirectory, originalFilePath); File.WriteAllText(fullPathFileA, originalFileContent); repo.Index.Stage(fullPathFileA); + repo.Commit("Initial commit", Constants.Signature, Constants.Signature); repo.CreateBranch(otherBranchName); From 4f034ba39c3ffab7aad028b9c6314fa41285609e Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Mon, 21 Jan 2013 23:07:05 -0500 Subject: [PATCH 4/4] Checkout branch as snapshot --- LibGit2Sharp.Tests/CheckoutFixture.cs | 58 +++++++++++++++++++++++++-- LibGit2Sharp/Branch.cs | 10 ++++- LibGit2Sharp/Repository.cs | 27 +++++++++---- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index 3bde9559a..3b219f3ed 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -233,10 +233,10 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() // Assert that normal checkout throws exception // for the conflict. - Assert.Throws(() => master.Checkout()); + Assert.Throws(() => repo.Checkout(master.CanonicalName)); // Checkout with force option should succeed. - master.Checkout(CheckoutOptions.Force, null); + repo.Checkout(master.CanonicalName, CheckoutOptions.Force, null); // Assert that master branch is checked out. Assert.True(repo.Branches["master"].IsCurrentRepositoryHead); @@ -493,7 +493,7 @@ public void ForceCheckoutRetainsIgnoredChanges() // The following check does not report ignored entries... // Assert.Equal(1, repo.Index.RetrieveStatus().Ignored.Count()); - + Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); repo.Checkout(otherBranchName, CheckoutOptions.Force, null); @@ -504,6 +504,56 @@ public void ForceCheckoutRetainsIgnoredChanges() } } + [Fact] + public void CheckoutBranchSnapshot() + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (var repo = Repository.Init(scd.DirectoryPath)) + { + PopulateBasicRepository(repo); + + // Get the current status of master + // and the current tip. + Branch initial = repo.Branches["master"]; + Commit initialCommit = initial.Tip; + + // Add commit to master + string fullPath = Path.Combine(repo.Info.WorkingDirectory, originalFilePath); + File.WriteAllText(fullPath, "Update : hello from master branch!\n"); + repo.Index.Stage(fullPath); + repo.Commit("2nd commit", Constants.Signature, Constants.Signature); + + initial.Checkout(); + + // Head should point at initial commit. + Assert.Equal(repo.Head.Tip, initialCommit); + Assert.False(repo.Index.RetrieveStatus().IsDirty); + // Verify that HEAD is detached. + Assert.Equal(repo.Refs["HEAD"].TargetIdentifier, initial.Tip.Sha); + } + } + + [Fact] + public void CheckingOutRemoteBranchResultsInDetachedHead() + { + TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + using (var repo = new Repository(path.RepositoryPath)) + { + Branch master = repo.Branches["master"]; + Assert.True(master.IsCurrentRepositoryHead); + + // Set the working directory to the current head + ResetAndCleanWorkingDirectory(repo); + + repo.Checkout("refs/remotes/origin/master"); + + // Verify that HEAD is detached. + Assert.Equal(repo.Refs["HEAD"].TargetIdentifier, repo.Branches["origin/master"].Tip.Sha); + + } + } + /// /// Helper method to populate a simple repository with /// a single file and two branches. @@ -515,7 +565,7 @@ private void PopulateBasicRepository(Repository repo) string gitIgnoreFilePath = Path.Combine(repo.Info.WorkingDirectory, ".gitignore"); File.WriteAllText(gitIgnoreFilePath, "bin"); repo.Index.Stage(gitIgnoreFilePath); - + string fullPathFileA = Path.Combine(repo.Info.WorkingDirectory, originalFilePath); File.WriteAllText(fullPathFileA, originalFileContent); repo.Index.Stage(fullPathFileA); diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index 8744b2ff1..da7e8dd73 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -189,7 +189,10 @@ public virtual Remote Remote } /// - /// Checkout this branch. + /// Checkout the tip commit of this object. + /// If this commit is the current tip of the branch, will checkout + /// the named branch. Otherwise, will checkout the tip commit as a + /// detached HEAD. /// public virtual void Checkout() { @@ -197,7 +200,10 @@ public virtual void Checkout() } /// - /// Checkout this branch with a callback for progress reporting. + /// Checkout the tip commit of this object + /// with a callback for progress reporting. If this commit is the + /// current tip of the branch, will checkout the named branch. Otherwise, + /// will checkout the tip commit as a detached HEAD. /// /// Options controlling checkout behavior. /// Callback method to report checkout progress updates through. diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index d44798b19..072c105a9 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -504,7 +504,9 @@ public Branch Checkout(string committishOrBranchSpec, CheckoutOptions checkoutOp } /// - /// Checkout the specified . + /// Checkout the tip commit of the specified object. If this commit is the + /// current tip of the branch, will checkout the named branch. Otherwise, will checkout the tip commit + /// as a detached HEAD. /// /// The to check out. /// controlling checkout behavior. @@ -514,14 +516,25 @@ public Branch Checkout(Branch branch, CheckoutOptions checkoutOptions, CheckoutP { Ensure.ArgumentNotNull(branch, "branch"); - // Get the current tip commit of the branch, instead of - // relying on branch.Tip, which references the commit at - // the time the passed in branch object was created. - Commit commit = LookupCommit(branch.CanonicalName); - CheckoutTree(commit.Tree, checkoutOptions, onCheckoutProgress); + // Make sure this is not an unborn branch. + if (branch.Tip.Tree == null) + { + throw new Exception("branch tip is null, nothing to checkout."); + } + + CheckoutTree(branch.Tip.Tree, checkoutOptions, onCheckoutProgress); // Update HEAD. - Refs.UpdateTarget("HEAD", branch.CanonicalName); + if (!branch.IsRemote && + string.Equals(Refs[branch.CanonicalName].TargetIdentifier, branch.Tip.Id.Sha, + StringComparison.OrdinalIgnoreCase)) + { + Refs.UpdateTarget("HEAD", branch.CanonicalName); + } + else + { + Refs.UpdateTarget("HEAD", branch.Tip.Id.Sha); + } return Head; }