git hook run: add an --ignore-missing flag
[git] / hook.c
1 #include "cache.h"
2 #include "hook.h"
3 #include "run-command.h"
4
5 const char *find_hook(const char *name)
6 {
7         static struct strbuf path = STRBUF_INIT;
8
9         strbuf_reset(&path);
10         strbuf_git_path(&path, "hooks/%s", name);
11         if (access(path.buf, X_OK) < 0) {
12                 int err = errno;
13
14 #ifdef STRIP_EXTENSION
15                 strbuf_addstr(&path, STRIP_EXTENSION);
16                 if (access(path.buf, X_OK) >= 0)
17                         return path.buf;
18                 if (errno == EACCES)
19                         err = errno;
20 #endif
21
22                 if (err == EACCES && advice_ignored_hook) {
23                         static struct string_list advise_given = STRING_LIST_INIT_DUP;
24
25                         if (!string_list_lookup(&advise_given, name)) {
26                                 string_list_insert(&advise_given, name);
27                                 advise(_("The '%s' hook was ignored because "
28                                          "it's not set as executable.\n"
29                                          "You can disable this warning with "
30                                          "`git config advice.ignoredHook false`."),
31                                        path.buf);
32                         }
33                 }
34                 return NULL;
35         }
36         return path.buf;
37 }
38
39 int hook_exists(const char *name)
40 {
41         return !!find_hook(name);
42 }
43
44 void run_hooks_opt_clear(struct run_hooks_opt *o)
45 {
46         strvec_clear(&o->env);
47         strvec_clear(&o->args);
48 }
49
50 static int pick_next_hook(struct child_process *cp,
51                           struct strbuf *out,
52                           void *pp_cb,
53                           void **pp_task_cb)
54 {
55         struct hook_cb_data *hook_cb = pp_cb;
56         struct hook *run_me = hook_cb->run_me;
57
58         if (!run_me)
59                 BUG("did we not return 1 in notify_hook_finished?");
60
61         cp->no_stdin = 1;
62         cp->env = hook_cb->options->env.v;
63         cp->stdout_to_stderr = 1;
64         cp->trace2_hook_name = hook_cb->hook_name;
65         cp->dir = hook_cb->options->dir;
66
67         /* add command */
68         strvec_push(&cp->args, run_me->hook_path);
69
70         /*
71          * add passed-in argv, without expanding - let the user get back
72          * exactly what they put in
73          */
74         strvec_pushv(&cp->args, hook_cb->options->args.v);
75
76         /* Provide context for errors if necessary */
77         *pp_task_cb = run_me;
78
79         return 1;
80 }
81
82 static int notify_start_failure(struct strbuf *out,
83                                 void *pp_cb,
84                                 void *pp_task_cp)
85 {
86         struct hook_cb_data *hook_cb = pp_cb;
87         struct hook *attempted = pp_task_cp;
88
89         /* |= rc in cb */
90         hook_cb->rc |= 1;
91
92         strbuf_addf(out, _("Couldn't start hook '%s'\n"),
93                     attempted->hook_path);
94
95         return 1;
96 }
97
98 static int notify_hook_finished(int result,
99                                 struct strbuf *out,
100                                 void *pp_cb,
101                                 void *pp_task_cb)
102 {
103         struct hook_cb_data *hook_cb = pp_cb;
104
105         /* |= rc in cb */
106         hook_cb->rc |= result;
107
108         return 1;
109 }
110
111
112 int run_found_hooks(const char *hook_name, const char *hook_path,
113                     struct run_hooks_opt *options)
114 {
115         struct strbuf abs_path = STRBUF_INIT;
116         struct hook my_hook = {
117                 .hook_path = hook_path,
118         };
119         struct hook_cb_data cb_data = {
120                 .rc = 0,
121                 .hook_name = hook_name,
122                 .options = options,
123         };
124         if (options->absolute_path) {
125                 strbuf_add_absolute_path(&abs_path, hook_path);
126                 my_hook.hook_path = abs_path.buf;
127         }
128         cb_data.run_me = &my_hook;
129
130         if (options->jobs != 1)
131                 BUG("we do not handle %d or any other != 1 job number yet", options->jobs);
132
133         run_processes_parallel_tr2(options->jobs,
134                                    pick_next_hook,
135                                    notify_start_failure,
136                                    notify_hook_finished,
137                                    &cb_data,
138                                    "hook",
139                                    hook_name);
140         if (options->absolute_path)
141                 strbuf_release(&abs_path);
142
143         return cb_data.rc;
144 }
145
146 int run_hooks(const char *hook_name, struct run_hooks_opt *options)
147 {
148         const char *hook_path;
149         int ret;
150         if (!options)
151                 BUG("a struct run_hooks_opt must be provided to run_hooks");
152
153         hook_path = find_hook(hook_name);
154
155         /* Care about nonexistence? Use run_found_hooks() */
156         if (!hook_path)
157                 return 0;
158
159         ret = run_found_hooks(hook_name, hook_path, options);
160         return ret;
161 }