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 Dec 26, 2025 at 14:23 UTC 7a7717427ea7253003d221c47b462d9334429053
1 file changed +52 -48
refs.c
+52 -48
@@ -2405,68 +2405,72 @@ static int ref_update_reject_duplicates(struct string_list *refnames,
2405 return 0;
2406 }
2407
2408 -static int run_transaction_hook(struct ref_transaction *transaction,
2409 - const char *state)
2408 +struct transaction_feed_cb_data {
2409 + size_t index;
2410 + struct strbuf buf;
2411 +};
2412 +
2413 +static int transaction_hook_feed_stdin(int hook_stdin_fd, void *pp_cb, void *pp_task_cb)
2414 {
2411 - struct child_process proc = CHILD_PROCESS_INIT;
2412 - struct strbuf buf = STRBUF_INIT;
2413 - const char *hook;
2414 - int ret = 0;
2415 + struct hook_cb_data *hook_cb = pp_cb;
2416 + struct ref_transaction *transaction = hook_cb->options->feed_pipe_ctx;
2417 + struct transaction_feed_cb_data *feed_cb_data = pp_task_cb;
2418 + struct strbuf *buf = &feed_cb_data->buf;
2419 + struct ref_update *update;
2420 + size_t i = feed_cb_data->index++;
2421 + int ret;
2422
2416 - hook = find_hook(transaction->ref_store->repo, "reference-transaction");
2417 - if (!hook)
2418 - return ret;
2423 + if (i >= transaction->nr)
2424 + return 1; /* No more refs to process */
2425
2420 - strvec_pushl(&proc.args, hook, state, NULL);
2421 - proc.in = -1;
2422 - proc.stdout_to_stderr = 1;
2423 - proc.trace2_hook_name = "reference-transaction";
2426 + update = transaction->updates[i];
2427
2425 - ret = start_command(&proc);
2426 - if (ret)
2427 - return ret;
2428 + if (update->flags & REF_LOG_ONLY)
2429 + return 0;
2430
2429 - sigchain_push(SIGPIPE, SIG_IGN);
2431 + strbuf_reset(buf);
2432
2431 - for (size_t i = 0; i < transaction->nr; i++) {
2432 - struct ref_update *update = transaction->updates[i];
2433 + if (!(update->flags & REF_HAVE_OLD))
2434 + strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2435 + else if (update->old_target)
2436 + strbuf_addf(buf, "ref:%s ", update->old_target);
2437 + else
2438 + strbuf_addf(buf, "%s ", oid_to_hex(&update->old_oid));
2439
2434 - if (update->flags & REF_LOG_ONLY)
2435 - continue;
2440 + if (!(update->flags & REF_HAVE_NEW))
2441 + strbuf_addf(buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2442 + else if (update->new_target)
2443 + strbuf_addf(buf, "ref:%s ", update->new_target);
2444 + else
2445 + strbuf_addf(buf, "%s ", oid_to_hex(&update->new_oid));
2446
2437 - strbuf_reset(&buf);
2447 + strbuf_addf(buf, "%s\n", update->refname);
2448
2439 - if (!(update->flags & REF_HAVE_OLD))
2440 - strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2441 - else if (update->old_target)
2442 - strbuf_addf(&buf, "ref:%s ", update->old_target);
2443 - else
2444 - strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
2449 + ret = write_in_full(hook_stdin_fd, buf->buf, buf->len);
2450 + if (ret < 0 && errno != EPIPE)
2451 + return ret;
2452
2446 - if (!(update->flags & REF_HAVE_NEW))
2447 - strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
2448 - else if (update->new_target)
2449 - strbuf_addf(&buf, "ref:%s ", update->new_target);
2450 - else
2451 - strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
2453 + return 0; /* no more input to feed */
2454 +}
2455 +
2456 +static int run_transaction_hook(struct ref_transaction *transaction,
2457 + const char *state)
2458 +{
2459 + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
2460 + struct transaction_feed_cb_data feed_ctx = { 0 };
2461 + int ret = 0;
2462
2453 - strbuf_addf(&buf, "%s\n", update->refname);
2463 + strvec_push(&opt.args, state);
2464
2455 - if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
2456 - if (errno != EPIPE) {
2457 - /* Don't leak errno outside this API */
2458 - errno = 0;
2459 - ret = -1;
2460 - }
2461 - break;
2462 - }
2463 - }
2465 + opt.feed_pipe = transaction_hook_feed_stdin;
2466 + opt.feed_pipe_ctx = transaction;
2467 + opt.feed_pipe_cb_data = &feed_ctx;
2468
2465 - close(proc.in);
2466 - sigchain_pop(SIGPIPE);
2467 - strbuf_release(&buf);
2469 + strbuf_init(&feed_ctx.buf, 0);
2470 +
2471 + ret = run_hooks_opt(transaction->ref_store->repo, "reference-transaction", &opt);
2472
2469 - ret |= finish_command(&proc);
2473 + strbuf_release(&feed_ctx.buf);
2474 return ret;
2475 }
2476