8000 Network: allow downloading from a url · crashsplash/libgit2sharp@1374884 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1374884

Browse files
committed
Network: allow downloading from a url
This is our implementation of in-memory remotes. Ask the repository to fetch/list from a url instead of a remote.
1 parent b9bb6d6 commit 1374884

File tree

4 files changed

+132
-23
lines changed

4 files changed

+132
-23
lines changed

LibGit2Sharp.Tests/NetworkFixture.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,37 @@ public void CanListRemoteReferences(string url)
4343
}
4444
}
4545

46+
[Theory]
47+
[InlineData("http://github.com/libgit2/TestGitRepository")]
48+
[InlineData("https://github.com/libgit2/TestGitRepository")]
49+
[InlineData("git://github.com/libgit2/TestGitRepository.git")]
50+
public void CanListRemoteReferencesFromUrl(string url)
51+
{
52+
string repoPath = InitNewRepository();
53+
54+
using (var repo = new Repository(repoPath))
55+
{
56+
IList<DirectReference> references = repo.Network.ListReferences(url).ToList();
57+
58+
foreach (var directReference in references)
59+
{
60+
// None of those references point to an existing
61+
// object in this brand new repository
62+
Assert.Null(directReference.Target);
63+
}
64+
65+
List<Tuple<string, string>> actualRefs = references.
66+
Select(directRef => new Tuple<string, string>(directRef.CanonicalName, directRef.TargetIdentifier)).ToList();
67+
68+
Assert.Equal(ExpectedRemoteRefs.Count, actualRefs.Count);
69+
for (int i = 0; i < ExpectedRemoteRefs.Count; i++)
70+
{
71+
Assert.Equal(ExpectedRemoteRefs[i].Item2, actualRefs[i].Item2);
72+
Assert.Equal(ExpectedRemoteRefs[i].Item1, actualRefs[i].Item1);
73+
}
74+
}
75+
}
76+
4677
[Fact]
4778
public void CanListRemoteReferenceObjects()
4879
{

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,13 @@ internal static extern int git_remote_create(
871871
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name,
872872
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url);
873873

874+
[DllImport(libgit2)]
875+
internal static extern int git_remote_create_inmemory(
876+
out RemoteSafeHandle remote,
877+
RepositorySafeHandle repo,
878+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec,
879+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url);
880+
874881
[DllImport(libgit2)]
875882
internal static extern void git_remote_disconnect(RemoteSafeHandle remote);
876883

LibGit2Sharp/Core/Proxy.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,18 @@ public static RemoteSafeHandle git_remote_create(RepositorySafeHandle repo, stri
15431543
}
15441544
}
15451545

1546+
public static RemoteSafeHandle git_remote_create_inmemory(RepositorySafeHandle repo, string url, string refspec)
1547+
{
1548+
using (ThreadAffinity())
1549+
{
1550+
RemoteSafeHandle handle;
1551+
int res = NativeMethods.git_remote_create_inmemory(out handle, repo, url, refspec);
1552+
Ensure.ZeroResult(res);
1553+
1554+
return handle;
1555+
}
1556+
}
1557+
15461558
public static void git_remote_connect(RemoteSafeHandle remote, GitDirection direction)
15471559
{
15481560
using (ThreadAffinity())

LibGit2Sharp/Network.cs

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,57 @@ public virtual IEnumerable<DirectReference> ListReferences(Remote remote)
7474
}
7575
}
7676

