Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions LibGit2Sharp.Tests/RefSpecFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
using Xunit.Extensions;

namespace LibGit2Sharp.Tests
{
Expand Down Expand Up @@ -35,6 +36,20 @@ public void CanIterateOverRefSpecs()
}
}

[Fact]
public void FetchAndPushRefSpecsComposeRefSpecs()
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];

var totalRefSpecs = remote.FetchRefSpecs.Concat(remote.PushRefSpecs);
var orderedRefSpecs = remote.RefSpecs.OrderBy(r => r.Direction == RefSpecDirection.Fetch ? 0 : 1);
Assert.Equal(orderedRefSpecs, totalRefSpecs);
}
}

[Fact]
public void CanReadRefSpecDetails()
{
Expand All @@ -51,5 +66,130 @@ public void CanReadRefSpecDetails()
Assert.Equal(true, refSpec.ForceUpdate);
}
}

[Theory]
[InlineData(new string[] { "+refs/tags/*:refs/tags/*" }, new string[] { "refs/heads/*:refs/remotes/test/*", "+refs/abc:refs/def" })]
[InlineData(new string[] { "+refs/abc/x:refs/def/x", "refs/def:refs/ghi" }, new string[0])]
[InlineData(new string[0], new string[] { "refs/ghi:refs/jkl/mno" })]
public void CanReplaceRefSpecs(string[] newFetchRefSpecs, string[] newPushRefSpecs)
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];
var oldRefSpecs = remote.RefSpecs.ToList();

var newRemote = repo.Network.Remotes.Update(remote,
r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs);

Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList());

var actualNewFetchRefSpecs = newRemote.RefSpecs
.Where(s => s.Direction == RefSpecDirection.Fetch)
.Select(r => r.Specification)
.ToArray();
Assert.Equal(newFetchRefSpecs, actualNewFetchRefSpecs);

var actualNewPushRefSpecs = newRemote.RefSpecs
.Where(s => s.Direction == RefSpecDirection.Push)
.Select(r => r.Specification)
.ToArray();
Assert.Equal(newPushRefSpecs, actualNewPushRefSpecs);
}
}

[Fact]
public void RemoteUpdaterSavesRefSpecsPermanently()
{
var fetchRefSpecs = new string[] { "refs/their/heads/*:refs/my/heads/*", "+refs/their/tag:refs/my/tag" };

var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
}

using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];
var actualRefSpecs = remote.RefSpecs
.Where(r => r.Direction == RefSpecDirection.Fetch)
.Select(r => r.Specification)
.ToArray();
Assert.Equal(fetchRefSpecs, actualRefSpecs);
}
}

[Fact]
public void CanAddAndRemoveRefSpecs()
{
string newRefSpec = "+refs/heads/test:refs/heads/other-test";

var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];

remote = repo.Network.Remotes.Update(remote,
r => r.FetchRefSpecs.Add(newRefSpec),
r => r.PushRefSpecs.Add(newRefSpec));

Assert.Contains(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
Assert.Contains(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));

remote = repo.Network.Remotes.Update(remote,
r => r.FetchRefSpecs.Remove(newRefSpec),
r => r.PushRefSpecs.Remove(newRefSpec));

Assert.DoesNotContain(newRefSpec, remote.FetchRefSpecs.Select(r => r.Specification));
Assert.DoesNotContain(newRefSpec, remote.PushRefSpecs.Select(r => r.Specification));
}
}

[Fact]
public void CanClearRefSpecs()
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];

// Push refspec does not exist in cloned repository
remote = repo.Network.Remotes.Update(remote, r => r.PushRefSpecs.Add("+refs/test:refs/test"));

remote = repo.Network.Remotes.Update(remote,
r => r.FetchRefSpecs.Clear(),
r => r.PushRefSpecs.Clear());

Assert.Empty(remote.FetchRefSpecs);
Assert.Empty(remote.PushRefSpecs);
Assert.Empty(remote.RefSpecs);
}
}

