8000 Merge branch 'ar/run-command-hook-take-2' · git/git@5c56c72 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5c56c72

Browse files
committed
Merge branch 'ar/run-command-hook-take-2'
Use the hook API to replace ad-hoc invocation of hook scripts via the run_command() API. * ar/run-command-hook-take-2: builtin/receive-pack: avoid spinning no-op sideband async threads receive-pack: convert receive hooks to hook API receive-pack: convert update hooks to new API run-command: poll child input in addition to output hook: add jobs option reference-transaction: use hook API instead of run-command transport: convert pre-push to hook API hook: allow separate std[out|err] streams hook: convert 'post-rewrite' hook in sequencer.c to hook API hook: provide stdin via callback run-command: add stdin callback for parallelization run-command: add helper for pp child states t1800: add hook output stream tests
2 parents 4aa72ea + 005f3fb commit 5c56c72

File tree

11 files changed

+750
-279
lines changed

11 files changed

+750
-279
lines changed

builtin/receive-pack.c

Lines changed: 149 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,48 @@ static int copy_to_sideband(int in, int out UNUSED, void *arg UNUSED)
561561
return 0;
562562
}
563563

564+
/*
565+
* Start an async thread which redirects hook stderr over the sideband.
566+
* The original stderr fd is saved to `saved_stderr` and STDERR_FILENO is
567+
* redirected to the async's input pipe.
568+
*/
569+
static void prepare_sideband_async(struct async *sideband_async, int *saved_stderr, int *started)
570+
{
571+
*started = 0;
572+
573+
if (!use_sideband)
574+
return;
575+
576+
memset(sideband_async, 0, sizeof(*sideband_async));
577+
sideband_async->proc = copy_to_sideband;
578+
sideband_async->in = -1;
579+
580+
if (!start_async(sideband_async)) {
581+
*started = 1;
582+
*saved_stderr = dup(STDERR_FILENO);
583+
if (*saved_stderr >= 0)
584+
dup2(sideband_async->in, STDERR_FILENO);
585+
close(sideband_async->in);
586+
}
587+
}
588+
589+
/*
590+
* Restore the original stderr and wait for the async sideband thread to finish.
591+
*/
592+
static void finish_sideband_async(struct async *sideband_async, int saved_stderr, int started)
593+
{
594+
if (!use_sideband)
595+
return;
596+
597+
if (saved_stderr >= 0) {
598+
dup2(saved_stderr, STDERR_FILENO);
599+
close(saved_stderr);
600+
}
601+
602+
if (started)
603+
finish_async(sideband_async);
604+
}
605+
564606
static void hmac_hash(unsigned char *out,
565607
const char *key_in, size_t key_len,
566608
const char *text, size_t text_len)
@@ -749,7 +791,7 @@ static int check_cert_push_options(const struct string_list *push_options)
749791
return retval;
750792
}
751793

