Skip to content

Commit c5aa469

Browse files
committed
Decorate Diff.Compare generics with interface
This allows for a typesafe Diff.Compare model. This commit introduces a new IDiffResult interface which marks classes which can be passed to Diff.Compare<T> so we get rid of the runtime exception.
1 parent 16e2dcc commit c5aa469

File tree

8 files changed

+108
-127
lines changed

8 files changed

+108
-127
lines changed

LibGit2Sharp.Tests/MetaFixture.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class MetaFixture
1717
{
1818
private static readonly HashSet<Type> explicitOnlyInterfaces = new HashSet<Type>
1919
{
20-
typeof(IBelongToARepository),
20+
typeof(IBelongToARepository), typeof(IDiffResult),
2121
};
2222

2323
[Fact]
@@ -401,6 +401,20 @@ where method.IsDefined(typeof(ExtensionAttribute), false)
401401
select method;
402402
return query;
403403
}
404+
405+
[Fact]
406+
public void AllIDiffResultsAreInChangesBuilder()
407+
{
408+
var diff = typeof(Diff).GetField("ChangesBuilders", BindingFlags.NonPublic | BindingFlags.Static);
409+
var changesBuilders = (System.Collections.IDictionary)diff.GetValue(null);
410+
411+
IEnumerable<Type> diffResults = typeof(Diff).Assembly.GetExportedTypes()
412+
.Where(type => type.GetInterface("IDiffResult") != null);
413+
414+
var nonBuilderTypes = diffResults.Where(diffResult => !changesBuilders.Contains(diffResult));
415+
Assert.False(nonBuilderTypes.Any(), "Classes which implement IDiffResult but are not registered under ChangesBuilders in Diff:" + Environment.NewLine +
416+
string.Join(Environment.NewLine, nonBuilderTypes.Select(type => type.FullName)));
417+
}
404418
}
405419

406420
internal static class TypeExtensions

LibGit2Sharp/Diff.cs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,28 @@ private static IDictionary<DiffTargets, Func<Repository, TreeComparisonHandleRet
9595
};
9696
}
9797

