Skip to content

Commit 665dd77

Browse files
committed
Add ability to diff two arbitrary buffers
1 parent 50d6978 commit 665dd77

File tree

5 files changed

+412
-0
lines changed

5 files changed

+412
-0
lines changed
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Text;
4+
using LibGit2Sharp.Tests.TestHelpers;
5+
using Xunit;
6+
7+
namespace LibGit2Sharp.Tests
8+
{
9+
public class DiffBufferToBufferFixture
10+
{
11+
12+
[Fact]
13+
public void ComparingTheSameBufferReturnsNoDifference()
14+
{
15+
const string text = @"
16+
1
17+
2
18+
3
19+
4
20+
5
21+
6";
22+
23+
var buffer = Encoding.UTF8.GetBytes(text);
24+
25+
var changes = Diff.CompareBuffers(buffer, buffer);
26+
27+
Assert.Equal(0, changes.LinesAdded);
28+
Assert.Equal(0, changes.LinesDeleted);
29+
Assert.Equal(string.Empty, changes.Patch);
30+
}
31+
32+
[Fact]
33+
public void CanCompareTwoBuffersWithADiffOfTwoHunks()
34+
{
35+
const string oldText = @"1
36+
2
37+
4
38+
5
39+
6
40+
7
41+
8";
42+
43+
const string newText = @"1
44+
2
45+
3
46+
4
47+
5
48+
7
49+
6
50+
7
51+
8
52+
9";
53+
54+
var oldBuffer = Encoding.UTF8.GetBytes(oldText);
55+
var newBuffer = Encoding.UTF8.GetBytes(newText);
56+
57+
var changes = Diff.CompareBuffers(oldBuffer, newBuffer);
58+
59+
Assert.False(changes.IsBinaryComparison);
60+
61+
Assert.Equal(3, changes.LinesAdded);
62+
Assert.Equal(1, changes.LinesDeleted);
63+
64+
var expected = new StringBuilder()
65+
.Append("@@ -1,4 +1,5 @@\n")
66+
.Append(" 1\n")
67+
.Append("+2\n")
68+
.Append(" 3\n")
69+
.Append(" 4\n")
70+
.Append(" 5\n")
71+
.Append("@@ -8,8 +9,9 @@\n")
72+
.Append(" 8\n")
73+
.Append(" 9\n")
74+
.Append(" 10\n")
75+
.Append("-12\n")
76+
.Append("+11\n")
77+
.Append(" 12\n")
78+
.Append(" 13\n")
79+
.Append(" 14\n")
80+
.Append(" 15\n")
81+
.Append("+16\n");
82+
83+
Assert.Equal(expected.ToString(), changes.Patch);
84+
}
85+
// [Fact]
86+
// public void ComparingABlobAgainstItselfReturnsNoDifference()
87+
// {
88+
// var path = SandboxStandardTestRepoGitDir();
89+
// using (var repo = new Repository(path))
90+
// {
91+
// var blob = repo.Lookup<Blob>("7909961");
92+
//
93+
// ContentChanges changes = repo.Diff.Compare(blob, blob);
94+
//
95+
// Assert.Equal(0, changes.LinesAdded);
96+
// Assert.Equal(0, changes.LinesDeleted);
97+
// Assert.Equal(string.Empty, changes.Patch);
98+
// }
99+
// }
100+
//
101+
// [Fact]
102+
// public void CanCompareTwoVersionsOfABlobWithADiffOfTwoHunks()
103+
// {
104+
// var path = SandboxStandardTestRepoGitDir();
105+
// using (var repo = new Repository(path))
106+
// {
107+
// var oldblob = repo.Lookup<Blob>("7909961");
108+
// var newblob = repo.Lookup<Blob>("4e935b7");
109+
//
110+
// ContentChanges changes = repo.Diff.Compare(oldblob, newblob);
111+
//
112+
// Assert.False(changes.IsBinaryComparison);
113+
//
114+
// Assert.Equal(3, changes.LinesAdded);
115+
// Assert.Equal(1, changes.LinesDeleted);
116+
//
117+
// var expected = new StringBuilder()
118+
// .Append("@@ -1,4 +1,5 @@\n")
119+
// .Append(" 1\n")
120+
// .Append("+2\n")
121+
// .Append(" 3\n")
122+
// .Append(" 4\n")
123+
// .Append(" 5\n")
124+
// .Append("@@ -8,8 +9,9 @@\n")
125+
// .Append(" 8\n")
126+
// .Append(" 9\n")
127+
// .Append(" 10\n")
128+
// .Append("-12\n")
129+
// .Append("+11\n")
130+
// .Append(" 12\n")
131+
// .Append(" 13\n")
132+
// .Append(" 14\n")
133+
// .Append(" 15\n")
134+
// .Append("+16\n");
135+
//
136+
// Assert.Equal(expected.ToString(), changes.Patch);
137+
// }
138+
// }
139+
//
140+
// Blob CreateBinaryBlob(IRepository repo)
141+
// {
142+
// string fullpath = Path.Combine(repo.Info.WorkingDirectory, "binary.bin");
143+
//
144+
// File.WriteAllBytes(fullpath, new byte[] { 17, 16, 0, 4, 65 });
145+
//
146+
// return repo.ObjectDatabase.CreateBlob(fullpath);
147+
// }
148+
//
149+
// [Fact]
150+
// public void CanCompareATextualBlobAgainstABinaryBlob()
151+
// {
152+
// string path = SandboxStandardTestRepo();
153+
// using (var repo = new Repository(path))
154+
// {
155+
// Blob binBlob = CreateBinaryBlob(repo);
156+
//
157+
// var blob = repo.Lookup<Blob>("7909961");
158+
//
159+
// ContentChanges changes = repo.Diff.Compare(blob, binBlob);
160+
//
161+
// Assert.True(changes.IsBinaryComparison);
162+
//
163+
// Assert.Equal(0, changes.LinesAdded);
164+
// Assert.Equal(0, changes.LinesDeleted);
165+
// }
166+
// }
167+
//
168+
// [Fact]
169+
// public void CanCompareABlobAgainstANullBlob()
170+
// {
171+
// var path = SandboxStandardTestRepoGitDir();
172+
// using (var repo = new Repository(path))
173+
// {
174+
// var blob = repo.Lookup<Blob>("7909961");
175+
//
176+
// ContentChanges changes = repo.Diff.Compare(null, blob);
177+
//
178+
// Assert.NotEqual(0, changes.LinesAdded);
179+
// Assert.Equal(0, changes.LinesDeleted);
180+
// Assert.NotEqual(string.Empty, changes.Patch);
181+
//
182+
// changes = repo.Diff.Compare(blob, null);
183+
//
184+
// Assert.Equal(0, changes.LinesAdded);
185+
// Assert.NotEqual(0, changes.LinesDeleted);
186+
// Assert.NotEqual(string.Empty, changes.Patch);
187+
// }
188+
// }
189+
//
190+
// [Fact]
191+
// public void ComparingTwoNullBlobsReturnsAnEmptyContentChanges()
192+
// {
193+
// var path = SandboxStandardTestRepoGitDir();
194+
// using (var repo = new Repository(path))
195+
// {
196+
// ContentChanges changes = repo.Diff.Compare((Blob)null, (Blob)null);
197+
//
198+
// Assert.False(changes.IsBinaryComparison);
199+
//
200+
// Assert.Equal(0, changes.LinesAdded);
201+
// Assert.Equal(0, changes.LinesDeleted);
202+
// }
203+
// }
204+
//
205+
// [Fact]
206+
// public void ComparingBlobsWithNoSpacesAndIndentHeuristicOptionMakesADifference()
207+
// {
208+
// var path = SandboxStandardTestRepoGitDir();
209+
// using (var repo = new Repository(path))
210+
// {
211+
// // Based on test diff indent heuristic from:
212+
// // https://github.com/git/git/blob/433860f3d0beb0c6f205290bd16cda413148f098/t/t4061-diff-indent.sh#L17
213+
// var oldContent =
214+
// @" 1
215+
// 2
216+
// a
217+
//
218+
// b
219+
// 3
220+
// 4";
221+
// var newContent =
222+
// @" 1
223+
// 2
224+
// a
225+
//
226+
// b
227+
// a
228+
//
229+
// b
230+
// 3
231+
// 4";
232+
// var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent)));
233+
// var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent)));
234+
// var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false };
235+
// var indentHeuristicOption = new CompareOptions { IndentHeuristic = true };
236+
//
237+
// ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption);
238+
// ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption);
239+
//
240+
// Assert.NotEqual(changes0.Patch, changes1.Patch);
241+
// Assert.Equal(CanonicalChangedLines(changes0), CanonicalChangedLines(changes1));
242+
// }
243+
// }
244+
//
245+
// [Fact]
246+
// public void ComparingBlobsWithNoSpacesIndentHeuristicOptionMakesNoDifference()
247+
// {
248+
// var path = SandboxStandardTestRepoGitDir();
249+
// using (var repo = new Repository(path))
250+
// {
251+
// var oldContent =
252+
// @" 1
253+
// 2
254+
// a
255+
// b
256+
// 3
257+
// 4";
258+
// var newContent =
259+
// @" 1
260+
// 2
261+
// a
262+
// b
263+
// a
264+
// b
265+
// 3
266+
// 4";
267+
// var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent)));
268+
// var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent)));
269+
// var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false };
270+
// var indentHeuristicOption = new CompareOptions { IndentHeuristic = true };
271+
//
272+
// ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption);
273+
// ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption);
274+
//
275+
// Assert.Equal(changes0.Patch, changes1.Patch);
276+
// }
277+
// }
278+
//
279+
// [Fact]
280+
// public void DiffSetsTheAddedAndDeletedLinesCorrectly()
281+
// {
282+
// var path = SandboxStandardTestRepoGitDir();
283+
//
284+
// using (var repo = new Repository(path))
285+
// {
286+
// var oldContent =
287+
// @"1
288+
// 2
289+
// 3
290+
// 4";
291+
//
292+
// var newContent =
293+
// @"1
294+
// 2
295+
// 3
296+
// 5";
297+
// var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent)));
298+
// var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent)));
299+
//
300+
// ContentChanges changes = repo.Diff.Compare(oldBlob, newBlob);
301+
//
302+
// Assert.Single(changes.AddedLines);
303+
// Assert.Single(changes.DeletedLines);
304+
//
305+
// Assert.Equal("4", changes.DeletedLines.First().Content);
306+
// Assert.Equal("5", changes.AddedLines.First().Content);
307+
//
308+
// Assert.Equal(4, changes.DeletedLines.First().LineNumber);
309+
// Assert.Equal(4, changes.AddedLines.First().LineNumber);
310+
// }
311+
// }
312+
//
313+
// static string CanonicalChangedLines(ContentChanges changes)
314+
// {
315+
// // Create an ordered representation of lines that have been added or removed
316+
// return string.Join("\n", changes.Patch.Split('\n').Where(l => l.StartsWith("+") || l.StartsWith("-")).OrderBy(l => l));
317+
// }
318+
}
319+
}

