From 63518a574a3a4b8c6f152b29cb1b6d1f66f6e6c7 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Thu, 27 Aug 2020 11:45:46 -0400 Subject: 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 ok refs/for/master/topic option refname refs/changes/24/124/1 option new-oid The client will report two new created references to the end user. Suggested-by: Junio C Hamano Suggested-by: Jeff King Signed-off-by: Jiang Xin Signed-off-by: Junio C Hamano --- transport-helper.c | 128 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 29 deletions(-) (limited to 'transport-helper.c') 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, -- cgit v1.2.3