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)