From 3f8942052306a9d62521bdb6e6d5e7636bc5526e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 6 Jun 2014 15:01:45 +0200 Subject: [PATCH 1/5] remote: allow overriding the refspecs for download and fetch With opportunistic ref updates, git has introduced the concept of having base refspecs *and* refspecs that are active for a particular fetch. Let's start by letting the user override the refspecs for download. --- examples/network/fetch.c | 2 +- include/git2/remote.h | 8 ++++++- src/clone.c | 4 ++-- src/remote.c | 41 ++++++++++++++++++++++++++++++------ tests/fetchhead/nonetwork.c | 2 +- tests/network/fetchlocal.c | 4 ++-- tests/network/remote/local.c | 18 ++++++++-------- tests/online/fetch.c | 10 ++++----- tests/online/fetchhead.c | 2 +- tests/online/push.c | 2 +- 10 files changed, 64 insertions(+), 29 deletions(-) diff --git a/examples/network/fetch.c b/examples/network/fetch.c index 8d882095b..ab78fc9bd 100644 --- a/examples/network/fetch.c +++ b/examples/network/fetch.c @@ -36,7 +36,7 @@ static void *download(void *ptr) // Download the packfile and index it. This function updates the // amount of received data and the indexer stats which lets you // inform the user about progress. - if (git_remote_download(data->remote) < 0) { + if (git_remote_download(data->remote, NULL) < 0) { data->ret = -1; goto exit; } diff --git a/include/git2/remote.h b/include/git2/remote.h index 055f5e517..b714d3469 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -306,9 +306,12 @@ GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_r * The .idx file will be created and both it and the packfile with be * renamed to their final name. * + * @param remote the remote + * @param refspecs the refspecs to use for this negotiation and + * download. Use NULL to use the base refspecs * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_download(git_remote *remote); +GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs); /** * Check whether the remote is connected @@ -373,6 +376,8 @@ GIT_EXTERN(int) git_remote_update_tips( * disconnect and update the remote-tracking branches. * * @param remote the remote to fetch from + * @param refspecs the refspecs to use for this fetch. Pass NULL to + * use the base refspecs. * @param signature The identity to use when updating reflogs * @param reflog_message The message to insert into the reflogs. If NULL, the * default is "fetch" @@ -380,6 +385,7 @@ GIT_EXTERN(int) git_remote_update_tips( */ GIT_EXTERN(int) git_remote_fetch( git_remote *remote, + const git_strarray *refspecs, const git_signature *signature, const char *reflog_message); diff --git a/src/clone.c b/src/clone.c index 43b839003..f18f07611 100644 --- a/src/clone.c +++ b/src/clone.c @@ -358,7 +358,7 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_check git_remote_set_update_fetchhead(remote, 0); git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); - if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0) + if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0) goto cleanup; error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message)); @@ -553,7 +553,7 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_ git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); - if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0) + if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0) goto cleanup; error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message)); diff --git a/src/remote.c b/src/remote.c index dfad946d5..8cf8c0353 100644 --- a/src/remote.c +++ b/src/remote.c @@ -21,7 +21,7 @@ static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); -static int add_refspec(git_remote *remote, const char *string, bool is_fetch) +static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) { git_refspec *spec; @@ -34,7 +34,7 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch) } spec->push = !is_fetch; - if (git_vector_insert(&remote->refspecs, spec) < 0) { + if (git_vector_insert(vector, spec) < 0) { git_refspec__free(spec); git__free(spec); return -1; @@ -43,6 +43,11 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch) return 0; } +static int add_refspec(git_remote *remote, const char *string, bool is_fetch) +{ + return add_refspec_to(&remote->refspecs, string, is_fetch); +} + static int download_tags_value(git_remote *remote, git_config *cfg) { const git_config_entry *ce; @@ -813,20 +818,37 @@ static int ls_to_vector(git_vector *out, git_remote *remote) return 0; } -int git_remote_download(git_remote *remote) +int git_remote_download(git_remote *remote, const git_strarray *refspecs) { int error; - git_vector refs; + size_t i; + git_vector refs, specs, *to_active; assert(remote); if (ls_to_vector(&refs, remote) < 0) return -1; + if ((git_vector_init(&specs, 0, NULL)) < 0) + goto on_error; + + if (!refspecs) { + to_active = &remote->refspecs; + } else { + for (i = 0; i < refspecs->count; i++) { + if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0) + goto on_error; + } + + to_active = &specs; + } + free_refspecs(&remote->active_refspecs); - error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs); + error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); git_vector_free(&refs); + free_refspecs(&specs); + git_vector_free(&specs); if (error < 0) return error; @@ -835,10 +857,17 @@ int git_remote_download(git_remote *remote) return error; return git_fetch_download_pack(remote); + +on_error: + git_vector_free(&refs); + free_refspecs(&specs); + git_vector_free(&specs); + return error; } int git_remote_fetch( git_remote *remote, + const git_strarray *refspecs, const git_signature *signature, const char *reflog_message) { @@ -849,7 +878,7 @@ int git_remote_fetch( if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) return error; - error = git_remote_download(remote); + error = git_remote_download(remote, refspecs); /* We don't need to be connected anymore */ git_remote_disconnect(remote); diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c index 7b64a6339..c8191c5f5 100644 --- a/tests/fetchhead/nonetwork.c +++ b/tests/fetchhead/nonetwork.c @@ -335,7 +335,7 @@ void test_fetchhead_nonetwork__unborn_with_upstream(void) cl_git_pass(git_remote_set_url(remote, cl_fixture("testrepo.git"))); cl_git_pass(git_remote_save(remote)); - cl_git_pass(git_remote_fetch(remote, NULL, NULL)); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); git_remote_free(remote); cl_git_pass(git_repository_fetchhead_foreach(repo, assert_master_for_merge, NULL)); diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c index 8809f427d..bf0d0872b 100644 --- a/tests/network/fetchlocal.c +++ b/tests/network/fetchlocal.c @@ -36,7 +36,7 @@ void test_network_fetchlocal__complete(void) cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); git_remote_set_callbacks(origin, &callbacks); cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(origin)); + cl_git_pass(git_remote_download(origin, NULL)); cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); cl_git_pass(git_reference_list(&refnames, repo)); @@ -74,7 +74,7 @@ void test_network_fetchlocal__partial(void) cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); git_remote_set_callbacks(origin, &callbacks); cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(origin)); + cl_git_pass(git_remote_download(origin, NULL)); cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); git_strarray_free(&refnames); diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index f1084fc38..0fbed6024 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -125,7 +125,7 @@ void test_network_remote_local__shorthand_fetch_refspec0(void) cl_git_pass(git_remote_add_fetch(remote, refspec)); cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); @@ -147,7 +147,7 @@ void test_network_remote_local__shorthand_fetch_refspec1(void) cl_git_pass(git_remote_add_fetch(remote, refspec)); cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); @@ -162,7 +162,7 @@ void test_network_remote_local__tagopt(void) connect_to_local_repository(cl_fixture("testrepo.git")); git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); @@ -181,7 +181,7 @@ void test_network_remote_local__push_to_bare_remote(void) /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -218,7 +218,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -258,7 +258,7 @@ void test_network_remote_local__push_to_non_bare_remote(void) /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -299,7 +299,7 @@ void test_network_remote_local__fetch(void) connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_fetch(remote, sig, "UPDAAAAAATE!!")); + cl_git_pass(git_remote_fetch(remote, NULL, sig, "UPDAAAAAATE!!")); cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); git_reference_free(ref); @@ -327,7 +327,7 @@ void test_network_remote_local__reflog(void) connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, sig, "UPDAAAAAATE!!")); cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); @@ -354,7 +354,7 @@ void test_network_remote_local__fetch_default_reflog_message(void) connect_to_local_repository(cl_fixture("testrepo.git")); cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_fetch(remote, sig, NULL)); + cl_git_pass(git_remote_fetch(remote, NULL, sig, NULL)); cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); cl_assert_equal_i(1, git_reflog_entrycount(log)); diff --git a/tests/online/fetch.c b/tests/online/fetch.c index f03a6faa6..ce92ee82b 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -47,7 +47,7 @@ static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n) git_remote_set_callbacks(remote, &callbacks); git_remote_set_autotag(remote, flag); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); cl_assert_equal_i(counter, n); @@ -86,11 +86,11 @@ void test_online_fetch__fetch_twice(void) git_remote *remote; cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git")); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); git_remote_disconnect(remote); git_remote_connect(remote, GIT_DIRECTION_FETCH); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); git_remote_disconnect(remote); git_remote_free(remote); @@ -128,7 +128,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date callbacks.transfer_progress = &transferProgressCallback; callbacks.payload = &invoked; git_remote_set_callbacks(remote, &callbacks); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_assert_equal_i(false, invoked); @@ -162,7 +162,7 @@ void test_online_fetch__can_cancel(void) git_remote_set_callbacks(remote, &callbacks); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_fail_with(git_remote_download(remote), -4321); + cl_git_fail_with(git_remote_download(remote, NULL), -4321); git_remote_disconnect(remote); git_remote_free(remote); } diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c index 3f27e1331..f6b9ab0af 100644 --- a/tests/online/fetchhead.c +++ b/tests/online/fetchhead.c @@ -50,7 +50,7 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet } cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); git_remote_free(remote); diff --git a/tests/online/push.c b/tests/online/push.c index 70ec705fe..b09c7ad1f 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -408,7 +408,7 @@ void test_online_push__initialize(void) /* Now that we've deleted everything, fetch from the remote */ cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(_remote)); + cl_git_pass(git_remote_download(_remote, NULL)); cl_git_pass(git_remote_update_tips(_remote, NULL, NULL)); git_remote_disconnect(_remote); } From 9c206a2248e74f385c5f1a9928560efd97e3922f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 6 Jun 2014 15:24:53 +0200 Subject: [PATCH 2/5] remote: use active refspec override in the tests This lets us test this bit as well as getting closer to what they were trying to do. --- tests/network/remote/local.c | 91 ++++++++++++++++++++++++++---------- tests/online/fetchhead.c | 8 ++-- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index 0fbed6024..ceccf45d0 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -116,16 +116,20 @@ void test_network_remote_local__nested_tags_are_completely_peeled(void) void test_network_remote_local__shorthand_fetch_refspec0(void) { - const char *refspec = "master:remotes/sloppy/master"; - const char *refspec2 = "master:boh/sloppy/master"; + char *refspec_strings[] = { + "master:remotes/sloppy/master", + "master:boh/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 2, + }; git_reference *ref; connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); @@ -137,17 +141,21 @@ void test_network_remote_local__shorthand_fetch_refspec0(void) void test_network_remote_local__shorthand_fetch_refspec1(void) { - const char *refspec = "master"; - const char *refspec2 = "hard_tag"; + char *refspec_strings[] = { + "master", + "hard_tag", + }; + git_strarray array = { + refspec_strings, + 2, + }; git_reference *ref; connect_to_local_repository(cl_fixture("testrepo.git")); git_remote_clear_refspecs(remote); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); @@ -174,14 +182,20 @@ void test_network_remote_local__tagopt(void) void test_network_remote_local__push_to_bare_remote(void) { + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; /* Should be able to push to a bare remote */ git_remote *localremote; git_push *push; /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -210,6 +224,13 @@ void test_network_remote_local__push_to_bare_remote(void) void test_network_remote_local__push_to_bare_remote_with_file_url(void) { + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; /* Should be able to push to a bare remote */ git_remote *localremote; git_push *push; @@ -217,8 +238,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -251,14 +271,20 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) void test_network_remote_local__push_to_non_bare_remote(void) { + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; /* Shouldn't be able to push to a non-bare remote */ git_remote *localremote; git_push *push; /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -287,7 +313,13 @@ void test_network_remote_local__push_to_non_bare_remote(void) void test_network_remote_local__fetch(void) { - const char *refspec = "master:remotes/sloppy/master"; + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; git_reflog *log; const git_reflog_entry *entry; @@ -297,9 +329,8 @@ void test_network_remote_local__fetch(void) cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_fetch(remote, NULL, sig, "UPDAAAAAATE!!")); + cl_git_pass(git_remote_fetch(remote, &array, sig, "UPDAAAAAATE!!")); cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); git_reference_free(ref); @@ -316,7 +347,13 @@ void test_network_remote_local__fetch(void) void test_network_remote_local__reflog(void) { - const char *refspec = "master:remotes/sloppy/master"; + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; git_reflog *log; const git_reflog_entry *entry; @@ -325,9 +362,8 @@ void test_network_remote_local__reflog(void) cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, &array)); cl_git_pass(git_remote_update_tips(remote, sig, "UPDAAAAAATE!!")); cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); @@ -342,7 +378,13 @@ void test_network_remote_local__reflog(void) void test_network_remote_local__fetch_default_reflog_message(void) { - const char *refspec = "master:remotes/sloppy/master"; + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; git_reflog *log; const git_reflog_entry *entry; @@ -352,9 +394,8 @@ void test_network_remote_local__fetch_default_reflog_message(void) cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_fetch(remote, NULL, sig, NULL)); + cl_git_pass(git_remote_fetch(remote, &array, sig, NULL)); cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); cl_assert_equal_i(1, git_reflog_entrycount(log)); diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c index f6b9ab0af..bd423bbb0 100644 --- a/tests/online/fetchhead.c +++ b/tests/online/fetchhead.c @@ -40,17 +40,19 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet git_remote *remote; git_buf fetchhead_buf = GIT_BUF_INIT; int equals = 0; + git_strarray array, *active_refs = NULL; cl_git_pass(git_remote_load(&remote, g_repo, "origin")); git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO); if(fetchspec != NULL) { - git_remote_clear_refspecs(remote); - git_remote_add_fetch(remote, fetchspec); + array.count = 1; + array.strings = (char **) &fetchspec; + active_refs = &array; } cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_download(remote, active_refs)); cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); git_remote_free(remote); From c300d84a64601b09abcf0f2d2e6d492b4578770d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Jul 2014 07:54:26 +0200 Subject: [PATCH 3/5] remote: don't DWIM refspecs unnecessarily We can only DWIM when we've connected to the remote and have the list of the remote's references. Adding or setting the refspecs should not trigger an attempt to DWIM the refspecs as we typically cannot do it, and even if we did, we would not use them for the current fetch. --- src/remote.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/remote.c b/src/remote.c index 8cf8c0353..b3e968e2e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1669,27 +1669,14 @@ void git_remote_clear_refspecs(git_remote *remote) git_vector_clear(&remote->refspecs); } -static int add_and_dwim(git_remote *remote, const char *str, int push) -{ - git_refspec *spec; - git_vector *vec; - - if (add_refspec(remote, str, !push) < 0) - return -1; - - vec = &remote->refspecs; - spec = git_vector_get(vec, vec->length - 1); - return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs); -} - int git_remote_add_fetch(git_remote *remote, const char *refspec) { - return add_and_dwim(remote, refspec, false); + return add_refspec(remote, refspec, true); } int git_remote_add_push(git_remote *remote, const char *refspec) { - return add_and_dwim(remote, refspec, true); + return add_refspec(remote, refspec, false); } static int set_refspecs(git_remote *remote, git_strarray *array, int push) @@ -1717,10 +1704,7 @@ static int set_refspecs(git_remote *remote, git_strarray *array, int push) return -1; } - free_refspecs(&remote->active_refspecs); - git_vector_clear(&remote->active_refspecs); - - return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs); + return 0; } int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array) From 2cdd5c5752f1dea43cd31be9b75be7f7f5e4eab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 3 Jul 2014 07:56:31 +0200 Subject: [PATCH 4/5] remote: store passive refspecs The configured/base fetch refspecs need to be taken into account in order to implement opportunistic remote-tracking branch updates. DWIM them and store them in the struct, but don't do anything with them yet. --- src/remote.c | 10 +++++++++- src/remote.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index b3e968e2e..c164db909 100644 --- a/src/remote.c +++ b/src/remote.c @@ -375,6 +375,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) if (git_vector_init(&remote->refs, 32, NULL) < 0 || git_vector_init(&remote->refspecs, 2, NULL) < 0 || + git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 || git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; @@ -843,9 +844,13 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs) to_active = &specs; } - free_refspecs(&remote->active_refspecs); + free_refspecs(&remote->passive_refspecs); + if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0) + goto on_error; + free_refspecs(&remote->active_refspecs); error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); + git_vector_free(&refs); free_refspecs(&specs); git_vector_free(&specs); @@ -1218,6 +1223,9 @@ void git_remote_free(git_remote *remote) free_refspecs(&remote->active_refspecs); git_vector_free(&remote->active_refspecs); + free_refspecs(&remote->passive_refspecs); + git_vector_free(&remote->passive_refspecs); + git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); diff --git a/src/remote.h b/src/remote.h index f88601e9b..73c1614f6 100644 --- a/src/remote.h +++ b/src/remote.h @@ -23,6 +23,7 @@ struct git_remote { git_vector refs; git_vector refspecs; git_vector active_refspecs; + git_vector passive_refspecs; git_transport_cb transport_cb; void *transport_cb_payload; git_transport *transport; From c5837cad85c2730d30cd3c8b1018bd392ca8115a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Jul 2014 09:03:33 +0200 Subject: [PATCH 5/5] remote: implement opportunistic remote-tracking branch updates When a list of refspecs is passed to fetch (what git would consider refspec passed on the command-line), we not only need to perform the updates described in that refspec, but also update the remote-tracking branch of the fetched remote heads according to the remote's configured refspecs. These "fetches" are not however to be written to FETCH_HEAD as they would be duplicate data, and it's not what the user asked for. --- CHANGELOG.md | 6 +++ src/remote.c | 96 ++++++++++++++++++++++++++++++++++++ src/remote.h | 1 + tests/network/remote/local.c | 21 ++++++++ 4 files changed, 124 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b23e07d93..8ad323c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,5 +56,11 @@ v0.21 + 1 * Add support for refspecs with the asterisk in the middle of a pattern. +* Fetching now performs opportunistic updates. To achieve this, we + introduce a difference between active and passive refspecs, which + make git_remote_download and git_remote_fetch to take a list of + resfpecs to be the active list, similarly to how git fetch accepts a + list on the command-line. + * Introduce git_merge_bases() and the git_oidarray type to expose all merge bases between two commits. diff --git a/src/remote.c b/src/remote.c index c164db909..432367deb 100644 --- a/src/remote.c +++ b/src/remote.c @@ -833,6 +833,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs) if ((git_vector_init(&specs, 0, NULL)) < 0) goto on_error; + remote->passed_refspecs = 0; if (!refspecs) { to_active = &remote->refspecs; } else { @@ -842,6 +843,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs) } to_active = &specs; + remote->passed_refspecs = 1; } free_refspecs(&remote->passive_refspecs); @@ -1140,6 +1142,96 @@ on_error: } +/** + * Iteration over the three vectors, with a pause whenever we find a match + * + * On each stop, we store the iteration stat in the inout i,j,k + * parameters, and return the currently matching passive refspec as + * well as the head which we matched. + */ +static int next_head(const git_remote *remote, git_vector *refs, + git_refspec **out_spec, git_remote_head **out_head, + size_t *out_i, size_t *out_j, size_t *out_k) +{ + const git_vector *active, *passive; + git_remote_head *head; + git_refspec *spec, *passive_spec; + size_t i, j, k; + + active = &remote->active_refspecs; + passive = &remote->passive_refspecs; + + i = *out_i; + j = *out_j; + k = *out_k; + + for (; i < refs->length; i++) { + head = git_vector_get(refs, i); + + if (!git_reference_is_valid_name(head->name)) + continue; + + for (; j < active->length; j++) { + spec = git_vector_get(active, j); + + if (!git_refspec_src_matches(spec, head->name)) + continue; + + for (; k < passive->length; k++) { + passive_spec = git_vector_get(passive, k); + + if (!git_refspec_src_matches(passive_spec, head->name)) + continue; + + *out_spec = passive_spec; + *out_head = head; + *out_i = i; + *out_j = j; + *out_k = k + 1; + return 0; + + } + k = 0; + } + j = 0; + } + + return GIT_ITEROVER; +} + +static int opportunistic_updates(const git_remote *remote, git_vector *refs, const git_signature *sig, const char *msg) +{ + size_t i, j, k; + git_refspec *spec; + git_remote_head *head; + git_reference *ref; + git_buf refname = GIT_BUF_INIT; + int error; + + i = j = k = 0; + + while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { + /* + * If we got here, there is a refspec which was used + * for fetching which matches the source of one of the + * passive refspecs, so we should update that + * remote-tracking branch, but not add it to + * FETCH_HEAD + */ + + if ((error = git_refspec_transform(&refname, spec, head->name)) < 0) + return error; + + error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, sig, msg); + git_buf_free(&refname); + + if (error < 0) + return error; + } + + return 0; +} + int git_remote_update_tips( git_remote *remote, const git_signature *signature, @@ -1170,6 +1262,10 @@ int git_remote_update_tips( goto out; } + /* only try to do opportunisitic updates if the refpec lists differ */ + if (remote->passed_refspecs) + error = opportunistic_updates(remote, &refs, signature, reflog_message); + out: git_vector_free(&refs); git_refspec__free(&tagspec); diff --git a/src/remote.h b/src/remote.h index 73c1614f6..b79ace438 100644 --- a/src/remote.h +++ b/src/remote.h @@ -33,6 +33,7 @@ struct git_remote { unsigned int need_pack; git_remote_autotag_option_t download_tags; int update_fetchhead; + int passed_refspecs; }; const char* git_remote__urlfordirection(struct git_remote *remote, int direction); diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index ceccf45d0..c6c9e4ca9 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -408,3 +408,24 @@ void test_network_remote_local__fetch_default_reflog_message(void) git_reflog_free(log); git_signature_free(sig); } + +void test_network_remote_local__opportunistic_update(void) +{ + git_reference *ref; + char *refspec_strings[] = { + "master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + + /* this remote has a passive refspec of "refs/heads/:refs/remotes/origin/" */ + cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"))); + /* and we pass the active refspec "master" */ + cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL)); + + /* and we expect that to update our copy of origin's master */ + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master")); + git_reference_free(ref); +}