Subject: Re: [PATCH v3 04/10] transport: convert pre-push to hook API

From: Junio C Hamano <[email-masked]>
Date: Mon, 24 Nov 2025 14:55:07 -0800

Adrian Ratiu <[email-masked]> writes:

> From: Emily Shaffer <[email-masked]>
>
> Move the pre-push hook from custom run-command invocations to
> the new hook API which doesn't require a custom child_process
> structure and signal toggling.
>
> Signed-off-by: Emily Shaffer <[email-masked]>
> Signed-off-by: Ævar Arnfjörð Bjarmason <[email-masked]>
> Signed-off-by: Adrian Ratiu <[email-masked]>
> ---
>  transport.c | 95 ++++++++++++++++++++++++++++-------------------------
>  1 file changed, 51 insertions(+), 44 deletions(-)

So, this completes what 01/10 hinted at when it created a
generalized interface modelled after how pre-push hook was run.  We
used to spawn the pre-push hook and fed its standard input by
calling write_in_full().  Now that is largely encapsulated in
run_hooks_opt(), but the application specific processing (namely,
what we write to the pre-push hook, i.e. the list of ref update
status) is given in pre_push_hook_feed_stdin() callback defined here
and given to the run_hooks_opt() call.

In other words, the mechanisms are very cleanly separated between
generic machinery and the client specific processing.  Nice.

How and where does the pipe we are writing into (i.e. hook_stdin_fd)
gets closed when we are done with the child?

> +static int pre_push_hook_feed_stdin(int hook_stdin_fd, void *pp_cb, void *pp_task_cb UNUSED)
> +{
> +	struct hook_cb_data *hook_cb = pp_cb;
> +	struct feed_pre_push_hook_data *data = hook_cb->options->feed_pipe_cb_data;
> +	const struct ref *r = data->refs;
> +	int ret = 0;
>  
> -	strvec_push(&proc.args, hook_path);
> -	strvec_push(&proc.args, transport->remote->name);
> -	strvec_push(&proc.args, transport->url);
> +	if (!r)
> +		return 1; /* no more refs */
>  
> -	proc.in = -1;
> -	proc.trace2_hook_name = "pre-push";
> +	data->refs = r->next;
>  
> -	if (start_command(&proc)) {
> -		finish_command(&proc);
> -		return -1;
> +	switch (r->status) {
> +	case REF_STATUS_REJECT_ALREADY_EXISTS:
> +	case REF_STATUS_REJECT_FETCH_FIRST:
> +	case REF_STATUS_REJECT_NEEDS_FORCE:
> +	case REF_STATUS_REJECT_NODELETE:
> +	case REF_STATUS_REJECT_NONFASTFORWARD:
> +	case REF_STATUS_REJECT_REMOTE_UPDATED:
> +	case REF_STATUS_REJECT_SHALLOW:
> +	case REF_STATUS_REJECT_STALE:
> +	case REF_STATUS_UPTODATE:
> +		return 0; /* skip refs which won't be pushed */
> +	default:
> +		break;
>  	}
>  
> -	sigchain_push(SIGPIPE, SIG_IGN);
> +	if (!r->peer_ref)
> +		return 0;
>  
> -	strbuf_init(&buf, 256);
> +	strbuf_reset(&data->buf);
> +	strbuf_addf(&data->buf, "%s %s %s %s\n",
> +		    r->peer_ref->name, oid_to_hex(&r->new_oid),
> +		    r->name, oid_to_hex(&r->old_oid));
>  
> -	for (r = remote_refs; r; r = r->next) {
> -		if (!r->peer_ref) continue;
> -		if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
> -		if (r->status == REF_STATUS_REJECT_STALE) continue;
> -		if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue;
> -		if (r->status == REF_STATUS_UPTODATE) continue;
> +	ret = write_in_full(hook_stdin_fd, data->buf.buf, data->buf.len);
> +	if (ret < 0 && errno != EPIPE)
> +		return ret; /* We do not mind if a hook does not read all refs. */
>  
> -		strbuf_reset(&buf);
> -		strbuf_addf( &buf, "%s %s %s %s\n",
> -			 r->peer_ref->name, oid_to_hex(&r->new_oid),
> -			 r->name, oid_to_hex(&r->old_oid));
> +	return 0;
> +}
>  
> -		if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
> -			/* We do not mind if a hook does not read all refs. */
> -			if (errno != EPIPE)
> -				ret = -1;
> -			break;
> -		}
> -	}
> +static int run_pre_push_hook(struct transport *transport,
> +			     struct ref *remote_refs)
> +{
> +	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
> +	struct feed_pre_push_hook_data data;
> +	int ret = 0;
> +
> +	strvec_push(&opt.args, transport->remote->name);
> +	strvec_push(&opt.args, transport->url);
>  
> -	strbuf_release(&buf);
> +	strbuf_init(&data.buf, 0);
> +	data.refs = remote_refs;
>  
> -	x = close(proc.in);
> -	if (!ret)
> -		ret = x;
> +	opt.feed_pipe = pre_push_hook_feed_stdin;
> +	opt.feed_pipe_cb_data = &data;
>  
> -	sigchain_pop(SIGPIPE);
> +	ret = run_hooks_opt(the_repository, "pre-push", &opt);
>  
> -	x = finish_command(&proc);
> -	if (!ret)
> -		ret = x;
> +	strbuf_release(&data.buf);
>  
>  	return ret;
>  }