Skip to content

Commit

Permalink
Network: allow downloading from a url
Browse files Browse the repository at this point in the history
This is our implementation of in-memory remotes. Ask the repository to
fetch/list from a url instead of a remote.
  • Loading branch information
carlosmn committed Dec 10, 2013
1 parent b9bb6d6 commit 1374884
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 23 deletions.
31 changes: 31 additions & 0 deletions LibGit2Sharp.Tests/NetworkFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,37 @@ public void CanListRemoteReferences(string url)
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
[InlineData("git://github.com/libgit2/TestGitRepository.git")]
public void CanListRemoteReferencesFromUrl(string url)
{
string repoPath = InitNewRepository();

using (var repo = new Repository(repoPath))
{
IList<DirectReference> references = repo.Network.ListReferences(url).ToList();

foreach (var directReference in references)
{
// None of those references point to an existing
// object in this brand new repository
Assert.Null(directReference.Target);
}

List<Tuple<string, string>> actualRefs = references.
Select(directRef => new Tuple<string, string>(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()
{
Expand Down
7 changes: 7 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,13 @@ internal static extern int git_remote_create(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url);

[DllImport(libgit2)]
internal static extern int git_remote_create_inmemory(
out RemoteSafeHandle remote,
RepositorySafeHandle repo,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url);

[DllImport(libgit2)]
internal static extern void git_remote_disconnect(RemoteSafeHandle remote);

Expand Down
12 changes: 12 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,18 @@ public static RemoteSafeHandle git_remote_create(RepositorySafeHandle repo, stri
}
}

public static RemoteSafeHandle git_remote_create_inmemory(RepositorySafeHandle repo, string url, string refspec)
{
using (ThreadAffinity())
{
RemoteSafeHandle handle;
int res = NativeMethods.git_remote_create_inmemory(out handle, repo, url, refspec);
Ensure.ZeroResult(res);

return handle;
}
}

public static void git_remote_connect(RemoteSafeHandle remote, GitDirection direction)
{
using (ThreadAffinity())
Expand Down
105 changes: 82 additions & 23 deletions LibGit2Sharp/Network.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,57 @@ public virtual IEnumerable<DirectReference> ListReferences(Remote remote)
}
}

/// <summary>
/// List references in a remote repository.
/// <para>
/// When the remote tips are ahead of the local ones, the retrieved
/// <see cref="DirectReference"/>s may point to non existing
/// <see cref="GitObject"/>s in the local repository. In that
/// case, <see cref="DirectReference.Target"/> will return <c>null</c>.
/// </para>
/// </summary>
/// <param name="url">The url to list from.</param>
/// <returns>The references in the remote repository.</returns>
public virtual IEnumerable<DirectReference> ListReferences(string url)
{
Ensure.ArgumentNotNull(url, "url");

using (RemoteSafeHandle remoteHandle = Proxy.git_remote_create_inmemory(repository.Handle, null, url))
{
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
return Proxy.git_remote_ls(repository, remoteHandle);
}
}

static void DoFetch(RemoteSafeHandle remoteHandle, GitRemoteCallbacks gitCallbacks, TagFetchMode? tagFetchMode)
{
if (tagFetchMode.HasValue)
{
Proxy.git_remote_set_autotag(remoteHandle, tagFetchMode.Value);
}

// It is OK to pass the reference to the GitCallbacks directly here because libgit2 makes a copy of
// the data in the git_remote_callbacks structure. If, in the future, libgit2 changes its implementation
// to store a reference to the git_remote_callbacks structure this would introduce a subtle bug
// where the managed layer could move the git_remote_callbacks to a different location in memory,
// but libgit2 would still reference the old address.
//
// Also, if GitRemoteCallbacks were a class instead of a struct, we would need to guard against
// GC occuring in between setting the remote callbacks and actual usage in one of the functions afterwords.
Proxy.git_remote_set_callbacks(remoteHandle, ref gitCallbacks);

try
{
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
Proxy.git_remote_download(remoteHandle);
Proxy.git_remote_update_tips(remoteHandle);
}
finally
{
Proxy.git_remote_disconnect(remoteHandle);
}
}

/// <summary>
/// Fetch from the <see cref="Remote"/>.
/// </summary>
Expand All @@ -99,31 +150,39 @@ public virtual void Fetch(
var callbacks = new RemoteCallbacks(onProgress, onTransferProgress, onUpdateTips, credentials);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();

if (tagFetchMode.HasValue)
{
Proxy.git_remote_set_autotag(remoteHandle, tagFetchMode.Value);
}
DoFetch(remoteHandle, gitCallbacks, tagFetchMode);
}
}

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

try
{
Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch);
Proxy.git_remote_download(remoteHandle);
Proxy.git_remote_update_tips(remoteHandle);
}
finally
{
Proxy.git_remote_disconnect(remoteHandle);
}
using (RemoteSafeHandle remoteHandle = Proxy.git_remote_create_inmemory(repository.Handle, null, url))
{
Proxy.git_remote_set_fetch_refspecs(remoteHandle, refspecs);
var callbacks = new RemoteCallbacks(onProgress, onTransferProgress, onUpdateTips, credentials);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();

DoFetch(remoteHandle, gitCallbacks, tagFetchMode);
}
}

Expand Down

0 comments on commit 1374884

Please sign in to comment.