Skip to content

Commit 4ac3557

Browse files
author
J Wyman
committed
Completed filter support via streaming filter API
Added complete callback to facilitate long running or transforming filters like Git-LFS. Added repository's working directory to callbacks to support extern process like Git-LFS, where knowledge of a repository's root maybe necissary but unobtainable in another fasion. Removed unused NativeMethods
1 parent eb442fc commit 4ac3557

15 files changed

+408
-133
lines changed

LibGit2Sharp.Tests/FilterFixture.cs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ namespace LibGit2Sharp.Tests
88
{
99
public class FilterFixture : BaseFixture
1010
{
11-
private const int GitPassThrough = -30;
12-
13-
readonly Func<Stream, Stream, int> successCallback = (reader, writer) => 0;
11+
readonly Action<Stream, Stream> successCallback = (reader, writer) =>
12+
{
13+
reader.CopyTo(writer);
14+
};
1415

1516
private const string FilterName = "the-filter";
1617
readonly List<FilterAttributeEntry> attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("test") };
@@ -19,7 +20,7 @@ public class FilterFixture : BaseFixture
1920
public void CanRegisterFilterWithSingleAttribute()
2021
{
2122
var filter = new EmptyFilter(FilterName, attributes);
22-
Assert.Equal( attributes , filter.Attributes);
23+
Assert.Equal(attributes, filter.Attributes);
2324
}
2425

2526
[Fact]
@@ -56,10 +57,9 @@ public void SameFilterIsEqual()
5657
public void InitCallbackNotMadeWhenFilterNeverUsed()
5758
{
5859
bool called = false;
59-
Func<int> initializeCallback = () =>
60+
Action initializeCallback = () =>
6061
{
6162
called = true;
62-
return 0;
6363
};
6464

6565
var filter = new FakeFilter(FilterName + 11, attributes,
@@ -78,10 +78,9 @@ public void InitCallbackNotMadeWhenFilterNeverUsed()
7878
public void InitCallbackMadeWhenUsingTheFilter()
7979
{
8080
bool called = false;
81-
Func<int> initializeCallback = () =>
81+
Action initializeCallback = () =>
8282
{
8383
called = true;
84-
return 0;
8584
};
8685

8786
var filter = new FakeFilter(FilterName + 12, attributes,
@@ -108,10 +107,10 @@ public void WhenStagingFileApplyIsCalledWithCleanForCorrectPath()
108107
string repoPath = InitNewRepository();
109108
bool called = false;
110109

111-
Func<Stream, Stream, int> clean = (reader, writer) =>
110+
Action<Stream, Stream> clean = (reader, writer) =>
112111
{
113112
called = true;
114-
return GitPassThrough;
113+
reader.CopyTo(writer);
115114
};
116115
var filter = new FakeFilter(FilterName + 15, attributes, clean);
117116

@@ -134,7 +133,7 @@ public void CleanFilterWritesOutputToObjectTree()
134133

135134
string repoPath = InitNewRepository();
136135

137-
Func<Stream, Stream, int> cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
136+
Action<Stream, Stream> cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
138137

139138
var filter = new FakeFilter(FilterName + 16, attributes, cleanCallback);
140139

@@ -154,7 +153,6 @@ public void CleanFilterWritesOutputToObjectTree()
154153
GlobalSettings.DeregisterFilter(filterRegistration);
155154
}
156155

157-
158156
[Fact]
159157
public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory()
160158
{
@@ -164,7 +162,7 @@ public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory()
164162
const string branchName = "branch";
165163
string repoPath = InitNewRepository();
166164

167-
Func<Stream, Stream, int> smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
165+
Action<Stream, Stream> smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
168166

169167
var filter = new FakeFilter(FilterName + 17, attributes, null, smudgeCallback);
170168
var filterRegistration = GlobalSettings.RegisterFilter(filter);
@@ -237,34 +235,55 @@ public EmptyFilter(string name, IEnumerable<FilterAttributeEntry> attributes)
237235

238236
class FakeFilter : Filter
239237
{
240-
private readonly Func<Stream, Stream, int> cleanCallback;
241-
private readonly Func<Stream, Stream, int> smudgeCallback;
242-
private readonly Func<int> initCallback;
238+
private readonly Action<Stream, Stream> cleanCallback;
239+
private readonly Action<Stream, Stream> smudgeCallback;
240+
private readonly Action initCallback;
243241

244242
public FakeFilter(string name, IEnumerable<FilterAttributeEntry> attributes,
245-
Func<Stream, Stream, int> cleanCallback = null,
246-
Func<Stream, Stream, int> smudgeCallback = null,
247-
Func<int> initCallback = null)
243+
Action<Stream, Stream> cleanCallback = null,
244+
Action<Stream, Stream> smudgeCallback = null,
245+
Action initCallback = null)
248246
: base(name, attributes)
249247
{
250248
this.cleanCallback = cleanCallback;
251249
this.smudgeCallback = smudgeCallback;
252250
this.initCallback = initCallback;
253251
}
254252

255-
protected override int Clean(string path, Stream input, Stream output)
253+
protected override void Clean(string path, string root, Stream input, Stream output)
256254
{
257-
return cleanCallback != null ? cleanCallback(input, output) : base.Clean(path, input, output);
255+
if (cleanCallback == null)
256+
{
257+
base.Clean(path, root, input, output);
258+
}
259+
else
260+
{
261+
cleanCallback(input, output);
262+
}
258263
}
259264

260-
protected override int Smudge(string path, Stream input, Stream output)
265+
protected override void Smudge(string path, string root, Stream input, Stream output)
261266
{
262-
return smudgeCallback != null ? smudgeCallback(input, output) : base.Smudge(path, input, output);
267+
if (smudgeCallback == null)
268+
{
269+
base.Smudge(path, root, input, output);
270+
}
271+
else
272+
{
273+
smudgeCallback(input, output);
274+
}
263275
}
264276

265-
protected override int Initialize()
277+
protected override void Initialize()
266278
{
267-
return initCallback != null ? initCallback() : base.Initialize();
279+
if (initCallback == null)
280+
{
281+
base.Initialize();
282+
}
283+
else
284+
{
285+
initCallback();
286+
}
268287
}
269288
}
270289
}

LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public void SmugdeIsNotCalledForFileWhichDoesNotMatchAnAttributeEntry()
1515
const string decodedInput = "This is a substitution cipher";
1616
const string encodedInput = "Guvf vf n fhofgvghgvba pvcure";
1717

18-
var attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("filter=rot13") };
18+
var attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("rot13") };
1919
var filter = new SubstitutionCipherFilter("cipher-filter", attributes);
2020
var filterRegistration = GlobalSettings.RegisterFilter(filter);
2121

@@ -55,7 +55,7 @@ public void CorrectlyEncodesAndDecodesInput()
5555
const string decodedInput = "This is a substitution cipher";
5656
const string encodedInput = "Guvf vf n fhofgvghgvba pvcure";
5757

58-
var attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("filter=rot13") };
58+
var attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("rot13") };
5959
var filter = new SubstitutionCipherFilter("cipher-filter", attributes);
6060
var filterRegistration = GlobalSettings.RegisterFilter(filter);
6161

