Merge pull request #2354 from libgit2/cmn/clone-into-mirror Allow mirror-clone via `git_clone_into()`
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
diff --git a/src/clone.c b/src/clone.c
index c6be00f..24f1cae 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -171,6 +171,10 @@ static int update_head_to_new_branch(
git_reference_free(tracking_branch);
+ /* if it already existed, then the user's refspec created it for us, ignore it' */
+ if (error == GIT_EEXISTS)
+ error = 0;
+
return error;
}
@@ -336,25 +340,30 @@ static bool should_checkout(
return !git_repository_head_unborn(repo);
}
-int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
+int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
int error = 0, old_fetchhead;
- git_strarray refspecs;
git_buf reflog_message = GIT_BUF_INIT;
+ git_remote *remote;
+ const git_remote_callbacks *callbacks;
- assert(repo && remote);
+ assert(repo && _remote);
if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
}
-
- if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+ if ((error = git_remote_dup(&remote, _remote)) < 0)
return error;
+ callbacks = git_remote_get_callbacks(_remote);
+ if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
+ (error = git_remote_set_callbacks(remote, git_remote_get_callbacks(_remote))) < 0)
+ goto cleanup;
+
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
- return error;
+ goto cleanup;
old_fetchhead = git_remote_update_fetchhead(remote);
git_remote_set_update_fetchhead(remote, 0);
@@ -375,15 +384,7 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_
cleanup:
git_remote_set_update_fetchhead(remote, old_fetchhead);
-
- /* Go back to the original refspecs */
- {
- int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs);
- if (!error)
- error = error_alt;
- }
-
- git_strarray_free(&refspecs);
+ git_remote_free(remote);
git_buf_free(&reflog_message);
return error;
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index 4c39394..0d23bef 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -86,3 +86,29 @@ void test_network_fetchlocal__partial(void)
git_strarray_free(&refnames);
git_remote_free(origin);
}
+
+void test_network_fetchlocal__clone_into_mirror(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+ git_remote *remote;
+ git_reference *head;
+
+ cl_git_pass(git_repository_init(&repo, "./foo.git", true));
+ cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git")));
+
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, "+refs/*:refs/*"));
+
+ cl_git_pass(git_clone_into(repo, remote, NULL, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&head, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_remote_free(remote);
+ git_reference_free(head);
+ git_repository_free(repo);
+ git_buf_free(&path);
+ cl_fixture_cleanup("./foo.git");
+}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 6e0e639..e269771 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -164,6 +164,39 @@ void test_online_clone__clone_into(void)
git_buf_free(&path);
}
+void test_online_clone__clone_mirror(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_remote *remote;
+ git_reference *head;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ bool fetch_progress_cb_was_called = false;
+
+ cl_git_pass(git_repository_init(&g_repo, "./foo.git", true));
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
+
+ callbacks.transfer_progress = &fetch_progress;
+ callbacks.payload = &fetch_progress_cb_was_called;
+ git_remote_set_callbacks(remote, &callbacks);
+
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, "+refs/*:refs/*"));
+
+ cl_git_pass(git_clone_into(g_repo, remote, NULL, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_remote_free(remote);
+ git_reference_free(head);
+ git_buf_free(&path);
+ cl_fixture_cleanup("./foo.git");
+}
+
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
{
int *callcount = (int*)payload;