[Theory]
[InlineData("refs/test:refs//double-slash")]
[InlineData("refs/trailing-slash/:refs/test")]
[InlineData("refs/.dotfile:refs/test")]
[InlineData("refs/.:refs/dotdir")]
[InlineData("refs/asterix:refs/not-matching/*")]
[InlineData("refs/double/*/asterix/*:refs/double/*asterix/*")]
[InlineData("refs/ whitespace:refs/test")]
public void SettingInvalidRefSpecsThrows(string refSpec)
{
var path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
var remote = repo.Network.Remotes["origin"];
var oldRefSpecs = remote.RefSpecs.Select(r => r.Specification).ToList();

Assert.Throws<LibGit2SharpException>(() =>
repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs.Add(refSpec)));

var newRemote = repo.Network.Remotes["origin"];
Assert.Equal(oldRefSpecs, newRemote.RefSpecs.Select(r => r.Specification).ToList());
}
}
}
}
39 changes: 23 additions & 16 deletions LibGit2Sharp/Core/GitStrArrayIn.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace LibGit2Sharp.Core
{
Expand All @@ -12,23 +9,33 @@ internal class GitStrArrayIn : IDisposable
public IntPtr strings;
public uint size;

public static GitStrArrayIn BuildFrom(string[] strings)
{
return BuildFrom(strings, StrictUtf8Marshaler.FromManaged);
}

public static GitStrArrayIn BuildFrom(FilePath[] paths)
{
var nbOfPaths = paths.Length;
var pathPtrs = new IntPtr[nbOfPaths];
return BuildFrom(paths, StrictFilePathMarshaler.FromManaged);
}

private static GitStrArrayIn BuildFrom<T>(T[] input, Func<T, IntPtr> marshaler)
{
var count = input.Length;
var pointers = new IntPtr[count];

for (int i = 0; i < nbOfPaths; i++)
for (int i = 0; i < count; i++)
{
var s = paths[i].Posix;
pathPtrs[i] = StrictFilePathMarshaler.FromManaged(s);
var item = input[i];
pointers[i] = marshaler(item);
}

int dim = IntPtr.Size * nbOfPaths;
int dim = IntPtr.Size * count;

IntPtr arrayPtr = Marshal.AllocHGlobal(dim);
Marshal.Copy(pathPtrs, 0, arrayPtr, nbOfPaths);
Marshal.Copy(pointers, 0, arrayPtr, count);

return new GitStrArrayIn { strings = arrayPtr, size = (uint)nbOfPaths };
return new GitStrArrayIn { strings = arrayPtr, size = (uint)count };
}

public void Dispose()
Expand All @@ -38,14 +45,14 @@ public void Dispose()
return;
}

var nbOfPaths = (int)size;
var count = (int)size;

var pathPtrs = new IntPtr[nbOfPaths];
Marshal.Copy(strings, pathPtrs, 0, nbOfPaths);
var pointers = new IntPtr[count];
Marshal.Copy(strings, pointers, 0, count);

for (int i = 0; i < nbOfPaths; i++)
for (int i = 0; i < count; i++)
{
EncodingMarshaler.Cleanup(pathPtrs[i]);
EncodingMarshaler.Cleanup(pointers[i]);
}

Marshal.FreeHGlobal(strings);
Expand Down
6 changes: 6 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,12 @@ internal static extern int git_remote_download(
[DllImport(libgit2)]
internal static extern UIntPtr git_remote_refspec_count(RemoteSafeHandle remote);

[DllImport(libgit2)]
internal static extern int git_remote_set_fetch_refspecs(RemoteSafeHandle remote, GitStrArrayIn array);

[DllImport(libgit2)]
internal static extern int git_remote_set_push_refspecs(RemoteSafeHandle remote, GitStrArrayIn array);

[DllImport(libgit2)]
internal static extern int git_remote_is_valid_name(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_name);
Expand Down
44 changes: 44 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,50 @@ public static int git_remote_refspec_count(RemoteSafeHandle remote)
return (int)NativeMethods.git_remote_refspec_count(remote);
}

