Skip to content

Commit bb202dc

Browse files
committed
Make Diff.Compare able to work against the working directory and the index
Fix libgit2#167
1 parent c0bc893 commit bb202dc

File tree

6 files changed

+262
-1
lines changed

6 files changed

+262
-1
lines changed

LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,146 @@ private static void SetUpSimpleDiffContext(Repository repo)
2323
File.AppendAllText(fullpath, "!!!\n");
2424
}
2525

26+
[Fact]
27+
/*
28+
* No direct git equivalent but should output
29+
*
30+
* diff --git a/file.txt b/file.txt
31+
* index ce01362..4f125e3 100644
32+
* --- a/file.txt
33+
* +++ b/file.txt
34+
* @@ -1 +1,3 @@
35+
* hello
36+
* +world
37+
* +!!!
38+
*/
39+
public void CanCompareASimpleTreeAgainstTheWorkDir()
40+
{
41+
var scd = BuildSelfCleaningDirectory();
42+
43+
using (var repo = Repository.Init(scd.RootedDirectoryPath))
44+
{
45+
SetUpSimpleDiffContext(repo);
46+
47+
TreeChanges changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTarget.WorkingDirectory);
48+
49+
var expected = new StringBuilder()
50+
.Append("diff --git a/file.txt b/file.txt\n")
51+
.Append("index ce01362..4f125e3 100644\n")
52+
.Append("--- a/file.txt\n")
53+
.Append("+++ b/file.txt\n")
54+
.Append("@@ -1 +1,3 @@\n")
55+
.Append(" hello\n")
56+
.Append("+world\n")
57+
.Append("+!!!\n");
58+
59+
Assert.Equal(expected.ToString(), changes.Patch);
60+
}
61+
}
62+
63+
[Fact]
64+
/*
65+
* $ git diff HEAD
66+
* diff --git a/file.txt b/file.txt
67+
* index ce01362..4f125e3 100644
68+
* --- a/file.txt
69+
* +++ b/file.txt
70+
* @@ -1 +1,3 @@
71+
* hello
72+
* +world
73+
* +!!!
74+
*/
75+
public void CanCompareASimpleTreeAgainstTheWorkDirAndTheIndex()
76+
{
77+
var scd = BuildSelfCleaningDirectory();
78+
79+
using (var repo = Repository.Init(scd.RootedDirectoryPath))
80+
{
81+
SetUpSimpleDiffContext(repo);
82+
83+
TreeChanges changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTarget.BothWorkingDirectoryAndIndex);
84+
85+
var expected = new StringBuilder()
86+
.Append("diff --git a/file.txt b/file.txt\n")
87+
.Append("index ce01362..4f125e3 100644\n")
88+
.Append("--- a/file.txt\n")
89+
.Append("+++ b/file.txt\n")
90+
.Append("@@ -1 +1,3 @@\n")
91+
.Append(" hello\n")
92+
.Append("+world\n")
93+
.Append("+!!!\n");
94+
95+
Assert.Equal(expected.ToString(), changes.Patch);
96+
}
97+
}
98+
99+
100+
[Fact]
101+
/*
102+
* $ git diff
103+
*
104+
* $ git diff HEAD
105+
* diff --git a/file.txt b/file.txt
106+
* deleted file mode 100644
107+
* index ce01362..0000000
108+
* --- a/file.txt
109+
* +++ /dev/null
110+
* @@ -1 +0,0 @@
111+
* -hello
112+
*
113+
* $ git diff --cached
114+
* diff --git a/file.txt b/file.txt
115+
* deleted file mode 100644
116+
* index ce01362..0000000
117+
* --- a/file.txt
118+
* +++ /dev/null
119+
* @@ -1 +0,0 @@
120+
* -hello
121+
*/
122+
public void ShowcaseTheDifferenceBetweenTheTwoKindOfComparison()
123+
{
124+
var scd = BuildSelfCleaningDirectory();
125+
126+
using (var repo = Repository.Init(scd.RootedDirectoryPath))
127+
{
128+
SetUpSimpleDiffContext(repo);
129+
130+
var fullpath = Path.Combine(repo.Info.WorkingDirectory, "file.txt");
131+
File.Move(fullpath, fullpath + ".bak");
132+
repo.Index.Stage(fullpath);
133+
File.Move(fullpath + ".bak", fullpath);
134+
135+
FileStatus state = repo.Index.RetrieveStatus("file.txt");
136+
Assert.Equal(FileStatus.Removed | FileStatus.Untracked, state);
137+
138+
139+
TreeChanges wrkDirToIdxToTree = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTarget.BothWorkingDirectoryAndIndex);
140+
var expected = new StringBuilder()
141+
.Append("diff --git a/file.txt b/file.txt\n")
142+
.Append("deleted file mode 100644\n")
143+
.Append("index ce01362..0000000\n")
144+
.Append("--- a/file.txt\n")
145+
.Append("+++ /dev/null\n")
146+
.Append("@@ -1 +0,0 @@\n")
147+
.Append("-hello\n");
148+
149+
Assert.Equal(expected.ToString(), wrkDirToIdxToTree.Patch);
150+
151+
TreeChanges wrkDirToTree = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTarget.WorkingDirectory);
152+
expected = new StringBuilder()
153+
.Append("diff --git a/file.txt b/file.txt\n")
154+
.Append("index ce01362..4f125e3 100644\n")
155+
.Append("--- a/file.txt\n")
156+
.Append("+++ b/file.txt\n")
157+
.Append("@@ -1 +1,3 @@\n")
158+
.Append(" hello\n")
159+
.Append("+world\n")
160+
.Append("+!!!\n");
161+
162+
Assert.Equal(expected.ToString(), wrkDirToTree.Patch);
163+
}
164+
}
165+
26166
[Fact]
27167
/*
28168
* $ git diff --cached
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Linq;
2+
using LibGit2Sharp.Tests.TestHelpers;
3+
using Xunit;
4+
5+
namespace LibGit2Sharp.Tests
6+
{
7+
public class DiffWorkdirToIndexFixture : BaseFixture
8+
{
9+
/*
10+
* $ git diff
11+
* diff --git a/deleted_unstaged_file.txt b/deleted_unstaged_file.txt
12+
* deleted file mode 100644
13+
* index f2e4113..0000000
14+
* --- a/deleted_unstaged_file.txt
15+
* +++ /dev/null
16+
* @@ -1 +0,0 @@
17+
* -stuff
18+
* diff --git a/modified_unstaged_file.txt b/modified_unstaged_file.txt
19+
* index 9217230..da6fd65 100644
20+
* --- a/modified_unstaged_file.txt
21+
* +++ b/modified_unstaged_file.txt
22+
* @@ -1 +1,2 @@
23+
* +some more text
24+
* more files! more files!
25+
*/
26+
[Fact]
27+
public void CanCompareTheWorkDirAgainstTheIndex()
28+
{
29+
using (var repo = new Repository(StandardTestRepoPath))
30+
{
31+
TreeChanges changes = repo.Diff.Compare();
32+
33+
Assert.Equal(2, changes.Count());
34+
Assert.Equal("deleted_unstaged_file.txt", changes.Deleted.Single().Path);
35+
Assert.Equal("modified_unstaged_file.txt", changes.Modified.Single().Path);
36+
}
37+
}
38+
}
39+
}