LibGit2Sharp/ContentChanges.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ internal unsafe ContentChanges(Repository repo, Blob oldBlob, Blob newBlob, GitD
3232
LineCallback);
3333
}
3434

35+
internal unsafe ContentChanges(byte[] oldBuffer, byte[] newBuffer, GitDiffOptions options)
36+
{
37+
Proxy.git_diff_buffers(oldBuffer,
38+
newBuffer,
39+
options,
40+
FileCallback,
41+
HunkCallback,
42+
LineCallback);
43+
}
44+
3545
internal ContentChanges(bool isBinaryComparison)
3646
{
3747
this.IsBinaryComparison = isBinaryComparison;

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,23 @@ internal static extern unsafe int git_diff_find_similar(
712712
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
713713
internal static extern unsafe git_diff_delta* git_diff_get_delta(git_diff* diff, UIntPtr idx);
714714

715+
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
716+
internal static extern unsafe int git_diff_buffers(
717+
IntPtr oldBuffer,
718+
UIntPtr oldBufferLength,
719+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]
720+
string oldAsPath,
721+
IntPtr newBuffer,
722+
UIntPtr newBufferLength,
723+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]
724+
string newAsPath,
725+
GitDiffOptions options,
726+
git_diff_file_cb fileCallback,
727+
git_diff_binary_cb binaryCallback,
728+
git_diff_hunk_cb hunkCallback,
729+
git_diff_line_cb lineCallback,
730+
IntPtr payload);
731+
715732
[DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
716733
internal static extern int git_filter_register(
717734
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name,

LibGit2Sharp/Core/Proxy.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,38 @@ public static unsafe int git_diff_num_deltas(DiffHandle diff)
854854
return NativeMethods.git_diff_get_delta(diff, (UIntPtr)idx);
855855
}
856856

857+
public static unsafe void git_diff_buffers(
858+
byte[] oldBuffer,
859+
byte[] newBuffer,
860+
GitDiffOptions options,
861+
NativeMethods.git_diff_file_cb fileCallback,
862+
NativeMethods.git_diff_hunk_cb hunkCallback,
863+
NativeMethods.git_diff_line_cb lineCallback)
864+
{
865+
int res;
866+
fixed(byte* oldP = oldBuffer)
867+
{
868+
fixed(byte* newP = newBuffer)
869+
{
870+
res = NativeMethods.git_diff_buffers(
871+
(IntPtr)oldP,
872+
new UIntPtr((ulong)oldBuffer.LongLength),
873+
null,
874+
(IntPtr)newP,
875+
new UIntPtr((ulong)newBuffer.LongLength),
876+
null,
877+
options,
878+
fileCallback,
879+
null,
880+
hunkCallback,
881+
lineCallback,
882+
IntPtr.Zero);
883+
}
884+
}
885+
886+
Ensure.ZeroResult(res);
887+
}
888+
857889
#endregion
858890

859891
#region git_error_

0 commit comments

Comments
 (0)