8000 Extended RemoteUpdater to update refspecs · pmiossec/libgit2sharp@b51570e · GitHub
[go: up one dir, main page]

Skip to content

Commit b51570e

Browse files
committed
Extended RemoteUpdater to update refspecs
Push/Pull Ref Specs can be updated via Remotes.Update(). The collections can either be manipulated or replaced completely. Any changes are saved immediately.
1 parent 439ee14 commit b51570e

File tree

7 files changed

+382
-17
lines changed

7 files changed

+382
-17
lines changed

LibGit2Sharp.Tests/RefSpecFixture.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using LibGit2Sharp.Tests.TestHelpers;
44
using Xunit;
5+
using Xunit.Extensions;
56

67
namespace LibGit2Sharp.Tests
78
{
@@ -35,6 +36,20 @@ public void CanIterateOverRefSpecs()
3536
}
3637
}
3738

39+
[Fact]
40+
public void FetchAndPushRefSpecsComposeRefSpecs()
41+
{
42+
var path = CloneStandardTestRepo();
43+
using (var repo = new Repository(path))
44+
{
45+
var remote = repo.Network.Remotes["origin"];
46+
47+
var totalRefSpecs = remote.FetchRefSpecs.Concat(remote.PushRefSpecs);
48+
var orderedRefSpecs = remote.RefSpecs.OrderBy(r => r.Direction == RefSpecDirection.Fetch ? 0 : 1);
49+
Assert.Equal(orderedRefSpecs, totalRefSpecs);
50+
}
51+
}
52+
3853
[Fact]
3954
public void CanReadRefSpecDetails()
4055
{
@@ -51,5 +66,130 @@ public void CanReadRefSpecDetails()
5166
Assert.Equal(true, refSpec.ForceUpdate);
5267
}
5368
}
69+
70+
[Theory]
71+
[InlineData(new string[] { "+refs/tags/*:refs/tags/*" }, new string[] { "refs/heads/*:refs/remotes/test/*", "+refs/abc:refs/def" })]
72+
[InlineData(new string[] { "+refs/abc/x:refs/def/x", "refs/def:refs/ghi" }, new string[0])]
73+
[InlineData(new string[0], new string[] { "refs/ghi:refs/jkl/mno" })]
74+
public void CanReplaceRefSpecs(string[] newFetchRefSpecs, string[] newPushRefSpecs)
75+
{
76+
var path = CloneStandardTestRepo();
77+
using (var repo = new Repository(path))
78+
{
79+
var remote = repo.Network.Remotes["origin"];
80+
var oldRefSpecs = remote.RefSpecs.ToList();
81+
82+
var newRemote = repo.Network.Remotes.Update(remote,
83+
r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs);
84+
85+
Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());
86+
87+
var actualNewFetchRefSpecs = newRemote.RefSpecs
88+
.Where(s => s.Direction == RefSpecDirection.Fetch)
89+
.Select(r => r.Specification)
90+
.ToArray();
91+
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);
92+
93+
var actualNewPushRefSpecs = newRemote.RefSpecs
94+
.Where(s => s.Direction == RefSpecDirection.Push)
95+
.Select(r => r.Specification)
96+
.ToArray();
97+
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
98+
}
99+
}
100+
101+
[Fact]
102+
public void RemoteUpdaterSavesRefSpecsPermanently()
103+
{
104+
var fetchRefSpecs = new string[] { "refs/their/heads/*:refs/my/heads/*", "+refs/their/tag:refs/my/tag" };
105+
106+
var path = CloneStandardTestRepo();
107+
using (var repo = new Repository(path))
108+
{
109+
var remote = repo.Network.Remotes["origin"];
110+
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
111+
}
112+
113+
using (var repo = new Repository(path))
114+
{
115+
var remote = repo.Network.Remotes["origin"];
116+
var actualRefSpecs = remote.RefSpecs
117+
.Where(r => r.Direction == RefSpecDirection.Fetch)
118+
.Select(r => r.Specification)
119+
.ToArray();
120+
Assert.Equal(fetchRefSpecs, actualRefSpecs);
121+
}
122+
}
123+
124+
[Fact]
125+
public void CanAddAndRemoveRefSpecs()
126+
{
127+
string newRefSpec = "+refs/heads/test:refs/heads/other-test";
128+
129+
var path = CloneStandardTestRepo();
130+
using (var repo = new Repository(path))
131+
{
132+
var remote = repo.Network.Remotes["origin"];
133+
134+
remote = repo.Network.Remotes.Update(remote,
135+
r => r.FetchRefSpecs.Add(newRefSpec),
136+
r => r.PushRefSpecs.Add(newRefSpec));
137+
138+
Assert.Contains(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
139+
Assert.Contains(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
140+
141+
remote = repo.Network.Remotes.Update(remote,
142+
r => r.FetchRefSpecs.Remove(newRefSpec),
143+
r => r.PushRefSpecs.Remove(newRefSpec));
144+
145+
Assert.DoesNotContain(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
146+
Assert.DoesNotContain(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
147+
}
148+
}
149+
150+
[Fact]
151+
public void CanClearRefSpecs()
152+
{
153+
var path = CloneStandardTestRepo();
154+
using (var repo = new Repository(path))
155+
{
156+
var remote = repo.Network.Remotes["origin"];
157+
158+
// Push refspec does not exist in cloned repository
159+
remote = repo.Network.Remotes.Update(remote, r => r.PushRefSpecs.Add("+refs/test:refs/test"));
160+
161+
remote = repo.Network.Remotes.Update(remote,
162+
r => r.FetchRefSpecs.Clear(),
163+
r => r.PushRefSpecs.Clear());
164+
165+
Assert.Empty(remote.FetchRefSpecs);
166+
Assert.Empty(remote.PushRefSpecs);
167+
Assert.Empty(remote.RefSpecs);
168+
}
169+
}
170+
171+
[Theory]
172+
[InlineData("refs/test:refs//double-slash")]
173+
[InlineData("refs/trailing-slash/:refs/test")]
174+
[InlineData("refs/.dotfile:refs/test")]
175+
[InlineData("refs/.:refs/dotdir")]
176+
[InlineData("refs/asterix:refs/not-matching/*")]
177+
[InlineData("refs/double/*/asterix/*:refs/double/*asterix/*")]
178+
[InlineData("refs/ whitespace:refs/test")]
179+
public void SettingInvalidRefSpecsThrows(string refSpec)
180+
{
181+
var path = CloneStandardTestRepo();
182+
using (var repo = new Repository(path))
183+
{
184+
var remote = repo.Network.Remotes["origin"];
185+
var oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();
186+
187+
Assert.Throws<LibGit2SharpException>(() =>
188+
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs.Add(refSpec)));
189+
190+
var newRemote = repo.Network.Remotes["origin"];
191+
Assert.Equal(oldRefSpecs, newRemote.RefSpecs.Select(r => r.Specification).ToList());
192+
}
193+
}
54194
}
55195
}

LibGit2Sharp/Core/GitStrArrayIn.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42
using System.Runtime.InteropServices;
5-
using System.Text;
63

74
namespace LibGit2Sharp.Core
85
{
@@ -12,23 +9,33 @@ internal class GitStrArrayIn : IDisposable
129
public IntPtr strings;
1310
public uint size;
1411

12+
public static GitStrArrayIn BuildFrom(string[] strings)
13+
{
14+
return BuildFrom(strings, StrictUtf8Marshaler.FromManaged);
15+
}
16+
1517
public static GitStrArrayIn BuildFrom(FilePath[] paths)
1618
{
17-
var nbOfPaths = paths.Length;
18-
var pathPtrs = new IntPtr[nbOfPaths];
19+
return BuildFrom(paths, StrictFilePathMarshaler.FromManaged);
20+
}
21+
22+
private static GitStrArrayIn BuildFrom<T>(T[] input, Func<T, IntPtr> marshaler)
23+
{
24+
var count = input.Length;
25+
var pointers = new IntPtr[count];
1926

20-
for (int i = 0; i < nbOfPaths; i++)
27+
for (int i = 0; i < count; i++)
2128
{
22-
var s = paths[i].Posix;
23-
pathPtrs[i] = StrictFilePathMarshaler.FromManaged(s);
29+
var item = input[i];
30+
pointers[i] = marshaler(item);
2431
}
2532

26-
int dim = IntPtr.Size * nbOfPaths;
33+
int dim = IntPtr.Size * count;
2734

2835
IntPtr arrayPtr = Marshal.AllocHGlobal(dim);
29-
Marshal.Copy(pathPtrs, 0, arrayPtr, nbOfPaths);
36+
Marshal.Copy(pointers, 0, arrayPtr, count);
3037

31-
return new GitStrArrayIn { strings = arrayPtr, size = (uint)nbOfPaths };
38+
return new GitStrArrayIn { strings = arrayPtr, size = (uint)count };
3239
}
3340

3441
public void Dispose()
@@ -38,14 +45,14 @@ public void Dispose()
3845
return;
3946
}
4047

41-
var nbOfPaths = (int)size;
48+
var count = (int)size;
4249

43-
var pathPtrs = new IntPtr[nbOfPaths];
44-
Marshal.Copy(strings, pathPtrs, 0, nbOfPaths);
50+
var pointers = new IntPtr[count];
51+
Marshal.Copy(strings, pointers, 0, count);
4552

46-
for (int i = 0; i < nbOfPaths; i++)
53+
for (int i = 0; i < count; i++)
4754
{
48-
EncodingMarshaler.Cleanup(pathPtrs[i]);
55+
EncodingMarshaler.Cleanup(pointers[i]);
4956
}
5057

5158
Marshal.FreeHGlobal(strings);

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,12 @@ internal static extern int git_remote_download(
882882
[DllImport(libgit2)]
883883
internal static extern UIntPtr git_remote_refspec_count(RemoteSafeHandle remote);
884884

885+
[DllImport(libgit2)]
886+
internal static extern int git_remote_set_fetch_refspecs(RemoteSafeHandle remote, GitStrArrayIn array);
887+
888+
[DllImport(libgit2)]
889+
internal static extern int git_remote_set_push_refspecs(RemoteSafeHandle remote, GitStrArrayIn array);
890+
885891
[DllImport(libgit2)]
886892
internal static extern int git_remote_is_valid_name(
887893
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_name);

LibGit2Sharp/Core/Proxy.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,50 @@ public static int git_remote_refspec_count(RemoteSafeHandle remote)
15611561
return (int)NativeMethods.git_remote_refspec_count(remote);
15621562
}
15631563

1564+
public static IList<string> git_remote_get_fetch_refspecs(RemoteSafeHandle remote)
1565+
{
1566+
using (ThreadAffinity())
1567+
{
1568+
UnSafeNativeMethods.git_strarray arr;
1569+
int res = UnSafeNativeMethods.git_remote_get_fetch_refspecs(out arr, remote);
1570+
Ensure.ZeroResult(res);
1571+
1572+
return Libgit2UnsafeHelper.BuildListOf(arr);
1573+
}
1574+
}
1575+
1576+
public static IList<string> git_remote_get_push_refspecs(RemoteSafeHandle remote)
1577+
{
1578+
using (ThreadAffinity())
1579+
{
1580+
UnSafeNativeMethods.git_strarray arr;
1581+
int res = UnSafeNativeMethods.git_remote_get_push_refspecs(out arr, remote);
1582+
Ensure.ZeroResult(res);
1583+
1584+
return Libgit2UnsafeHelper.BuildListOf(arr);
1585+
}
1586+
}
1587+
1588+
public static void git_remote_set_fetch_refspecs(RemoteSafeHandle remote, IEnumerable<string> refSpecs)
1589+
{
1590+
using (ThreadAffinity())
1591+
using (GitStrArrayIn array = GitStrArrayIn.BuildFrom(refSpecs.ToArray()))
1592+
{
1593+
int res = NativeMethods.git_remote_set_fetch_refspecs(remote, array);
1594+
Ensure.ZeroResult(res);
1595+
}
1596+
}
1597+
1598+
public static void git_remote_set_push_refspecs(RemoteSafeHandle remote, IEnumerable<string> refSpecs)
1599+
{
1600+
using (ThreadAffinity())
1601+
using (GitStrArrayIn array = GitStrArrayIn.BuildFrom(refSpecs.ToArray()))
1602+
{
1603+
int res = NativeMethods.git_remote_set_push_refspecs(remote, array);
1604+
Ensure.ZeroResult(res);
1605+
}
1606+
}
1607+
15641608
public static void git_remote_download(RemoteSafeHandle remote)
15651609
{
15661610
using (ThreadAffinity())

LibGit2Sharp/Core/UnSafeNativeMethods.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ internal static unsafe class UnSafeNativeMethods
2020
[DllImport(libgit2)]
2121
internal static extern void git_strarray_free(ref git_strarray array);
2222

23+
[DllImport(libgit2)]
24+
internal static extern int git_remote_get_fetch_refspecs(out git_strarray array, RemoteSafeHandle remote);
25+
26+
[DllImport(libgit2)]
27+
internal static extern int git_remote_get_push_refspecs(out git_strarray array, RemoteSafeHandle remote);
28+
2329
#region Nested type: git_strarray
2430

2531
internal struct git_strarray

LibGit2Sharp/Remote.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Diagnostics;
45
using System.Globalization;
56
using LibGit2Sharp.Core;
@@ -66,6 +67,24 @@ internal static Remote BuildFromPtr(RemoteSafeHandle handle, Repository repo)
6667
/// </summary>
6768
public virtual IEnumerable<RefSpec> RefSpecs { get { return refSpecs; } }
6869

70+
/// <summary>
71+
/// Gets the list of <see cref="RefSpec"/>s defined for this <see cref="Remote"/>
72+
/// that are intended to be used during a Fetch operation
73+
/// </summary>
74+
public virtual IEnumerable<RefSpec> FetchRefSpecs
75+
{
76+
get { return refSpecs.Where(r => r.Direction == RefSpecDirection.Fetch); }
77+
}
78+
79+
/// <summary>
80+
/// Gets the list of <see cref="RefSpec"/>s defined for this <see cref="Remote"/>
81+
/// that are intended to be used during a Push operation
82+
/// </summary>
83+
public virtual IEnumerable<RefSpec> PushRefSpecs
84+
{
85+
get { return refSpecs.Where(r => r.Direction == RefSpecDirection.Push); }
86+
}
87+
6988
/// <summary>
7089
/// Transform a reference to its source reference using the <see cref="Remote"/>'s default fetchspec.
7190
/// </summary>

0 commit comments

Comments
 (0)
0