98+
private static readonly IDictionary<Type, Func<DiffSafeHandle, object>> ChangesBuilders = new Dictionary<Type, Func<DiffSafeHandle, object>>
99+
{
100+
{ typeof(Patch), diff => new Patch(diff) },
101+
{ typeof(TreeChanges), diff => new TreeChanges(diff) },
102+
{ typeof(PatchStats), diff => new PatchStats(diff) },
103+
};
104+
105+
106+
private static T BuildDiffResult<T>(DiffSafeHandle diff) where T : class, IDiffResult
107+
{
108+
Func<DiffSafeHandle, object> builder;
109+
110+
if (!ChangesBuilders.TryGetValue(typeof(T), out builder))
111+
{
112+
throw new LibGit2SharpException(CultureInfo.InvariantCulture,
113+
"User-defined types passed to Compare are not supported. Supported values are: {0}",
114+
string.Join(", ", ChangesBuilders.Keys.Select(x => x.Name)));
115+
}
116+
117+
return (T)builder(diff);
118+
}
119+
98120
/// <summary>
99121
/// Show changes between two <see cref="Blob"/>s.
100122
/// </summary>
@@ -127,7 +149,7 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions
127149
/// <param name="oldTree">The <see cref="Tree"/> you want to compare from.</param>
128150
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
129151
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
130-
public virtual T Compare<T>(Tree oldTree, Tree newTree) where T : class, IDiffResult<T>, new()
152+
public virtual T Compare<T>(Tree oldTree, Tree newTree) where T : class, IDiffResult
131153
{
132154
return Compare<T>(oldTree, newTree, null, null, null);
133155
}
@@ -139,7 +161,7 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions
139161
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
140162
/// <param name="paths">The list of paths (either files or directories) that should be compared.</param>
141163
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
142-
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths) where T : class, IDiffResult<T>, new()
164+
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths) where T : class, IDiffResult
143165
{
144166
return Compare<T>(oldTree, newTree, paths, null, null);
145167
}
@@ -156,7 +178,7 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions
156178
/// </param>
157179
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
158180
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths,
159-
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult<T>, new()
181+
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
160182
{
161183
return Compare<T>(oldTree, newTree, paths, explicitPathsOptions, null);
162184
}
@@ -169,7 +191,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
169191
/// <param name="paths">The list of paths (either files or directories) that should be compared.</param>
170192
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
171193
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
172-
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, CompareOptions compareOptions) where T : class, IDiffResult<T>, new()
194+
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, CompareOptions compareOptions) where T : class, IDiffResult
173195
{
174196
return Compare<T>(oldTree, newTree, paths, null, compareOptions);
175197
}
@@ -181,7 +203,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
181203
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
182204
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
183205
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
184-
public virtual T Compare<T>(Tree oldTree, Tree newTree, CompareOptions compareOptions) where T : class, IDiffResult<T>, new()
206+
public virtual T Compare<T>(Tree oldTree, Tree newTree, CompareOptions compareOptions) where T : class, IDiffResult
185207
{
186208
return Compare<T>(oldTree, newTree, null, null, compareOptions);
187209
}
@@ -199,9 +221,8 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
199221
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
200222
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
201223
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, ExplicitPathsOptions explicitPathsOptions,
202-
CompareOptions compareOptions) where T : class, IDiffResult<T>, new()
224+
CompareOptions compareOptions) where T : class, IDiffResult
203225
{
204-
205226
var comparer = TreeToTree(repo);
206227
ObjectId oldTreeId = oldTree != null ? oldTree.Id : null;
207228
ObjectId newTreeId = newTree != null ? newTree.Id : null;
@@ -219,10 +240,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
219240

220241
using (DiffSafeHandle diff = BuildDiffList(oldTreeId, newTreeId, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
221242
{
222-
using (var proxy = new DiffSafeHandleProxy(diff))
223-
{
224-
return new T().FromNative(proxy);
225-
}
243+
return BuildDiffResult<T>(diff);
226244
}
227245
}
228246

@@ -238,7 +256,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
238256
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
239257
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
240258
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
241-
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets) where T : class, IDiffResult<T>, new()
259+
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets) where T : class, IDiffResult
242260
{
243261
return Compare<T>(oldTree, diffTargets, null, null, null);
244262
}
@@ -256,7 +274,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
256274
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
257275
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
258276
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
259-
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths) where T : class, IDiffResult<T>, new()
277+
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths) where T : class, IDiffResult
260278
{
261279
return Compare<T>(oldTree, diffTargets, paths, null, null);
262280
}
@@ -279,7 +297,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
279297
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
280298
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
281299
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths,
282-
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult<T>, new()
300+
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
283301
{
284302
return Compare<T>(oldTree, diffTargets, paths, explicitPathsOptions, null);
285303
}
@@ -303,7 +321,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
303321
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
304322
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
305323
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths,
306-
ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) where T : class, IDiffResult<T>, new()
324+
ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) where T : class, IDiffResult
307325
{
308326
var comparer = HandleRetrieverDispatcher[diffTargets](repo);
309327
ObjectId oldTreeId = oldTree != null ? oldTree.Id : null;
@@ -324,10 +342,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
324342

325343
using (DiffSafeHandle diff = BuildDiffList(oldTreeId, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
326344
{
327-
using (var proxy = new DiffSafeHandleProxy(diff))
328-
{
329-
return new T().FromNative(proxy);
330-
}
345+
return BuildDiffResult<T>(diff);
331346
}
332347
}
333348

@@ -341,7 +356,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
341356
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
342357
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
343358
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
344-
public virtual T Compare<T>() where T : class, IDiffResult<T>, new()
359+
public virtual T Compare<T>() where T : class, IDiffResult
345360
{
346361
return Compare<T>(DiffModifiers.None);
347362
}
@@ -357,7 +372,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
357372
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
358373
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
359374
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
360-
public virtual T Compare<T>(IEnumerable<string> paths) where T : class, IDiffResult<T>, new()
375+
public virtual T Compare<T>(IEnumerable<string> paths) where T : class, IDiffResult
361376
{
362377
return Compare<T>(DiffModifiers.None, paths);
363378
}
@@ -374,7 +389,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
374389
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
375390
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
376391
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
377-
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked) where T : class, IDiffResult<T>, new()
392+
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked) where T : class, IDiffResult
378393
{
379394
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths);
380395
}
@@ -395,7 +410,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
395410
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
396411
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
397412
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
398-
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked, ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult<T>, new()
413+
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked, ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
399414
{
400415
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions);
401416
}
@@ -421,7 +436,7 @@ public virtual T Compare<T>(
421436
IEnumerable<string> paths,
422437
bool includeUntracked,
423438
ExplicitPathsOptions explicitPathsOptions,
424-
CompareOptions compareOptions) where T : class, IDiffResult<T>, new()
439+
CompareOptions compareOptions) where T : class, IDiffResult
425440
{
426441
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions, compareOptions);
427442
}
@@ -430,7 +445,7 @@ internal virtual T Compare<T>(
430445
DiffModifiers diffOptions,
431446
IEnumerable<string> paths = null,
432447
ExplicitPathsOptions explicitPathsOptions = null,
433-
CompareOptions compareOptions = null) where T : class, IDiffResult<T>, new()
448+
CompareOptions compareOptions = null) where T : class, IDiffResult
434449
{
435450
var comparer = WorkdirToIndex(repo);
436451

@@ -446,10 +461,7 @@ internal virtual T Compare<T>(
446461

447462
using (DiffSafeHandle diff = BuildDiffList(null, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
448463
{
449-
using (var proxy = new DiffSafeHandleProxy(diff))
450-
{
451-
return new T().FromNative(proxy);
452-
}
464+
return BuildDiffResult<T>(diff);
453465
}
454466
}
455467

LibGit2Sharp/DiffSafeHandleProxy.cs

Lines changed: 0 additions & 25 deletions
This file was deleted.

LibGit2Sharp/IDiffResult.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
using System;
2-
3-
namespace LibGit2Sharp
1+
namespace LibGit2Sharp
42
{
5-
public interface IDiffResult<T> where T:class
6-
{
7-
T FromNative(DiffSafeHandleProxy diff);
8-
}
3+
public interface IDiffResult
4+
{ }
95
}
10-

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@
381381
<Compile Include="Core\GitCertificateSshType.cs" />
382382
<Compile Include="CertificateSsh.cs" />
383383
<Compile Include="IDiffResult.cs" />
384-
<Compile Include="DiffSafeHandleProxy.cs" />
385384
</ItemGroup>
386385
<ItemGroup>
387386
<CodeAnalysisDictionary Include="CustomDictionary.xml" />

LibGit2Sharp/Patch.cs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace LibGit2Sharp
1616
/// deleted, modified, ..., then consider using a simpler <see cref="TreeChanges"/>.</para>
1717
/// </summary>
1818
[DebuggerDisplay("{DebuggerDisplay,nq}")]
19-
public class Patch : IEnumerable<PatchEntryChanges>, IDiffResult<Patch>
19+
public class Patch : IEnumerable<PatchEntryChanges>, IDiffResult
2020
{
2121
private readonly StringBuilder fullPatchBuilder = new StringBuilder();
2222

@@ -27,9 +27,23 @@ public class Patch : IEnumerable<PatchEntryChanges>, IDiffResult<Patch>
2727
/// <summary>
2828
/// Needed for mocking purposes.
2929
/// </summary>
30-
public Patch()
30+
protected Patch()
3131
{ }
3232

33+
internal Patch(DiffSafeHandle diff)
34+
{
35+
int count = Proxy.git_diff_num_deltas(diff);
36+
for (int i = 0; i < count; i++)
37+
{
38+
using (var patch = Proxy.git_patch_from_diff(diff, i))
39+
{
40+
var delta = Proxy.git_diff_get_delta(diff, i);
41+
AddFileChange(delta);
42+
Proxy.git_patch_print(patch, PrintCallBack);
43+
}
44+
}
45+
}
46+
3347
private void AddFileChange(GitDiffDelta delta)
3448
{
3549
var treeEntryChanges = new TreeEntryChanges(delta);
@@ -100,25 +114,6 @@ IEnumerator IEnumerable.GetEnumerator()
100114

101115
#endregion
102116

103-
#region IDiffResult implementation
104-
105-
Patch IDiffResult<Patch>.FromNative(DiffSafeHandleProxy diff)
106-
{
107-
int count = Proxy.git_diff_num_deltas(diff.nativeHandle);
108-
for (int i = 0; i < count; i++)
109-
{
110-
using (var patch = Proxy.git_patch_from_diff(diff.nativeHandle, i))
111-
{
112-
var delta = Proxy.git_diff_get_delta(diff.nativeHandle, i);
113-
AddFileChange(delta);
114-
Proxy.git_patch_print(patch, PrintCallBack);
115-
}
116-
}
117-
return this;
118-
}
119-
120-
#endregion
121-
122117
/// <summary>
123118
/// Gets the <see cref="ContentChanges"/> corresponding to the specified <paramref name="path"/>.
124119
/// </summary>

0 commit comments

Comments
 (0)