Skip to content

Commit 12c4f9b

Browse files
committed
Add Functionality to List Remote References without creating a Repository
1 parent 2c98992 commit 12c4f9b

File tree

9 files changed

+192
-42
lines changed

9 files changed

+192
-42
lines changed

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<Compile Include="TestHelpers\SignatureExtensions.cs" />
125125
<Compile Include="TestHelpers\SkippableFactAttribute.cs" />
126126
<Compile Include="LogFixture.cs" />
127+
<Compile Include="TestHelpers\TestRemoteRefs.cs" />
127128
<Compile Include="TreeDefinitionFixture.cs" />
128129
<Compile Include="TreeFixture.cs" />
129130
<Compile Include="UnstageFixture.cs" />

LibGit2Sharp.Tests/NetworkFixture.cs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public void CanListRemoteReferences(string url)
3434
List<Tuple<string, string>> actualRefs = references.
3535
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.TargetIdentifier)).ToList();
3636

37-
Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count);
38-
for (int i = 0; i < ExpectedRemoteRefs.Count; i++)
37+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
38+
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
3939
{
40-
Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
41-
Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
40+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
41+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
4242
}
4343
}
4444
}
@@ -65,11 +65,11 @@ public void CanListRemoteReferencesFromUrl(string url)
6565
List<Tuple<string, string>> actualRefs = references.
6666
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.TargetIdentifier)).ToList();
6767

68-
Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count);
69-
for (int i = 0; i < ExpectedRemoteRefs.Count; i++)
68+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
69+
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
7070
{
71-
Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
72-
Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
71+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
72+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
7373
}
7474
}
7575
}
@@ -98,11 +98,11 @@ public void CanListRemoteReferenceObjects()
9898
actualRefs.Add(new Tuple<string, string>(reference.CanonicalName, reference.Target.Id.Sha));
9999
}
100100

101-
Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count);
102-
for (int i = 0; i < ExpectedRemoteRefs.Count; i++)
101+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
102+
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
103103
{
104-
Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
105-
Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
104+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
105+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
106106
}
107107
}
108108
}
@@ -254,33 +254,5 @@ public void CanMergeFetchedRefs()
254254
Assert.Equal(mergeResult.Status, MergeStatus.NonFastForward);
255255
}
256256
}
257-
258-
/*
259-
* git ls-remote http://github.com/libgit2/TestGitRepository
260-
* 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD
261-
* 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge
262-
* 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master
263-
* 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent
264-
* d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag
265-
* c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{}
266-
* 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob
267-
* 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree
268-
* 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling
269-
*/
270-
/// <summary>
271-
/// Expected references on http://github.com/libgit2/TestGitRepository
272-
/// </summary>
273-
private static List<Tuple<string, string>> ExpectedRemoteRefs = new List<Tuple<string, string>>()
274-
{
275-
new Tuple<string, string>("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0"),
276-
new Tuple<string, string>("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6"),
277-
new Tuple<string, string>("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0"),
278-
new Tuple<string, string>("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"),
279-
new Tuple<string, string>("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb"),
280-
new Tuple<string, string>("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a"),
281-
new Tuple<string, string>("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905"),
282-
new Tuple<string, string>("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee"),
283-
new Tuple<string, string>("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e"),
284-
};
285257
}
286258
}

LibGit2Sharp.Tests/RepositoryFixture.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using LibGit2Sharp.Tests.TestHelpers;
66
using Xunit;
7+
using Xunit.Extensions;
78

