Skip to content

Commit 78a0f4b

Browse files
committed
Merge branch 'cmn/refspec-transform'
2 parents 4b8d5ad + 24c4a8e commit 78a0f4b

File tree

7 files changed

+212
-53
lines changed

7 files changed

+212
-53
lines changed

LibGit2Sharp.Tests/RefSpecFixture.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,43 @@ public void SettingInvalidRefSpecsThrows(string refSpec)
190190
Assert.Equal(oldRefSpecs, newRemote.RefSpecs.Select(r => r.Specification).ToList());
191191
}
192192
}
193+
194+
[Theory]
195+
[InlineData("refs/heads/master", true, false)]
196+
[InlineData("refs/heads/some/master", true, false)]
197+
[InlineData("refs/remotes/foo/master", false, true)]
198+
[InlineData("refs/tags/foo", false, false)]
199+
public void CanCheckForMatches(string reference, bool shouldMatchSource, bool shouldMatchDest)
200+
{
201+
var path = SandboxStandardTestRepo();
202+
using (var repo = InitIsolatedRepository(path))
203+
{
204+
var remote = repo.Network.Remotes.Add("foo", "blahblah", "refs/heads/*:refs/remotes/foo/*");
205+
var refspec = remote.RefSpecs.Single();
206+
207+
Assert.Equal(shouldMatchSource, refspec.SourceMatches(reference));
208+
Assert.Equal(shouldMatchDest, refspec.DestinationMatches(reference));
209+
}
210+
}
211+
212+
[Theory]
213+
[InlineData("refs/heads/master", "refs/remotes/foo/master")]
214+
[InlineData("refs/heads/bar/master", "refs/remotes/foo/bar/master")]
215+
[InlineData("refs/heads/master", "refs/remotes/foo/master")]
216+
public void CanTransformRefspecs(string lhs, string rhs)
217+
{
218+
var path = SandboxStandardTestRepo();
219+
using (var repo = InitIsolatedRepository(path))
220+
{
221+
var remote = repo.Network.Remotes.Add("foo", "blahblah", "refs/heads/*:refs/remotes/foo/*");
222+
var refspec = remote.RefSpecs.Single();
223+
224+
var actualTransformed = refspec.Transform(lhs);
225+
var actualReverseTransformed = refspec.ReverseTransform(rhs);
226+
227+
Assert.Equal(rhs, actualTransformed);
228+
Assert.Equal(lhs, actualReverseTransformed);
229+
}
230+
}
193231
}
194232
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,12 @@ internal static extern IntPtr git_reflog_entry_committer(
11121112
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))]
11131113
internal static extern string git_reflog_entry_message(SafeHandle entry);
11141114

