Skip to content

Commit d1e775e

Browse files
committed
Merge branch 'ab/hook-api-with-stdin' into seen
Extend the run-hooks API to allow feeding data from the standard input when running the hook script(s). * ab/hook-api-with-stdin: hook: support a --to-stdin=<path> option for testing sequencer: use the new hook API for the simpler "post-rewrite" call hook API: support passing stdin to hooks, convert am's 'post-rewrite' run-command: allow stdin for run_processes_parallel run-command.c: remove dead assignment in while-loop
2 parents d5f9f0d + ad2f236 commit d1e775e

File tree

8 files changed

+56
-37
lines changed

8 files changed

+56
-37
lines changed

Documentation/git-hook.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-hook - Run git hooks
88
SYNOPSIS
99
--------
1010
[verse]
11-
'git hook' run [--ignore-missing] <hook-name> [-- <hook-args>]
11+
'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
1212

1313
DESCRIPTION
1414
-----------
@@ -31,6 +31,11 @@ linkgit:githooks[5] for arguments hooks might expect (if any).
3131
OPTIONS
3232
-------
3333

34+
--to-stdin::
35+
For "run"; Specify a file which will be streamed into the
36+
hook's stdin. The hook will receive the entire file from
37+
beginning to EOF.
38+
3439
--ignore-missing::
3540
Ignore any missing hook by quietly returning zero. Used for
3641
tools that want to do a blind one-shot run of a hook that may

builtin/am.c

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -495,24 +495,12 @@ static int run_applypatch_msg_hook(struct am_state *state)
495495
*/
496496
static int run_post_rewrite_hook(const struct am_state *state)
497497
{
498-
struct child_process cp = CHILD_PROCESS_INIT;
499-
const char *hook = find_hook("post-rewrite");
500-
int ret;
501-
502-
if (!hook)
503-
return 0;
498+
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
504499

505-
strvec_push(&cp.args, hook);
506-
strvec_push(&cp.args, "rebase");
500+
strvec_push(&opt.args, "rebase");
501+
opt.path_to_stdin = am_path(state, "rewritten");
507502

508-
cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
509-
cp.stdout_to_stderr = 1;
510-
cp.trace2_hook_name = "post-rewrite";
511-
512-
ret = run_command(&cp);
513-
514-
close(cp.in);
515-
return ret;
503+
return run_hooks_opt("post-rewrite", &opt);
516504
}
517505

518506
/**

builtin/hook.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include "strvec.h"
88

99
#define BUILTIN_HOOK_RUN_USAGE \
10-
N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
10+
N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
1111

1212
static const char * const builtin_hook_usage[] = {
1313
BUILTIN_HOOK_RUN_USAGE,
@@ -28,6 +28,8 @@ static int run(int argc, const char **argv, const char *prefix)
2828
struct option run_options[] = {
2929
OPT_BOOL(0, "ignore-missing", &ignore_missing,
3030
N_("silently ignore missing requested <hook-name>")),
31+
OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
32+
N_("file to read into hooks' stdin")),
3133
OPT_END(),
3234
};
3335
int ret;

hook.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,14 @@ static int pick_next_hook(struct child_process *cp,
5353
if (!hook_path)
5454
return 0;
5555

56-
cp->no_stdin = 1;
5756
strvec_pushv(&cp->env, hook_cb->options->env.v);
57+
/* reopen the file for stdin; run_command closes it. */
58+
if (hook_cb->options->path_to_stdin) {
59+
cp->no_stdin = 0;
60+
cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
61+
} else {
62+
cp->no_stdin = 1;
63+
}
5864
cp->stdout_to_stderr = 1;
5965
cp->trace2_hook_name = hook_cb->hook_name;
6066
cp->dir = hook_cb->options->dir;

hook.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ struct run_hooks_opt
3030
* was invoked.
3131
*/
3232
int *invoked_hook;
33+
34+
/**
35+
* Path to file which should be piped to stdin for each hook.
36+
*/
37+
const char *path_to_stdin;
3338
};
3439

