From: Adrian Ratiu Date: Wed, 21 Jan 2026 21:54:35 GMT Subject: [PATCH v7 11/12] receive-pack: convert update hooks to new API Message-ID: <20260121215436.1473800-12-adrian.ratiu@collabora.com> In-Reply-To: <20260121215436.1473800-1-adrian.ratiu@collabora.com> The hook API avoids creating a custom struct child_process and other internal hook plumbing (e.g. calling find_hook()) and prepares for the specification of hooks via configs or running parallel hooks. Execution is still sequential through the run_hooks_opt .jobs == 1, which is the unchanged default for all hooks. When jobs==1 the async muxer thread reads the hook stderr and writes to sideband 2, so run-command's poll loop is avoided and there's no need for ungroup=0 when running sequentially (Jeff's suggestion). When running in parallel, run-command with ungroup=0 will capture and de-interleave the output of each hook, then write to the parent stderr which is redirected via dup2 to the sideband muxer, so that parallel hook output is presented clearly to the client. Suggested-by: Jeff King Signed-off-by: Emily Shaffer Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Adrian Ratiu --- builtin/receive-pack.c | 91 +++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 9c49174616..bcd019786e 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -941,29 +941,41 @@ static int run_receive_hook(struct command *commands, static int run_update_hook(struct command *cmd) { - struct child_process proc = CHILD_PROCESS_INIT; + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; + struct async muxer; int code; - const char *hook_path = find_hook(the_repository, "update"); + int saved_stderr = -1; + int muxer_started = 0; - if (!hook_path) - return 0; + strvec_pushl(&opt.args, + cmd->ref_name, + oid_to_hex(&cmd->old_oid), + oid_to_hex(&cmd->new_oid), + NULL); - 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)); + if (use_sideband) { + memset(&muxer, 0, sizeof(muxer)); + muxer.proc = copy_to_sideband; + muxer.in = -1; + if (!start_async(&muxer)) { + muxer_started = 1; + saved_stderr = dup(STDERR_FILENO); + if (saved_stderr >= 0) + dup2(muxer.in, STDERR_FILENO); + close(muxer.in); + } + } - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.trace2_hook_name = "update"; + code = run_hooks_opt(the_repository, "update", &opt); - code = start_command(&proc); - if (code) - return code; - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - return finish_command(&proc); + if (saved_stderr >= 0) { + dup2(saved_stderr, STDERR_FILENO); + close(saved_stderr); + } + if (muxer_started) + finish_async(&muxer); + + return code; } static struct command *find_command_by_refname(struct command *list, @@ -1639,34 +1651,41 @@ static const char *update(struct command *cmd, struct shallow_info *si) static void run_update_post_hook(struct command *commands) { + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; + struct async muxer; struct command *cmd; - struct child_process proc = CHILD_PROCESS_INIT; - const char *hook; - - hook = find_hook(the_repository, "post-update"); - if (!hook) - return; + int saved_stderr = -1; + int muxer_started = 0; for (cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; - if (!proc.args.nr) - strvec_push(&proc.args, hook); - strvec_push(&proc.args, cmd->ref_name); + strvec_push(&opt.args, cmd->ref_name); } - if (!proc.args.nr) + if (!opt.args.nr) return; - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.trace2_hook_name = "post-update"; + if (use_sideband) { + memset(&muxer, 0, sizeof(muxer)); + muxer.proc = copy_to_sideband; + muxer.in = -1; + if (!start_async(&muxer)) { + muxer_started = 1; + saved_stderr = dup(STDERR_FILENO); + if (saved_stderr >= 0) + dup2(muxer.in, STDERR_FILENO); + close(muxer.in); + } + } - if (!start_command(&proc)) { - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - finish_command(&proc); + run_hooks_opt(the_repository, "post-update", &opt); + + if (saved_stderr >= 0) { + dup2(saved_stderr, STDERR_FILENO); + close(saved_stderr); } + if (muxer_started) + finish_async(&muxer); } static void check_aliased_update_internal(struct command *cmd, -- 2.52.0.732.gb351b5166d.dirty