1115+
[DllImport(libgit2)]
1116+
internal static extern int git_refspec_transform(
1117+
GitBuf buf,
1118+
GitRefSpecHandle refspec,
1119+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name);
1120+
11151121
[DllImport(libgit2)]
11161122
internal static extern int git_refspec_rtransform(
11171123
GitBuf buf,
@@ -1139,6 +1145,16 @@ internal static extern string git_refspec_src(
11391145
[DllImport(libgit2)]
11401146
internal static extern bool git_refspec_force(GitRefSpecHandle refSpec);
11411147

1148+
[DllImport(libgit2)]
1149+
internal static extern bool git_refspec_src_matches(
1150+
GitRefSpecHandle resfpec,
1151+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reference);
1152+
1153+
[DllImport(libgit2)]
1154+
internal static extern bool git_refspec_dst_matches(
1155+
GitRefSpecHandle resfpec,
1156+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reference);
1157+
11421158
[DllImport(libgit2)]
11431159
internal static extern int git_remote_autotag(RemoteSafeHandle remote);
11441160

LibGit2Sharp/Core/Proxy.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,17 @@ public static string git_reflog_entry_message(SafeHandle entry)
20172017

20182018
#region git_refspec
20192019

2020+
public static string git_refspec_transform(GitRefSpecHandle refSpecPtr, string name)
2021+
{
2022+
using (var buf = new GitBuf())
2023+
{
2024+
int res = NativeMethods.git_refspec_transform(buf, refSpecPtr, name);
2025+
Ensure.ZeroResult(res);
2026+
2027+
return LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty;
2028+
}
2029+
}
2030+
20202031
public static string git_refspec_rtransform(GitRefSpecHandle refSpecPtr, string name)
20212032
{
20222033
using (var buf = new GitBuf())
@@ -2053,6 +2064,16 @@ public static bool git_refspec_force(GitRefSpecHandle refSpec)
20532064
return NativeMethods.git_refspec_force(refSpec);
20542065
}
20552066

2067+
public static bool git_refspec_src_matches(GitRefSpecHandle refspec, string reference)
2068+
{
2069+
return NativeMethods.git_refspec_src_matches(refspec, reference);
2070+
}
2071+
2072+
public static bool git_refspec_dst_matches(GitRefSpecHandle refspec, string reference)
2073+
{
2074+
return NativeMethods.git_refspec_dst_matches(refspec, reference);
2075+
}
2076+
20562077
#endregion
20572078

20582079
#region git_remote_

LibGit2Sharp/RefSpec.cs

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Diagnostics;
23
using System.Globalization;
34
using LibGit2Sharp.Core;
45
using LibGit2Sharp.Core.Handles;
@@ -11,17 +12,13 @@ namespace LibGit2Sharp
1112
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1213
public class RefSpec
1314
{
14-
private RefSpec(string refSpec, RefSpecDirection direction, string source, string destination, bool forceUpdate)
15-
{
16-
Ensure.ArgumentNotNullOrEmptyString(refSpec, "refSpec");
17-
Ensure.ArgumentNotNull(source, "source");
18-
Ensure.ArgumentNotNull(destination, "destination");
15+
readonly Remote remote;
16+
readonly GitRefSpecHandle handle;
1917

20-
Specification = refSpec;
21-
Direction = direction;
22-
Source = source;
23-
Destination = destination;
24-
ForceUpdate = forceUpdate;
18+
internal RefSpec(Remote remote, GitRefSpecHandle handle)
19+
{
20+
this.remote = remote;
21+
this.handle = handle;
2522
}
2623

2724
/// <summary>
@@ -30,38 +27,100 @@ private RefSpec(string refSpec, RefSpecDirection direction, string source, strin
3027
protected RefSpec()
3128
{ }
3229

33-
internal static RefSpec BuildFromPtr(GitRefSpecHandle handle)
34-
{
35-
Ensure.ArgumentNotNull(handle, "handle");
36-
37-
return new RefSpec(Proxy.git_refspec_string(handle), Proxy.git_refspec_direction(handle),
38-
Proxy.git_refspec_src(handle), Proxy.git_refspec_dst(handle), Proxy.git_refspec_force(handle));
39-
}
40-
4130
/// <summary>
4231
/// Gets the pattern describing the mapping between remote and local references
4332
/// </summary>
44-
public virtual string Specification { get; private set; }
33+
public virtual string Specification
34+
{
35+
get
36+
{
37+
return Proxy.git_refspec_string(this.handle);
38+
}
39+
}
4540

4641
/// <summary>
4742
/// Indicates whether this <see cref="RefSpec"/> is intended to be used during a Push or Fetch operation
4843
/// </summary>
49-
public virtual RefSpecDirection Direction { get; private set; }
44+
public virtual RefSpecDirection Direction
45+
{
46+
get
47+
{
48+
return Proxy.git_refspec_direction(this.handle);
49+
}
50+
}
5051

5152
/// <summary>
5253
/// The source reference specifier
5354
/// </summary>
54-
public virtual string Source { get; private set; }
55+
public virtual string Source
56+
{
57+
get
58+
{
59+
return Proxy.git_refspec_src(this.handle);
60+
}
61+
}
5562

5663
/// <summary>
5764
/// The target reference specifier
5865
/// </summary>
59-
public virtual string Destination { get; private set; }
66+
public virtual string Destination
67+
{
68+
get
69+
{
70+
return Proxy.git_refspec_dst(this.handle);
71+
}
72+
}
6073

6174
/// <summary>
6275
/// Indicates whether the destination will be force-updated if fast-forwarding is not possible
6376
/// </summary>
64-
public virtual bool ForceUpdate { get; private set; }
77+
public virtual bool ForceUpdate
78+
{
79+
get
80+
{
81+
return Proxy.git_refspec_force(this.handle);
82+
}
83+
}
84+
85+
/// <summary>
86+
/// Check whether the given reference matches the source (lhs) part of
87+
/// this refspec.
88+
/// </summary>
89+
/// <param name="reference">The reference name to check</param>
90+
public virtual bool SourceMatches(string reference)
91+
{
92+
return Proxy.git_refspec_src_matches(handle, reference);
93+
}
94+
95+
/// <summary>
96+
/// Check whether the given reference matches the target (rhs) part of
97+
/// this refspec.
98+
/// </summary>
99+
/// <param name="reference">The reference name to check</param>
100+
public virtual bool DestinationMatches(string reference)
101+
{
102+
return Proxy.git_refspec_dst_matches(handle, reference);
103+
}
104+
105+
/// <summary>
106+
/// Perform the transformation described by this refspec on the given
107+
/// reference name (from source to destination).
108+
/// </summary>
109+
/// <param name="reference">The reference name to transform</param>
110+
public virtual string Transform(string reference)
111+
{
112+
return Proxy.git_refspec_transform(handle, reference);
113+
}
114+
115+
/// <summary>
116+
/// Perform the reverse of the transformation described by this refspec
117+
/// on the given reference name (from destination to source).
118+
/// </summary>
119+
/// <param name="reference">The reference name to transform</param>
120+
public virtual string ReverseTransform(string reference)
121+
{
122+
return Proxy.git_refspec_rtransform(handle, reference);
123+
}
65124

66125
private string DebuggerDisplay
67126
{

LibGit2Sharp/RefSpecCollection.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections;
1+
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Diagnostics;
45
using System.Globalization;
@@ -14,36 +15,37 @@ namespace LibGit2Sharp
1415
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1516
public class RefSpecCollection : IEnumerable<RefSpec>
1617
{
17-
readonly IList<RefSpec> refspecs;
18+
readonly Remote remote;
19+
readonly RemoteSafeHandle handle;
20+
readonly Lazy<IList<RefSpec>> refspecs;
1821

1922
/// <summary>
2023
/// Needed for mocking purposes.
2124
/// </summary>
2225
protected RefSpecCollection()
2326
{ }
2427

25-
internal RefSpecCollection(RemoteSafeHandle handle)
28+
internal RefSpecCollection(Remote remote, RemoteSafeHandle handle)
2629
{
2730
Ensure.ArgumentNotNull(handle, "handle");
2831

29-
refspecs = RetrieveRefSpecs(handle);
32+
this.remote = remote;
33+
this.handle = handle;
34+
35+
refspecs = new Lazy<IList<RefSpec>>(() => RetrieveRefSpecs(remote, handle));
3036
}
3137

32-
static IList<RefSpec> RetrieveRefSpecs(RemoteSafeHandle remoteHandle)
38+
static IList<RefSpec> RetrieveRefSpecs(Remote remote, RemoteSafeHandle remoteHandle)
3339
{
3440
int count = Proxy.git_remote_refspec_count(remoteHandle);
3541
List<RefSpec> refSpecs = new List<RefSpec>();
3642

3743
for (int i = 0; i < count; i++)
3844
{
39-
using (GitRefSpecHandle handle = Proxy.git_remote_get_refspec(remoteHandle, i))
40-
{
41-
refSpecs.Add(RefSpec.BuildFromPtr(handle));
42-
}
45+
refSpecs.Add(new RefSpec(remote, Proxy.git_remote_get_refspec(remoteHandle, i)));
4346
}
4447

4548
return refSpecs;
46-
4749
}
4850

4951
/// <summary>
@@ -52,7 +54,7 @@ static IList<RefSpec> RetrieveRefSpecs(RemoteSafeHandle remoteHandle)
5254
/// <returns>An <see cref="IEnumerator{T}"/> object that can be used to iterate through the collection.</returns>
5355
public virtual IEnumerator<RefSpec> GetEnumerator()
5456
{
55-
return refspecs.GetEnumerator();
57+
return refspecs.Value.GetEnumerator();
5658
}
5759

5860
/// <summary>

LibGit2Sharp/Remote.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace LibGit2Sharp
1212
/// A remote repository whose branches are tracked.
1313
/// </summary>
1414
[DebuggerDisplay("{DebuggerDisplay,nq}")]
15-
public class Remote : IEquatable<Remote>, IBelongToARepository
15+
public class Remote : IEquatable<Remote>, IBelongToARepository, IDisposable
1616
{
1717
private static readonly LambdaEqualityHelper<Remote> equalityHelper =
1818
new LambdaEqualityHelper<Remote>(x => x.Name, x => x.Url, x => x.PushUrl);
@@ -22,29 +22,58 @@ public class Remote : IEquatable<Remote>, IBelongToARepository
2222
private readonly RefSpecCollection refSpecs;
2323
private string pushUrl;
2424

25+
readonly RemoteSafeHandle handle;
26+
2527
/// <summary>
2628
/// Needed for mocking purposes.
2729
/// </summary>
2830
protected Remote()
2931
{ }
3032

31-
private Remote(RemoteSafeHandle handle, Repository repository)
33+
internal Remote(RemoteSafeHandle handle, Repository repository)
3234
{
3335
this.repository = repository;
36+
this.handle = handle;
3437
Name = Proxy.git_remote_name(handle);
3538
Url = Proxy.git_remote_url(handle);
3639
PushUrl = Proxy.git_remote_pushurl(handle);
3740
TagFetchMode = Proxy.git_remote_autotag(handle);
38-
refSpecs = new RefSpecCollection(handle);
41+
refSpecs = new RefSpecCollection(this, handle);
42+
}
43+
44+
~Remote()
45+
{
46+
Dispose(false);
47+
}
48+
49+
#region IDisposable
50+
51+
bool disposedValue = false; // To detect redundant calls
52+
53+
/// <summary>
54+
/// Release the unmanaged remote object
55+
/// </summary>
56+
public void Dispose()
57+
{
58+
Dispose(true);
59+
GC.SuppressFinalize(this);
3960
}
4061

41-
internal static Remote BuildFromPtr(RemoteSafeHandle handle, Repository repo)
62+
void Dispose(bool disposing)
4263
{
43-
var remote = new Remote(handle, repo);
64+
if (!disposedValue)
65+
{
66+
if (handle != null)
67+
{
68+
handle.Dispose();
69+
}
4470

45-
return remote;
71+
disposedValue = true;
72+
}
4673
}
4774

75+
#endregion
76+
4877
/// <summary>
4978
/// Gets the alias of this remote repository.
5079
/// </summary>

0 commit comments

Comments
 (0)