3540
#define RUN_HOOKS_OPT_INIT { \

run-command.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,14 @@ static int pp_start_one(struct parallel_processes *pp,
15891589
if (i == opts->processes)
15901590
BUG("bookkeeping is hard");
15911591

1592+
/*
1593+
* By default, do not inherit stdin from the parent process - otherwise,
1594+
* all children would share stdin! Users may overwrite this to provide
1595+
* something to the child's stdin by having their 'get_next_task'
1596+
* callback assign 0 to .no_stdin and an appropriate integer to .in.
1597+
*/
1598+
pp->children[i].process.no_stdin = 1;
1599+
15921600
code = opts->get_next_task(&pp->children[i].process,
15931601
opts->ungroup ? NULL : &pp->children[i].err,
15941602
opts->data,
@@ -1604,7 +1612,6 @@ static int pp_start_one(struct parallel_processes *pp,
16041612
pp->children[i].process.err = -1;
16051613
pp->children[i].process.stdout_to_stderr = 1;
16061614
}
1607-
pp->children[i].process.no_stdin = 1;
16081615

16091616
if (start_command(&pp->children[i].process)) {
16101617
if (opts->start_failure)
@@ -1635,9 +1642,7 @@ static void pp_buffer_stderr(struct parallel_processes *pp,
16351642
const struct run_process_parallel_opts *opts,
16361643
int output_timeout)
16371644
{
1638-
int i;
1639-
1640-
while ((i = poll(pp->pfd, opts->processes, output_timeout) < 0)) {
1645+
while (poll(pp->pfd, opts->processes, output_timeout) < 0) {
16411646
if (errno == EINTR)
16421647
continue;
16431648
pp_cleanup(pp, opts);

sequencer.c

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4844,8 +4844,7 @@ static int pick_commits(struct repository *r,
48444844
if (!stat(rebase_path_rewritten_list(), &st) &&
48454845
st.st_size > 0) {
48464846
struct child_process child = CHILD_PROCESS_INIT;
4847-
const char *post_rewrite_hook =
4848-
find_hook("post-rewrite");
4847+
struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
48494848

48504849
child.in = open(rebase_path_rewritten_list(), O_RDONLY);
48514850
child.git_cmd = 1;
@@ -4855,18 +4854,9 @@ static int pick_commits(struct repository *r,
48554854
/* we don't care if this copying failed */
48564855
run_command(&child);
48574856

4858-
if (post_rewrite_hook) {
4859-
struct child_process hook = CHILD_PROCESS_INIT;
4860-
4861-
hook.in = open(rebase_path_rewritten_list(),
4862-
O_RDONLY);
4863-
hook.stdout_to_stderr = 1;
4864-
hook.trace2_hook_name = "post-rewrite";
4865-
strvec_push(&hook.args, post_rewrite_hook);
4866-
strvec_push(&hook.args, "rebase");
4867-
/* we don't care if this hook failed */
4868-
run_command(&hook);
4869-
}
4857+
hook_opt.path_to_stdin = rebase_path_rewritten_list();
4858+
strvec_push(&hook_opt.args, "rebase");
4859+
run_hooks_opt("post-rewrite", &hook_opt);
48704860
}
48714861
apply_autostash(rebase_path_autostash());
48724862

t/t1800-hook.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,22 @@ test_expect_success 'git hook run a hook with a bad shebang' '
177177
test_cmp expect actual
178178
'
179179

180+
test_expect_success 'stdin to hooks' '
181+
write_script .git/hooks/test-hook <<-\EOF &&
182+
echo BEGIN stdin
183+
cat
184+
echo END stdin
185+
EOF
186+
187+
cat >expect <<-EOF &&
188+
BEGIN stdin
189+
hello
190+
END stdin
191+
EOF
192+
193+
echo hello >input &&
194+
git hook run --to-stdin=input test-hook 2>actual &&
195+
test_cmp expect actual
196+
'
197+
180198
test_done

0 commit comments

Comments
 (0)