89
namespace LibGit2Sharp.Tests
910
{
@@ -651,5 +652,64 @@ public void CanDetectShallowness()
651652
Assert.False(repo.Info.IsShallow);
652653
}
653654
}
655+
656+
[SkippableFact]
657+
public void CanListRemoteReferencesWithCredentials()
658+
{
659+
InconclusiveIf(() => string.IsNullOrEmpty(Constants.PrivateRepoUrl),
660+
"Populate Constants.PrivateRepo* to run this test");
661+
662+
IEnumerable<DirectReference> references = Repository.ListRemoteReferences(Constants.PrivateRepoUrl,
663+
Constants.PrivateRepoCredentials);
664+
665+
foreach (var reference in references)
666+
{
667+
Assert.NotNull(reference);
668+
}
669+
}
670+
671+
[Theory]
672+
[InlineData("http://github.com/libgit2/TestGitRepository")]
673+
[InlineData("https://github.com/libgit2/TestGitRepository")]
674+
[InlineData("git://github.com/libgit2/TestGitRepository.git")]
675+
public void CanListRemoteReferences(string url)
676+
{
677+
IEnumerable<DirectReference> references = Repository.ListRemoteReferences(url);
678+
679+
List<Tuple<string, string>> actualRefs = references.
680+
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.TargetIdentifier)).ToList();
681+
682+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs.Count, actualRefs.Count);
683+
for (int i = 0; i < TestRemoteRefs.ExpectedRemoteRefs.Count; i++)
684+
{
685+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
686+
Assert.Equal(TestRemoteRefs.ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
687+
}
688+
}
689+
690+
[Theory]
691+
[InlineData("http://github.com/libgit2/TestGitRepository")]
692+
public void ReadingReferenceRepositoryThroughListRemoteReferencesThrows(string url)
693+
{
694+
IEnumerable<DirectReference> references = Repository.ListRemoteReferences(url);
695+
696+
foreach (var reference in references)
697+
{
698+
IBelongToARepository repositoryReference = reference;
699+
Assert.Throws<InvalidOperationException>(() => repositoryReference.Repository);
700+
}
701+
}
702+
703+
[Theory]
704+
[InlineData("http://github.com/libgit2/TestGitRepository")]
705+
public void ReadingReferenceTargetFromListRemoteReferencesThrows(string url)
706+
{
707+
IEnumerable<DirectReference> references = Repository.ListRemoteReferences(url);
708+
709+
foreach (var reference in references)
710+
{
711+
Assert.Throws<InvalidOperationException>(() => reference.Target);
712+
}
713+
}
654714
}
655715
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace LibGit2Sharp.Tests.TestHelpers
7+
{
8+
public class TestRemoteRefs
9+
{
10+
/*
11+
* git ls-remote http://github.com/libgit2/TestGitRepository
12+
* 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD
13+
* 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge
14+
* 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master
15+
* 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent
16+
* d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag
17+
* c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{}
18+
* 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob
19+
* 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree
20+
* 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling
21+
*/
22+
/// <summary>
23+
/// Expected references on http://github.com/libgit2/TestGitRepository
24+
/// </summary>
25+
public static List<Tuple<string, string>> ExpectedRemoteRefs = new List<Tuple<string, string>>()
26+
{
27+
new Tuple<string, string>("HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0"),
28+
new Tuple<string, string>("refs/heads/first-merge", "0966a434eb1a025db6b71485ab63a3bfbea520b6"),
29+
new Tuple<string, string>("refs/heads/master", "49322bb17d3acc9146f98c97d078513228bbf3c0"),
30+
new Tuple<string, string>("refs/heads/no-parent", "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"),
31+
new Tuple<string, string>("refs/tags/annotated_tag", "d96c4e80345534eccee5ac7b07fc7603b56124cb"),
32+
new Tuple<string, string>("refs/tags/annotated_tag^{}", "c070ad8c08840c8116da865b2d65593a6bb9cd2a"),
33+
new Tuple<string, string>("refs/tags/blob", "55a1a760df4b86a02094a904dfa511deb5655905"),
34+
new Tuple<string, string>("refs/tags/commit_tree", "8f50ba15d49353813cc6e20298002c0d17b0a9ee"),
35+
new Tuple<string, string>("refs/tags/nearly-dangling", "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e"),
36+
};
37+
}
38+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,9 @@ internal static extern int git_repository_state(
12851285
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))]
12861286
internal static extern FilePath git_repository_workdir(RepositorySafeHandle repository);
12871287

1288+
[DllImport(libgit2)]
1289+
internal static extern int git_repository_new(out RepositorySafeHandle repo);
1290+
12881291
[DllImport(libgit2)]
12891292
internal static extern int git_reset(
12901293
RepositorySafeHandle repo,

LibGit2Sharp/Core/Proxy.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,16 @@ public static RepositorySafeHandle git_repository_open(string path)
21672167
return repo;
21682168
}
21692169

