Merge branch 'jx/proc-receive-hook'

"git receive-pack" that accepts requests by "git push" learned to
outsource most of the ref updates to the new "proc-receive" hook.

* jx/proc-receive-hook:
  doc: add documentation for the proc-receive hook
  transport: parse report options for tracking refs
  t5411: test updates of remote-tracking branches
  receive-pack: new config receive.procReceiveRefs
  doc: add document for capability report-status-v2
  New capability "report-status-v2" for git-push
  receive-pack: feed report options to post-receive
  receive-pack: add new proc-receive hook
  t5411: add basic test cases for proc-receive hook
  transport: not report a non-head push as a branch
This commit is contained in:
Junio C Hamano
2020-09-25 15:25:39 -07:00
48 changed files with 4328 additions and 103 deletions

View File

@@ -723,13 +723,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;
@@ -785,16 +833,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'.
@@ -803,9 +851,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);
}
@@ -813,37 +863,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;
if (push_update_ref_status(&buf, &ref, remote_refs))
continue;
if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
continue;
/* propagate back the update to the remote namespace */
private = apply_refspecs(&data->rs, ref->name);
if (!private)
continue;
update_ref("update by helper", private, &ref->new_oid, NULL,
0, 0);
free(private);
push_update_ref_status(&buf, &state, remote_refs);
}
strbuf_release(&buf);
return ret;
if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)
return 0;
/* propagate back the update to the remote namespace */
for (ref = remote_refs; ref; ref = ref->next) {
char *private;
if (ref->status != REF_STATUS_OK)
continue;
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);
}
}
}
return 0;
}
static void set_common_push_options(struct transport *transport,