reference-transaction: use hook API instead of run-command

Convert the reference-transaction hook to the new hook API, so it doesn't need to set up a struct child_process, call find_hook or toggle the pipe signals. The stdin feed callback is processing one ref update per call. I haven't noticed any performance degradation due to this, however we can batch as many we want in each call, to ensure a good pipe throughtput (i.e. the child does not wait after stdin). Helped-by: Emily Shaffer <nasamuffin@google.com> Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Adrian Ratiu committed Jan 28, 2026 at 23:39 UTC b7b2157d0d23f6ed98da6fce548d614c5198713d
1 file changed +52 -50
refs.c
+52 -50
@@ -15,7 +15,6 @@
15 #include "iterator.h"
16 #include "refs.h"
17 #include "refs/refs-internal.h"
18 -#include "run-command.h"
18 #include "hook.h"
19 #include "object-name.h"
20 #include "odb.h"
@@ -26,7 +25,6 @@
25 #include "strvec.h"
26 #include "repo-settings.h"
27 #include "setup.h"
29 -#include "sigchain.h"
28 #include "date.h"
29 #include "commit.h"
30 #include "wildmatch.h"
@@ -2422,68 +2420,72 @@ static int ref_update_reject_duplicates(struct string_list *refnames,
2420 return 0;
2421 }
2422
2425 -static int run_transaction_hook(struct ref_transaction *transaction,
2426 - const char *state)
2423 +struct transaction_feed_cb_data {
2424 + size_t index;
2425 + struct strbuf buf;
2426 +};
2427 +
2428 +static int transaction_hook_feed_stdin(int hook_stdin_fd, void *pp_cb, void *pp_task_cb)
2429 {
2428 - struct child_process proc = CHILD_PROCESS_INIT;
2429 - struct strbuf buf = STRBUF_INIT;
2430 - const char *hook;
2431 - int ret = 0;
2430 + struct hook_cb_data *hook_cb = pp_cb;
2431 + struct ref_transaction *transaction = hook_cb->options->feed_pipe_ctx;
2432 + struct transaction_feed_cb_data *feed_cb_data = pp_task_cb;
2433 + struct strbuf *buf = &feed_cb_data->buf;
2434 + struct ref_update *update;
2435 + size_t i = feed_cb_data->index++;
2436 + int ret;
2437
2433 - hook = find_hook(transaction->ref_store->repo, "reference-transaction");
2434 - if (!hook)
2435 - return ret;
2438 + if (i >= transaction->nr)
2439 + return 1; /* No more refs to process */
2440
2437 - strvec_pushl(&proc.args, hook, state, NULL);
2438 - proc.in = -1;
2439 - proc.stdout_to_stderr = 1;
2440 - proc.trace2_hook_name = "reference-transaction";
2441 + update = transaction->updates[i];
2442
2442 - ret = start_command(&proc);
2443 - if (ret)
2444 - return ret;
2443 + if (update->flags & REF_LOG_ONLY)
2444 + return 0;
2445
2446 - sigchain_push(SIGPIPE, SIG_IGN);
2446 + strbuf_reset(buf);
2447
2448 - for (size_t i = 0; i < transaction->nr; i++) {
2449 - struct ref_update *update = transaction->updates[i];
2448 + if (!(update->flags & REF_HAVE_OLD))
2449 + strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2450 + else if (update->old_target)
2451 + strbuf_addf(buf, "ref:%s ", update->old_target);
2452 + else
2453 + strbuf_addf(buf, "%s ", oid_to_hex(&update->old_oid));
2454
2451 - if (update->flags & REF_LOG_ONLY)
2452 - continue;
2455 + if (!(update->flags & REF_HAVE_NEW))
2456 + strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2457 + else if (update->new_target)
2458 + strbuf_addf(buf, "ref:%s ", update->new_target);
2459 + else
2460 + strbuf_addf(buf, "%s ", oid_to_hex(&update->new_oid));
2461
2454 - strbuf_reset(&buf);
2462 + strbuf_addf(buf, "%s\n", update->refname);
2463
2456 - if (!(update->flags & REF_HAVE_OLD))
2457 - strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2458 - else if (update->old_target)
2459 - strbuf_addf(&buf, "ref:%s ", update->old_target);
2460 - else
2461 - strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
2464 + ret = write_in_full(hook_stdin_fd, buf->buf, buf->len);
2465 + if (ret < 0 && errno != EPIPE)
2466 + return ret;
2467
2463 - if (!(update->flags & REF_HAVE_NEW))
2464 - strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2465 - else if (update->new_target)
2466 - strbuf_addf(&buf, "ref:%s ", update->new_target);
2467 - else
2468 - strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
2468 + return 0; /* no more input to feed */
2469 +}
2470 +
2471 +static int run_transaction_hook(struct ref_transaction *transaction,
2472 + const char *state)
2473 +{
2474 + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
2475 + struct transaction_feed_cb_data feed_ctx = { 0 };
2476 + int ret = 0;
2477
2470 - strbuf_addf(&buf, "%s\n", update->refname);
2478 + strvec_push(&opt.args, state);
2479
2472 - if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
2473 - if (errno != EPIPE) {
2474 - /* Don't leak errno outside this API */
2475 - errno = 0;
2476 - ret = -1;
2477 - }
2478 - break;
2479 - }
2480 - }
2480 + opt.feed_pipe = transaction_hook_feed_stdin;
2481 + opt.feed_pipe_ctx = transaction;
2482 + opt.feed_pipe_cb_data = &feed_ctx;
2483
2482 - close(proc.in);
2483 - sigchain_pop(SIGPIPE);
2484 - strbuf_release(&buf);
2484 + strbuf_init(&feed_ctx.buf, 0);
2485 +
2486 + ret = run_hooks_opt(transaction->ref_store->repo, "reference-transaction", &opt);
2487
2486 - ret |= finish_command(&proc);
2488 + strbuf_release(&feed_ctx.buf);
2489 return ret;
2490 }
2491