public static IList<string> git_remote_get_fetch_refspecs(RemoteSafeHandle remote)
{
using (ThreadAffinity())
{
UnSafeNativeMethods.git_strarray arr;
int res = UnSafeNativeMethods.git_remote_get_fetch_refspecs(out arr, remote);
Ensure.ZeroResult(res);

return Libgit2UnsafeHelper.BuildListOf(arr);
}
}

public static IList<string> git_remote_get_push_refspecs(RemoteSafeHandle remote)
{
using (ThreadAffinity())
{
UnSafeNativeMethods.git_strarray arr;
int res = UnSafeNativeMethods.git_remote_get_push_refspecs(out arr, remote);
Ensure.ZeroResult(res);

return Libgit2UnsafeHelper.BuildListOf(arr);
}
}

public static void git_remote_set_fetch_refspecs(RemoteSafeHandle remote, IEnumerable<string> refSpecs)
{
using (ThreadAffinity())
using (GitStrArrayIn array = GitStrArrayIn.BuildFrom(refSpecs.ToArray()))
{
int res = NativeMethods.git_remote_set_fetch_refspecs(remote, array);
Ensure.ZeroResult(res);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need ThreadAffinity()

}
}

public static void git_remote_set_push_refspecs(RemoteSafeHandle remote, IEnumerable<string> refSpecs)
{
using (ThreadAffinity())
using (GitStrArrayIn array = GitStrArrayIn.BuildFrom(refSpecs.ToArray()))
{
int res = NativeMethods.git_remote_set_push_refspecs(remote, array);
Ensure.ZeroResult(res);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need ThreadAffinity()

}
}

public static void git_remote_download(RemoteSafeHandle remote)
{
using (ThreadAffinity())
Expand Down
6 changes: 6 additions & 0 deletions LibGit2Sharp/Core/UnSafeNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ internal static unsafe class UnSafeNativeMethods
[DllImport(libgit2)]
internal static extern void git_strarray_free(ref git_strarray array);

[DllImport(libgit2)]
internal static extern int git_remote_get_fetch_refspecs(out git_strarray array, RemoteSafeHandle remote);

[DllImport(libgit2)]
internal static extern int git_remote_get_push_refspecs(out git_strarray array, RemoteSafeHandle remote);

#region Nested type: git_strarray

internal struct git_strarray
Expand Down
19 changes: 19 additions & 0 deletions LibGit2Sharp/Remote.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using LibGit2Sharp.Core;
Expand Down Expand Up @@ -66,6 +67,24 @@ internal static Remote BuildFromPtr(RemoteSafeHandle handle, Repository repo)
/// </summary>
public virtual IEnumerable<RefSpec> RefSpecs { get { return refSpecs; } }

/// <summary>
/// Gets the list of <see cref="RefSpec"/>s defined for this <see cref="Remote"/>
/// that are intended to be used during a Fetch operation
/// </summary>
public virtual IEnumerable<RefSpec> FetchRefSpecs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An there maybe

public virtual IEnumerable<RefSpec> FetchRefSpecs
{
   get { return refSpecs.Where(r => r.Direction == RefSpecDirection.Fetch); }
}

{
get { return refSpecs.Where(r => r.Direction == RefSpecDirection.Fetch); }
}

/// <summary>
/// Gets the list of <see cref="RefSpec"/>s defined for this <see cref="Remote"/>
/// that are intended to be used during a Push operation
/// </summary>
public virtual IEnumerable<RefSpec> PushRefSpecs
{
get { return refSpecs.Where(r => r.Direction == RefSpecDirection.Push); }
}

/// <summary>
/// Transform a reference to its source reference using the <see cref="Remote"/>'s default fetchspec.
/// </summary>
Expand Down
Loading