Add support for multiple pre-push hooks. Remove find_hook since there are no longer any callers of it. Signed-off-by: brian m. carlson <sandals@xxxxxxxxxxxxxxxxxxxx> --- run-command.c | 12 ----- run-command.h | 6 --- t/t5571-pre-push-hook.sh | 19 ++++++++ transport.c | 98 ++++++++++++++++++++++------------------ 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/run-command.c b/run-command.c index 669af5ebc7..2c387a7aef 100644 --- a/run-command.c +++ b/run-command.c @@ -1340,18 +1340,6 @@ static int has_hook(struct strbuf *path, int strip) return 1; } -const char *find_hook(const char *name) -{ - static struct strbuf path = STRBUF_INIT; - - strbuf_reset(&path); - strbuf_git_path(&path, "hooks/%s", name); - if (has_hook(&path, 1)) { - return path.buf; - } - return NULL; -} - struct string_list *find_hooks(const char *name) { struct string_list *list = xmalloc(sizeof(*list)); diff --git a/run-command.h b/run-command.h index 7266dc2969..a657c09a6c 100644 --- a/run-command.h +++ b/run-command.h @@ -62,12 +62,6 @@ int finish_command(struct child_process *); int finish_command_in_signal(struct child_process *); int run_command(struct child_process *); -/* - * Returns the path to the hook file, or NULL if the hook is missing - * or disabled. Note that this points to static storage that will be - * overwritten by further calls to find_hook and run_hook_*. - */ -extern const char *find_hook(const char *name); /* * Returns the paths to all hook files, or NULL if all hooks are missing or * disabled. diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh index ac53d63869..754ad8eb93 100755 --- a/t/t5571-pre-push-hook.sh +++ b/t/t5571-pre-push-hook.sh @@ -2,6 +2,7 @@ test_description='check pre-push hooks' . ./test-lib.sh +. "$TEST_DIRECTORY/lib-hooks.sh" # Setup hook that always succeeds HOOKDIR="$(git rev-parse --git-dir)/hooks" @@ -125,4 +126,22 @@ test_expect_success 'sigpipe does not cause pre-push hook failure' ' git push parent1 "refs/heads/b/*:refs/heads/b/*" ' +push_command () { + test_commit "$1" && + git push hooks refs/heads/master:refs/heads/master +} + +push_no_verify_command () { + test_commit "$1" && + git push --no-verify hooks refs/heads/master:refs/heads/master +} + +test_expect_success 'setup' ' + git checkout master && + git init --bare hooktest && + git remote add hooks hooktest +' + +test_multiple_hooks pre-push push_command push_no_verify_command + test_done diff --git a/transport.c b/transport.c index d0608df5c9..a2a9437c5b 100644 --- a/transport.c +++ b/transport.c @@ -1049,61 +1049,73 @@ static int run_pre_push_hook(struct transport *transport, { int ret = 0, x; struct ref *r; - struct child_process proc = CHILD_PROCESS_INIT; + struct child_process proc; + struct string_list *hooks; + struct string_list_item *p; struct strbuf buf; const char *argv[4]; - if (!(argv[0] = find_hook("pre-push"))) + hooks = find_hooks("pre-push"); + if (!hooks) return 0; - argv[1] = transport->remote->name; - argv[2] = transport->url; - argv[3] = NULL; - proc.argv = argv; - proc.in = -1; - proc.trace2_hook_name = "pre-push"; + for_each_string_list_item(p, hooks) { + child_process_init(&proc); - if (start_command(&proc)) { - finish_command(&proc); - return -1; - } + argv[0] = p->string; + argv[1] = transport->remote->name; + argv[2] = transport->url; + argv[3] = NULL; - sigchain_push(SIGPIPE, SIG_IGN); + proc.argv = argv; + proc.in = -1; + proc.trace2_hook_name = "pre-push"; - strbuf_init(&buf, 256); - - for (r = remote_refs; r; r = r->next) { - if (!r->peer_ref) continue; - if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue; - if (r->status == REF_STATUS_REJECT_STALE) continue; - if (r->status == REF_STATUS_UPTODATE) continue; - - strbuf_reset(&buf); - strbuf_addf( &buf, "%s %s %s %s\n", - r->peer_ref->name, oid_to_hex(&r->new_oid), - r->name, oid_to_hex(&r->old_oid)); - - if (write_in_full(proc.in, buf.buf, buf.len) < 0) { - /* We do not mind if a hook does not read all refs. */ - if (errno != EPIPE) - ret = -1; + if (start_command(&proc)) { + finish_command(&proc); + ret = -1; break; } + + sigchain_push(SIGPIPE, SIG_IGN); + + strbuf_init(&buf, 256); + + for (r = remote_refs; r; r = r->next) { + if (!r->peer_ref) continue; + if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue; + if (r->status == REF_STATUS_REJECT_STALE) continue; + if (r->status == REF_STATUS_UPTODATE) continue; + + strbuf_reset(&buf); + strbuf_addf( &buf, "%s %s %s %s\n", + r->peer_ref->name, oid_to_hex(&r->new_oid), + r->name, oid_to_hex(&r->old_oid)); + + if (write_in_full(proc.in, buf.buf, buf.len) < 0) { + /* We do not mind if a hook does not read all refs. */ + if (errno != EPIPE) + ret = -1; + break; + } + } + + strbuf_release(&buf); + + x = close(proc.in); + if (!ret) + ret = x; + + sigchain_pop(SIGPIPE); + + x = finish_command(&proc); + if (!ret) + ret = x; + if (ret) + break; } - - strbuf_release(&buf); - - x = close(proc.in); - if (!ret) - ret = x; - - sigchain_pop(SIGPIPE); - - x = finish_command(&proc); - if (!ret) - ret = x; - + free_hooks(hooks); return ret; }