@@ -94,9 +94,9 @@ public void CorrectlyEncodesAndDecodesInput()
9494
[InlineData("*.txt", ".txt", 1, 0)]
9595
public void WhenStagedFileDoesNotMatchPathSpecFileIsNotFiltered(string pathSpec, string fileExtension, int cleanCount, int smudgeCount)
9696
{
97-
const string filterName = "filter=rot13";
97+
const string filterName = "rot13";
9898
const string decodedInput = "This is a substitution cipher";
99-
string attributeFileEntry = string.Format("{0} {1}", pathSpec, filterName);
99+
string attributeFileEntry = string.Format("{0} filter={1}", pathSpec, filterName);
100100

101101
var filterForAttributes = new List<FilterAttributeEntry> { new FilterAttributeEntry(filterName) };
102102
var filter = new SubstitutionCipherFilter("cipher-filter", filterForAttributes);
@@ -122,15 +122,13 @@ public void WhenStagedFileDoesNotMatchPathSpecFileIsNotFiltered(string pathSpec,
122122
}
123123

124124
[Theory]
125-
[InlineData("filter=rot13", "*.txt filter=rot13", 1)]
126-
[InlineData("filter=rot13", "*.txt filter=fake", 0)]
127-
[InlineData("filter=rot13", "*.bat filter=rot13", 0)]
128125
[InlineData("rot13", "*.txt filter=rot13", 1)]
129126
[InlineData("rot13", "*.txt filter=fake", 0)]
127+
[InlineData("rot13", "*.bat filter=rot13", 0)]
128+
[InlineData("rot13", "*.txt filter=fake", 0)]
130129
[InlineData("fake", "*.txt filter=fake", 1)]
131-
[InlineData("filter=fake", "*.txt filter=fake", 1)]
132-
[InlineData("filter=fake", "*.bat filter=fake", 0)]
133-
[InlineData("filter=rot13", "*.txt filter=rot13 -crlf", 1)]
130+
[InlineData("fake", "*.bat filter=fake", 0)]
131+
[InlineData("rot13", "*.txt filter=rot13 -crlf", 1)]
134132
public void CleanIsCalledIfAttributeEntryMatches(string filterAttribute, string attributeEntry, int cleanCount)
135133
{
136134
const string decodedInput = "This is a substitution cipher";
@@ -158,12 +156,10 @@ public void CleanIsCalledIfAttributeEntryMatches(string filterAttribute, string
158156
}
159157

160158
[Theory]
161-
[InlineData("filter=rot13", "*.txt filter=rot13", 1)]
162-
[InlineData("filter=rot13", "*.txt filter=fake", 0)]
163-
[InlineData("filter=rot13", "*.bat filter=rot13", 0)]
159+
164160
[InlineData("rot13", "*.txt filter=rot13", 1)]
165161
[InlineData("rot13", "*.txt filter=fake", 0)]
166-
[InlineData("filter=rot13", "*.txt filter=rot13 -crlf", 1)]
162+
[InlineData("rot13", "*.txt filter=rot13 -crlf", 1)]
167163
public void SmudgeIsCalledIfAttributeEntryMatches(string filterAttribute, string attributeEntry, int smudgeCount)
168164
{
169165
const string decodedInput = "This is a substitution cipher";

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,4 @@
163163
<Target Name="AfterBuild">
164164
</Target>
165165
-->
166-
</Project>
166+
</Project>

LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ public SubstitutionCipherFilter(string name, IEnumerable<FilterAttributeEntry> a
1313
{
1414
}
1515

16-
protected override int Clean(string path, Stream input, Stream output)
16+
protected override void Clean(string path, string root, Stream input, Stream output)
1717
{
1818
CleanCalledCount++;
19-
return RotateByThirteenPlaces(input, output);
19+
RotateByThirteenPlaces(input, output);
2020
}
2121

22-
protected override int Smudge(string path, Stream input, Stream output)
22+
protected override void Smudge(string path, string root, Stream input, Stream output)
2323
{
2424
SmudgeCalledCount++;
25-
return RotateByThirteenPlaces(input, output);
25+
RotateByThirteenPlaces(input, output);
2626
}
2727

28-
public static int RotateByThirteenPlaces(Stream input, Stream output)
28+
public static void RotateByThirteenPlaces(Stream input, Stream output)
2929
{
3030
int value;
3131

@@ -42,8 +42,6 @@ public static int RotateByThirteenPlaces(Stream input, Stream output)
4242

4343
output.WriteByte((byte)value);
4444
}
45-
46-
return 0;
4745
}
4846
}
4947
}

LibGit2Sharp/Core/Ensure.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,33 @@ public static void ArgumentDoesNotContainZeroByte(string argumentValue, string a
8787
"Zero bytes ('\\0') are not allowed. A zero byte has been found at position {0}.", zeroPos), argumentName);
8888
}
8989

