Skip to content

Commit 3f5bf00

Browse files
yorahnulltoken
authored andcommitted
Add ExplicitPathsOptions to Repository.Diff.Compare()
All the overloads can now report and/or fail upon unmatched explicit paths. By default, the passed list of paths will be treated as a list of pathspecs. When an ExplicitPathsOptions is passed to the overloads, this list of paths will be treated as explicit paths. In that case, the default behavior is to throw when one of this explicit path is unmatched.
1 parent 538fb13 commit 3f5bf00

13 files changed

+510
-39
lines changed

LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex()
283283
Tree tree = repo.Head.Tip.Tree;
284284

285285
TreeChanges changes = repo.Diff.Compare(tree, DiffTargets.Index,
286-
new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" });
286+
new[] { "deleted_staged_file.txt", "1/branch_file.txt" });
287287

288288
Assert.NotNull(changes);
289289

@@ -292,6 +292,42 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex()
292292
}
293293
}
294294

295+
private static void AssertCanCompareASubsetOfTheTreeAgainstTheIndex(TreeChanges changes)
296+
{
297+
Assert.NotNull(changes);
298+
Assert.Equal(1, changes.Count());
299+
Assert.Equal("deleted_staged_file.txt", changes.Deleted.Single().Path);
300+
}
301+
302+
[Fact]
303+
public void CanCompareASubsetofTheTreeAgainstTheIndexWithLaxExplicitPathsValidationAndANonExistentPath()
304+
{
305+
using (var repo = new Repository(StandardTestRepoPath))
306+
{
307+
Tree tree = repo.Head.Tip.Tree;
308+
309+
TreeChanges changes = repo.Diff.Compare(tree, DiffTargets.Index,
310+
new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false });
311+
AssertCanCompareASubsetOfTheTreeAgainstTheIndex(changes);
312+
313+
changes = repo.Diff.Compare(tree, DiffTargets.Index,
314+
new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" });
315+
AssertCanCompareASubsetOfTheTreeAgainstTheIndex(changes);
316+
}
317+
}
318+
319+
[Fact]
320+
public void ComparingASubsetofTheTreeAgainstTheIndexWithStrictExplicitPathsValidationAndANonExistentPathThrows()
321+
{
322+
using (var repo = new Repository(StandardTestRepoPath))
323+
{
324+
Tree tree = repo.Head.Tip.Tree;
325+
326+
Assert.Throws<UnmatchedPathException>(() => repo.Diff.Compare(tree, DiffTargets.Index,
327+
new[] { "deleted_staged_file.txt", "1/branch_file.txt", "I-do/not-exist" }, new ExplicitPathsOptions()));
328+
}
329+
}
330+
295331
[Fact]
296332
/*
297333
* $ git init .

LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public void CanCompareASubsetofTheTreeAgainstOneOfItsAncestor()
8787
Tree tree = repo.Head.Tip.Tree;
8888
Tree ancestor = repo.Lookup<Commit>("9fd738e").Tree;
8989

90-
TreeChanges changes = repo.Diff.Compare(ancestor, tree, new[]{ "1", "2/" });
90+
TreeChanges changes = repo.Diff.Compare(ancestor, tree, new[]{ "1" });
9191
Assert.NotNull(changes);
9292

9393
Assert.Equal(1, changes.Count());
@@ -133,6 +133,38 @@ public void CanCompareACommitTreeAgainstATreeWithNoCommonAncestor()
133133
}
134134
}
135135

136+
[Fact]
137+
public void CanCompareATreeAgainstAnotherTreeWithLaxExplicitPathsValidationAndNonExistentPath()
138+
{
139+
using (var repo = new Repository(StandardTestRepoPath))
140+
{
141+
Tree commitTree = repo.Head.Tip.Tree;
142+
Tree commitTreeWithDifferentAncestor = repo.Branches["refs/remotes/origin/test"].Tip.Tree;
143+
144+
TreeChanges changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree,
145+
new[] { "if-I-exist-this-test-is-really-unlucky.txt" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false });
146+
Assert.Equal(0, changes.Count());
147+
148+
changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree,
149+
new[] { "if-I-exist-this-test-is-really-unlucky.txt" });
150+
Assert.Equal(0, changes.Count());
151+
}
152+
}
153+
154+
[Fact]
155+
public void ComparingATreeAgainstAnotherTreeWithStrictExplicitPathsValidationThrows()
156+
{
157+
using (var repo = new Repository(StandardTestRepoPath))
158+
{
159+
Tree commitTree = repo.Head.Tip.Tree;
160+
Tree commitTreeWithDifferentAncestor = repo.Branches["refs/remotes/origin/test"].Tip.Tree;
161+
162+
Assert.Throws<UnmatchedPathException>(() =>
163+
repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree,
164+
new[] { "if-I-exist-this-test-is-really-unlucky.txt" }, new ExplicitPathsOptions()));
165+
}
166+
}
167+
136168
/*
137169
* $ git diff -M f8d44d7..4be51d6
138170
* diff --git a/my-name-does-not-feel-right.txt b/super-file.txt

LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
using System;
2+
using System.IO;
13
using System.Linq;
4+
using System.Text;
25
using LibGit2Sharp.Tests.TestHelpers;
36
using Xunit;
7+
using Xunit.Extensions;
48

59
namespace LibGit2Sharp.Tests
610
{
@@ -36,6 +40,129 @@ public void CanCompareTheWorkDirAgainstTheIndex()
3640
}
3741
}
3842

43+
[Theory]
44+
[InlineData("new_untracked_file.txt", FileStatus.Untracked)]
45+
[InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)]
46+
public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsValidation(string relativePath, FileStatus currentStatus)
47+
{
48+
using (var repo = new Repository(StandardTestRepoPath))
49+
{
50+
Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath));
51+
52+
TreeChanges changes = repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false });
53+
Assert.Equal(0, changes.Count());
54+
55+
changes = repo.Diff.Compare(new[] { relativePath });
56+
Assert.Equal(0, changes.Count());
57+
}
58+
}
59+
60+
[Theory]
61+
[InlineData("new_untracked_file.txt", FileStatus.Untracked)]
62+
[InlineData("really-i-cant-exist.txt", FileStatus.Nonexistent)]
63+
public void ComparingTheWorkDirAgainstTheIndexWithStrictUnmatchedExplicitPathsValidationAndANonExistentPathspecThrows(string relativePath, FileStatus currentStatus)
64+
{
65+
using (var repo = new Repository(StandardTestRepoPath))
66+
{
67+
Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath));
68+
69+
Assert.Throws<UnmatchedPathException>(() => repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions()));
70+
}
71+
}
72+
73+
[Theory]
74+
[InlineData("new_untracked_file.txt", FileStatus.Untracked)]
75+
[InlineData("where-am-I.txt", FileStatus.Nonexistent)]
76+
public void CallbackForUnmatchedExplicitPathsIsCalledWhenSet(string relativePath, FileStatus currentStatus)
77+
{
78+
var callback = new AssertUnmatchedPathspecsCallbackIsCalled();
79+
80+
using (var repo = new Repository(StandardTestRepoPath))
81+
{
82+
Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath));
83+
84+
repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false,
85+
OnUnmatchedPath = callback.OnUnmatchedPath });
86+
87+
Assert.True(callback.WasCalled);
88+
}
89+
}
90+
91+
private class AssertUnmatchedPathspecsCallbackIsCalled
92+
{
93+
public bool WasCalled;
94+
95+
public void OnUnmatchedPath(string unmatchedpath)
96+
{
97+
WasCalled = true;
98+
}
99+
}
100+
101+
[Fact]
102+
public void ComparingReliesOnProvidedConfigEntriesIfAny()
103+
{
104+
const string file = "1/branch_file.txt";
105+
106+
string path = CloneStandardTestRepo();
107+
using (var repo = new Repository(path))
108+
{
109+
TreeEntry entry = repo.Head[file];
110+
Assert.Equal(Mode.ExecutableFile, entry.Mode);
111+
112+
// Recreate the file in the workdir without the executable bit
113+
string fullpath = Path.Combine(repo.Info.WorkingDirectory, file);
114+
File.Delete(fullpath);
115+
File.WriteAllBytes(fullpath, ((Blob)(entry.Target)).Content);
116+
117+
// Unset the local core.filemode, if any.
118+
repo.Config.Unset("core.filemode", ConfigurationLevel.Local);
119+
}
120+
121+
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
122+
123+
var options = BuildFakeSystemConfigFilemodeOption(scd, true);
124+
125+
using (var repo = new Repository(path, options))
126+
{
127+
TreeChanges changes = repo.Diff.Compare(new[] { file });
128+
129+
Assert.Equal(1, changes.Count());
130+
131+
var change = changes.Modified.Single();
132+
Assert.Equal(Mode.ExecutableFile, change.OldMode);
133+
Assert.Equal(Mode.NonExecutableFile, change.Mode);
134+
}
135+
136+
options = BuildFakeSystemConfigFilemodeOption(scd, false);
137+
138+
using (var repo = new Repository(path, options))
139+
{
140+
TreeChanges changes = repo.Diff.Compare(new[] { file });
141+
142+
Assert.Equal(0, changes.Count());
143+
}
144+
}
145+
146+
private RepositoryOptions BuildFakeSystemConfigFilemodeOption(
147+
SelfCleaningDirectory scd,
148+
bool value)
149+
{
150+
Directory.CreateDirectory(scd.DirectoryPath);
151+
152+
var options = new RepositoryOptions
153+
{
154+
SystemConfigurationLocation = Path.Combine(
155+
scd.RootedDirectoryPath, "fake-system.config")
156+
};
157+
158+
StringBuilder sb = new StringBuilder()
159+
.AppendFormat("[core]{0}", Environment.NewLine)
160+
.AppendFormat("filemode = {1}{0}", Environment.NewLine, value);
161+
File.WriteAllText(options.SystemConfigurationLocation, sb.ToString());
162+
163+
return options;
164+
}
165+
39166
[Fact]
40167
public void CanCompareTheWorkDirAgainstTheIndexWithUntrackedFiles()
41168
{

LibGit2Sharp.Tests/MetaFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class MetaFixture
1818
typeof(Repository),
1919
typeof(RepositoryOptions),
2020
typeof(Signature),
21+
typeof(ExplicitPathsOptions),
2122
};
2223

2324
// Related to https://github.com/libgit2/libgit2sharp/pull/251

LibGit2Sharp/Core/GitDiff.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ public void Dispose()
159159
}
160160
}
161161

162+
internal delegate int diff_notify_cb(
163+
IntPtr diff_so_far,
164+
IntPtr delta_to_add,
165+
IntPtr matched_pathspec,
166+
IntPtr payload);
167+
162168
[StructLayout(LayoutKind.Sequential)]
163169
internal class GitDiffOptions : IDisposable
164170
{
@@ -174,7 +180,7 @@ internal class GitDiffOptions : IDisposable
174180
public GitStrArrayIn PathSpec;
175181
public Int64 MaxSize;
176182

177-
public IntPtr NotifyCallback;
183+
public diff_notify_cb NotifyCallback;
178184
public IntPtr NotifyPayload;
179185

180186
public void Dispose()

0 commit comments

Comments
 (0)