77+
/// <summary>
78+
/// List references in a remote repository.
79+
/// <para>
80+
/// When the remote tips are ahead of the local ones, the retrieved
81+
/// <see cref="DirectReference"/>s may point to non existing
82+
/// <see cref="GitObject"/>s in the local repository. In that
83+
/// case, <see cref="DirectReference.Target"/> will return <c>null</c>.
84+
/// </para>
85+
/// </summary>
86+
/// <param name="url">The url to list from.</param>
87+
/// <returns>The references in the remote repository.</returns>
88+
public virtual IEnumerable<DirectReference> ListReferences(string url)
89+
{
90+
Ensure.ArgumentNotNull(url, "url");
91+
92+
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_create_inmemory(repository.Handle, null, url))
93+
{
94+
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
95+
return Proxy.git_remote_ls(repository, remoteHandle);
96+
}
97+
}
98+
99+
static void DoFetch(RemoteSafeHandle remoteHandle, GitRemoteCallbacks gitCallbacks, TagFetchMode? tagFetchMode)
100+
{
101+
if (tagFetchMode.HasValue)
102+
{
103+
Proxy.git_remote_set_autotag(remoteHandle, tagFetchMode.Value);
104+
}
105+
106+
// It is OK to pass the reference to the GitCallbacks directly here because libgit2 makes a copy of
107+
// the data in the git_remote_callbacks structure. If, in the future, libgit2 changes its implementation
108+
// to store a reference to the git_remote_callbacks structure this would introduce a subtle bug
109+
// where the managed layer could move the git_remote_callbacks to a different location in memory,
110+
// but libgit2 would still reference the old address.
111+
//
112+
// Also, if GitRemoteCallbacks were a class instead of a struct, we would need to guard against
113+
// GC occuring in between setting the remote callbacks and actual usage in one of the functions afterwords.
114+
Proxy.git_remote_set_callbacks(remoteHandle, ref gitCallbacks);
115+
116+
try
117+
{
118+
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
119+
Proxy.git_remote_download(remoteHandle);
120+
Proxy.git_remote_update_tips(remoteHandle);
121+
}
122+
finally
123+
{
124+
Proxy.git_remote_disconnect(remoteHandle);
125+
}
126+
}
127+
77128
/// <summary>
78129
/// Fetch from the <see cref="Remote"/>.
79130
/// </summary>
@@ -99,31 +150,39 @@ public virtual void Fetch(
99150
var callbacks = new RemoteCallbacks(onProgress, onTransferProgress, onUpdateTips, credentials);
100151
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
101152

102-
if (tagFetchMode.HasValue)
103-
{
104-
Proxy.git_remote_set_autotag(remoteHandle, tagFetchMode.Value);
105-
}
153+
DoFetch(remoteHandle, gitCallbacks, tagFetchMode);
154+
}
155+
}
106156

107-
// It is OK to pass the reference to the GitCallbacks directly here because libgit2 makes a copy of
108-
// the data in the git_remote_callbacks structure. If, in the future, libgit2 changes its implementation
109-
// to store a reference to the git_remote_callbacks structure this would introduce a subtle bug
110-
// where the managed layer could move the git_remote_callbacks to a different location in memory,
111-
// but libgit2 would still reference the old address.
112-
//
113-
// Also, if GitRemoteCallbacks were a class instead of a struct, we would need to guard against
114-
// GC occuring in between setting the remote callbacks and actual usage in one of the functions afterwords.
115-
Proxy.git_remote_set_callbacks(remoteHandle, ref gitCallbacks);
157+
/// <summary>
158+
/// Fetch from a url with a set of fetch refspecs
159+
/// </summary>
160+
/// <param name="url">The url to fetch from</param>
161+
/// <param name="refspecs">The list of resfpecs to use</param>
162+
/// <param name="tagFetchMode">Optional parameter indicating what tags to download.</param>
163+
/// <param name="onProgress">Progress callback. Corresponds to libgit2 progress callback.</param>
164+
/// <param name="onUpdateTips">UpdateTips callback. Corresponds to libgit2 update_tips callback.</param>
165+
/// <param name="onTransferProgress">Callback method that transfer progress will be reported through.
166+
/// Reports the client's state regarding the received and processed (bytes, objects) from the server.</param>
167+
/// <param name="credentials">Credentials to use for username/password authentication.</param>
168+
public virtual void Fetch(
169+
string url,
170+
IEnumerable<string> refspecs,
171+
TagFetchMode? tagFetchMode = null,
172+
ProgressHandler onProgress = null,
173+
UpdateTipsHandler onUpdateTips = null,
174+
TransferProgressHandler onTransferProgress = null,
175+
Credentials credentials = null)
176+
{
177+
Ensure.ArgumentNotNull(url, "url");
116178

117-
try
118-
{
119-
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
120-
Proxy.git_remote_download(remoteHandle);
121-
Proxy.git_remote_update_tips(remoteHandle);
122-
}
123-
finally
124-
{
125-
Proxy.git_remote_disconnect(remoteHandle);
126-
}
179+
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_create_inmemory(repository.Handle, null, url))
180+
{
181+
Proxy.git_remote_set_fetch_refspecs(remoteHandle, refspecs);
182+
var callbacks = new RemoteCallbacks(onProgress, onTransferProgress, onUpdateTips, credentials);
183+
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
184+
185+
DoFetch(remoteHandle, gitCallbacks, tagFetchMode);
127186
}
128187
}
129188

0 commit comments

Comments
 (0)
0