transport: convert pre-push to hook API

Move the pre-push hook from custom run-command invocations to the new hook API which doesn't require a custom child_process structure and signal toggling. 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>

Emily Shaffer committed Jan 28, 2026 at 23:39 UTC 9ac9612c828fc8c28afc833ca0a3511f2f406628
1 file changed +51 -44
transport.c
+51 -44
@@ -1316,65 +1316,72 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing)
1316 die(_("Aborting."));
1317 }
1318
1319 -static int run_pre_push_hook(struct transport *transport,
1320 - struct ref *remote_refs)
1321 -{
1322 - int ret = 0, x;
1323 - struct ref *r;
1324 - struct child_process proc = CHILD_PROCESS_INIT;
1319 +struct feed_pre_push_hook_data {
1320 struct strbuf buf;
1326 - const char *hook_path = find_hook(the_repository, "pre-push");
1321 + const struct ref *refs;
1322 +};
1323
1328 - if (!hook_path)
1329 - return 0;
1324 +static int pre_push_hook_feed_stdin(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_task_cb)
1325 +{
1326 + struct feed_pre_push_hook_data *data = pp_task_cb;
1327 + const struct ref *r = data->refs;
1328 + int ret = 0;
1329
1331 - strvec_push(&proc.args, hook_path);
1332 - strvec_push(&proc.args, transport->remote->name);
1333 - strvec_push(&proc.args, transport->url);
1330 + if (!r)
1331 + return 1; /* no more refs */
1332
1335 - proc.in = -1;
1336 - proc.trace2_hook_name = "pre-push";
1333 + data->refs = r->next;
1334
1338 - if (start_command(&proc)) {
1339 - finish_command(&proc);
1340 - return -1;
1335 + switch (r->status) {
1336 + case REF_STATUS_REJECT_NONFASTFORWARD:
1337 + case REF_STATUS_REJECT_REMOTE_UPDATED:
1338 + case REF_STATUS_REJECT_STALE:
1339 + case REF_STATUS_UPTODATE:
1340 + return 0; /* skip refs which won't be pushed */
1341 + default:
1342 + break;
1343 }
1344
1343 - sigchain_push(SIGPIPE, SIG_IGN);
1345 + if (!r->peer_ref)
1346 + return 0;
1347
1345 - strbuf_init(&buf, 256);
1348 + strbuf_reset(&data->buf);
1349 + strbuf_addf(&data->buf, "%s %s %s %s\n",
1350 + r->peer_ref->name, oid_to_hex(&r->new_oid),
1351 + r->name, oid_to_hex(&r->old_oid));
1352
1347 - for (r = remote_refs; r; r = r->next) {
1348 - if (!r->peer_ref) continue;
1349 - if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
1350 - if (r->status == REF_STATUS_REJECT_STALE) continue;
1351 - if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue;
1352 - if (r->status == REF_STATUS_UPTODATE) continue;
1353 + ret = write_in_full(hook_stdin_fd, data->buf.buf, data->buf.len);
1354 + if (ret < 0 && errno != EPIPE)
1355 + return ret; /* We do not mind if a hook does not read all refs. */
1356
1354 - strbuf_reset(&buf);
1355 - strbuf_addf( &buf, "%s %s %s %s\n",
1356 - r->peer_ref->name, oid_to_hex(&r->new_oid),
1357 - r->name, oid_to_hex(&r->old_oid));
1357 + return 0;
1358 +}
1359
1359 - if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
1360 - /* We do not mind if a hook does not read all refs. */
1361 - if (errno != EPIPE)
1362 - ret = -1;
1363 - break;
1364 - }
1365 - }
1360 +static int run_pre_push_hook(struct transport *transport,
1361 + struct ref *remote_refs)
1362 +{
1363 + struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
1364 + struct feed_pre_push_hook_data data;
1365 + int ret = 0;
1366 +
1367 + strvec_push(&opt.args, transport->remote->name);
1368 + strvec_push(&opt.args, transport->url);
1369
1367 - strbuf_release(&buf);
1370 + strbuf_init(&data.buf, 0);
1371 + data.refs = remote_refs;
1372
1369 - x = close(proc.in);
1370 - if (!ret)
1371 - ret = x;
1373 + opt.feed_pipe = pre_push_hook_feed_stdin;
1374 + opt.feed_pipe_cb_data = &data;
1375 +
1376 + /*
1377 + * pre-push hooks expect stdout & stderr to be separate, so don't merge
1378 + * them to keep backwards compatibility with existing hooks.
1379 + */
1380 + opt.stdout_to_stderr = 0;
1381
1373 - sigchain_pop(SIGPIPE);
1382 + ret = run_hooks_opt(the_repository, "pre-push", &opt);
1383
1375 - x = finish_command(&proc);
1376 - if (!ret)
1377 - ret = x;
1384 + strbuf_release(&data.buf);
1385
1386 return ret;
1387 }