remote: add update_refs callback

Add an `update_refs` callback that includes the refspec; `update_tips`
is retained for backward compatibility.
This commit is contained in:
Edward Thomson
2024-10-19 23:42:26 +01:00
parent 6ea625cdad
commit c1b2b25ebc
10 changed files with 214 additions and 52 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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));

View File

@@ -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);

View File

@@ -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));

View File

@@ -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

View File

@@ -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);
}