Subject: [PATCH v5 00/11] Convert remaining hooks to hook.h

From: Adrian Ratiu <[email-masked]>
Date: Thu, 18 Dec 2025 19:11:14 +0200

Hello everyone,

This series finishes the hook.[ch] conversion for the remaining hooks in
preparation for adding config-based hooks and enabling parallel hook
execution where possible (that will be a separate series from this one).

v5 is just a minor cleanup refresh: details + range-diff below.

It is based on the latest master branch. There are no conflicts with
next and seen branches, the code is available on GitHub [1] and a
successful CI run [2] is also provided.

1: https://github.com/10ne1/git/tree/dev/aratiu/hooks-conversion-v5
2: https://github.com/10ne1/git/actions/runs/20274558658

Changes in v5:
* Dropped some REF_STATUS_REJECT_* additions which are mostly unrelated (Patrick)
* Made a null output buffer in hook_output_to_sideband() trigger a BUG (Patrick)
* Cleaned up the diff in patch 11 to not unnecessarily move hook_output_to_sideband()
  around, though this looks a bit ugly in the range-diff for patches 10 & 11 (Adrian)
* Two small nitpicks / typos (Patrick)

Range-diff between v4 -> v5:
 1:  b252d447f5 =  1:  8c16f1bcbf run-command: add first helper for pp child states
 2:  f6fd3b9b0f =  2:  5e6e05ba92 run-command: add stdin callback for parallelization
 3:  59c07b618e !  3:  3669acfe6a hook: provide stdin via callback
    @@ hook.h: struct run_hooks_opt
     +	 * used for smaller inputs, where it gets called only once.
     +	 *
     +	 * Add hook callback initalization context to `feed_pipe_ctx`.
    -+	 * Add Hook callback internal state to `feed_pipe_cb_data`.
    ++	 * Add hook callback internal state to `feed_pipe_cb_data`.
     +	 *
     +	 */
     +	feed_pipe_fn feed_pipe;
 4:  8f591319a4 =  4:  bf9d8680e4 hook: convert 'post-rewrite' hook in sequencer.c to hook API
 5:  2427717a26 !  5:  bdcc1cff34 transport: convert pre-push to hook API
    @@ transport.c: static void die_with_unpushed_submodules(struct string_list *needs_
     -		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 */
 6:  22467bb074 =  6:  9c1d5e8726 reference-transaction: use hook API instead of run-command
 7:  9b0b13379c =  7:  0b986bf0fb hook: allow overriding the ungroup option
 8:  cae3c984e2 =  8:  5f07d07acc run-command: allow capturing of collated output
 9:  0383be2ebd =  9:  c4ff1e2270 hooks: allow callers to capture output
10:  d8e234c453 ! 10:  15c831ca15 receive-pack: convert update hooks to new API
    @@ Commit message
         Signed-off-by: Adrian Ratiu <[email-masked]>
     
      ## builtin/receive-pack.c ##
    -@@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
    - 	return status;
    +@@ builtin/receive-pack.c: static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
    + 	return 0;
      }
      
    --static int run_update_hook(struct command *cmd)
     +static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
    ++{
    ++	if (!output)
    ++		BUG("output must be non-NULL");
    ++
    ++	/* buffer might be empty for keepalives */
    ++	if (output->len)
    ++		send_sideband(1, 2, output->buf, output->len, use_sideband);
    ++}
    ++
    + static int run_receive_hook(struct command *commands,
    + 			    const char *hook_name,
    + 			    int skip_broken,
    +@@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
    + 
    + static int run_update_hook(struct command *cmd)
      {
     -	struct child_process proc = CHILD_PROCESS_INIT;
     -	int code;
    @@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
     -
     -	if (!hook_path)
     -		return 0;
    -+	if (output && output->len)
    -+		send_sideband(1, 2, output->buf, output->len, use_sideband);
    -+}
    - 
    +-
     -	strvec_push(&proc.args, hook_path);
     -	strvec_push(&proc.args, cmd->ref_name);
     -	strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
     -	strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
    -+static int run_update_hook(struct command *cmd)
    -+{
     +	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
      
     -	proc.no_stdin = 1;
11:  6bad9aed4d ! 11:  4bd2c2974a receive-pack: convert receive hooks to hook API
    @@ builtin/receive-pack.c: struct receive_hook_feed_state {
     -		finish_async(&muxer);
      
     -	sigchain_pop(SIGPIPE);
    -+	state->cmd = cmd;
    - 
    +-
     -	return finish_command(&proc);
    -+	if (state->buf.len > 0) {
    -+		int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
    -+		if (ret < 0) {
    -+			if (errno == EPIPE)
    -+				return 1; /* child closed pipe */
    -+			return ret;
    -+		}
    -+	}
    -+
    -+	return state->cmd ? 0 : 1;  /* 0 = more to come, 1 = EOF */
    - }
    +-}
    ++	state->cmd = cmd;
      
     -static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
    -+static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
    - {
    +-{
     -	struct receive_hook_feed_state *state = state_;
     -	struct command *cmd = state->cmd;
     -
    @@ builtin/receive-pack.c: struct receive_hook_feed_state {
     -	if (bufp) {
     -		*bufp = state->buf.buf;
     -		*sizep = state->buf.len;
    --	}
    ++	if (state->buf.len > 0) {
    ++		int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
    ++		if (ret < 0) {
    ++			if (errno == EPIPE)
    ++				return 1; /* child closed pipe */
    ++			return ret;
    ++		}
    + 	}
     -	return 0;
    -+	if (output && output->len)
    -+		send_sideband(1, 2, output->buf, output->len, use_sideband);
    ++
    ++	return state->cmd ? 0 : 1;  /* 0 = more to come, 1 = EOF */
      }
      
    - static int run_receive_hook(struct command *commands,
    + static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
     @@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
      			    int skip_broken,
      			    const struct string_list *push_options)
    @@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
     -	status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
     -	strbuf_release(&state.buf);
     -	return status;
    --}
    - 
    --static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
    --{
    --	if (output && output->len)
    --		send_sideband(1, 2, output->buf, output->len, use_sideband);
    ++
     +	if (push_options) {
     +		int i;
     +		for (i = 0; i < push_options->nr; i++)

Adrian Ratiu (3):
  run-command: add first helper for pp child states
  reference-transaction: use hook API instead of run-command
  hook: allow overriding the ungroup option

Emily Shaffer (8):
  run-command: add stdin callback for parallelization
  hook: provide stdin via callback
  hook: convert 'post-rewrite' hook in sequencer.c to hook API
  transport: convert pre-push to hook API
  run-command: allow capturing of collated output
  hooks: allow callers to capture output
  receive-pack: convert update hooks to new API
  receive-pack: convert receive hooks to hook API

 builtin/hook.c              |   6 +
 builtin/receive-pack.c      | 272 ++++++++++++++++--------------------
 commit.c                    |   3 +
 hook.c                      |  29 +++-
 hook.h                      |  51 +++++++
 refs.c                      | 100 ++++++-------
 run-command.c               | 142 +++++++++++++++----
 run-command.h               |  38 +++++
 sequencer.c                 |  42 +++---
 t/helper/test-run-command.c |  67 ++++++++-
 t/t0061-run-command.sh      |  38 +++++
 transport.c                 |  89 ++++++------
 12 files changed, 585 insertions(+), 292 deletions(-)

-- 
2.51.2