diff --git a/examples/fetch.c b/examples/fetch.c index bbd882cfb..a8b3527a8 100644 --- a/examples/fetch.c +++ b/examples/fetch.c @@ -13,20 +13,24 @@ static int progress_cb(const char *str, int len, void *data) * updated. The message we output depends on whether it's a new one or * an update. */ -static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) +static int update_cb(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data) { char a_str[GIT_OID_SHA1_HEXSIZE+1], b_str[GIT_OID_SHA1_HEXSIZE+1]; + git_buf remote_name; (void)data; + if (git_refspec_rtransform(&remote_name, spec, refname) < 0) + return -1; + git_oid_fmt(b_str, b); b_str[GIT_OID_SHA1_HEXSIZE] = '\0'; if (git_oid_is_zero(a)) { - printf("[new] %.20s %s\n", b_str, refname); + printf("[new] %.20s %s -> %s\n", b_str, remote_name.ptr, refname); } else { git_oid_fmt(a_str, a); a_str[GIT_OID_SHA1_HEXSIZE] = '\0'; - printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname); + printf("[updated] %.10s..%.10s %s -> %s\n", a_str, b_str, remote_name.ptr, refname); } return 0; @@ -72,7 +76,7 @@ int lg2_fetch(git_repository *repo, int argc, char **argv) goto on_error; /* Set up the callbacks (only update_tips for now) */ - fetch_opts.callbacks.update_tips = &update_cb; + fetch_opts.callbacks.update_refs = &update_cb; fetch_opts.callbacks.sideband_progress = &progress_cb; fetch_opts.callbacks.transfer_progress = transfer_progress_cb; fetch_opts.callbacks.credentials = cred_acquire_cb; diff --git a/include/git2/remote.h b/include/git2/remote.h index 5505f6c35..662fc9352 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -83,7 +83,7 @@ typedef enum { /* Write the fetch results to FETCH_HEAD. */ GIT_REMOTE_UPDATE_FETCHHEAD = (1 << 0), - /* Report unchanged tips in the update_tips callback. */ + /* Report unchanged tips in the update_refs callback. */ GIT_REMOTE_UPDATE_REPORT_UNCHANGED = (1 << 1) } git_remote_update_flags; @@ -568,7 +568,8 @@ struct git_remote_callbacks { * Completion is called when different parts of the download * process are done (currently unused). */ - int GIT_CALLBACK(completion)(git_remote_completion_t type, void *data); + int GIT_CALLBACK(completion)(git_remote_completion_t type, + void *data); /** * This will be called if the remote host requires @@ -594,11 +595,22 @@ struct git_remote_callbacks { */ git_indexer_progress_cb transfer_progress; +#ifdef GIT_DEPRECATE_HARD + void *reserved_update_tips; +#else /** - * Each time a reference is updated locally, this function - * will be called with information about it. + * Deprecated callback for reference updates, callers should + * set `update_refs` instead. This is retained for backward + * compatibility; if you specify both `update_refs` and + * `update_tips`, then only the `update_refs` function will + * be called. + * + * @deprecated the `update_refs` callback in this structure + * should be preferred */ - int GIT_CALLBACK(update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); + int GIT_CALLBACK(update_tips)(const char *refname, + const git_oid *a, const git_oid *b, void *data); +#endif /** * Function to call with progress information during pack @@ -655,6 +667,19 @@ struct git_remote_callbacks { */ git_url_resolve_cb resolve_url; #endif + + /** + * Each time a reference is updated locally, this function + * will be called with information about it. This should be + * preferred over the `update_tips` callback in this + * structure. + */ + int GIT_CALLBACK(update_refs)( + const char *refname, + const git_oid *a, + const git_oid *b, + git_refspec *spec, + void *data); }; #define GIT_REMOTE_CALLBACKS_VERSION 1 diff --git a/src/libgit2/push.c b/src/libgit2/push.c index 882092039..b0e84173c 100644 --- a/src/libgit2/push.c +++ b/src/libgit2/push.c @@ -221,12 +221,25 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks) fire_callback = 0; } - if (fire_callback && callbacks && callbacks->update_tips) { - error = callbacks->update_tips(git_str_cstr(&remote_ref_name), - &push_spec->roid, &push_spec->loid, callbacks->payload); + if (!fire_callback || !callbacks) + continue; - if (error < 0) - goto on_error; + if (callbacks->update_refs) + error = callbacks->update_refs( + git_str_cstr(&remote_ref_name), + &push_spec->roid, &push_spec->loid, + &push_spec->refspec, callbacks->payload); +#ifndef GIT_DEPRECATE_HARD + else if (callbacks->update_tips) + error = callbacks->update_tips( + git_str_cstr(&remote_ref_name), + &push_spec->roid, &push_spec->loid, + callbacks->payload); +#endif + + if (error < 0) { + git_error_set_after_callback_function(error, "git_remote_push"); + goto on_error; } } diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c index 070b7df06..5e59ce894 100644 --- a/src/libgit2/remote.c +++ b/src/libgit2/remote.c @@ -1724,14 +1724,23 @@ int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks) git_oid_cpy(&id, git_reference_target(ref)); error = git_reference_delete(ref); git_reference_free(ref); - if (error < 0) - goto cleanup; - - if (callbacks && callbacks->update_tips) - error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload); if (error < 0) goto cleanup; + + if (callbacks && callbacks->update_refs) + error = callbacks->update_refs(refname, &id, + &zero_id, NULL, callbacks->payload); +#ifndef GIT_DEPRECATE_HARD + else if (callbacks && callbacks->update_tips) + error = callbacks->update_tips(refname, &id, + &zero_id, callbacks->payload); +#endif + + if (error < 0) { + git_error_set_after_callback_function(error, "git_remote_fetch"); + goto cleanup; + } } cleanup: @@ -1744,6 +1753,7 @@ static int update_ref( const git_remote *remote, const char *ref_name, git_oid *id, + git_refspec *spec, const char *msg, const git_remote_callbacks *callbacks) { @@ -1772,9 +1782,19 @@ static int update_ref( if (error < 0) return error; - if (callbacks && callbacks->update_tips && - (error = callbacks->update_tips(ref_name, &old_id, id, callbacks->payload)) < 0) + if (callbacks && callbacks->update_refs) + error = callbacks->update_refs(ref_name, &old_id, + id, spec, callbacks->payload); +#ifndef GIT_DEPRECATE_HARD + else if (callbacks && callbacks->update_tips) + error = callbacks->update_tips(ref_name, &old_id, + id, callbacks->payload); +#endif + + if (error < 0) { + git_error_set_after_callback_function(error, "git_remote_fetch"); return error; + } return 0; } @@ -1880,9 +1900,20 @@ static int update_one_tip( } } - if (callbacks && callbacks->update_tips != NULL && - (updated || (update_flags & GIT_REMOTE_UPDATE_REPORT_UNCHANGED)) && - (error = callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload)) < 0) + if (!callbacks || + (!updated && (update_flags & GIT_REMOTE_UPDATE_REPORT_UNCHANGED) == 0)) + goto done; + + if (callbacks && callbacks->update_refs) + error = callbacks->update_refs(refname.ptr, &old, + &head->oid, spec, callbacks->payload); +#ifndef GIT_DEPRECATE_HARD + else if (callbacks && callbacks->update_tips) + error = callbacks->update_tips(refname.ptr, &old, + &head->oid, callbacks->payload); +#endif + + if (error < 0) git_error_set_after_callback_function(error, "git_remote_fetch"); done: @@ -1932,7 +1963,7 @@ static int update_tips_for_spec( goto on_error; if (spec->dst && - (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0) + (error = update_ref(remote, spec->dst, &id, spec, log_message, callbacks)) < 0) goto on_error; git_oid_cpy(&oid_head.oid, &id); @@ -2044,7 +2075,7 @@ static int opportunistic_updates( git_str_clear(&refname); if ((error = git_refspec__transform(&refname, spec, head->name)) < 0 || - (error = update_ref(remote, refname.ptr, &head->oid, msg, callbacks)) < 0) + (error = update_ref(remote, refname.ptr, &head->oid, spec, msg, callbacks)) < 0) goto cleanup; } @@ -2995,7 +3026,7 @@ int git_remote_upload( if (connect_opts.callbacks.push_update_reference) { const int cb_error = git_push_status_foreach(push, connect_opts.callbacks.push_update_reference, connect_opts.callbacks.payload); - if (!error) + if (!error) error = cb_error; } diff --git a/tests/libgit2/network/fetchlocal.c b/tests/libgit2/network/fetchlocal.c index dc37c38ab..fef7ed528 100644 --- a/tests/libgit2/network/fetchlocal.c +++ b/tests/libgit2/network/fetchlocal.c @@ -108,14 +108,15 @@ void test_network_fetchlocal__prune(void) git_repository_free(repo); } -static int update_tips_fail_on_call(const char *ref, const git_oid *old, const git_oid *new, void *data) +static int update_refs_fail_on_call(const char *ref, const git_oid *old, const git_oid *new, git_refspec *refspec, void *data) { GIT_UNUSED(ref); GIT_UNUSED(old); GIT_UNUSED(new); + GIT_UNUSED(refspec); GIT_UNUSED(data); - cl_fail("update tips called"); + cl_fail("update refs called"); return 0; } @@ -175,7 +176,7 @@ void test_network_fetchlocal__prune_overlapping(void) git_remote_free(origin); cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); - options.callbacks.update_tips = update_tips_fail_on_call; + options.callbacks.update_refs = update_refs_fail_on_call; cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL)); assert_ref_exists(repo, "refs/remotes/origin/master"); @@ -190,7 +191,7 @@ void test_network_fetchlocal__prune_overlapping(void) git_remote_free(origin); cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); - options.callbacks.update_tips = update_tips_fail_on_call; + options.callbacks.update_refs = update_refs_fail_on_call; cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL)); git_config_free(config); @@ -510,20 +511,21 @@ void test_network_fetchlocal__prune_load_fetch_prune_config(void) git_repository_free(repo); } -static int update_tips_error(const char *ref, const git_oid *old, const git_oid *new, void *data) +static int update_refs_error(const char *ref, const git_oid *old, const git_oid *new, git_refspec *refspec, void *data) { int *callcount = (int *) data; GIT_UNUSED(ref); GIT_UNUSED(old); GIT_UNUSED(new); + GIT_UNUSED(refspec); (*callcount)++; return -1; } -void test_network_fetchlocal__update_tips_error_is_propagated(void) +void test_network_fetchlocal__update_refs_error_is_propagated(void) { git_repository *repo; git_reference_iterator *iterator; @@ -537,7 +539,7 @@ void test_network_fetchlocal__update_tips_error_is_propagated(void) cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/remotes/update-tips/*")); - options.callbacks.update_tips = update_tips_error; + options.callbacks.update_refs = update_refs_error; options.callbacks.payload = &callcount; cl_git_fail(git_remote_fetch(remote, NULL, &options, NULL)); @@ -550,3 +552,83 @@ void test_network_fetchlocal__update_tips_error_is_propagated(void) git_remote_free(remote); git_repository_free(repo); } + +#ifndef GIT_DEPRECATE_HARD +static int update_tips(const char *ref, const git_oid *old, const git_oid *new, void *data) +{ + int *called = (int *) data; + + GIT_UNUSED(ref); + GIT_UNUSED(old); + GIT_UNUSED(new); + + (*called) += 1; + + return 0; +} + +static int update_refs(const char *ref, const git_oid *old, const git_oid *new, git_refspec *spec, void *data) +{ + int *called = (int *) data; + + GIT_UNUSED(ref); + GIT_UNUSED(old); + GIT_UNUSED(new); + GIT_UNUSED(spec); + + (*called) += 0x10000; + + return 0; +} +#endif + +void test_network_fetchlocal__update_tips_backcompat(void) +{ +#ifndef GIT_DEPRECATE_HARD + git_repository *repo; + git_remote *remote; + git_fetch_options options = GIT_FETCH_OPTIONS_INIT; + int callcount = 0; + + cl_git_pass(git_repository_init(&repo, "foo.git", true)); + cl_set_cleanup(cleanup_local_repo, "foo.git"); + + cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/remotes/update-tips/*")); + + options.callbacks.update_tips = update_tips; + options.callbacks.payload = &callcount; + + cl_git_pass(git_remote_fetch(remote, NULL, &options, NULL)); + cl_assert_equal_i(0, (callcount & 0xffff0000)); + cl_assert((callcount & 0x0000ffff) > 0); + + git_remote_free(remote); + git_repository_free(repo); +#endif +} + +void test_network_fetchlocal__update_refs_is_preferred(void) +{ +#ifndef GIT_DEPRECATE_HARD + git_repository *repo; + git_remote *remote; + git_fetch_options options = GIT_FETCH_OPTIONS_INIT; + int callcount = 0; + + cl_git_pass(git_repository_init(&repo, "foo.git", true)); + cl_set_cleanup(cleanup_local_repo, "foo.git"); + + cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/remotes/update-tips/*")); + + options.callbacks.update_tips = update_tips; + options.callbacks.update_refs = update_refs; + options.callbacks.payload = &callcount; + + cl_git_pass(git_remote_fetch(remote, NULL, &options, NULL)); + cl_assert_equal_i(0, (callcount & 0x0000ffff)); + cl_assert((callcount & 0xffff0000) > 0); + + git_remote_free(remote); + git_repository_free(repo); +#endif +} diff --git a/tests/libgit2/online/clone.c b/tests/libgit2/online/clone.c index 207dd8391..e9ed2eeb9 100644 --- a/tests/libgit2/online/clone.c +++ b/tests/libgit2/online/clone.c @@ -319,10 +319,10 @@ void test_online_clone__clone_mirror(void) cl_fixture_cleanup("./foo.git"); } -static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload) +static int update_refs(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *payload) { int *callcount = (int*)payload; - GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); + GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(spec); *callcount = *callcount + 1; return 0; } @@ -331,7 +331,7 @@ void test_online_clone__custom_remote_callbacks(void) { int callcount = 0; - g_options.fetch_opts.callbacks.update_tips = update_tips; + g_options.fetch_opts.callbacks.update_refs = update_refs; g_options.fetch_opts.callbacks.payload = &callcount; cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); diff --git a/tests/libgit2/online/fetch.c b/tests/libgit2/online/fetch.c index 08a14c631..75ec0cde5 100644 --- a/tests/libgit2/online/fetch.c +++ b/tests/libgit2/online/fetch.c @@ -39,9 +39,13 @@ void test_online_fetch__cleanup(void) git__free(_remote_redirect_subsequent); } -static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data) +static int update_refs(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data) { - GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data); + GIT_UNUSED(refname); + GIT_UNUSED(a); + GIT_UNUSED(b); + GIT_UNUSED(spec); + GIT_UNUSED(data); ++counter; @@ -62,7 +66,7 @@ static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n) size_t bytes_received = 0; options.callbacks.transfer_progress = progress; - options.callbacks.update_tips = update_tips; + options.callbacks.update_refs = update_refs; options.callbacks.payload = &bytes_received; options.download_tags = flag; counter = 0; @@ -163,7 +167,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date options.callbacks.transfer_progress = &transferProgressCallback; options.callbacks.payload = &invoked; - options.callbacks.update_tips = update_tips; + options.callbacks.update_refs = update_refs; cl_git_pass(git_remote_download(remote, NULL, &options)); cl_assert_equal_i(false, invoked); @@ -201,7 +205,7 @@ void test_online_fetch__report_unchanged_tips(void) options.callbacks.transfer_progress = &transferProgressCallback; options.callbacks.payload = &invoked; - options.callbacks.update_tips = update_tips; + options.callbacks.update_refs = update_refs; cl_git_pass(git_remote_download(remote, NULL, &options)); cl_assert_equal_i(false, invoked); diff --git a/tests/libgit2/online/push_util.c b/tests/libgit2/online/push_util.c index 372eec8aa..88651672d 100644 --- a/tests/libgit2/online/push_util.c +++ b/tests/libgit2/online/push_util.c @@ -35,11 +35,13 @@ void record_callbacks_data_clear(record_callbacks_data *data) data->transfer_progress_calls = 0; } -int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) +int record_update_refs_cb(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data) { updated_tip *t; record_callbacks_data *record_data = (record_callbacks_data *)data; + GIT_UNUSED(spec); + cl_assert(t = git__calloc(1, sizeof(*t))); cl_assert(t->name = git__strdup(refname)); diff --git a/tests/libgit2/online/push_util.h b/tests/libgit2/online/push_util.h index 5f669feaf..07b46bf44 100644 --- a/tests/libgit2/online/push_util.h +++ b/tests/libgit2/online/push_util.h @@ -12,7 +12,7 @@ extern const git_oid OID_ZERO; * @param data pointer to a record_callbacks_data instance */ #define RECORD_CALLBACKS_INIT(data) \ - { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, NULL, data, NULL } + { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, data, NULL, record_update_refs_cb } typedef struct { char *name; @@ -50,7 +50,7 @@ void record_callbacks_data_clear(record_callbacks_data *data); * * @param data (git_vector *) of updated_tip instances */ -int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data); +int record_update_refs_cb(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data); /** * Create a set of refspecs that deletes each of the inputs diff --git a/tests/libgit2/submodule/update.c b/tests/libgit2/submodule/update.c index 052a4a1fe..674d1a4fe 100644 --- a/tests/libgit2/submodule/update.c +++ b/tests/libgit2/submodule/update.c @@ -30,7 +30,7 @@ void test_submodule_update__uninitialized_submodule_no_init(void) } struct update_submodule_cb_payload { - int update_tips_called; + int update_refs_called; int checkout_progress_called; int checkout_notify_called; }; @@ -71,15 +71,16 @@ static int checkout_notify_cb( return 0; } -static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data) +static int update_refs(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data) { struct update_submodule_cb_payload *update_payload = data; GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); + GIT_UNUSED(spec); - update_payload->update_tips_called = 1; + update_payload->update_refs_called = 1; return 1; } @@ -96,7 +97,7 @@ void test_submodule_update__update_submodule(void) update_options.checkout_opts.progress_cb = checkout_progress_cb; update_options.checkout_opts.progress_payload = &update_payload; - update_options.fetch_opts.callbacks.update_tips = update_tips; + update_options.fetch_opts.callbacks.update_refs = update_refs; update_options.fetch_opts.callbacks.payload = &update_payload; /* get the submodule */ @@ -126,7 +127,7 @@ void test_submodule_update__update_submodule(void) /* verify that the expected callbacks have been called. */ cl_assert_equal_i(1, update_payload.checkout_progress_called); - cl_assert_equal_i(1, update_payload.update_tips_called); + cl_assert_equal_i(1, update_payload.update_refs_called); git_submodule_free(sm); } @@ -143,7 +144,7 @@ void test_submodule_update__update_submodule_with_path(void) update_options.checkout_opts.progress_cb = checkout_progress_cb; update_options.checkout_opts.progress_payload = &update_payload; - update_options.fetch_opts.callbacks.update_tips = update_tips; + update_options.fetch_opts.callbacks.update_refs = update_refs; update_options.fetch_opts.callbacks.payload = &update_payload; /* get the submodule */ @@ -173,7 +174,7 @@ void test_submodule_update__update_submodule_with_path(void) /* verify that the expected callbacks have been called. */ cl_assert_equal_i(1, update_payload.checkout_progress_called); - cl_assert_equal_i(1, update_payload.update_tips_called); + cl_assert_equal_i(1, update_payload.update_refs_called); git_submodule_free(sm); }