LibGit2Sharp.Tests/IndexFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public void ReadIndexWithBadParamsFails()
8484

8585
[Theory]
8686
[InlineData("1/branch_file.txt", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)]
87+
[InlineData("README", FileStatus.Unaltered, true, FileStatus.Unaltered, true, 0)]
8788
[InlineData("deleted_unstaged_file.txt", FileStatus.Missing, true, FileStatus.Removed, false, -1)]
8889
[InlineData("modified_unstaged_file.txt", FileStatus.Modified, true, FileStatus.Staged, true, 0)]
8990
[InlineData("new_untracked_file.txt", FileStatus.Untracked, false, FileStatus.Added, true, 1)]

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Compile Include="NoteFixture.cs" />
6666
<Compile Include="DiffBlobToBlobFixture.cs" />
6767
<Compile Include="DiffTreeToTargetFixture.cs" />
68+
<Compile Include="DiffWorkdirToIndexFixture.cs" />
6869
<Compile Include="ObjectDatabaseFixture.cs" />
6970
<Compile Include="DiffTreeToTreeFixture.cs" />
7071
<Compile Include="RepositoryOptionsFixture.cs" />

LibGit2Sharp/Diff.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ private static IDictionary<DiffTarget, Func<Repository, TreeComparisonHandleRetr
113113
return new Dictionary<DiffTarget, Func<Repository, TreeComparisonHandleRetriever>>
114114
{
115115
{ DiffTarget.Index, r => IndexToTree(r) },
116+
{ DiffTarget.WorkingDirectory, r => WorkdirToTree(r) },
117+
{ DiffTarget.BothWorkingDirectoryAndIndex, r => WorkdirAndIndexToTree(r) },
116118
};
117119
}
118120

