builtin/receive-pack: avoid spinning no-op sideband async threads

Exit early if the hooks do not exist, to avoid spinning up/down sideband async threads which no-op. It is important to call the hook_exists() API provided by hook.[ch] because it covers both config-defined hooks and the "traditional" hooks from the hookdir. find_hook() only covers the hookdir hooks. The regression happened because the no-op async threads add some additional overhead which can be measured with the receive-refs test of the benchmarks suite [1]. Reproduced using: cd benchmarks/receive-refs && \ ./run --revisions /path/to/git \ fc148b146ad41be71a7852c4867f0773cbfe1ff9~,fc148b146ad41be71a7852c4867f0773cbfe1ff9 \ --parameter-list refformat reftable --parameter-list refcount 10000 1: https://gitlab.com/gitlab-org/data-access/git/benchmarks Fixes: fc148b146ad4 ("receive-pack: convert update hooks to new API") Reported-by: Patrick Steinhardt <ps@pks.im> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> [jc: avoid duplicated hardcoded hook names] Signed-off-by: Junio C Hamano <gitster@pobox.com>

Adrian Ratiu committed Mar 2, 2026 at 21:17 UTC 005f3fbe07a20dd5f7dea57f6f46cd797387e56a
1 file changed +13 -2
builtin/receive-pack.c
+13 -2
@@ -914,6 +914,9 @@ static int run_receive_hook(struct command *commands,
914 int saved_stderr = -1;
915 int ret;
916
917 + if (!hook_exists(the_repository, hook_name))
918 + return 0;
919 +
920 /* if there are no valid commands, don't invoke the hook at all. */
921 while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
922 iter = iter->next;
@@ -955,12 +958,16 @@ static int run_receive_hook(struct command *commands,
958
959 static int run_update_hook(struct command *cmd)
960 {
961 + static const char hook_name[] = "update";
962 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
963 struct async sideband_async;
964 int sideband_async_started = 0;
965 int saved_stderr = -1;
966 int code;
967
968 + if (!hook_exists(the_repository, hook_name))
969 + return 0;
970 +
971 strvec_pushl(&opt.args,
972 cmd->ref_name,
973 oid_to_hex(&cmd->old_oid),
@@ -969,7 +976,7 @@ static int run_update_hook(struct command *cmd)
976
977 prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
978
972 - code = run_hooks_opt(the_repository, "update", &opt);
979 + code = run_hooks_opt(the_repository, hook_name, &opt);
980
981 finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
982
@@ -1649,12 +1656,16 @@ out:
1656
1657 static void run_update_post_hook(struct command *commands)
1658 {
1659 + static const char hook_name[] = "post-update";
1660 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
1661 struct async sideband_async;
1662 struct command *cmd;
1663 int sideband_async_started = 0;
1664 int saved_stderr = -1;
1665
1666 + if (!hook_exists(the_repository, hook_name))
1667 + return;
1668 +
1669 for (cmd = commands; cmd; cmd = cmd->next) {
1670 if (cmd->error_string || cmd->did_not_exist)
1671 continue;
@@ -1665,7 +1676,7 @@ static void run_update_post_hook(struct command *commands)
1676
1677 prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
1678
1668 - run_hooks_opt(the_repository, "post-update", &opt);
1679 + run_hooks_opt(the_repository, hook_name, &opt);
1680
1681 finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
1682 }