Merge branch 'ab/config-based-hooks-base' into seen
[git] / hook.c
1 #include "cache.h"
2 #include "hook.h"
3 #include "run-command.h"
4 #include "hook-list.h"
5
6 static int known_hook(const char *name)
7 {
8         const char **p;
9         size_t len = strlen(name);
10         for (p = hook_name_list; *p; p++) {
11                 const char *hook = *p;
12
13                 if (!strncmp(name, hook, len) && hook[len] == '\0')
14                         return 1;
15         }
16         if (!strcmp(name, "test-hook") ||
17             !strcmp(name, "does-not-exist"))
18                 return 1;
19
20         return 0;
21 }
22
23 const char *find_hook(const char *name)
24 {
25         static struct strbuf path = STRBUF_INIT;
26
27         if (!known_hook(name))
28                 die(_("the hook '%s' is not known to git, should be in hook-list.h via githooks(5)"),
29                     name);
30
31         strbuf_reset(&path);
32         strbuf_git_path(&path, "hooks/%s", name);
33         if (access(path.buf, X_OK) < 0) {
34                 int err = errno;
35
36 #ifdef STRIP_EXTENSION
37                 strbuf_addstr(&path, STRIP_EXTENSION);
38                 if (access(path.buf, X_OK) >= 0)
39                         return path.buf;
40                 if (errno == EACCES)
41                         err = errno;
42 #endif
43
44                 if (err == EACCES && advice_ignored_hook) {
45                         static struct string_list advise_given = STRING_LIST_INIT_DUP;
46
47                         if (!string_list_lookup(&advise_given, name)) {
48                                 string_list_insert(&advise_given, name);
49                                 advise(_("The '%s' hook was ignored because "
50                                          "it's not set as executable.\n"
51                                          "You can disable this warning with "
52                                          "`git config advice.ignoredHook false`."),
53                                        path.buf);
54                         }
55                 }
56                 return NULL;
57         }
58         return path.buf;
59 }
60
61 int hook_exists(const char *name)
62 {
63         return !!find_hook(name);
64 }
65
66 void run_hooks_opt_clear(struct run_hooks_opt *o)
67 {
68         strvec_clear(&o->env);
69         strvec_clear(&o->args);
70 }
71
72 int pipe_from_string_list(struct strbuf *pipe, void *pp_cb, void *pp_task_cb)
73 {
74         int *item_idx;
75         struct hook *ctx = pp_task_cb;
76         struct hook_cb_data *hook_cb = pp_cb;
77         struct string_list *to_pipe = hook_cb->options->feed_pipe_ctx;
78
79         /* Bootstrap the state manager if necessary. */
80         if (!ctx->feed_pipe_cb_data) {
81                 ctx->feed_pipe_cb_data = xmalloc(sizeof(unsigned int));
82                 *(int*)ctx->feed_pipe_cb_data = 0;
83         }
84
85         item_idx = ctx->feed_pipe_cb_data;
86
87         if (*item_idx < to_pipe->nr) {
88                 strbuf_addf(pipe, "%s\n", to_pipe->items[*item_idx].string);
89                 (*item_idx)++;
90                 return 0;
91         }
92         return 1;
93 }
94
95 static int pick_next_hook(struct child_process *cp,
96                           struct strbuf *out,
97                           void *pp_cb,
98                           void **pp_task_cb)
99 {
100         struct hook_cb_data *hook_cb = pp_cb;
101         struct hook *run_me = hook_cb->run_me;
102
103         if (!run_me)
104                 BUG("did we not return 1 in notify_hook_finished?");
105
106         /* reopen the file for stdin; run_command closes it. */
107         if (hook_cb->options->path_to_stdin) {
108                 cp->no_stdin = 0;
109                 cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
110         } else if (hook_cb->options->feed_pipe) {
111                 /* ask for start_command() to make a pipe for us */
112                 cp->in = -1;
113                 cp->no_stdin = 0;
114         } else {
115                 cp->no_stdin = 1;
116         }
117         cp->env = hook_cb->options->env.v;
118         cp->stdout_to_stderr = 1;
119         cp->trace2_hook_name = hook_cb->hook_name;
120         cp->dir = hook_cb->options->dir;
121
122         /* add command */
123         strvec_push(&cp->args, run_me->hook_path);
124
125         /*
126          * add passed-in argv, without expanding - let the user get back
127          * exactly what they put in
128          */
129         strvec_pushv(&cp->args, hook_cb->options->args.v);
130
131         /* Provide context for errors if necessary */
132         *pp_task_cb = run_me;
133
134         return 1;
135 }
136
137 static int notify_start_failure(struct strbuf *out,
138                                 void *pp_cb,
139                                 void *pp_task_cp)
140 {
141         struct hook_cb_data *hook_cb = pp_cb;
142         struct hook *attempted = pp_task_cp;
143
144         /* |= rc in cb */
145         hook_cb->rc |= 1;
146
147         strbuf_addf(out, _("Couldn't start hook '%s'\n"),
148                     attempted->hook_path);
149
150         return 1;
151 }
152
153 static int notify_hook_finished(int result,
154                                 struct strbuf *out,
155                                 void *pp_cb,
156                                 void *pp_task_cb)
157 {
158         struct hook_cb_data *hook_cb = pp_cb;
159
160         /* |= rc in cb */
161         hook_cb->rc |= result;
162
163         if (hook_cb->invoked_hook)
164                 *hook_cb->invoked_hook = 1;
165
166         return 1;
167 }
168
169 int run_found_hooks(const char *hook_name, const char *hook_path,
170                     struct run_hooks_opt *options)
171 {
172         struct strbuf abs_path = STRBUF_INIT;
173         struct hook my_hook = {
174                 .hook_path = hook_path,
175         };
176         struct hook_cb_data cb_data = {
177                 .rc = 0,
178                 .hook_name = hook_name,
179                 .options = options,
180                 .invoked_hook = options->invoked_hook,
181         };
182         if (options->absolute_path) {
183                 strbuf_add_absolute_path(&abs_path, hook_path);
184                 my_hook.hook_path = abs_path.buf;
185         }
186         cb_data.run_me = &my_hook;
187
188         if (options->jobs != 1)
189                 BUG("we do not handle %d or any other != 1 job number yet", options->jobs);
190
191         run_processes_parallel_tr2(options->jobs,
192                                    pick_next_hook,
193                                    notify_start_failure,
194                                    options->feed_pipe,
195                                    options->consume_sideband,
196                                    notify_hook_finished,
197                                    &cb_data,
198                                    "hook",
199                                    hook_name);
200         if (options->absolute_path)
201                 strbuf_release(&abs_path);
202
203         return cb_data.rc;
204 }
205
206 int run_hooks(const char *hook_name, struct run_hooks_opt *options)
207 {
208         const char *hook_path;
209         int ret;
210         if (!options)
211                 BUG("a struct run_hooks_opt must be provided to run_hooks");
212
213         if (options->path_to_stdin && options->feed_pipe)
214                 BUG("choose only one method to populate stdin");
215
216         hook_path = find_hook(hook_name);
217
218         /* Care about nonexistence? Use run_found_hooks() */
219         if (!hook_path)
220                 return 0;
221
222         ret = run_found_hooks(hook_name, hook_path, options);
223         return ret;
224 }