From b2067248632e8bf88f8be40a18079fab95b68f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 15 May 2014 09:03:30 +0200 Subject: [PATCH 1/3] clone: add failing test for a mirror-clone with clone_into Show a failure to perform a mirror-clone from a repository, both local and remote. --- tests/network/fetchlocal.c | 26 ++++++++++++++++++++++++++ tests/online/clone.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c index 4c39394bb..0d23bef48 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 6e0e63950..e269771c0 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; From 3c607685da2cf9a22559f53fd7670e1c4cad45e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 19 May 2014 13:36:00 +0200 Subject: [PATCH 2/3] clone: duplicate the remote Instead of changing the user-provided remote, duplicate it so we can add the extra refspec without having to worry about unsetting it before returning. --- src/clone.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/clone.c b/src/clone.c index c6be00f0e..c627edbc0 100644 --- a/src/clone.c +++ b/src/clone.c @@ -336,25 +336,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 +380,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; From 32332fccc9f1e316bcecf3ab5133331315e31871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 19 May 2014 14:15:40 +0200 Subject: [PATCH 3/3] clone: don't error out if the branch already exists We set up the current branch after we fetch from the remote. This means that the user's refspec may have already created this reference. It is therefore not an error if we cannot create the branch because it already exists. This allows for the user to replicate git-clone's --mirror option. --- src/clone.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/clone.c b/src/clone.c index c627edbc0..24f1cae59 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; }