diff options
-rw-r--r-- | LibGit2Sharp.Tests/CheckoutFixture.cs | 162 | ||||
-rw-r--r-- | LibGit2Sharp/Configuration.cs | 11 | ||||
-rw-r--r-- | LibGit2Sharp/Repository.cs | 29 | ||||
-rw-r--r-- | LibGit2Sharp/RepositoryExtensions.cs | 4 |
4 files changed, 200 insertions, 6 deletions
diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index b01ef319..8bdca0a8 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -44,6 +44,14 @@ namespace LibGit2Sharp.Tests // Working directory should not be dirty Assert.False(repo.Index.RetrieveStatus().IsDirty); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(master.Tip.Id, reflogEntry.From); + Assert.Equal(branch.Tip.Id, reflogEntry.To); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal(string.Format("checkout: moving from master to {0}", branchName), reflogEntry.Message); } } @@ -74,6 +82,14 @@ namespace LibGit2Sharp.Tests // Working directory should not be dirty Assert.False(repo.Index.RetrieveStatus().IsDirty); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(master.Tip.Id, reflogEntry.From); + Assert.Equal(repo.Branches[branchName].Tip.Id, reflogEntry.To); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal(string.Format("checkout: moving from master to {0}", branchName), reflogEntry.Message); } } @@ -111,6 +127,14 @@ namespace LibGit2Sharp.Tests Assert.Equal("(no branch)", detachedHead.CanonicalName); Assert.False(master.IsCurrentRepositoryHead); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(master.Tip.Id, reflogEntry.From); + Assert.Equal(commitSha, reflogEntry.To.Sha); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal(string.Format("checkout: moving from master to {0}", commitPointer), reflogEntry.Message); } } @@ -572,6 +596,14 @@ namespace LibGit2Sharp.Tests // Verify that HEAD is detached. Assert.Equal(repo.Refs["HEAD"].TargetIdentifier, repo.Branches["origin/master"].Tip.Sha); Assert.True(repo.Info.IsHeadDetached); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(master.Tip.Id, reflogEntry.From); + Assert.Equal(repo.Branches["origin/master"].Tip.Id, reflogEntry.To); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal("checkout: moving from master to origin/master", reflogEntry.Message); } } @@ -601,6 +633,136 @@ namespace LibGit2Sharp.Tests } } + [Theory] + [InlineData("a447ba2ca8")] + [InlineData("refs/tags/lw")] + [InlineData("e90810^{}")] + public void CheckoutFromDetachedHead(string commitPointer) + { + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + // Set the working directory to the current head + ResetAndCleanWorkingDirectory(repo); + Assert.False(repo.Index.RetrieveStatus().IsDirty); + + var commitSha = repo.Lookup(commitPointer).Sha; + + Branch initialHead = repo.Checkout("6dcf9bf"); + + repo.Checkout(commitPointer); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(initialHead.Tip.Id, reflogEntry.From); + Assert.Equal(commitSha, reflogEntry.To.Sha); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal(string.Format("checkout: moving from {0} to {1}", initialHead.Tip.Sha, commitPointer), reflogEntry.Message); + } + } + + [Fact] + public void CheckoutBranchFromDetachedHead() + { + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + // Set the working directory to the current head + ResetAndCleanWorkingDirectory(repo); + Assert.False(repo.Index.RetrieveStatus().IsDirty); + + Branch initialHead = repo.Checkout("6dcf9bf"); + + Assert.True(repo.Info.IsHeadDetached); + + Branch newHead = repo.Checkout(repo.Branches["master"]); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(initialHead.Tip.Id, reflogEntry.From); + Assert.Equal(newHead.Tip.Id, reflogEntry.To); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal(string.Format("checkout: moving from {0} to {1}", initialHead.Tip.Sha, newHead.Name), reflogEntry.Message); + } + } + + + [Fact(Skip = "Current libgit2 revparse implementation only returns the object being pointed at, not the reference pointing at it.")] + public void CheckoutPreviousCheckedOutBranch() + { + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + // Set the working directory to the current head + ResetAndCleanWorkingDirectory(repo); + Assert.False(repo.Index.RetrieveStatus().IsDirty); + + Branch previousHead = repo.Checkout("i-do-numbers"); + Branch newHead = repo.Checkout("diff-test-cases"); + + //Go back to previous branch checked out + repo.Checkout(@"@{-1}"); + + // Assert reflog entry is created + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.Equal(newHead.Tip.Id, reflogEntry.From); + Assert.Equal(previousHead.Tip.Id, reflogEntry.To); + Assert.NotNull(reflogEntry.Commiter.Email); + Assert.NotNull(reflogEntry.Commiter.Name); + Assert.Equal("checkout: moving from diff-test-cases to i-do-numbers", reflogEntry.Message); + } + } + + [Fact] + public void CheckoutCurrentReference() + { + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + Branch master = repo.Branches["master"]; + Assert.True(master.IsCurrentRepositoryHead); + + ResetAndCleanWorkingDirectory(repo); + Assert.False(repo.Index.RetrieveStatus().IsDirty); + + var reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); + + // Checkout branch + repo.Checkout(master); + + Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); + + // Checkout in detached mode + repo.Checkout(master.Tip.Sha); + + Assert.True(repo.Info.IsHeadDetached); + var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); + Assert.True(reflogEntry.To == reflogEntry.From); + Assert.Equal(string.Format("checkout: moving from master to {0}", master.Tip.Sha), reflogEntry.Message); + + // Checkout detached "HEAD" => nothing should happen + reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); + + repo.Checkout(repo.Head); + + Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); + + // Checkout attached "HEAD" => nothing should happen + repo.Checkout("master"); + reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); + + repo.Checkout(repo.Head); + + Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); + + repo.Checkout("HEAD"); + + Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); + } + } + [Fact] public void CheckoutLowerCasedHeadThrows() { diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 1942ec8c..c47be960 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -288,17 +288,20 @@ namespace LibGit2Sharp }); } - internal Signature BuildSignatureFromGlobalConfiguration(DateTimeOffset now) + internal Signature BuildSignatureFromGlobalConfiguration(DateTimeOffset now, bool shouldThrowIfNotFound) { var name = Get<string>("user.name"); var email = Get<string>("user.email"); - if ((name == null) || (email == null)) + if (shouldThrowIfNotFound && ((name == null) || (email == null))) { - throw new LibGit2SharpException("Can not find Name and Email settings of the current user in Git configuration."); + throw new LibGit2SharpException("Can not find Name or Email setting of the current user in Git configuration."); } - return new Signature(name.Value, email.Value, now); + return new Signature( + name != null ? name.Value : "unknown", + email != null ? email.Value : string.Format("{0}@{1}", Environment.UserName, Environment.UserDomainName), + now); } } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 39cd5f1b..a14a61e3 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -586,11 +586,17 @@ namespace LibGit2Sharp return Checkout(branch, checkoutOptions, onCheckoutProgress); } + var previousHeadName = Info.IsHeadDetached ? Head.Tip.Sha : Head.Name; + Commit commit = LookupCommit(committishOrBranchSpec); CheckoutTree(commit.Tree, checkoutOptions, onCheckoutProgress); // Update HEAD. Refs.UpdateTarget("HEAD", commit.Id.Sha); + if (committishOrBranchSpec != "HEAD") + { + LogCheckout(previousHeadName, commit.Id, committishOrBranchSpec); + } return Head; } @@ -633,6 +639,9 @@ namespace LibGit2Sharp "The tip of branch '{0}' is null. There's nothing to checkout.", branch.Name)); } + var branchIsCurrentRepositoryHead = branch.IsCurrentRepositoryHead; + var previousHeadName = Info.IsHeadDetached ? Head.Tip.Sha : Head.Name; + CheckoutTree(branch.Tip.Tree, checkoutOptions, onCheckoutProgress); // Update HEAD. @@ -647,9 +656,29 @@ namespace LibGit2Sharp Refs.UpdateTarget("HEAD", branch.Tip.Id.Sha); } + if (!branchIsCurrentRepositoryHead) + { + LogCheckout(previousHeadName, branch); + } + return Head; } + private void LogCheckout(string previousHeadName, Branch newHead) + { + LogCheckout(previousHeadName, newHead.Tip.Id, newHead.Name); + } + + private void LogCheckout(string previousHeadName, ObjectId newHeadTip, string newHeadSpec) + { + // Compute reflog message + string reflogMessage = string.Format("checkout: moving from {0} to {1}", previousHeadName, newHeadSpec); + + // Log checkout + Signature author = Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now, false); + Refs.Log(Refs.Head).Append(newHeadTip, author, reflogMessage); + } + /// <summary> /// Internal implementation of Checkout that expects the ID of the checkout target /// to already be in the form of a canonical branch name or a commit ID. diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index 42919412..c653e9fe 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -173,7 +173,7 @@ namespace LibGit2Sharp /// <returns>The generated <see cref = "LibGit2Sharp.Commit" />.</returns> public static Commit Commit(this IRepository repository, string message, bool amendPreviousCommit = false) { - Signature author = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now); + Signature author = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now, true); return repository.Commit(message, author, amendPreviousCommit); } @@ -191,7 +191,7 @@ namespace LibGit2Sharp /// <returns>The generated <see cref = "LibGit2Sharp.Commit" />.</returns> public static Commit Commit(this IRepository repository, string message, Signature author, bool amendPreviousCommit = false) { - Signature committer = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now); + Signature committer = repository.Config.BuildSignatureFromGlobalConfiguration(DateTimeOffset.Now, true); return repository.Commit(message, author, committer, amendPreviousCommit); } |