Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2 #include "builtin.h"
3 #include "config.h"
4 #include "environment.h"
5 #include "gettext.h"
6 #include "hook.h"
7 #include "parse-options.h"
8 #include "thread-utils.h"
9
10 #define BUILTIN_HOOK_RUN_USAGE \
11 N_("git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] [(-j|--jobs) <n>]\n" \
12 "<hook-name> [-- <hook-args>]")
13 #define BUILTIN_HOOK_LIST_USAGE \
14 N_("git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>")
15
16 static const char * const builtin_hook_usage[] = {
17 BUILTIN_HOOK_RUN_USAGE,
18 BUILTIN_HOOK_LIST_USAGE,
19 NULL
20 };
21
22 static const char * const builtin_hook_run_usage[] = {
23 BUILTIN_HOOK_RUN_USAGE,
24 NULL
25 };
26
27 static int list(int argc, const char **argv, const char *prefix,
28 struct repository *repo)
29 {
30 static const char *const builtin_hook_list_usage[] = {
31 BUILTIN_HOOK_LIST_USAGE,
32 NULL
33 };
34 struct string_list *head;
35 struct string_list_item *item;
36 const char *hookname = NULL;
37 int line_terminator = '\n';
38 int show_scope = 0;
39 int allow_unknown = 0;
40 int ret = 0;
41
42 struct option list_options[] = {
43 OPT_SET_INT('z', NULL, &line_terminator,
44 N_("use NUL as line terminator"), '\0'),
45 OPT_BOOL(0, "show-scope", &show_scope,
46 N_("show the config scope that defined each hook")),
47 OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown,
48 N_("allow running a hook with a non-native hook name")),
49 OPT_END(),
50 };
51
52 argc = parse_options(argc, argv, prefix, list_options,
53 builtin_hook_list_usage, 0);
54
55 /*
56 * The only unnamed argument provided should be the hook-name; if we add
57 * arguments later they probably should be caught by parse_options.
58 */
59 if (argc != 1)
60 usage_msg_opt(_("you must specify a hook event name to list"),
61 builtin_hook_list_usage, list_options);
62
63 hookname = argv[0];
64
65 if (!allow_unknown && !is_known_hook(hookname)) {
66 error(_("unknown hook event '%s';\n"
67 "use --allow-unknown-hook-name to allow non-native hook names"),
68 hookname);
69 return 1;
70 }
71
72 head = list_hooks(repo, hookname, NULL);
73
74 if (!head->nr) {
75 warning(_("no hooks found for event '%s'"), hookname);
76 ret = 1; /* no hooks found */
77 goto cleanup;
78 }
79
80 for_each_string_list_item(item, head) {
81 struct hook *h = item->util;
82
83 switch (h->kind) {
84 case HOOK_TRADITIONAL:
85 printf("%s%c", _("hook from hookdir"), line_terminator);
86 break;
87 case HOOK_CONFIGURED: {
88 const char *name = h->u.configured.friendly_name;
89 const char *scope = show_scope ?
90 config_scope_name(h->u.configured.scope) : NULL;
91 /*
92 * Show the most relevant disable reason. Event-level
93 * takes precedence: if the whole event is off, that
94 * is what the user needs to know. The per-hook
95 * "disabled" surfaces once the event is re-enabled.
96 */
97 const char *disability =
98 h->u.configured.event_disabled ? "event-disabled\t" :
99 h->u.configured.disabled ? "disabled\t" :
100 "";
101 if (scope)
102 printf("%s\t%s%s%c", scope, disability, name,
103 line_terminator);
104 else
105 printf("%s%s%c", disability, name,
106 line_terminator);
107 break;
108 }
109 default:
110 BUG("unknown hook kind");
111 }
112 }
113
114 cleanup:
115 string_list_clear_func(head, hook_free);
116 free(head);
117 return ret;
118 }
119
120 static int run(int argc, const char **argv, const char *prefix,
121 struct repository *repo UNUSED)
122 {
123 int i;
124 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
125 int ignore_missing = 0;
126 int allow_unknown = 0;
127 int jobs = 0;
128 const char *hook_name;
129 struct option run_options[] = {
130 OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown,
131 N_("allow running a hook with a non-native hook name")),
132 OPT_BOOL(0, "ignore-missing", &ignore_missing,
133 N_("silently ignore missing requested <hook-name>")),
134 OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"),
135 N_("file to read into hooks' stdin")),
136 OPT_INTEGER('j', "jobs", &jobs,
137 N_("run up to <n> hooks simultaneously (-1 for CPU count)")),
138 OPT_END(),
139 };
140 int ret;
141
142 argc = parse_options(argc, argv, prefix, run_options,
143 builtin_hook_run_usage,
144 PARSE_OPT_KEEP_DASHDASH);
145
146 if (jobs == -1)
147 opt.jobs = online_cpus();
148 else if (jobs < 0)
149 die(_("invalid value for -j: %d"
150 " (use -1 for CPU count or a"
151 " positive integer)"), jobs);
152 else
153 opt.jobs = jobs;
154
155 if (!argc)
156 goto usage;
157
158 /*
159 * Having a -- for "run" when providing <hook-args> is
160 * mandatory.
161 */
162 if (argc > 1 && strcmp(argv[1], "--") &&
163 strcmp(argv[1], "--end-of-options"))
164 goto usage;
165
166 /* Add our arguments, start after -- */
167 for (i = 2 ; i < argc; i++)
168 strvec_push(&opt.args, argv[i]);
169
170 /* Need to take into account core.hooksPath */
171 repo_config(the_repository, git_default_config, NULL);
172
173 hook_name = argv[0];
174
175 if (!allow_unknown && !is_known_hook(hook_name)) {
176 error(_("unknown hook event '%s';\n"
177 "use --allow-unknown-hook-name to allow non-native hook names"),
178 hook_name);
179 return 1;
180 }
181
182 if (!ignore_missing)
183 opt.error_if_missing = 1;
184 ret = run_hooks_opt(the_repository, hook_name, &opt);
185 if (ret < 0) /* error() return */
186 ret = 1;
187 return ret;
188 usage:
189 usage_with_options(builtin_hook_run_usage, run_options);
190 }
191
192 int cmd_hook(int argc,
193 const char **argv,
194 const char *prefix,
195 struct repository *repo)
196 {
197 parse_opt_subcommand_fn *fn = NULL;
198 struct option builtin_hook_options[] = {
199 OPT_SUBCOMMAND("run", &fn, run),
200 OPT_SUBCOMMAND("list", &fn, list),
201 OPT_END(),
202 };
203
204 argc = parse_options(argc, argv, NULL, builtin_hook_options,
205 builtin_hook_usage, 0);
206
207 return fn(argc, argv, prefix, repo);
208 }