diff options
-rw-r--r-- | LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 3 | ||||
-rw-r--r-- | LibGit2Sharp.Tests/ResetHeadFixture.cs (renamed from LibGit2Sharp.Tests/ResetFixture.cs) | 2 | ||||
-rw-r--r-- | LibGit2Sharp.Tests/ResetIndexFixture.cs | 103 | ||||
-rw-r--r-- | LibGit2Sharp/Index.cs | 37 | ||||
-rw-r--r-- | LibGit2Sharp/Repository.cs | 20 |
5 files changed, 162 insertions, 3 deletions
diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 7020fd38..3f85a748 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -62,9 +62,10 @@ <Compile Include="ObjectDatabaseFixture.cs" /> <Compile Include="DiffTreeToTreeFixture.cs" /> <Compile Include="RepositoryOptionsFixture.cs" /> - <Compile Include="ResetFixture.cs" /> + <Compile Include="ResetHeadFixture.cs" /> <Compile Include="LazyFixture.cs" /> <Compile Include="RemoteFixture.cs" /> + <Compile Include="ResetIndexFixture.cs" /> <Compile Include="StatusFixture.cs" /> <Compile Include="TestHelpers\BaseFixture.cs" /> <Compile Include="BlobFixture.cs" /> diff --git a/LibGit2Sharp.Tests/ResetFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index b2912701..44d43f06 100644 --- a/LibGit2Sharp.Tests/ResetFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -6,7 +6,7 @@ using Xunit.Extensions; namespace LibGit2Sharp.Tests { - public class ResetFixture : BaseFixture + public class ResetHeadFixture : BaseFixture { [Theory] [InlineData(true)] diff --git a/LibGit2Sharp.Tests/ResetIndexFixture.cs b/LibGit2Sharp.Tests/ResetIndexFixture.cs new file mode 100644 index 00000000..ac9f6815 --- /dev/null +++ b/LibGit2Sharp.Tests/ResetIndexFixture.cs @@ -0,0 +1,103 @@ +using System.IO; +using System.Linq; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class ResetIndexFixture : BaseFixture + { + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ResetANewlyInitializedRepositoryThrows(bool isBare) + { + SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); + + using (Repository repo = Repository.Init(scd.DirectoryPath, isBare)) + { + Assert.Throws<LibGit2SharpException>(() => repo.Reset()); + } + } + + [Fact] + public void ResettingInABareRepositoryThrows() + { + using (var repo = new Repository(BareTestRepoPath)) + { + Assert.Throws<LibGit2SharpException>(() => repo.Reset()); + } + } + + private bool IsStaged(StatusEntry entry) + { + if ((entry.State & FileStatus.Added) == FileStatus.Added) + { + return true; + } + + if ((entry.State & FileStatus.Staged) == FileStatus.Staged) + { + return true; + } + + if ((entry.State & FileStatus.Removed) == FileStatus.Removed) + { + return true; + } + + return false; + } + + [Fact] + public void ResetTheIndexWithTheHeadUnstagesEverything() + { + TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(path.DirectoryPath)) + { + RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + Assert.Equal(3, oldStatus.Where(IsStaged).Count()); + + repo.Reset(); + + RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + Assert.Equal(0, newStatus.Where(IsStaged).Count()); + } + } + + [Fact] + public void CanResetTheIndexToTheContentOfACommit() + { + TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(path.DirectoryPath)) + { + repo.Reset("be3563a"); + + RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + + var expected = new[] { "1.txt", "1" + Path.DirectorySeparatorChar + "branch_file.txt", "deleted_staged_file.txt", + "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt" }; + + Assert.Equal(expected.Length, newStatus.Where(IsStaged).Count()); + Assert.Equal(expected, newStatus.Removed); + } + } + + [Fact] + public void CanResetTheIndexToASubsetOfTheContentOfACommit() + { + TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoWorkingDirPath); + + using (var repo = new Repository(path.DirectoryPath)) + { + repo.Reset("5b5b025", new[]{ "new.txt" }); + + Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", repo.Index["README"].Id.Sha); + Assert.Equal("fa49b077972391ad58037050f2a75f74e3671e92", repo.Index["new.txt"].Id.Sha); + } + } + } +} diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index 1cc99977..2f474e27 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -504,5 +504,42 @@ namespace LibGit2Sharp UpdatePhysicalIndex(); } } + + internal void Reset(TreeChanges changes) + { + foreach (TreeEntryChanges treeEntryChanges in changes) + { + switch (treeEntryChanges.Status) + { + case ChangeKind.Added: + RemoveFromIndex(treeEntryChanges.Path); + continue; + + case ChangeKind.Deleted: + /* Fall through */ + case ChangeKind.Modified: + ReplaceIndexEntryWith(treeEntryChanges); + continue; + + default: + throw new InvalidOperationException(string.Format("Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, Enum.GetName(typeof(ChangeKind), treeEntryChanges.Status))); + } + } + + UpdatePhysicalIndex(); + } + + private void ReplaceIndexEntryWith(TreeEntryChanges treeEntryChanges) + { + var indexEntry = new GitIndexEntry + { + Mode = (uint)treeEntryChanges.OldMode, + oid = treeEntryChanges.OldOid.Oid, + Path = utf8Marshaler.MarshalManagedToNative(treeEntryChanges.OldPath), + }; + + Ensure.Success(NativeMethods.git_index_add2(handle, indexEntry)); + utf8Marshaler.CleanUpNativeData(indexEntry.Path); + } } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index e3d537ae..aebc3a06 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -460,7 +460,7 @@ namespace LibGit2Sharp throw new LibGit2SharpException("Mixed reset is not allowed in a bare repository"); } - var commit = LookupCommit(shaOrReferenceName); + Commit commit = LookupCommit(shaOrReferenceName); //TODO: Check for unmerged entries @@ -482,6 +482,24 @@ namespace LibGit2Sharp throw new NotImplementedException(); } + /// <summary> + /// Replaces entries in the <see cref="Index"/> with entries from the specified commit. + /// </summary> + /// <param name = "shaOrReferenceName">The sha or reference canonical name of the target commit object.</param> + /// <param name = "paths">The list of paths (either files or directories) that should be considered.</param> + public void Reset(string shaOrReferenceName = "HEAD", IEnumerable<string> paths = null) + { + if (Info.IsBare) + { + throw new LibGit2SharpException("Reset is not allowed in a bare repository"); + } + + Commit commit = LookupCommit(shaOrReferenceName); + TreeChanges changes = Diff.Compare(commit.Tree, DiffTarget.Index, paths); + + Index.Reset(changes); + } + internal T RegisterForCleanup<T>(T disposable) where T : IDisposable { toCleanup.Push(disposable); |