From c8f416f13a87356f5068ad8de162db09bf88d151 Mon Sep 17 00:00:00 2001 From: Jameson Miller Date: Mon, 11 Feb 2013 15:32:04 -0500 Subject: [PATCH] Teach Network to list references on a remote --- LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 1 + LibGit2Sharp.Tests/NetworkFixture.cs | 96 ++++++++++++++++++++ LibGit2Sharp/Core/GitRemoteHead.cs | 14 +++ LibGit2Sharp/Core/NativeMethods.cs | 5 + LibGit2Sharp/Core/Proxy.cs | 9 ++ LibGit2Sharp/LibGit2Sharp.csproj | 1 + LibGit2Sharp/Network.cs | 37 ++++++++ 7 files changed, 163 insertions(+) create mode 100644 LibGit2Sharp.Tests/NetworkFixture.cs create mode 100644 LibGit2Sharp/Core/GitRemoteHead.cs diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 770c38d60..f95292b68 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -69,6 +69,7 @@ + diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs new file mode 100644 index 000000000..87c9f25cb --- /dev/null +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; +using LibGit2Sharp.Core.Compat; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class NetworkFixture : BaseFixture + { + [Theory] + [InlineData("https://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + [InlineData("git://github.com/libgit2/TestGitRepository.git")] + public void CanListRemoteReferences(string url) + { + string remoteName = "testRemote"; + + var scd = BuildSelfCleaningDirectory(); + using (var repo = Repository.Init(scd.RootedDirectoryPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, url); + IEnumerable references = repo.Network.ListReferences(remote); + + List> actualRefs = references. + Select(directRef => new Tuple(directRef.CanonicalName, directRef.TargetIdentifier)).ToList(); + + Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count); + for (int i = 0; i < ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + } + } + } + + [Fact] + public void CanListRemoteReferenceObjects() + { + string url = "https://github.com/libgit2/TestGitRepository"; + string remoteName = "origin"; + + var scd = BuildSelfCleaningDirectory(); + using (Repository repo = Repository.Clone(url, scd.RootedDirectoryPath)) + { + Remote remote = repo.Network.Remotes[remoteName]; + IEnumerable references = repo.Network.ListReferences(remote); + + List> actualRefs = new List>(); + + foreach(DirectReference reference in references) + { + Assert.NotNull(reference.CanonicalName); + Assert.NotNull(reference.Target); + actualRefs.Add(new Tuple(reference.CanonicalName, reference.Target.Id.Sha)); + } + + Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count); + for (int i = 0; i < ExpectedRemoteRefs.Count; i++) + { + Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1); + Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2); + } + } + } + + /* + * git ls-remote https://github.com/libgit2/TestGitRepository + * 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD + * 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge + * 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master + * 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent + * d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag + * c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{} + * 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob + * 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree + * 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling + */ + /// + /// Expected references on https://github.com/libgit2/TestGitRepository + /// + private static List> ExpectedRemoteRefs = new List>() + { + new Tuple("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0"), + new Tuple("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6"), + new Tuple("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0"), + new Tuple("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"), + new Tuple("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb"), + new Tuple("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a"), + new Tuple("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905"), + new Tuple("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee"), + new Tuple("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e"), + }; + } +} diff --git a/LibGit2Sharp/Core/GitRemoteHead.cs b/LibGit2Sharp/Core/GitRemoteHead.cs new file mode 100644 index 000000000..2c6bce911 --- /dev/null +++ b/LibGit2Sharp/Core/GitRemoteHead.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal struct GitRemoteHead + { + public bool Local; + public GitOid Oid; + public GitOid Loid; + public IntPtr NamePtr; + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 8503a9a77..0cbb9efef 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -690,6 +690,11 @@ internal static extern void git_remote_set_cred_acquire_cb( [DllImport(libgit2)] internal static extern void git_remote_disconnect(RemoteSafeHandle remote); + [DllImport(libgit2)] + internal static extern int git_remote_ls(RemoteSafeHandle remote, git_headlist_cb headlist_cb, IntPtr payload); + + internal delegate int git_headlist_cb(ref GitRemoteHead remoteHeadPtr, IntPtr payload); + [DllImport(libgit2)] internal static extern int git_remote_download( RemoteSafeHandle remote, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 3b4135ccd..f779be35a 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -1270,6 +1270,15 @@ public static IList git_remote_list(RepositorySafeHandle repo) } } + public static void git_remote_ls(RemoteSafeHandle remote, NativeMethods.git_headlist_cb headlist_cb) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_remote_ls(remote, headlist_cb, IntPtr.Zero); + Ensure.ZeroResult(res); + } + } + public static RemoteSafeHandle git_remote_load(RepositorySafeHandle repo, string name, bool throwsIfNotFound) { using (ThreadAffinity()) diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index c3fec4418..2beac8b88 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -66,6 +66,7 @@ + diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index 97afce0ab..9915491bc 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -52,6 +52,43 @@ public virtual RemoteCollection Remotes get { return remotes.Value; } } + /// + /// List references in a repository. + /// + /// The to list from. + /// The references in the repository. + public virtual IEnumerable ListReferences(Remote remote) + { + Ensure.ArgumentNotNull(remote, "remote"); + + List directReferences = new List(); + using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repository.Handle, remote.Name, true)) + { + Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch); + + NativeMethods.git_headlist_cb cb = (ref GitRemoteHead remoteHead, IntPtr payload) => + { + // The name pointer should never be null - if it is, + // this indicates a bug somewhere (libgit2, server, etc). + if (remoteHead.NamePtr == IntPtr.Zero) + { + Proxy.giterr_set_str(GitErrorCategory.Invalid, "Not expecting null value for reference name."); + return -1; + } + + ObjectId oid = new ObjectId(remoteHead.Oid); + string name = Utf8Marshaler.FromNative(remoteHead.NamePtr); + directReferences.Add(new DirectReference(name, this.repository, oid)); + + return 0; + }; + + Proxy.git_remote_ls(remoteHandle, cb); + } + + return directReferences; + } + /// /// Push the objectish to the destination reference on the . ///