10000 Expose pluggable reference database backend · leoniDEV/libgit2sharp@ff1c734 · GitHub
[go: up one dir, main page]

Skip to content

Commit ff1c734

Browse files
ethomsonnulltoken
authored andcommitted
Expose pluggable reference database backend
1 parent 88cf0d9 commit ff1c734

12 files changed

+1119
-4
lines changed

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Compile Include="RemoteFixture.cs" />
6464
<Compile Include="PushFixture.cs" />
6565
<Compile Include="ReflogFixture.cs" />
66+
<Compile Include="RefdbBackendFixture.cs" />
6667
<Compile Include="StageFixture.cs" />
6768
<Compile Include="StashFixture.cs" />
6869
<Compile Include="CloneFixture.cs" />
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
using LibGit2Sharp.Tests.TestHelpers;
5+
using Xunit;
6+
7+
namespace LibGit2Sharp.Tests
8+
{
9+
public class RefdbBackendFixture : BaseFixture
10+
{
11+
[Fact]
12+
public void CanWriteToRefdbBackend()
13+
{
14+
string path = CloneStandardTestRepo();
15+
16+
using (var repository = new Repository(path))
17+
{
18+
MockRefdbBackend backend = SetupBackend(repository);
19+
20+
repository.Refs.Add("refs/heads/newref", new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"), true);
21+
22+
Assert.Equal(backend.References["refs/heads/newref"], new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644")));
23+
}
24+
}
25+
26+
[Fact]
27+
public void CanReadFromRefdbBackend()
28+
{
29+
var scd = new SelfCleaningDirectory(this);
30+
31+
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
32+
{
33+
MockRefdbBackend backend = SetupBackend(repository);
34+
35+
backend.References["HEAD"] = new MockRefdbReference("refs/heads/testref");
36+
backend.References["refs/heads/testref"] = new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
37+
38+
Assert.True(repository.Refs["HEAD"].TargetIdentifier.Equals("refs/heads/testref"));
39+
Assert.True(repository.Refs["HEAD"].ResolveToDirectReference().TargetIdentifier.Equals("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
40+
41+
Branch branch = repository.Head;
42+
43+
Assert.True(branch.CanonicalName.Equals("refs/heads/testref"));
44+
}
45+
}
46+
47+
[Fact]
48+
public void CanDeleteFromRefdbBackend()
49+
{
50+
var scd = new SelfCleaningDirectory(this);
51+
52+
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
53+
{
54+
MockRefdbBackend backend = SetupBackend(repository);
55+
56+
backend.References["HEAD"] = new MockRefdbReference("refs/heads/testref");
57+
backend.References["refs/heads/testref"] = new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
58+
59+
repository.Refs.Remove("refs/heads/testref");
60+
61+
Assert.True(!backend.References.ContainsKey("refs/heads/testref"));
62+
}
63+
}
64+
65+
[Fact]
66+
public void CannotOverwriteExistingInRefdbBackend()
67+
{
68+
string path = CloneStandardTestRepo();
69+
using (var repository = new Repository(path))
70+
{
71+
SetupBackend(repository);
72+
73+
repository.Refs.Add("refs/heads/newref", new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"), false);
74+
75+
Assert.Throws<NameConflictException>(() => repository.Refs.Add("refs/heads/newref", new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"), false));
76+
}
77+
}
78+
79+
[Fact]
80+
public void CanIterateRefdbBackend()
81+
{
82+
var scd = new SelfCleaningDirectory(this);
83+
84+
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
85+
{
86+
MockRefdbBackend backend = SetupBackend(repository);
87+
88+
backend.References["HEAD"] = new MockRefdbReference("refs/heads/testref");
89+
backend.References["refs/heads/testref"] = new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
90+
backend.References["refs/heads/othersymbolic"] = new MockRefdbReference("refs/heads/testref");
91+
92+
Assert.True(repository.Refs.Select(r => r.CanonicalName).SequenceEqual(backend.References.Keys));
93+
}
94+
}
95+
96+
[Fact]
97+
public void CanIterateTypesInRefdbBackend()
98+
{
99+
var scd = new SelfCleaningDirectory(this);
100+
101+
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
102+
{
103+
MockRefdbBackend backend = SetupBackend(repository);
104+
105+
backend.References["refs/tags/broken1"] = new MockRefdbReference("tags/shouldnt/be/symbolic");
106+
backend.References["refs/tags/broken2"] = new MockRefdbReference("but/are/here/for/testing");
107+
backend.References["refs/tags/broken3"] = new MockRefdbReference("the/type/filtering");
108+
backend.References["refs/tags/correct1"] = new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
109+
110+
Assert.True(repository.Tags.Select(r => r.CanonicalName).SequenceEqual(new List<string> { "refs/tags/correct1" }));
111+
}
112+
}
113+
114+
[Fact]
115+
public void CanIterateRefdbBackendWithGlob()
116+
{
117+
var scd = new SelfCleaningDirectory(this);
118+
119+
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
120+
{
121+
MockRefdbBackend backend = SetupBackend(repository);
122+
123+
backend.References["HEAD"] = new MockRefdbReference("refs/heads/testref");
124+
backend.References["refs/heads/testref"] = new MockRefdbReference(new ObjectId("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
125+
backend.References["refs/heads/othersymbolic"] = new MockRefdbReference("refs/heads/testref");
126+
127+
Assert.True(repository.Refs.FromGlob("refs/heads/*").Select(r => r.CanonicalName).SequenceEqual(new List<string>() { "refs/heads/othersymbolic", "refs/heads/testref" }));
128+
Assert.True(repository.Refs.FromGlob("refs/heads/?estref").Select(r => r.CanonicalName).SequenceEqual(new List<string>() { "refs/heads/testref" }));
129+
}
130+
}
131+
132+
#region MockRefdbBackend
133+
134+
135+
/// <summary>
136+
/// Kind type of a <see cref="MockRefdbReference"/>
137+
/// </summary>
138+
private enum ReferenceType
139+
{
140+
/// <summary>
141+
/// A direct reference, the target is an object ID.
142+
/// </summary>
143+
Oid = 1,
144+
145+
/// <summary>
146+
/// A symbolic reference, the target is another reference.
147+
/// </summary>
148+
Symbolic = 2,
149+
}
150+
151+
private class MockRefdbReference
152+
{
153+
public MockRefdbReference(string target)
154+
{
155+
Type = ReferenceType.Symbolic;
156+
Symbolic = target;
157+
}
158+
159+
public MockRefdbReference(ObjectId target)
160+
{
161+
Type = ReferenceType.Oid;
162+
Oid = target;
163+
}
164+
165+
public ReferenceType Type
166+
{
167+
get;
168+
private set;
169+
}
170+
171+
public ObjectId Oid
172+
{
173+
get;
174+
private set;
175+
}
176+
177+
public string Symbolic
178+
{
179+
get;
180+
private set;
181+
}
182+
183+
public override int GetHashCode()
184+
{
185+
int result = 17;
186+
187+
result = 37 * result + (int)Type;
188+
189+
if (Type == ReferenceType.Symbolic)
190+
{
191+
result = 37 * result + Symbolic.GetHashCode();
192+
}
193+
else
194+
{
195+
result = 37 * result + Oid.GetHashCode();
196+
}
197+
198+
return result;
199+
}
200+
201+
public override bool Equals(object obj)
202+
{
203+
var other = obj as MockRefdbReference;
204+
205+
if (other == null || Type != other.Type)
206+
{
207+
return false;
208+
}
209+
210+
if (Type == ReferenceType.Symbolic)
211+
{
212+
return Symbolic.Equals(other.Symbolic);
213+
}
214+
215+
return Oid.Equals(other.Oid);
216+
}
217+
}
218+
219+
private class MockRefdbBackend : RefdbBackend
220+
{
221+
private readonly Repository repository;
222+
223+
private readonly SortedDictionary<string, MockRefdbReference> references =
224+
new SortedDictionary<string, MockRefdbReference>();
225+
226+
public MockRefdbBackend(Repository repository)
227+
{
228+
this.repository = repository;
229+
}
230+
231+
protected override Repository Repository
232+
{
233+
get { return repository; }
234+
}
235+
236+
public SortedDictionary<string, MockRefdbReference> References
237+
{
238+
get { return references; }
239+
}
240+
241+
public bool Compressed { get; private set; }
242+
243+
protected override RefdbBackendOperations SupportedOperations
244+
{
245+
get
246+
{
247+
return RefdbBackendOperations.Compress | RefdbBackendOperations.ForeachGlob;
248+
}
249+
}
250+
251+
public override bool Exists(string referenceName)
252+
{
253+
return references.ContainsKey(referenceName);
254+
}
255+
256+
public override bool Lookup(string referenceName, out bool isSymbolic, out ObjectId oid, out string symbolic)
257+
{
258+
MockRefdbReference reference = references[referenceName];
259+
260+
if (reference == null)
261+
{
262+
isSymbolic = false;
263+
oid = null;
264+
symbolic = null;
265+
return false;
266+
}
267+
268+
isSymbolic = reference.Type == ReferenceType.Symbolic;
269+
oid = reference.Oid;
270+
symbolic = reference.Symbolic;
271+
return true;
272+
}
273+
274+
public override int Foreach(ForeachCallback callback, bool includeSymbolicRefs, bool includeDirectRefs)
275+
{
276+
int result = 0;
277+
278+
foreach (KeyValuePair<string, MockRefdbReference> kvp in references)
279+
{
280+
var referenceType = kvp.Value.Type;
281+
282+
if ((referenceType == ReferenceType.Symbolic && !includeSymbolicRefs) ||
283+
(referenceType == ReferenceType.Oid && !includeDirectRefs))
284+
{
285+
continue;
286+
}
287+
288+
if ((result = callback(kvp.Key)) != 0)
289+
{
290+
return result;
291+
}
292+
}
293+
294+
return result;
295+
}
296+
297+
public override int ForeachGlob(string glob, ForeachCallback callback, bool includeSymbolicRefs, bool includeDirectRefs)
298+
{
299+
int result = 0;
300+
301+
var globRegex = new Regex("^" +
302+
Regex.Escape(glob).Replace(@"\*", ".*").Replace(@"\?", ".") +
303+
"$");
304+
305+
foreach (KeyValuePair<string, MockRefdbReference> kvp in references)
306+
{
307+
var referenceType = kvp.Value.Type;
308+
309+
if ((referenceType == ReferenceType.Symbolic && !includeSymbolicRefs) ||
310+
(referenceType == ReferenceType.Oid && !includeDirectRefs))
311+
{
312+
continue;
313+
}
314+
315+
if (!globRegex.IsMatch(kvp.Key))
316+
{
317+
continue;
318+
}
319+
320+
if ((result = callback(kvp.Key)) != 0)
321+
{
322+
return result;
323+
}
324+
}
325+
326+
return result;
327+
}
328+
329+
public override void WriteDirectReference(string referenceCanonicalName, ObjectId target)
330+
{
331+
var storage = new MockRefdbReference(target);
332+
references.Add(referenceCanonicalName, storage);
333+
}
334+
335+
public override void WriteSymbolicReference(string referenceCanonicalName, string targetCanonicalName)
336+
{
337+
var storage = new MockRefdbReference(targetCanonicalName);
338+
references.Add(referenceCanonicalName, storage);
339+
}
340+
341+
public override void Delete(string referenceCanonicalName)
342+
{
343+
references.Remove(referenceCanonicalName);
344+
}
345+
346+
public override void Compress()
347+
{
348+
Compressed = true;
349+
}
350+
351+
public override void Free()
352+
{
353+
references.Clear();
354+
}
355+
}
356+
357+
#endregion
358+
359+
private static MockRefdbBackend SetupBackend(Repository repository)
360+
{
361+
var backend = new MockRefdbBackend(repository);
362+
repository.Refs.SetBackend(backend);
363+
364+
return backend;
365+
}
366+
}
367+
}

0 commit comments

Comments
 (0)
0