Skip to content

Commit ac88bb8

Browse files
committed
Repository.Clone
1 parent 95b7f52 commit ac88bb8

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

LibGit2Sharp.Tests/RepositoryFixture.cs

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

89
namespace LibGit2Sharp.Tests
910
{
1011
public class RepositoryFixture : BaseFixture
1112
{
1213
private const string commitSha = "8496071c1b46c854b31185ea97743be6a8774479";
14+
private const string TestRepoUrl = "git://github.com/libgit2/TestGitRepository";
1315

1416
[Fact]
1517
public void CanCreateBareRepo()
@@ -465,5 +467,72 @@ public void CanDetectIfTheHeadIsOrphaned()
465467
Assert.False(repo.Info.IsHeadOrphaned);
466468
}
467469
}
470+
471+
[Theory]
472+
[InlineData("http://github.com/libgit2/TestGitRepository")]
473+
[InlineData("https://github.com/libgit2/TestGitRepository")]
474+
[InlineData("git://github.com/libgit2/TestGitRepository")]
475+
//[InlineData("[email protected]:libgit2/TestGitRepository")]
476+
public void CanClone(string url)
477+
{
478+
var scd = BuildSelfCleaningDirectory();
479+
using (Repository repo = Repository.Clone(url, scd.RootedDirectoryPath))
480+
{
481+
string dir = repo.Info.Path;
482+
Assert.True(Path.IsPathRooted(dir));
483+
Assert.True(Directory.Exists(dir));
484+
485+
Assert.NotNull(repo.Info.WorkingDirectory);
486+
Assert.Equal(Path.Combine(scd.RootedDirectoryPath, ".git" + Path.DirectorySeparatorChar), repo.Info.Path);
487+
Assert.False(repo.Info.IsBare);
488+
489+
Assert.True(File.Exists(Path.Combine(scd.RootedDirectoryPath, "master.txt")));
490+
Assert.Equal(repo.Head.Name, "master");
491+
Assert.Equal(repo.Head.Tip.Id.ToString(), "49322bb17d3acc9146f98c97d078513228bbf3c0");
492+
}
493+
}
494+
495+
[Theory]
496+
[InlineData("http://github.com/libgit2/TestGitRepository")]
497+
[InlineData("https://github.com/libgit2/TestGitRepository")]
498+
[InlineData("git://github.com/libgit2/TestGitRepository")]
499+
//[InlineData("[email protected]:libgit2/TestGitRepository")]
500+
public void CanCloneBarely(string url)
501+
{
502+
var scd = BuildSelfCleaningDirectory();
503+
using (Repository repo = Repository.Clone(url, scd.RootedDirectoryPath, bare:true))
504+
{
505+
string dir = repo.Info.Path;
506+
Assert.True(Path.IsPathRooted(dir));
507+
Assert.True(Directory.Exists(dir));
508+
509+
Assert.Null(repo.Info.WorkingDirectory);
510+
Assert.Equal(scd.RootedDirectoryPath + Path.DirectorySeparatorChar, repo.Info.Path);
511+
Assert.True(repo.Info.IsBare);
512+
}
513+
}
514+
515+
[Fact]
516+
public void WontCheckoutIfAskedNotTo()
517+
{
518+
var scd = BuildSelfCleaningDirectory();
519+
using (Repository repo = Repository.Clone(TestRepoUrl, scd.RootedDirectoryPath, checkout:false))
520+
{
521+
Assert.False(File.Exists(Path.Combine(scd.RootedDirectoryPath, "master.txt")));
522+
}
523+
}
524+
525+
[Fact]
526+
public void CallsTransferProgress()
527+
{
528+
bool wasCalled = false;
529+
530+
var scd = BuildSelfCleaningDirectory();
531+
using (Repository repo = Repository.Clone(TestRepoUrl, scd.RootedDirectoryPath,
532+
onTransferProgress: (_) => wasCalled = true))
533+
{
534+
Assert.True(wasCalled);
535+
}
536+
}
468537
}
469538
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,23 @@ internal static extern int git_checkout_head(
154154
RepositorySafeHandle repo,
155155
GitCheckoutOpts opts);
156156