@@ -136,8 +138,76 @@ public virtual TreeChanges Compare(Tree oldTree, DiffTarget diffTarget, IEnumera
136138
}
137139
}
138140

141+
/// <summary>
142+
/// Show changes between the working directory and the index.
143+
/// </summary>
144+
/// <param name = "paths">The list of paths (either files or directories) that should be compared.</param>
145+
/// <returns>A <see cref = "TreeChanges"/> containing the changes between the working directory and the index.</returns>
146+
public virtual TreeChanges Compare(IEnumerable<string> paths = null)
147+
{
148+
var comparer = WorkdirToIndex(repo);
149+
150+
using (GitDiffOptions options = BuildOptions(paths))
151+
using (DiffListSafeHandle dl = BuildDiffListFromComparer(null, comparer, options))
152+
{
153+
return new TreeChanges(dl);
154+
}
155+
}
156+
139157
private delegate DiffListSafeHandle TreeComparisonHandleRetriever(GitObjectSafeHandle treeHandle, GitDiffOptions options);
140158

159+
private static TreeComparisonHandleRetriever WorkdirToIndex(Repository repo)
160+
{
161+
TreeComparisonHandleRetriever comparisonHandleRetriever = (h, o) =>
162+
{
163+
DiffListSafeHandle diff;
164+
Ensure.Success(NativeMethods.git_diff_workdir_to_index(repo.Handle, o, out diff));
165+
return diff;
166+
};
167+
168+
return comparisonHandleRetriever;
169+
}
170+
171+
private static TreeComparisonHandleRetriever WorkdirToTree(Repository repo)
172+
{
173+
TreeComparisonHandleRetriever comparisonHandleRetriever = (h, o) =>
174+
{
175+
DiffListSafeHandle diff;
176+
Ensure.Success(NativeMethods.git_diff_workdir_to_tree(repo.Handle, o, h, out diff));
177+
return diff;
178+
};
179+
180+
return comparisonHandleRetriever;
181+
}
182+
183+
private static TreeComparisonHandleRetriever WorkdirAndIndexToTree(Repository repo)
184+
{
185+
TreeComparisonHandleRetriever comparisonHandleRetriever = (h, o) =>
186+
{
187+
DiffListSafeHandle diff = null, diff2 = null;
188+
189+
try
190+
{
191+
Ensure.Success(NativeMethods.git_diff_index_to_tree(repo.Handle, o, h, out diff));
192+
Ensure.Success(NativeMethods.git_diff_workdir_to_index(repo.Handle, o, out diff2));
193+
Ensure.Success(NativeMethods.git_diff_merge(diff, diff2));
194+
}
195+
catch
196+
{
197+
diff.SafeDispose();
198+
throw;
199+
}
200+
finally
201+
{
202+
diff2.SafeDispose();
203+
}
204+
205+
return diff;
206+
};
207+
208+
return comparisonHandleRetriever;
209+
}
210+
141211
private static TreeComparisonHandleRetriever IndexToTree(Repository repo)
142212
{
143213
TreeComparisonHandleRetriever comparisonHandleRetriever = (h, o) =>

LibGit2Sharp/DiffTarget.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
namespace LibGit2Sharp
22
{
33
/// <summary>
4-
/// The target for a diff comparison.
4+
/// The target of a Tree based diff comparison.
55
/// </summary>
66
public enum DiffTarget
77
{
8+
/// <summary>
9+
/// The working directory.
10+
/// </summary>
11+
WorkingDirectory,
12+
813
/// <summary>
914
/// The repository index.
1015
/// </summary>
1116
Index,
17+
18+
/// <summary>
19+
/// Both the working directory and the repository index.
20+
/// </summary>
21+
BothWorkingDirectoryAndIndex,
1222
}
1323
}

0 commit comments

Comments
 (0)