diff options
author | Jiang Xin <zhiyou.jx@alibaba-inc.com> | 2020-08-27 18:45:46 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2020-08-27 22:47:47 +0300 |
commit | 63518a574a3a4b8c6f152b29cb1b6d1f66f6e6c7 (patch) | |
tree | a967d86e5cecce2b80e1b529afd8ef6aa66ff7d8 /transport-helper.c | |
parent | 195d6eaea3b886fe13f422ce6675009ea1351224 (diff) |
New capability "report-status-v2" for git-push
The new introduced "proc-receive" hook may handle a command for a
pseudo-reference with a zero-old as its old-oid, while the hook may
create or update a reference with different name, different new-oid,
and different old-oid (the reference may exist already with a non-zero
old-oid). Current "report-status" protocol cannot report the status for
such reference rewrite.
Add new capability "report-status-v2" and new report protocol which is
not backward compatible for report of git-push.
If a user pushes to a pseudo-reference "refs/for/master/topic", and
"receive-pack" creates two new references "refs/changes/23/123/1" and
"refs/changes/24/124/1", for client without the knowledge of
"report-status-v2", "receive-pack" will only send "ok/ng" directives in
the report, such as:
ok ref/for/master/topic
But for client which has the knowledge of "report-status-v2",
"receive-pack" will use "option" directives to report more attributes
for the reference given by the above "ok/ng" directive.
ok refs/for/master/topic
option refname refs/changes/23/123/1
option new-oid <new-oid>
ok refs/for/master/topic
option refname refs/changes/24/124/1
option new-oid <new-oid>
The client will report two new created references to the end user.
Suggested-by: Junio C Hamano <gitster@pobox.com>
Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'transport-helper.c')
-rw-r--r-- | transport-helper.c | 128 |
1 files changed, 99 insertions, 29 deletions
diff --git a/transport-helper.c b/transport-helper.c index a46afcb69d..1738c81ea0 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -719,13 +719,61 @@ static int fetch(struct transport *transport, return -1; } +struct push_update_ref_state { + struct ref *hint; + struct ref_push_report *report; + int new_report; +}; + static int push_update_ref_status(struct strbuf *buf, - struct ref **ref, + struct push_update_ref_state *state, struct ref *remote_refs) { char *refname, *msg; int status, forced = 0; + if (starts_with(buf->buf, "option ")) { + struct object_id old_oid, new_oid; + const char *key, *val; + char *p; + + if (!state->hint || !(state->report || state->new_report)) + die(_("'option' without a matching 'ok/error' directive")); + if (state->new_report) { + if (!state->hint->report) { + state->hint->report = xcalloc(1, sizeof(struct ref_push_report)); + state->report = state->hint->report; + } else { + state->report = state->hint->report; + while (state->report->next) + state->report = state->report->next; + state->report->next = xcalloc(1, sizeof(struct ref_push_report)); + state->report = state->report->next; + } + state->new_report = 0; + } + key = buf->buf + 7; + p = strchr(key, ' '); + if (p) + *p++ = '\0'; + val = p; + if (!strcmp(key, "refname")) + state->report->ref_name = xstrdup_or_null(val); + else if (!strcmp(key, "old-oid") && val && + !parse_oid_hex(val, &old_oid, &val)) + state->report->old_oid = oiddup(&old_oid); + else if (!strcmp(key, "new-oid") && val && + !parse_oid_hex(val, &new_oid, &val)) + state->report->new_oid = oiddup(&new_oid); + else if (!strcmp(key, "forced-update")) + state->report->forced_update = 1; + /* Not update remote namespace again. */ + return 1; + } + + state->report = NULL; + state->new_report = 0; + if (starts_with(buf->buf, "ok ")) { status = REF_STATUS_OK; refname = buf->buf + 3; @@ -781,16 +829,16 @@ static int push_update_ref_status(struct strbuf *buf, } } - if (*ref) - *ref = find_ref_by_name(*ref, refname); - if (!*ref) - *ref = find_ref_by_name(remote_refs, refname); - if (!*ref) { + if (state->hint) + state->hint = find_ref_by_name(state->hint, refname); + if (!state->hint) + state->hint = find_ref_by_name(remote_refs, refname); + if (!state->hint) { warning(_("helper reported unexpected status of %s"), refname); return 1; } - if ((*ref)->status != REF_STATUS_NONE) { + if (state->hint->status != REF_STATUS_NONE) { /* * Earlier, the ref was marked not to be pushed, so ignore the ref * status reported by the remote helper if the latter is 'no match'. @@ -799,9 +847,11 @@ static int push_update_ref_status(struct strbuf *buf, return 1; } - (*ref)->status = status; - (*ref)->forced_update |= forced; - (*ref)->remote_status = msg; + if (status == REF_STATUS_OK) + state->new_report = 1; + state->hint->status = status; + state->hint->forced_update |= forced; + state->hint->remote_status = msg; return !(status == REF_STATUS_OK); } @@ -809,37 +859,57 @@ static int push_update_refs_status(struct helper_data *data, struct ref *remote_refs, int flags) { + struct ref *ref; + struct ref_push_report *report; struct strbuf buf = STRBUF_INIT; - struct ref *ref = remote_refs; - int ret = 0; + struct push_update_ref_state state = { remote_refs, NULL, 0 }; for (;;) { - char *private; - if (recvline(data, &buf)) { - ret = 1; - break; + strbuf_release(&buf); + return 1; } - if (!buf.len) break; + push_update_ref_status(&buf, &state, remote_refs); + } + strbuf_release(&buf); - if (push_update_ref_status(&buf, &ref, remote_refs)) - continue; + if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update) + return 0; - if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update) - continue; + /* propagate back the update to the remote namespace */ + for (ref = remote_refs; ref; ref = ref->next) { + char *private; - /* propagate back the update to the remote namespace */ - private = apply_refspecs(&data->rs, ref->name); - if (!private) + if (ref->status != REF_STATUS_OK) continue; - update_ref("update by helper", private, &ref->new_oid, NULL, - 0, 0); - free(private); + + if (!ref->report) { + private = apply_refspecs(&data->rs, ref->name); + if (!private) + continue; + update_ref("update by helper", private, &(ref->new_oid), + NULL, 0, 0); + free(private); + } else { + for (report = ref->report; report; report = report->next) { + private = apply_refspecs(&data->rs, + report->ref_name + ? report->ref_name + : ref->name); + if (!private) + continue; + update_ref("update by helper", private, + report->new_oid + ? report->new_oid + : &(ref->new_oid), + NULL, 0, 0); + free(private); + } + } } - strbuf_release(&buf); - return ret; + return 0; } static void set_common_push_options(struct transport *transport, |