90+
/// <summary>
91+
/// Checks an argument to ensure it isn't a IntPtr.Zero (aka null).
92+
/// </summary>
93+
/// <param name="argumentValue">The argument value to check.</param>
94+
/// <param name="argumentName">The name of the argument.</param>
95+
public static void ArgumentNotZeroIntPtr(IntPtr argumentValue, string argumentName)
96+
{
97+
if (argumentValue == IntPtr.Zero)
98+
{
99+
throw new ArgumentNullException(argumentName);
100+
}
101+
}
102+
103+
/// <summary>
104+
/// Checks a pointer argument to ensure it is the expected pointer value.
105+
/// </summary>
106+
/// <param name="argumentValue">The argument value to check.</param>
107+
/// <param name="expectedValue">The expected value.</param>
108+
/// <param name="argumentName">The name of the argument.</param>
109+
public static void ArgumentIsExpectedIntPtr(IntPtr argumentValue, IntPtr expectedValue, string argumentName)
110+
{
111+
if (argumentValue != expectedValue)
112+
{
113+
throw new ArgumentException("Unexpected IntPtr value", argumentName);
114+
}
115+
}
116+
90117
private static readonly Dictionary<GitErrorCode, Func<string, GitErrorCode, GitErrorCategory, LibGit2SharpException>>
91118
GitErrorsToLibGit2SharpExceptions =
92119
new Dictionary<GitErrorCode, Func<string, GitErrorCode, GitErrorCategory, LibGit2SharpException>>

