Skip to content

Commit f333029

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. Comments? * 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 b7850ca + 840b450 commit f333029

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
@@ -748,7 +748,7 @@ static int check_cert_push_options(const struct string_list *push_options)
748748
return retval;
749749
}
750750

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

@@ -774,195 +774,191 @@ static void prepare_push_cert_sha1(struct child_process *proc)
774774
nonce_status = check_nonce(sigcheck.payload);
775775
}
776776
if (!is_null_oid(&push_cert_oid)) {
777-
strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
777+
strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
778778
oid_to_hex(&push_cert_oid));
779-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
779+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
780780
sigcheck.signer ? sigcheck.signer : "");
781-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
781+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
782782
sigcheck.key ? sigcheck.key : "");
783-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
783+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
784784
sigcheck.result);
785785
if (push_cert_nonce) {
786-
strvec_pushf(&proc->env,
786+
strvec_pushf(&opt->env,
787787
"GIT_PUSH_CERT_NONCE=%s",
788788
push_cert_nonce);
789-
strvec_pushf(&proc->env,
789+
strvec_pushf(&opt->env,
790790
"GIT_PUSH_CERT_NONCE_STATUS=%s",
791791
nonce_status);
792792
if (nonce_status == NONCE_SLOP)
793-
strvec_pushf(&proc->env,
793+
strvec_pushf(&opt->env,
794794
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
795795
nonce_stamp_slop);
796796
}
797797
}
798798
}
799799

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

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

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

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

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

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

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

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

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

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

873-
sigchain_pop(SIGPIPE);
854+
state->cmd = cmd;
874855

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

878-
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
868+
static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb, void *pp_task_cb UNUSED)
879869
{
880-
struct receive_hook_feed_state *state = state_;
881-
struct command *cmd = state->cmd;
870+
struct hook_cb_data *hook_cb = pp_cb;
871+
struct receive_hook_feed_state *feed_state = hook_cb->options->feed_pipe_cb_data;
882872

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

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

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

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

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

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

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

16491640
for (cmd = commands; cmd; cmd = cmd->next) {
16501641
if (cmd->error_string || cmd->did_not_exist)
16511642
continue;
1652-
if (!proc.args.nr)
1653-
strvec_push(&proc.args, hook);
1654-
strvec_push(&proc.args, cmd->ref_name);
1643+
strvec_push(&opt.args, cmd->ref_name);
16551644
}
1656-
if (!proc.args.nr)
1645+
if (!opt.args.nr)
16571646
return;
16581647

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

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

16711654
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
@@ -1965,6 +1965,9 @@ int run_commit_hook(int editor_is_used, const char *index_file,
19651965
strvec_push(&opt.args, arg);
19661966
va_end(args);
19671967

1968+
/* All commit hook use-cases require ungrouping child output. */
1969+
opt.ungroup = 1;
1970+
19681971
opt.invoked_hook = invoked_hook;
19691972
return run_hooks_opt(the_repository, name, &opt);
19701973
}

0 commit comments

Comments
 (0)