752-
static void prepare_push_cert_sha1(struct child_process *proc)
794+
static void prepare_push_cert_sha1(struct run_hooks_opt *opt)
753795
{
754796
static int already_done;
755797

@@ -775,23 +817,23 @@ static void prepare_push_cert_sha1(struct child_process *proc)
775817
nonce_status = check_nonce(sigcheck.payload);
776818
}
777819
if (!is_null_oid(&push_cert_oid)) {
778-
strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
820+
strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s",
779821
oid_to_hex(&push_cert_oid));
780-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_SIGNER=%s",
822+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s",
781823
sigcheck.signer ? sigcheck.signer : "");
782-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_KEY=%s",
824+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s",
783825
sigcheck.key ? sigcheck.key : "");
784-
strvec_pushf(&proc->env, "GIT_PUSH_CERT_STATUS=%c",
826+
strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c",
785827
sigcheck.result);
786828
if (push_cert_nonce) {
787-
strvec_pushf(&proc->env,
829+
strvec_pushf(&opt->env,
788830
"GIT_PUSH_CERT_NONCE=%s",
789831
push_cert_nonce);
790-
strvec_pushf(&proc->env,
832+
strvec_pushf(&opt->env,
791833
"GIT_PUSH_CERT_NONCE_STATUS=%s",
792834
nonce_status);
793835
if (nonce_status == NONCE_SLOP)
794-
strvec_pushf(&proc->env,
836+
strvec_pushf(&opt->env,
795837
"GIT_PUSH_CERT_NONCE_SLOP=%ld",
796838
nonce_stamp_slop);
797839
}
@@ -803,94 +845,25 @@ struct receive_hook_feed_state {
803845
struct ref_push_report *report;
804846
int skip_broken;
805847
struct strbuf buf;
806-
const struct string_list *push_options;
807848
};
808849

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)
850+
static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_task_cb)
812851
{
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);
817-
818-
if (!hook_path)
819-
return 0;
820-
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");
837-
838-
if (tmp_objdir)
839-
strvec_pushv(&proc.env, tmp_objdir_env(tmp_objdir));
840-
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-
}
850-
851-
prepare_push_cert_sha1(&proc);
852-
853-
code = start_command(&proc);
854-
if (code) {
855-
if (use_sideband)
856-
finish_async(&muxer);
857-
return code;
858-
}
859-
860-
sigchain_push(SIGPIPE, SIG_IGN);
861-
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;
869-
}
870-
close(proc.in);
871-
if (use_sideband)
872-
finish_async(&muxer);
873-
874-
sigchain_pop(SIGPIPE);
875-
876-
return finish_command(&proc);
877-
}
878-
879-
static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
880-
{
881-
struct receive_hook_feed_state *state = state_;
852+
struct receive_hook_feed_state *state = pp_task_cb;
882853
struct command *cmd = state->cmd;
883854

855+
strbuf_reset(&state->buf);
856+
884857
while (cmd &&
885858
state->skip_broken && (cmd->error_string || cmd->did_not_exist))
886859
cmd = cmd->next;
860+
887861
if (!cmd)
888-
return -1; /* EOF */
889-
if (!bufp)
890-
return 0; /* OK, can feed something. */
891-
strbuf_reset(&state->buf);
862+
return 1; /* no more commands left */
863+
892864
if (!state->report)
893865
state->report = cmd->report;
866+
894867
if (state->report) {
895868
struct object_id *old_oid;
896869
struct object_id *new_oid;
@@ -899,71 +872,115 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
899872
old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid;
900873
new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid;
901874
ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name;
875+
902876
strbuf_addf(&state->buf, "%s %s %s\n",
903877
oid_to_hex(old_oid), oid_to_hex(new_oid),
904878
ref_name);
879+
905880
state->report = state->report->next;
906881
if (!state->report)
907-
state->cmd = cmd->next;
882+
cmd = cmd->next;
908883
} else {
909884
strbuf_addf(&state->buf, "%s %s %s\n",
910885
oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
911886
cmd->ref_name);
912-
state->cmd = cmd->next;
887+
cmd = cmd->next;
913888
}
914-
if (bufp) {
915-
*bufp = state->buf.buf;
916-
*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+
}
917899
}
918-
return 0;
900+
901+
return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
919902
}
920903