2170+
public static RepositorySafeHandle git_repository_new()
2171+
{
2172+
RepositorySafeHandle repo;
2173+
int res = NativeMethods.git_repository_new(out repo);
2174+
2175+
Ensure.ZeroResult(res);
2176+
2177+
return repo;
2178+
}
2179+
21702180
public static void git_repository_open_ext(string path, RepositoryOpenFlags flags, string ceilingDirs)
21712181
{
21722182
int res;

LibGit2Sharp/DirectReference.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,21 @@ protected DirectReference()
1818
internal DirectReference(string canonicalName, IRepository repo, ObjectId targetId)
1919
: base(repo, canonicalName, targetId.Sha)
2020
{
21-
targetBuilder = new Lazy<GitObject>(() => repo.Lookup(targetId));
21+
targetBuilder = new Lazy<GitObject>(() =>
22+
{
23+
if (repo == null)
24+
{
25+
throw new InvalidOperationException("Target requires a local repository");
26+
}
27+
28+
return repo.Lookup(targetId);
29+
});
2230
}
2331

2432
/// <summary>
2533
/// Gets the target of this <see cref="DirectReference"/>
2634
/// </summary>
35+
/// <exception cref="InvalidOperationException">Throws if Local Repository is not set.</exception>
2736
public virtual GitObject Target
2837
{
2938
get { return targetBuilder.Value; }

LibGit2Sharp/Reference.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ private string DebuggerDisplay
199199
}
200200
}
201201

202-
IRepository IBelongToARepository.Repository { get { return repo; } }
202+
IRepository IBelongToARepository.Repository
203+
{
204+
get
205+
{
206+
if (repo == null)
207+
{
208+
throw new InvalidOperationException("Repository requires a local repository");
209+
}
210+
211+
return repo;
212+
}
213+
}
203214
}
204215
}

LibGit2Sharp/Repository.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,52 @@ internal Commit LookupCommit(string committish)
545545
LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit);
546546
}
547547

548+
/// <summary>
549+
/// Lists the Remote Repository References.
550+
/// </summary>
551+
/// <para>
552+
/// Does not require a local Repository. The retrieved
553+
/// <see cref="IBelongToARepository.Repository"/>
554+
/// throws <see cref="InvalidOperationException"/> in this case.
555+
/// </para>
556+
/// <param name="url">The url to list from.</param>
557+
/// <returns>The references in the remote repository.</returns>
558+
public static IEnumerable<DirectReference> ListRemoteReferences(string url)
559+
{
560+
return ListRemoteReferences(url, null);
561+
}
562+
563+
/// <summary>
564+
/// Lists the Remote Repository References.
565+
/// </summary>
566+
/// <para>
567+
/// Does not require a local Repository. The retrieved
568+
/// <see cref="IBelongToARepository.Repository"/>
569+
/// throws <see cref="InvalidOperationException"/> in this case.
570+
/// </para>
571+
/// <param name="url">The url to list from.</param>
572+
/// <param name="credentialsProvider">The <see cref="Func{Credentials}"/> used to connect to remote repository.</param>
573+
/// <returns>The references in the remote repository.</returns>
574+
public static IEnumerable<DirectReference> ListRemoteReferences(string url, CredentialsHandler credentialsProvider)
575+
{
576+
Ensure.ArgumentNotNull(url, "url");
577+
578+
using (RepositorySafeHandle repositoryHandle = Proxy.git_repository_new())
579+
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_create_anonymous(repositoryHandle, url))
580+
{
581+
var gitCallbacks = new GitRemoteCallbacks { version = 1 };
582+
583+
if (credentialsProvider != null)
584+
{
585+
var callbacks = new RemoteCallbacks(credentialsProvider);
586+
gitCallbacks = callbacks.GenerateCallbacks();
587+
}
588+
589+
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks);
590+
return Proxy.git_remote_ls(null, remoteHandle);
591+
}
592+
}
593+
548594
/// <summary>
549595
/// Probe for a git repository.
550596
/// <para>The lookup start from <paramref name="startingPath"/> and walk upward parent directories if nothing has been found.</para>

0 commit comments

Comments
 (0)