LibGit2Sharp/Core/GitFilter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ internal class GitFilter
2424
[MarshalAs(UnmanagedType.FunctionPtr)]
2525
public git_filter_apply_fn apply;
2626

27+
[MarshalAs(UnmanagedType.FunctionPtr)]
28+
public git_filter_stream_fn stream;
29+
2730
[MarshalAs(UnmanagedType.FunctionPtr)]
2831
public git_filter_cleanup_fn cleanup;
2932

@@ -81,6 +84,9 @@ public delegate int git_filter_check_fn(
8184
public delegate int git_filter_apply_fn(
8285
GitFilter gitFilter, IntPtr payload, IntPtr gitBufTo, IntPtr gitBufFrom, IntPtr filterSource);
8386

87+
public delegate int git_filter_stream_fn(
88+
out IntPtr git_writestream_out, GitFilter self, IntPtr payload, IntPtr filterSource, IntPtr git_writestream_next);
89+
8490
/// <summary>
8591
/// Callback to clean up after filtering has been applied. Specified as `filter.cleanup`, this is an optional callback invoked
8692
/// after the filter has been applied. If the `check` or `apply` callbacks allocated a `payload`

LibGit2Sharp/Core/GitWriteStream.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace LibGit2Sharp.Core
5+
{
6+
[StructLayout(LayoutKind.Sequential)]
7+
internal class GitWriteStream
8+
{
9+
[MarshalAs(UnmanagedType.FunctionPtr)]
10+
public write_fn write;
11+
12+
[MarshalAs(UnmanagedType.FunctionPtr)]
13+
public close_fn close;
14+
15+
[MarshalAs(UnmanagedType.FunctionPtr)]
16+
public free_fn free;
17+
18+
public delegate int write_fn(IntPtr stream, IntPtr buffer, UIntPtr len);
19+
public delegate int close_fn(IntPtr stream);
20+
public delegate void free_fn(IntPtr stream);
21+
}
22+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,6 @@ internal static extern int git_branch_remote_name(
191191
RepositorySafeHandle repo,
192192
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string canonical_branch_name);
193193

194-
[DllImport(libgit2)]
195-
internal static extern int git_buf_grow(IntPtr buffer, UIntPtr targetSize);
196-
197-
[DllImport(libgit2)]
198-
internal static extern int git_buf_put(IntPtr buffer, IntPtr data, UIntPtr len);
199-
200194
[DllImport(libgit2)]
201195
internal static extern int git_remote_rename(
202196
ref GitStrArray problems,
@@ -523,9 +517,6 @@ internal static extern int git_filter_unregister(
523517
[DllImport(libgit2)]
524518
internal static extern int git_filter_source_mode(IntPtr source);
525519

526-
[DllImport(libgit2)]
527-
internal static extern void git_filter_free(IntPtr filterSafeHandle);
528-
529520
[DllImport(libgit2)]
530521
internal static extern int git_libgit2_features();
531522

@@ -1304,6 +1295,10 @@ internal static extern int git_repository_state(
13041295
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))]
13051296
internal static extern FilePath git_repository_workdir(RepositorySafeHandle repository);
13061297

1298+
[DllImport(libgit2)]
1299+
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))]
1300+
internal static extern FilePath git_repository_workdir(IntPtr repository);
1301+
13071302
[DllImport(libgit2)]
13081303
internal static extern int git_repository_new(out RepositorySafeHandle repo);
13091304

0 commit comments

Comments
 (0)