diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs index 3a3517432..5e25bc12b 100644 --- a/LibGit2Sharp.Tests/NetworkFixture.cs +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -8,6 +8,44 @@ namespace LibGit2Sharp.Tests { public class NetworkFixture : BaseFixture { + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanFetchDefaultBranchName(string url) + { + string remoteName = "testRemote"; + + string expectedDefaultBranchName = TestRemoteRefs.ExpectedRemoteRefs + .First(remoteRef => remoteRef.Item3).Item1; + + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, url); + + string defaultBranchName = repo.Network.DefaultBranchName(remote); + Assert.Equal(expectedDefaultBranchName, defaultBranchName); + } + } + + [Theory] + [InlineData("http://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] + public void CanFetchDefaultBranchNameFromUrl(string url) + { + string expectedDefaultBranchName = TestRemoteRefs.ExpectedRemoteRefs + .First(remoteRef => remoteRef.Item3).Item1; + + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + string defaultBranchName = repo.Network.DefaultBranchName(url); + Assert.Equal(expectedDefaultBranchName, defaultBranchName); + } + } + [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] diff --git a/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs b/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs index a3e1e58c4..a2aa34029 100644 --- a/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs +++ b/LibGit2Sharp.Tests/TestHelpers/TestRemoteRefs.cs @@ -20,17 +20,17 @@ public class TestRemoteRefs /// /// Expected references on http://github.com/libgit2/TestGitRepository /// - public static List> ExpectedRemoteRefs = new List>() + public 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"), + new Tuple("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0", false), + new Tuple("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6", false), + new Tuple("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0", true), + new Tuple("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", false), + new Tuple("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb", false), + new Tuple("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a", false), + new Tuple("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905", false), + new Tuple("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee", false), + new Tuple("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e", false), }; } } diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index f5d45f3cf..567ef4ccb 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1391,6 +1391,9 @@ internal static extern unsafe int git_remote_create_with_fetchspec( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_remote_default_branch(GitBuf buf, git_remote* remote); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_delete( git_repository* repo, diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 50cefc0df..2fa0150cd 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -2179,6 +2179,23 @@ public static unsafe void git_remote_connect(RemoteHandle remote, GitDirection d } } + public static unsafe string git_remote_default_branch(RemoteHandle remote) + { + using (var buf = new GitBuf()) + { + int res = NativeMethods.git_remote_default_branch(buf, remote); + + if (res == (int)GitErrorCode.NotFound) + { + return null; + } + + Ensure.ZeroResult(res); + + return LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty; + } + } + public static unsafe void git_remote_delete(RepositoryHandle repo, string name) { int res = NativeMethods.git_remote_delete(repo, name); diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index d5f032058..718e8a098 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -37,6 +37,76 @@ public virtual RemoteCollection Remotes get { return remotes.Value; } } + /// + /// Lookup the default branch name in a Remote repository + /// + /// to get the default branch from. + /// The canonical name of the Remote repository's default branch. + public virtual string DefaultBranchName(Remote remote) + { + Ensure.ArgumentNotNull(remote, "remote"); + + return DefaultBranchNameInternal(remote.Url, null); + } + + /// + /// Lookup the default branch name in a Remote repository + /// + /// to get the default branch from. + /// used to connect to the remote repository. + /// The canonical name of the Remote repository's default branch. + public virtual string DefaultBranchName(Remote remote, CredentialsHandler credentialsProvider) + { + Ensure.ArgumentNotNull(remote, "remote"); + Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); + + return DefaultBranchNameInternal(remote.Url, credentialsProvider); + } + + /// + /// Lookup the default branch name in a Remote repository + /// + /// + /// The canonical name of the Remote repository's default branch. + public virtual string DefaultBranchName(string url) + { + Ensure.ArgumentNotNull(url, "url"); + + return DefaultBranchNameInternal(url, null); + } + + /// + /// Lookup the default branch name in a Remote repository + /// + /// + /// used to connect to the remote repository. + /// The canonical name of the Remote repository's default branch. + public virtual string DefaultBranchName(string url, CredentialsHandler credentialsProvider) + { + Ensure.ArgumentNotNull(url, "url"); + Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); + + return DefaultBranchNameInternal(url, credentialsProvider); + } + + private string DefaultBranchNameInternal(string url, CredentialsHandler credentialsProvider) + { + using (RemoteHandle remoteHandle = BuildRemoteHandle(repository.Handle, url)) + { + GitRemoteCallbacks gitCallbacks = new GitRemoteCallbacks { version = 1 }; + GitProxyOptions proxyOptions = new GitProxyOptions { Version = 1 }; + + if (credentialsProvider != null) + { + var callbacks = new RemoteCallbacks(credentialsProvider); + gitCallbacks = callbacks.GenerateCallbacks(); + } + + Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref proxyOptions); + return Proxy.git_remote_default_branch(remoteHandle); + } + } + /// /// List references in a repository. ///