@@ -133,13 +133,16 @@ struct hook_config_cache_entry {
133133 * event_hooks: event-name to list of friendly-names map.
134134 * disabled_hooks: set of friendly-names with hook.name.enabled = false.
135135 * parallel_hooks: friendly-name to parallel flag.
136+ * event_jobs: event-name to per-event jobs count (heap-allocated unsigned int *,
137+ * where NULL == unset).
136138 * jobs: value of the global hook.jobs key. Defaults to 0 if unset.
137139 */
138140struct hook_all_config_cb {
139141 struct strmap commands ;
140142 struct strmap event_hooks ;
141143 struct string_list disabled_hooks ;
142144 struct strmap parallel_hooks ;
145+ struct strmap event_jobs ;
143146 unsigned int jobs ;
144147};
145148
@@ -222,6 +225,20 @@ static int hook_config_lookup_all(const char *key, const char *value,
222225 int v = git_parse_maybe_bool (value );
223226 if (v >= 0 )
224227 strmap_put (& data -> parallel_hooks , hook_name , (void * )(uintptr_t )v );
228+ } else if (!strcmp (subkey , "jobs" )) {
229+ unsigned int v ;
230+ if (!git_parse_uint (value , & v ))
231+ warning (_ ("hook.%s.jobs must be a positive integer, ignoring: '%s'" ),
232+ hook_name , value );
233+ else if (!v )
234+ warning (_ ("hook.%s.jobs must be positive, ignoring: 0" ), hook_name );
235+ else {
236+ unsigned int * old ;
237+ unsigned int * p = xmalloc (sizeof (* p ));
238+ * p = v ;
239+ old = strmap_put (& data -> event_jobs , hook_name , p );
240+ free (old );
241+ }
225242 }
226243
227244 free (hook_name );
@@ -252,6 +269,7 @@ void hook_cache_clear(struct hook_config_cache *cache)
252269 free (hooks );
253270 }
254271 strmap_clear (& cache -> hooks , 0 );
272+ strmap_clear (& cache -> event_jobs , 1 ); /* free heap-allocated unsigned int * values */
255273}
256274
257275/* Populate `cache` with the complete hook configuration */
@@ -266,6 +284,7 @@ static void build_hook_config_map(struct repository *r,
266284 strmap_init (& cb_data .event_hooks );
267285 string_list_init_dup (& cb_data .disabled_hooks );
268286 strmap_init (& cb_data .parallel_hooks );
287+ strmap_init (& cb_data .event_jobs );
269288
270289 /* Parse all configs in one run, capturing hook.* including hook.jobs. */
271290 repo_config (r , hook_config_lookup_all , & cb_data );
@@ -305,6 +324,7 @@ static void build_hook_config_map(struct repository *r,
305324 }
306325
307326 cache -> jobs = cb_data .jobs ;
327+ cache -> event_jobs = cb_data .event_jobs ;
308328
309329 strmap_clear (& cb_data .commands , 1 );
310330 strmap_clear (& cb_data .parallel_hooks , 0 ); /* values are uintptr_t, not heap ptrs */
@@ -513,6 +533,7 @@ static void run_hooks_opt_clear(struct run_hooks_opt *options)
513533/* Determine how many jobs to use for hook execution. */
514534static unsigned int get_hook_jobs (struct repository * r ,
515535 struct run_hooks_opt * options ,
536+ const char * hook_name ,
516537 struct string_list * hook_list )
517538{
518539 unsigned int jobs ;
@@ -529,22 +550,36 @@ static unsigned int get_hook_jobs(struct repository *r,
529550 return 1 ;
530551
531552 /*
532- * Resolve effective job count: -jN (when given) overrides config.
533- * Default to 1 when both config an -jN are missing.
553+ * Resolve effective job count: -j N (when given) overrides config.
554+ * hook.<event>.jobs overrides hook.jobs.
555+ * Unset configs and -jN default to 1.
534556 */
535- if (options -> jobs > 1 )
557+ if (options -> jobs > 1 ) {
536558 jobs = options -> jobs ;
537- else if (r && r -> gitdir && r -> hook_config_cache )
559+ } else if (r && r -> gitdir && r -> hook_config_cache ) {
538560 /* Use the already-parsed cache (in-repo) */
561+ unsigned int * event_jobs = strmap_get (& r -> hook_config_cache -> event_jobs ,
562+ hook_name );
539563 jobs = r -> hook_config_cache -> jobs ? r -> hook_config_cache -> jobs : 1 ;
540- else
564+ if (event_jobs )
565+ jobs = * event_jobs ;
566+ } else {
541567 /* No cache present (out-of-repo call), use direct cfg lookup */
568+ unsigned int event_jobs ;
569+ char * key ;
542570 jobs = repo_config_get_uint (r , "hook.jobs" , & jobs ) ? 1 : jobs ;
571+ key = xstrfmt ("hook.%s.jobs" , hook_name );
572+ if (!repo_config_get_uint (r , key , & event_jobs ) && event_jobs )
573+ jobs = event_jobs ;
574+ free (key );
575+ }
543576
544577 /*
545578 * Cap to serial any configured hook not marked as parallel = true.
546579 * This enforces the parallel = false default, even for "traditional"
547580 * hooks from the hookdir which cannot be marked parallel = true.
581+ * The same restriction applies whether jobs came from hook.jobs or
582+ * hook.<event>.jobs.
548583 */
549584 for (size_t i = 0 ; jobs > 1 && i < hook_list -> nr ; i ++ ) {
550585 struct hook * h = hook_list -> items [i ].util ;
@@ -566,7 +601,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
566601 .options = options ,
567602 };
568603 int ret = 0 ;
569- unsigned int jobs = get_hook_jobs (r , options , hook_list );
604+ unsigned int jobs = get_hook_jobs (r , options , hook_name , hook_list );
570605 const struct run_process_parallel_opts opts = {
571606 .tr2_category = "hook" ,
572607 .tr2_label = hook_name ,
0 commit comments