921904
static int run_receive_hook(struct command *commands,
922905
const char *hook_name,
923906
int skip_broken,
924907
const struct string_list *push_options)
925908
{
926-
struct receive_hook_feed_state state;
927-
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+
917+
if (!hook_exists(the_repository, hook_name))
918+
return 0;
928919

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))
920+
/* if there are no valid commands, don't invoke the hook at all. */
921+
while (iter && skip_broken && (iter->error_string || iter->did_not_exist))
922+
iter = iter->next;
923+
if (!iter)
934924
return 0;
935-
state.cmd = commands;
936-
state.push_options = push_options;
937-
status = run_and_feed_hook( C307 hook_name, feed_receive_hook, &state);
938-
strbuf_release(&state.buf);
939-
return status;
925+
926+
if (push_options) {
927+
for (int i = 0; i < push_options->nr; i++)
928+
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i,
929+
push_options->items[i].string);
930+
strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
931+
(uintmax_t)push_options->nr);
932+
} else {
933+
strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT");
934+
}
935+
936+
if (tmp_objdir)
937+
strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir));
938+
939+
prepare_push_cert_sha1(&opt);
940+
941+
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
942+
943+
/* set up stdin callback */
944+
feed_state.cmd = commands;
945+
feed_state.skip_broken = skip_broken;
946+
feed_state.report = NULL;
947+
strbuf_init(&feed_state.buf, 0);
948+
opt.feed_pipe_cb_data = &feed_state;
949+
opt.feed_pipe = feed_receive_hook_cb;
950+
951+
ret = run_hooks_opt(the_repository, hook_name, &opt);
952+
953+
strbuf_release(&feed_state.buf);
954+
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
955+
956+
return ret;
940957
}
941958

942959
static int run_update_hook(struct command *cmd)
943960
{
944-
struct child_process proc = CHILD_PROCESS_INIT;
961+
static const char hook_name[] = "update";
962+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
963+
struct async sideband_async;
964+
int sideband_async_started = 0;
965+
int saved_stderr = -1;
945966
int code;
946-
const char *hook_path = find_hook(the_repository, "update");
947967

948-
if (!hook_path)
968+
if (!hook_exists(the_repository, hook_name))
949969
return 0;
950970

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));
971+
strvec_pushl(&opt.args,
972+
cmd->ref_name,
973+
oid_to_hex(&cmd->old_oid),
974+
oid_to_hex(&cmd->new_oid),
975+
NULL);
955976

956-
proc.no_stdin = 1;
957-
proc.stdout_to_stderr = 1;
958-
proc.err = use_sideband ? -1 : 0;
959-
proc.trace2_hook_name = "update";
977+
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
960978

961-
code = start_command(&proc);
962-
if (code)
963-
return code;
964-
if (use_sideband)
965-
copy_to_sideband(proc.err, -1, NULL);
966-
return finish_command(&proc);
979+
code = run_hooks_opt(the_repository, hook_name, &opt);
980+
981+
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
982+
983+
return code;
967984
}
968985

969986
static struct command *find_command_by_refname(struct command *list,
@@ -1639,34 +1656,29 @@ static const char *update(struct command *cmd, struct shallow_info *si)
16391656

16401657
static void run_update_post_hook(struct command *commands)
16411658
{
1659+
static const char hook_name[] = "post-update";
1660+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
1661+
struct async sideband_async;
16421662
struct command *cmd;
1643-
struct child_process proc = CHILD_PROCESS_INIT;
1644-
const char *hook;
1663+
int sideband_async_started = 0;
1664+
int saved_stderr = -1;
16451665

1646-
hook = find_hook(the_repository, "post-update");
1647-
if (!hook)
1666+
if (!hook_exists(the_repository, hook_name))
16481667
return;
16491668

16501669
for (cmd = commands; cmd; cmd = cmd->next) {
16511670
if (cmd->error_string || cmd->did_not_exist)
16521671
continue;
1653-
if (!proc.args.nr)
1654-
strvec_push(&proc.args, hook);
1655-
strvec_push(&proc.args, cmd->ref_name);
1672+
strvec_push(&opt.args, cmd->ref_name);
16561673
}
1657-
if (!proc.args.nr)
1674+
if (!opt.args.nr)
16581675
return;
16591676

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";
1677+
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
16641678

1665-
if (!start_command(&proc)) {
1666-
if (use_sideband)
1667-
copy_to_sideband(proc.err, -1, NULL);
1668-
finish_command(&proc);
1669-
}
1679+
run_hooks_opt(the_repository, hook_name, &opt);
1680+
1681+
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
16701682
}
16711683

16721684
static void check_aliased_update_internal(struct command *cmd,

0 commit comments

Comments
 (0)
0