Skip to content

Commit a438a27

Browse files
committed
Merge branch 'ar/run-command-hook' into seen
Use hook API to replace ad-hoc invocation of hook scripts with the run_command() API. Seems to leak and break CI cf. <[email protected]> * ar/run-command-hook: receive-pack: convert receive hooks to hook API receive-pack: convert update hooks to new API hooks: allow callers to capture output run-command: allow capturing of collated output reference-transaction: use hook API instead of run-command hook: allow overriding the ungroup option transport: convert pre-push to hook API hook: convert 'post-rewrite' hook in sequencer.c to hook API hook: provide stdin via callback run-command: add stdin callback for parallelization
2 parents 5159cd9 + 840b450 commit a438a27

File tree

12 files changed

+557
-281
lines changed

12 files changed

+557
-281
lines changed

builtin/hook.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ static int run(int argc, const char **argv, const char *prefix,
4343
if (!argc)
4444
goto usage;
4545

46+
/*
47+
* All current "hook run" use-cases require ungrouped child output.
48+
* If this changes, a hook run argument can be added to toggle it.
49+
*/
50+
opt.ungroup = 1;
51+
4652
/*
4753
* Having a -- for "run" when providing <hook-args> is
4854
* mandatory.

builtin/receive-pack.c

Lines changed: 136 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ static int check_cert_push_options(const struct string_list *push_options)
749749
return retval;
750750
}
751751

752-
static void prepare_push_cert_sha1(struct child_process *proc)
752+
static void prepare_push_cert_sha1(struct run_hooks_opt *opt)
753753
{
754754
static int already_done;
755755

@@ -775,195 +775,191 @@ static void prepare_push_cert_sha1(struct child_process *proc)
775775
nonce_status = check_nonce(sigcheck.payload);
776776
}
777777
if (!is_null_oid(&push_cert_oid)) {
778-
strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
778+
strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
779779
oid_to_hex(&push_cert_oid));
780-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
780+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
781781
sigcheck.signer ? sigcheck.signer : "");
782-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
782+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
783783
sigcheck.key ? sigcheck.key : "");
784-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
784+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
785785
sigcheck.result);
786786
if (push_cert_nonce) {
787-
strvec_pushf(&proc->env,
787+
strvec_pushf(&opt->env,
788788
"GIT_PUSH_CERT_NONCE=%s",
789789
push_cert_nonce);
790-
strvec_pushf(&proc->env,
790+
strvec_pushf(&opt->env,
791791
"GIT_PUSH_CERT_NONCE_STATUS=%s",
792792
nonce_status);
793793
if (nonce_status == NONCE_SLOP)
794-
strvec_pushf(&proc->env,
794+
strvec_pushf(&opt->env,
795795
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
796796
nonce_stamp_slop);
797797
}
798798
}
799799
}
800800

801+
struct receive_hook_feed_context {
802+
struct command *cmd;
803+
int skip_broken;
804+
};
805+
801806
struct receive_hook_feed_state {
802807
struct command *cmd;
803808
struct ref_push_report *report;
804809
int skip_broken;
805810
struct strbuf buf;
806-
const struct string_list *push_options;
807811
};
808812

809-
typedef int (*feed_fn)(void *, const char **, size_t *);
810-
static int run_and_feed_hook(const char *hook_name, feed_fn feed,
811-
struct receive_hook_feed_state *feed_state)
813+
static int feed_receive_hook(int hook_stdin_fd, struct receive_hook_feed_state *state, int lines_batch_size)
812814
{
813-
struct child_process proc = CHILD_PROCESS_INIT;
814-
struct async muxer;
815-
int code;
816-
const char *hook_path = find_hook(the_repository, hook_name);
815+
struct command *cmd = state->cmd;
817816

818-
if (!hook_path)
819-
return 0;
817+
strbuf_reset(&state->buf);
820818

821-
strvec_push(&proc.args, hook_path);
822-
proc.in = -1;
823-
proc.stdout_to_stderr = 1;
824-
proc.trace2_hook_name = hook_name;
825-
826-
if (feed_state->push_options) {
827-
size_t i;
828-
for (i = 0; i < feed_state->push_options->nr; i++)
829-
strvec_pushf(&proc.env,
830-
"GIT_PUSH_OPTION_%"PRIuMAX"=%s",
831-
(uintmax_t)i,
832-
feed_state->push_options->items[i].string);
833-
strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
834-
(uintmax_t)feed_state->push_options->nr);
835-
} else
836-
strvec_pushf(&proc.env, "GIT_PUSH_OPTION_COUNT");
819+
/* batch lines to avoid going through run-command's ppoll for each line */
820+
for (int i = 0; i < lines_batch_size; i++) {
821+
while (cmd &&
822+
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
823+
cmd = cmd->next;
837824

838-
if (tmp_objdir)
839-
strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
825+
if (!cmd)
826+
break; /* no more commands left */
840827

841-
if (use_sideband) {
842-
memset(&muxer, 0, sizeof(muxer));
843-
muxer.proc = copy_to_sideband;
844-
muxer.in = -1;
845-
code = start_async(&muxer);
846-
if (code)
847-
return code;
848-
proc.err = muxer.in;
849-
}
828+
if (!state->report)
829+
state->report = cmd->report;
850830

851-
prepare_push_cert_sha1(&proc);
831+
if (state->report) {
832+
struct object_id *old_oid;
833+
struct object_id *new_oid;
834+
const char *ref_name;
852835

853-
code = start_command(&proc);
854-
if (code) {
855-
if (use_sideband)
856-
finish_async(&muxer);
857-
return code;
858-
}
836+
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
837+
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
838+
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
859839

860-
sigchain_push(SIGPIPE, SIG_IGN);
840+
strbuf_addf(&state->buf, "%s %s %s\n",
841+
oid_to_hex(old_oid), oid_to_hex(new_oid),
842+
ref_name);
861843

862-
while (1) {
863-
const char *buf;
864-
size_t n;
865-
if (feed(feed_state, &buf, &n))
866-
break;
867-
if (write_in_full(proc.in, buf, n) < 0)
868-
break;
844+
state->report = state->report->next;
845+
if (!state->report)
846+
cmd = cmd->next;
847+
} else {
848+
strbuf_addf(&state->buf, "%s %s %s\n",
849+
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
850+
cmd->ref_name);
851+
cmd = cmd->next;
852+
}
869853
}
870-
close(proc.in);
871-
if (use_sideband)
872-
finish_async(&muxer);
873854

874-
sigchain_pop(SIGPIPE);
855+
state->cmd = cmd;
875856

876-
return finish_command(&proc);
857+
if (state->buf.len > 0) {
858+
int ret = write_in_full(hook_stdin_fd, state->buf.buf, state->buf.len);
859+
if (ret < 0) {
860+
if (errno == EPIPE)
861+
return 1; /* child closed pipe */
862+
return ret;
863+
}
864+
}
865+
866+
return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
877867
}
878868

879-
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
869+
static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb, void *pp_task_cb UNUSED)
880870
{
881-
struct receive_hook_feed_state *state = state_;
882-
struct command *cmd = state->cmd;
871+
struct hook_cb_data *hook_cb = pp_cb;
872+
struct receive_hook_feed_state *feed_state = hook_cb->options->feed_pipe_cb_data;
883873

884-
while (cmd &&
885-
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
886-
cmd = cmd->next;
887-
if (!cmd)
888-
return -1; /* EOF */
889-
if (!bufp)
890-
return 0; /* OK, can feed something. */
891-
strbuf_reset(&state->buf);
892-
if (!state->report)
893-
state->report = cmd->report;
894-
if (state->report) {
895-
struct object_id *old_oid;
896-
struct object_id *new_oid;
897-
const char *ref_name;
898-
899-
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
900-
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
901-
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
902-
strbuf_addf(&state->buf, "%s %s %s\n",
903-
oid_to_hex(old_oid), oid_to_hex(new_oid),
904-
ref_name);
905-
state->report = state->report->next;
906-
if (!state->report)
907-
state->cmd = cmd->next;
908-
} else {
909-
strbuf_addf(&state->buf, "%s %s %s\n",
910-
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
911-
cmd->ref_name);
912-
state->cmd = cmd->next;
913-
}
914-
if (bufp) {
915-
*bufp = state->buf.buf;
916-
*sizep = state->buf.len;
874+
/* first-time setup */
875+
if (!hook_cb->options->feed_pipe_cb_data) {
876+
struct receive_hook_feed_context *ctx = hook_cb->options->feed_pipe_ctx;
877+
if (!ctx)
878+
BUG("run_hooks_opt.feed_pipe_ctx required for receive hook");
879+
880+
hook_cb->options->feed_pipe_cb_data = xmalloc(sizeof(struct receive_hook_feed_state));
881+
feed_state = hook_cb->options->feed_pipe_cb_data;
882+
strbuf_init(&feed_state->buf, 0);
883+
feed_state->cmd = ctx->cmd;
884+
feed_state->skip_broken = ctx->skip_broken;
885+
feed_state->report = NULL;
917886
}
918-
return 0;
887+
888+
/* batch 500 lines at once to avoid going through the run-command ppoll loop too often */
889+
if (feed_receive_hook(hook_stdin_fd, feed_state, 500) == 0)
890+
return 0; /* still have more data to feed */
891+
892+
strbuf_release(&feed_state->buf);
893+
894+
if (hook_cb->options->feed_pipe_cb_data)
895+
FREE_AND_NULL(hook_cb->options->feed_pipe_cb_data);
896+
897+
return 1; /* done feeding, run-command can close pipe */
898+
}
899+
900+
static void hook_output_to_sideband(struct strbuf *output, void *cb_data UNUSED)
901+
{
902+
if (output && output->len)
903+
send_sideband(1, 2, output->buf, output->len, use_sideband);
919904
}
920905

921906
static int run_receive_hook(struct command *commands,
922907
const char *hook_name,
923908
int skip_broken,
924909
const struct string_list *push_options)
925910
{
926-
struct receive_hook_feed_state state;
927-
int status;
911+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
912+
struct receive_hook_feed_context ctx;
913+
struct command *iter = commands;
928914

929-
strbuf_init(&state.buf, 0);
930-
state.cmd = commands;
931-
state.skip_broken = skip_broken;
932-
state.report = NULL;
933-
if (feed_receive_hook(&state, NULL, NULL))
915+
/* if there are no valid commands, don't invoke the hook at all. */
916+
while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
917+
iter = iter->next;
918+
if (!iter)
934919
return 0;
935-
state.cmd = commands;
936-
state.push_options = push_options;
937-
status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
938-
strbuf_release(&state.buf);
939-
return status;
920+
921+
if (push_options) {
922+
int i;
923+
for (i = 0; i < push_options->nr; i++)
924+
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i,
925+
push_options->items[i].string);
926+
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
927+
(uintmax_t)push_options->nr);
928+
} else
929+
strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
930+
931+
if (tmp_objdir)
932+
strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
933+
934+
prepare_push_cert_sha1(&opt);
935+
936+
/* set up sideband printer */
937+
if (use_sideband)
938+
opt.consume_sideband = hook_output_to_sideband;
939+
940+
/* set up stdin callback */
941+
ctx.cmd = commands;
942+
ctx.skip_broken = skip_broken;
943+
opt.feed_pipe = feed_receive_hook_cb;
944+
opt.feed_pipe_ctx = &ctx;
945+
946+
return run_hooks_opt(the_repository, hook_name, &opt);
940947
}
941948

942949
static int run_update_hook(struct command *cmd)
943950
{
944-
struct child_process proc = CHILD_PROCESS_INIT;
945-
int code;
946-
const char *hook_path = find_hook(the_repository, "update");
947-
948-
if (!hook_path)
949-
return 0;
950-
951-
strvec_push(&proc.args, hook_path);
952-
strvec_push(&proc.args, cmd->ref_name);
953-
strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
954-
strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
951+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
955952

956-
proc.no_stdin = 1;
957-
proc.stdout_to_stderr = 1;
958-
proc.err = use_sideband ? -1 : 0;
959-
proc.trace2_hook_name = "update";
953+
strvec_pushl(&opt.args,
954+
cmd->ref_name,
955+
oid_to_hex(&cmd->old_oid),
956+
oid_to_hex(&cmd->new_oid),
957+
NULL);
960958

961-
code = start_command(&proc);
962-
if (code)
963-
return code;
964959
if (use_sideband)
965-
copy_to_sideband(proc.err, -1, NULL);
966-
return finish_command(&proc);
960+
opt.consume_sideband = hook_output_to_sideband;
961+
962+
return run_hooks_opt(the_repository, "update", &opt);
967963
}
968964

969965
static struct command *find_command_by_refname(struct command *list,
@@ -1640,33 +1636,20 @@ static const char *update(struct command *cmd, struct shallow_info *si)
16401636
static void run_update_post_hook(struct command *commands)
16411637
{
16421638
struct command *cmd;
1643-
struct child_process proc = CHILD_PROCESS_INIT;
1644-
const char *hook;
1645-
1646-
hook = find_hook(the_repository, "post-update");
1647-
if (!hook)
1648-
return;
1639+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
16491640

16501641
for (cmd = commands; cmd; cmd = cmd->next) {
16511642
if (cmd->error_string || cmd->did_not_exist)
16521643
continue;
1653-
if (!proc.args.nr)
1654-
strvec_push(&proc.args, hook);
1655-
strvec_push(&proc.args, cmd->ref_name);
1644+
strvec_push(&opt.args, cmd->ref_name);
16561645
}
1657-
if (!proc.args.nr)
1646+
if (!opt.args.nr)
16581647
return;
16591648

1660-
proc.no_stdin = 1;
1661-
proc.stdout_to_stderr = 1;
1662-
proc.err = use_sideband ? -1 : 0;
1663-
proc.trace2_hook_name = "post-update";
1649+
if (use_sideband)
1650+
opt.consume_sideband = hook_output_to_sideband;
16641651

1665-
if (!start_command(&proc)) {
1666-
if (use_sideband)
1667-
copy_to_sideband(proc.err, -1, NULL);
1668-
finish_command(&proc);
1669-
}
1652+
run_hooks_opt(the_repository, "post-update", &opt);
16701653
}
16711654

16721655
static void check_aliased_update_internal(struct command *cmd,

commit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,6 +1978,9 @@ int run_commit_hook(int editor_is_used, const char *index_file,
19781978
strvec_push(&opt.args, arg);
19791979
va_end(args);
19801980

1981+
/* All commit hook use-cases require ungrouping child output. */
1982+
opt.ungroup = 1;
1983+
19811984
opt.invoked_hook = invoked_hook;
19821985
return run_hooks_opt(the_repository, name, &opt);
19831986
}

0 commit comments

Comments
 (0)