hook: add -z option to "git hook list"

Add a NUL-terminate mode to git hook list, just in case hooks are configured with weird characters like newlines in their names. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Adrian Ratiu committed Feb 19, 2026 at 00:23 UTC 4b12cd3ae3acbc819189758d09f9c983bde16040
3 files changed +25 -5
Documentation/git-hook.adoc
+6 -2
@@ -9,7 +9,7 @@ SYNOPSIS
9 --------
10 [verse]
11 'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
12 -'git hook' list <hook-name>
12 +'git hook' list [-z] <hook-name>
13
14 DESCRIPTION
15 -----------
@@ -113,9 +113,10 @@ Any positional arguments to the hook should be passed after a
113 mandatory `--` (or `--end-of-options`, see linkgit:gitcli[7]). See
114 linkgit:githooks[5] for arguments hooks might expect (if any).
115
116 -list::
116 +list [-z]::
117 Print a list of hooks which will be run on `<hook-name>` event. If no
118 hooks are configured for that event, print a warning and return 1.
119 + Use `-z` to terminate output lines with NUL instead of newlines.
120
121 OPTIONS
122 -------
@@ -130,6 +131,9 @@ OPTIONS
131 tools that want to do a blind one-shot run of a hook that may
132 or may not be present.
133
134 +-z::
135 + Terminate "list" output lines with NUL instead of newlines.
136 +
137 WRAPPERS
138 --------
139
builtin/hook.c
+6 -3
@@ -11,7 +11,7 @@
11 #define BUILTIN_HOOK_RUN_USAGE \
12 N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
13 #define BUILTIN_HOOK_LIST_USAGE \
14 - N_("git hook list <hook-name>")
14 + N_("git hook list [-z] <hook-name>")
15
16 static const char * const builtin_hook_usage[] = {
17 BUILTIN_HOOK_RUN_USAGE,
@@ -34,9 +34,12 @@ static int list(int argc, const char **argv, const char *prefix,
34 struct string_list *head;
35 struct string_list_item *item;
36 const char *hookname = NULL;
37 + int line_terminator = '\n';
38 int ret = 0;
39
40 struct option list_options[] = {
41 + OPT_SET_INT('z', NULL, &line_terminator,
42 + N_("use NUL as line terminator"), '\0'),
43 OPT_END(),
44 };
45
@@ -66,10 +69,10 @@ static int list(int argc, const char **argv, const char *prefix,
69
70 switch (h->kind) {
71 case HOOK_TRADITIONAL:
69 - printf("%s\n", _("hook from hookdir"));
72 + printf("%s%c", _("hook from hookdir"), line_terminator);
73 break;
74 case HOOK_CONFIGURED:
72 - printf("%s\n", h->u.configured.friendly_name);
75 + printf("%s%c", h->u.configured.friendly_name, line_terminator);
76 break;
77 default:
78 BUG("unknown hook kind");
t/t1800-hook.sh
+13
@@ -61,6 +61,19 @@ test_expect_success 'git hook list: configured hook' '
61 test_cmp expect actual
62 '
63
64 +test_expect_success 'git hook list: -z shows NUL-terminated output' '
65 + test_hook test-hook <<-EOF &&
66 + echo Test hook
67 + EOF
68 + test_config hook.myhook.command "echo Hello" &&
69 + test_config hook.myhook.event test-hook --add &&
70 +
71 + printf "myhookQhook from hookdirQ" >expect &&
72 + git hook list -z test-hook >actual.raw &&
73 + nul_to_q <actual.raw >actual &&
74 + test_cmp expect actual
75 +'
76 +
77 test_expect_success 'git hook run: nonexistent hook' '
78 cat >stderr.expect <<-\EOF &&
79 error: cannot find a hook named test-hook