@@ -47,9 +47,49 @@ const char *find_hook(struct repository *r, const char *name)
4747 return path .buf ;
4848}
4949
50+ /*
51+ * Provides a list of hook commands to run for the 'hookname' event.
52+ *
53+ * This function consolidates hooks from two sources:
54+ * 1. The config-based hooks (not yet implemented).
55+ * 2. The "traditional" hook found in the repository hooks directory
56+ * (e.g., .git/hooks/pre-commit).
57+ *
58+ * The list is ordered by execution priority.
59+ *
60+ * The caller is responsible for freeing the memory of the returned list
61+ * using string_list_clear() and free().
62+ */
63+ static struct string_list * list_hooks (struct repository * r , const char * hookname )
64+ {
65+ struct string_list * hook_head ;
66+
67+ if (!hookname )
68+ BUG ("null hookname was provided to hook_list()!" );
69+
70+ hook_head = xmalloc (sizeof (struct string_list ));
71+ string_list_init_dup (hook_head );
72+
73+ /*
74+ * Add the default hook from hookdir. It does not have a friendly name
75+ * like the hooks specified via configs, so add it with an empty name.
76+ */
77+ if (r -> gitdir && find_hook (r , hookname ))
78+ string_list_append (hook_head , "" );
79+
80+ return hook_head ;
81+ }
82+
5083int hook_exists (struct repository * r , const char * name )
5184{
52- return !!find_hook (r , name );
85+ int exists = 0 ;
86+ struct string_list * hooks = list_hooks (r , name );
87+
88+ exists = hooks -> nr > 0 ;
89+
90+ string_list_clear (hooks , 1 );
91+ free (hooks );
92+ return exists ;
5393}
5494
5595static int pick_next_hook (struct child_process * cp ,
@@ -58,10 +98,11 @@ static int pick_next_hook(struct child_process *cp,
5898 void * * pp_task_cb )
5999{
60100 struct hook_cb_data * hook_cb = pp_cb ;
61- const char * hook_path = hook_cb -> hook_path ;
101+ struct string_list * hook_list = hook_cb -> hook_command_list ;
102+ struct string_list_item * to_run = hook_cb -> next_hook ++ ;
62103
63- if (!hook_path )
64- return 0 ;
104+ if (!to_run || to_run >= hook_list -> items + hook_list -> nr )
105+ return 0 ; /* no hook left to run */
65106
66107 cp -> no_stdin = 1 ;
67108 strvec_pushv (& cp -> env , hook_cb -> options -> env .v );
@@ -85,33 +126,50 @@ static int pick_next_hook(struct child_process *cp,
85126 cp -> trace2_hook_name = hook_cb -> hook_name ;
86127 cp -> dir = hook_cb -> options -> dir ;
87128
88- strvec_push (& cp -> args , hook_path );
129+ /* find hook commands */
130+ if (!* to_run -> string ) {
131+ /* ...from hookdir signified by empty name */
132+ const char * hook_path = find_hook (hook_cb -> repository ,
133+ hook_cb -> hook_name );
134+ if (!hook_path )
135+ BUG ("hookdir in hook list but no hook present in filesystem" );
136+
137+ if (hook_cb -> options -> dir )
138+ hook_path = absolute_path (hook_path );
139+
140+ strvec_push (& cp -> args , hook_path );
141+ }
142+
143+ if (!cp -> args .nr )
144+ BUG ("configured hook must have at least one command" );
145+
89146 strvec_pushv (& cp -> args , hook_cb -> options -> args .v );
90147
91148 /*
92149 * Provide per-hook internal state via task_cb for easy access, so
93150 * hook callbacks don't have to go through hook_cb->options.
94151 */
95- * pp_task_cb = hook_cb -> options -> feed_pipe_cb_data ;
96-
97- /*
98- * This pick_next_hook() will be called again, we're only
99- * running one hook, so indicate that no more work will be
100- * done.
101- */
102- hook_cb -> hook_path = NULL ;
152+ * pp_task_cb = to_run ;
103153
104154 return 1 ;
105155}
106156
107- static int notify_start_failure (struct strbuf * out UNUSED ,
157+ static int notify_start_failure (struct strbuf * out ,
108158 void * pp_cb ,
109- void * pp_task_cp UNUSED )
159+ void * pp_task_cb )
110160{
111161 struct hook_cb_data * hook_cb = pp_cb ;
162+ struct string_list_item * hook = pp_task_cb ;
112163
113164 hook_cb -> rc |= 1 ;
114165
166+ if (out && hook ) {
167+ if (* hook -> string )
168+ strbuf_addf (out , _ ("Couldn't start hook '%s'\n" ), hook -> string );
169+ else
170+ strbuf_addstr (out , _ ("Couldn't start hook from hooks directory\n" ));
171+ }
172+
115173 return 1 ;
116174}
117175
@@ -145,8 +203,9 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
145203 .rc = 0 ,
146204 .hook_name = hook_name ,
147205 .options = options ,
206+ .hook_command_list = list_hooks (r , hook_name ),
207+ .repository = r ,
148208 };
149- const char * const hook_path = find_hook (r , hook_name );
150209 int ret = 0 ;
151210 const struct run_process_parallel_opts opts = {
152211 .tr2_category = "hook" ,
@@ -172,26 +231,50 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
172231 if (!options -> jobs )
173232 BUG ("run_hooks_opt must be called with options.jobs >= 1" );
174233
234+ /*
235+ * Ensure cb_data copy and free functions are either provided together,
236+ * or neither one is provided.
237+ */
238+ if ((options -> copy_feed_pipe_cb_data && !options -> free_feed_pipe_cb_data ) ||
239+ (!options -> copy_feed_pipe_cb_data && options -> free_feed_pipe_cb_data ))
240+ BUG ("copy_feed_pipe_cb_data and free_feed_pipe_cb_data must be set together" );
241+
175242 if (options -> invoked_hook )
176243 * options -> invoked_hook = 0 ;
177244
178- if (!hook_path && !options -> error_if_missing )
179- goto cleanup ;
180-
181- if (!hook_path ) {
182- ret = error ("cannot find a hook named %s" , hook_name );
245+ if (!cb_data .hook_command_list -> nr ) {
246+ if (options -> error_if_missing )
247+ ret = error ("cannot find a hook named %s" , hook_name );
183248 goto cleanup ;
184249 }
185250
186- cb_data .hook_path = hook_path ;
187- if (options -> dir ) {
188- strbuf_add_absolute_path (& abs_path , hook_path );
189- cb_data .hook_path = abs_path .buf ;
251+ /*
252+ * Initialize the iterator/cursor which holds the next hook to run.
253+ * run_process_parallel() calls pick_next_hook() which increments it for
254+ * each hook command in the list until all hooks have been run.
255+ */
256+ cb_data .next_hook = cb_data .hook_command_list -> items ;
257+
258+ /*
259+ * Give each hook its own copy of the initial void *pp_task_cb state, if
260+ * a copy callback was provided.
261+ */
262+ if (options -> copy_feed_pipe_cb_data ) {
263+ struct string_list_item * item ;
264+ for_each_string_list_item (item , cb_data .hook_command_list )
265+ item -> util = options -> copy_feed_pipe_cb_data (options -> feed_pipe_cb_data );
190266 }
191267
192268 run_processes_parallel (& opts );
193269 ret = cb_data .rc ;
194270cleanup :
271+ if (options -> free_feed_pipe_cb_data ) {
272+ struct string_list_item * item ;
273+ for_each_string_list_item (item , cb_data .hook_command_list )
274+ options -> free_feed_pipe_cb_data (item -> util );
275+ }
276+ string_list_clear (cb_data .hook_command_list , 0 );
277+ free (cb_data .hook_command_list );
195278 strbuf_release (& abs_path );
196279 run_hooks_opt_clear (options );
197280 return ret ;
0 commit comments