receive-pack: convert receive hooks to hook API
This converts the last remaining hooks to the new hook API, for the same benefits as the previous conversions (no need to toggle signals, manage custom struct child_process, call find_hook(), prepares for specifying hooks via configs, etc.). See the previous three commits for a more in-depth explanation of how this all works. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Helped-by: Jeff King <peff@peff.net> 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
b5e9ad508c2f340bd2d2547d7370ae7ac6a0d65d
1 file changed
+75
-103
builtin/receive-pack.c
+75
-103
@@ -791,7 +791,7 @@ static int check_cert_push_options(const struct string_list *push_options)
791
return retval;
792
}
793
794
-static void prepare_push_cert_sha1(struct child_process *proc)
794
+static void prepare_push_cert_sha1(struct run_hooks_opt *opt)
795
{
796
static int already_done;
797
@@ -817,23 +817,23 @@ static void prepare_push_cert_sha1(struct child_process *proc)
817
nonce_status = check_nonce(sigcheck.payload);
818
}
819
if (!is_null_oid(&push_cert_oid)) {
820
- strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
820
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
821
oid_to_hex(&push_cert_oid));
822
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
822
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
823
sigcheck.signer ? sigcheck.signer : "");
824
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
824
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
825
sigcheck.key ? sigcheck.key : "");
826
- strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
826
+ strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
827
sigcheck.result);
828
if (push_cert_nonce) {
829
- strvec_pushf(&proc->env,
829
+ strvec_pushf(&opt->env,
830
"GIT_PUSH_CERT_NONCE=%s",
831
push_cert_nonce);
832
- strvec_pushf(&proc->env,
832
+ strvec_pushf(&opt->env,
833
"GIT_PUSH_CERT_NONCE_STATUS=%s",
834
nonce_status);
835
if (nonce_status == NONCE_SLOP)
836
- strvec_pushf(&proc->env,
836
+ strvec_pushf(&opt->env,
837
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
838
nonce_stamp_slop);
839
}
@@ -845,94 +845,25 @@ struct receive_hook_feed_state {
845
struct ref_push_report *report;
846
int skip_broken;
847
struct strbuf buf;
848
- const struct string_list *push_options;
848
};
849
851
-typedef int (*feed_fn)(void *, const char **, size_t *);
852
-static int run_and_feed_hook(const char *hook_name, feed_fn feed,
853
- struct receive_hook_feed_state *feed_state)
850
+static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_task_cb)
851
{
855
- struct child_process proc = CHILD_PROCESS_INIT;
856
- struct async muxer;
857
- int code;
858
- const char *hook_path = find_hook(the_repository, hook_name);
859
-
860
- if (!hook_path)
861
- return 0;
862
-
863
- strvec_push(&proc.args, hook_path);
864
- proc.in = -1;
865
- proc.stdout_to_stderr = 1;
866
- proc.trace2_hook_name = hook_name;
867
-
868
- if (feed_state->push_options) {
869
- size_t i;
870
- for (i = 0; i < feed_state->push_options->nr; i++)
871
- strvec_pushf(&proc.env,
872
- "GIT_PUSH_OPTION_%"PRIuMAX"=%s",
873
- (uintmax_t)i,
874
- feed_state->push_options->items[i].string);
875
- strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
876
- (uintmax_t)feed_state->push_options->nr);
877
- } else
878
- strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT");
879
-
880
- if (tmp_objdir)
881
- strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
882
-
883
- if (use_sideband) {
884
- memset(&muxer, 0, sizeof(muxer));
885
- muxer.proc = copy_to_sideband;
886
- muxer.in = -1;
887
- code = start_async(&muxer);
888
- if (code)
889
- return code;
890
- proc.err = muxer.in;
891
- }
892
-
893
- prepare_push_cert_sha1(&proc);
894
-
895
- code = start_command(&proc);
896
- if (code) {
897
- if (use_sideband)
898
- finish_async(&muxer);
899
- return code;
900
- }
901
-
902
- sigchain_push(SIGPIPE, SIG_IGN);
903
-
904
- while (1) {
905
- const char *buf;
906
- size_t n;
907
- if (feed(feed_state, &buf, &n))
908
- break;
909
- if (write_in_full(proc.in, buf, n) < 0)
910
- break;
911
- }
912
- close(proc.in);
913
- if (use_sideband)
914
- finish_async(&muxer);
915
-
916
- sigchain_pop(SIGPIPE);
917
-
918
- return finish_command(&proc);
919
-}
920
-
921
-static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
922
-{
923
- struct receive_hook_feed_state *state = state_;
852
+ struct receive_hook_feed_state *state = pp_task_cb;
853
struct command *cmd = state->cmd;
854
855
+ strbuf_reset(&state->buf);
856
+
857
while (cmd &&
858
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
859
cmd = cmd->next;
860
+
861
if (!cmd)
930
- return -1; /* EOF */
931
- if (!bufp)
932
- return 0; /* OK, can feed something. */
933
- strbuf_reset(&state->buf);
862
+ return 1; /* no more commands left */
863
+
864
if (!state->report)
865
state->report = cmd->report;
866
+
867
if (state->report) {
868
struct object_id *old_oid;
869
struct object_id *new_oid;
@@ -941,23 +872,33 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
872
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
873
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
874
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
875
+
876
strbuf_addf(&state->buf, "%s %s %s\n",
877
oid_to_hex(old_oid), oid_to_hex(new_oid),
878
ref_name);
879
+
880
state->report = state->report->next;
881
if (!state->report)
949
- state->cmd = cmd->next;
882
+ cmd = cmd->next;
883
} else {
884
strbuf_addf(&state->buf, "%s %s %s\n",
885
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
886
cmd->ref_name);
954
- state->cmd = cmd->next;
887
+ cmd = cmd->next;
888
}
956
- if (bufp) {
957
- *bufp = state->buf.buf;
958
- *sizep = state->buf.len;
889
+
890
+ state->cmd = cmd;
891
+
892
+ if (state->buf.len > 0) {
893
+ int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
894
+ if (ret < 0) {
895
+ if (errno == EPIPE)
896
+ return 1; /* child closed pipe */
897
+ return ret;
898
+ }
899
}
960
- return 0;
900
+
901
+ return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
902
}
903
904
static int run_receive_hook(struct command *commands,
@@ -965,20 +906,51 @@ static int run_receive_hook(struct command *commands,
906
int skip_broken,
907
const struct string_list *push_options)
908
{
968
- struct receive_hook_feed_state state;
969
- int status;
909
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
910
+ struct command *iter = commands;
911
+ struct receive_hook_feed_state feed_state;
912
+ struct async sideband_async;
913
+ int sideband_async_started = 0;
914
+ int saved_stderr = -1;
915
+ int ret;
916
971
- strbuf_init(&state.buf, 0);
972
- state.cmd = commands;
973
- state.skip_broken = skip_broken;
974
- state.report = NULL;
975
- if (feed_receive_hook(&state, NULL, NULL))
917
+ /* if there are no valid commands, don't invoke the hook at all. */
918
+ while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
919
+ iter = iter->next;
920
+ if (!iter)
921
return 0;
977
- state.cmd = commands;
978
- state.push_options = push_options;
979
- status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
980
- strbuf_release(&state.buf);
981
- return status;
922
+
923
+ if (push_options) {
924
+ for (int i = 0; i < push_options->nr; i++)
925
+ strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i,
926
+ push_options->items[i].string);
927
+ strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
928
+ (uintmax_t)push_options->nr);
929
+ } else {
930
+ strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
931
+ }
932
+
933
+ if (tmp_objdir)
934
+ strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
935
+
936
+ prepare_push_cert_sha1(&opt);
937
+
938
+ prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
939
+
940
+ /* set up stdin callback */
941
+ feed_state.cmd = commands;
942
+ feed_state.skip_broken = skip_broken;
943
+ feed_state.report = NULL;
944
+ strbuf_init(&feed_state.buf, 0);
945
+ opt.feed_pipe_cb_data = &feed_state;
946
+ opt.feed_pipe = feed_receive_hook_cb;
947
+
948
+ ret = run_hooks_opt(the_repository, hook_name, &opt);
949
+
950
+ strbuf_release(&feed_state.buf);
951
+ finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
952
+
953
+ return ret;
954
}
955
956
static int run_update_hook(struct command *cmd)