157+
[DllImport(libgit2)]
158+
internal static extern int git_clone(
159+
out RepositorySafeHandle repo,
160+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string origin_url,
161+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] FilePath workdir_path,
162+
git_transfer_progress_callback transfer_callback,
163+
IntPtr transfer_payload,
164+
GitCheckoutOpts checkout_opts);
165+
166+
[DllImport(libgit2)]
167+
internal static extern int git_clone_bare(
168+
out RepositorySafeHandle repo,
169+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string url,
170+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))] FilePath destination,
171+
git_transfer_progress_callback transfer_callback,
172+
IntPtr transfer_payload);
173+
157174
[DllImport(libgit2)]
158175
internal static extern IntPtr git_commit_author(GitObjectSafeHandle commit);
159176

LibGit2Sharp/Core/Proxy.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,39 @@ public static void git_checkout_head(RepositorySafeHandle repo, GitCheckoutOpts
190190

191191
#endregion
192192

193+
#region git_clone_
194+
195+
public static RepositorySafeHandle git_clone(
196+
string url,
197+
string workdir,
198+
NativeMethods.git_transfer_progress_callback transfer_cb,
199+
GitCheckoutOpts checkoutOptions)
200+
{
201+
using (ThreadAffinity())
202+
{
203+
RepositorySafeHandle repo;
204+
int res = NativeMethods.git_clone(out repo, url, workdir, transfer_cb, IntPtr.Zero, checkoutOptions);
205+
Ensure.Success(res);
206+
return repo;
207+
}
208+
}
209+
210+
public static RepositorySafeHandle git_clone_bare(
211+
string url,
212+
string workdir,
213+
NativeMethods.git_transfer_progress_callback transfer_cb)
214+
{
215+
using (ThreadAffinity())
216+
{
217+
RepositorySafeHandle repo;
218+
int res = NativeMethods.git_clone_bare(out repo, url, workdir, transfer_cb, IntPtr.Zero);
219+
Ensure.Success(res);
220+
return repo;
221+
}
222+
}
223+
224+
#endregion
225+
193226
#region git_commit_
194227

195228
public static Signature git_commit_author(GitObjectSafeHandle obj)

LibGit2Sharp/Repository.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,41 @@ public static string Discover(string startingPath)
398398
return discoveredPath.Native;
399399
}
400400

401+
/// <summary>
402+
/// Clone with specified options.
403+
/// </summary>
404+
/// <param name="sourceUrl">URI for the remote repository</param>
405+
/// <param name="workdirPath">Local path to clone into</param>
406+
/// <param name="bare">True will result in a bare clone, false a full clone.</param>
407+
/// <param name="checkout">If true, the origin's HEAD will be checked out. This only applies
408+
/// to non-bare repositories.</param>
409+
/// <param name="onTransferProgress">Handler for progress information</param>
410+
/// <returns></returns>
411+
public static Repository Clone(string sourceUrl, string workdirPath,
412+
bool bare = false,
413+
bool checkout = true,
414+
TransferProgressHandler onTransferProgress = null)
415+
{
416+
GitCheckoutOpts nativeOpts = null;
417+
if (checkout)
418+
{
419+
nativeOpts = new GitCheckoutOpts
420+
{
421+
checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_CREATE_MISSING
422+
};
423+
}
424+
425+
NativeMethods.git_transfer_progress_callback cb =
426+
TransferCallbacks.GenerateCallback(onTransferProgress);
427+
428+
RepositorySafeHandle repo = bare
429+
? Proxy.git_clone_bare(sourceUrl, workdirPath, cb)
430+
: Proxy.git_clone(sourceUrl, workdirPath, cb, nativeOpts);
431+
repo.SafeDispose();
432+
433+
return new Repository(workdirPath);
434+
}
435+
401436
/// <summary>
402437
/// Checkout the specified <see cref = "Branch" />, reference or SHA.
403438
/// </summary>

0 commit comments

Comments
 (0)