1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
#ifndef HOOK_H
#define HOOK_H
#include "config.h"
#include "run-command.h"
#include "string-list.h"
#include "strmap.h"
#include "strvec.h"
struct repository;
typedef void (*hook_data_free_fn)(void *data);
typedef void *(*hook_data_alloc_fn)(void *init_ctx);
/**
* Represents a hook command to be run.
* Hooks can be:
* 1. "traditional" (found in the hooks directory)
* 2. "configured" (defined in Git's configuration via hook.<friendly-name>.event).
* The 'kind' field determines which part of the union 'u' is valid.
*/
struct hook {
enum {
HOOK_TRADITIONAL,
HOOK_CONFIGURED,
} kind;
union {
struct {
const char *path;
} traditional;
struct {
const char *friendly_name;
const char *command;
enum config_scope scope;
bool disabled;
bool event_disabled;
} configured;
} u;
/**
* Whether this hook may run in parallel with other hooks for the same
* event. Only useful for configured (named) hooks. Traditional hooks
* always default to 0 (serial). Set via `hook.<name>.parallel = true`.
*/
bool parallel;
/**
* Opaque data pointer used to keep internal state across callback calls.
*
* It can be accessed directly via the third hook callback arg:
* struct ... *state = pp_task_cb;
*
* The caller is responsible for managing the memory for this data by
* providing alloc/free callbacks to `run_hooks_opt`.
*
* Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it.
*/
void *feed_pipe_cb_data;
/**
* Callback to free `feed_pipe_cb_data`.
*
* It is called automatically and points to the `feed_pipe_cb_data_free`
* provided via the `run_hook_opt` parameter.
*/
hook_data_free_fn data_free;
};
struct run_hooks_opt {
/* Environment vars to be set for each hook */
struct strvec env;
/* Args to be passed to each hook */
struct strvec args;
/* Emit an error if the hook is missing */
unsigned int error_if_missing:1;
/**
* Number of processes to parallelize across.
*
* If > 1, output will be buffered and de-interleaved (ungroup=0).
* If == 1, output will be real-time (ungroup=1).
* If == 0, the 'hook.jobs' config is used or, if the config is unset,
* defaults to 1 (serial execution).
*/
unsigned int jobs;
/**
* An optional initial working directory for the hook,
* translates to "struct child_process"'s "dir" member.
*/
const char *dir;
/**
* A pointer which if provided will be set to 1 or 0 depending
* on if a hook was started, regardless of whether or not that
* was successful. I.e. if the underlying start_command() was
* successful this will be set to 1.
*
* Used for avoiding TOCTOU races in code that would otherwise
* call hook_exist() after a "maybe hook run" to see if a hook
* was invoked.
*/
int *invoked_hook;
/**
* Send the hook's stdout to stderr.
*
* This is the default behavior for all hooks except pre-push,
* which keeps stdout and stderr separate for backwards compatibility.
* When parallel execution is requested (jobs > 1), get_hook_jobs()
* overrides this to 1 for all hooks so run-command can de-interleave
* their outputs correctly.
*/
unsigned int stdout_to_stderr:1;
/**
* Path to file which should be piped to stdin for each hook.
*/
const char *path_to_stdin;
/**
* Callback used to incrementally feed a child hook stdin pipe.
*
* Useful especially if a hook consumes large quantities of data
* (e.g. a list of all refs in a client push), so feeding it via
* in-memory strings or slurping to/from files is inefficient.
* While the callback allows piecemeal writing, it can also be
* used for smaller inputs, where it gets called only once.
*
* Add hook callback initalization context to `feed_pipe_ctx`.
* Add hook callback internal state to `feed_pipe_cb_data`.
*
*/
feed_pipe_fn feed_pipe;
/**
* Opaque data pointer used to pass context to `feed_pipe_fn`.
*
* It can be accessed via the second callback arg 'pp_cb':
* ((struct hook_cb_data *) pp_cb)->hook_cb->options->feed_pipe_ctx;
*
* The caller is responsible for managing the memory for this data.
* Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it.
*/
void *feed_pipe_ctx;
/**
* Some hooks need to create a fresh `feed_pipe_cb_data` internal state,
* so they can keep track of progress without affecting one another.
*
* If provided, this function will be called to alloc & initialize the
* `feed_pipe_cb_data` for each hook.
*
* The `feed_pipe_ctx` pointer can be used to pass initialization data.
*/
hook_data_alloc_fn feed_pipe_cb_data_alloc;
/**
* Called to free the memory initialized by `feed_pipe_cb_data_alloc`.
*
* Must always be provided when `feed_pipe_cb_data_alloc` is provided.
*/
hook_data_free_fn feed_pipe_cb_data_free;
};
/**
* Default initializer for hooks. Parallelism is opt-in: .jobs = 0 defers to
* the 'hook.jobs' config, falling back to serial (1) if unset.
*/
#define RUN_HOOKS_OPT_INIT { \
.env = STRVEC_INIT, \
.args = STRVEC_INIT, \
.stdout_to_stderr = 1, \
.jobs = 0, \
}
/**
* Initializer for hooks that must always run sequentially regardless of
* 'hook.jobs'. Use this when git knows the hook cannot safely be parallelized
* .jobs = 1 is non-overridable.
*/
#define RUN_HOOKS_OPT_INIT_FORCE_SERIAL { \
.env = STRVEC_INIT, \
.args = STRVEC_INIT, \
.stdout_to_stderr = 1, \
.jobs = 1, \
}
struct hook_cb_data {
/* rc reflects the cumulative failure state */
int rc;
const char *hook_name;
/**
* A list of hook commands/paths to run for the 'hook_name' event.
*
* The 'string' member of each item holds the path (for traditional hooks)
* or the unique friendly-name for hooks specified in configs.
* The 'util' member of each item points to the corresponding struct hook.
*/
struct string_list *hook_command_list;
/* Iterator/cursor for the above list, pointing to the next hook to run. */
size_t hook_to_run_index;
struct run_hooks_opt *options;
};
/**
* Provides a list of hook commands to run for the 'hookname' event.
*
* This function consolidates hooks from two sources:
* 1. The config-based hooks (not yet implemented).
* 2. The "traditional" hook found in the repository hooks directory
* (e.g., .git/hooks/pre-commit).
*
* The list is ordered by execution priority.
*
* The caller is responsible for freeing the memory of the returned list
* using string_list_clear() and free().
*/
struct string_list *list_hooks(struct repository *r, const char *hookname,
struct run_hooks_opt *options);
/**
* Frees a struct hook stored as the util pointer of a string_list_item.
* Suitable for use as a string_list_clear_func_t callback.
*/
void hook_free(void *p, const char *str);
/**
* Frees the hook configuration cache stored in `struct repository`.
* Called by repo_clear().
*/
void hook_cache_clear(struct strmap *cache);
/**
* Returns true if `name` is a recognized hook event name
* (e.g. "pre-commit", "post-receive").
*/
bool is_known_hook(const char *name);
/**
* 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_*.
*/
const char *find_hook(struct repository *r, const char *name);
/**
* A boolean version of find_hook()
*/
int hook_exists(struct repository *r, const char *hookname);
/**
* Takes a `hook_name`, resolves it to a path with find_hook(), and
* runs the hook for you with the options specified in "struct
* run_hooks opt". Will free memory associated with the "struct run_hooks_opt".
*
* Returns the status code of the run hook, or a negative value on
* error().
*/
int run_hooks_opt(struct repository *r, const char *hook_name,
struct run_hooks_opt *options);
/**
* A wrapper for run_hooks_opt() which provides a dummy "struct
* run_hooks_opt" initialized with "RUN_HOOKS_OPT_INIT".
*/
int run_hooks(struct repository *r, const char *hook_name);
/**
* Like run_hooks(), a wrapper for run_hooks_opt().
*
* In addition to the wrapping behavior provided by run_hooks(), this
* wrapper takes a list of strings terminated by a NULL
* argument. These things will be used as positional arguments to the
* hook. This function behaves like the old run_hook_le() API.
*/
LAST_ARG_MUST_BE_NULL
int run_hooks_l(struct repository *r, const char *hook_name, ...);
#endif
|