diff options
author | Jameson Miller <jamill@microsoft.com> | 2013-01-09 04:59:23 +0400 |
---|---|---|
committer | nulltoken <emeric.fermas@gmail.com> | 2013-01-22 18:14:35 +0400 |
commit | 5dc82d2af943232cc0fc0b74781309cff994e92a (patch) | |
tree | 72f90a5999b63521286e5d72f04819d101710e40 | |
parent | 86547599ed4ec0685d34762376fa1322393b00a1 (diff) |
Update checkout in response to libgit2 checkout changes
-rw-r--r-- | LibGit2Sharp.Tests/CheckoutFixture.cs | 53 | ||||
-rw-r--r-- | LibGit2Sharp/Repository.cs | 64 |
2 files changed, 61 insertions, 56 deletions
diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index cb28d808..082b7d7d 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -10,6 +10,7 @@ namespace LibGit2Sharp.Tests { 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 @@ namespace LibGit2Sharp.Tests } [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 @@ namespace LibGit2Sharp.Tests 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<LibGit2SharpException>(() => 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 @@ namespace LibGit2Sharp.Tests // Assert that checking out master throws // when there are unstaged commits - Assert.Throws<MergeConflictException>(() => repo.Checkout("master")); + Assert.Throws<LibGit2SharpException>(() => repo.Checkout("master")); // And when there are staged commits repo.Index.Stage(fullPath); - Assert.Throws<MergeConflictException>(() => repo.Checkout("master")); + Assert.Throws<LibGit2SharpException>(() => repo.Checkout("master")); } } @@ -267,8 +284,8 @@ namespace LibGit2Sharp.Tests { using (var repo = new Repository(BareTestRepoPath)) { - Assert.Throws<InvalidOperationException>(() => repo.Checkout(repo.Branches["refs/heads/test"])); - Assert.Throws<InvalidOperationException>(() => repo.Checkout("refs/heads/test")); + Assert.Throws<BareRepositoryException>(() => repo.Checkout(repo.Branches["refs/heads/test"])); + Assert.Throws<BareRepositoryException>(() => repo.Checkout("refs/heads/test")); } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 379c7448..668da5bd 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -504,11 +504,16 @@ namespace LibGit2Sharp 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; } /// <summary> @@ -522,7 +527,16 @@ namespace LibGit2Sharp { 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; } /// <summary> @@ -533,47 +547,21 @@ namespace LibGit2Sharp /// <param name="checkoutOptions"><see cref = "CheckoutOptions" /> controlling checkout behavior.</param> /// <param name="onCheckoutProgress"><see cref = "CheckoutProgressHandler" /> that checkout progress is reported through.</param> /// <returns>The <see cref = "Branch" /> that was checked out.</returns> - 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); } /// <summary> |