hook: allow parallel hook execution

Hooks always run in sequential order due to the hardcoded jobs == 1
passed to run_process_parallel(). Remove that hardcoding to allow
users to run hooks in parallel (opt-in).

Users need to decide which hooks to run in parallel, by specifying
"parallel = true" in the config, because Git cannot know if their
specific hooks are safe to run or not in parallel (for e.g. two hooks
might write to the same file or call the same program).

Some hooks are unsafe to run in parallel by design: these will marked
in the next commit using RUN_HOOKS_OPT_INIT_FORCE_SERIAL.

The hook.jobs config specifies the default number of jobs applied to all
hooks which have parallelism enabled.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Emily Shaffer
2026-04-10 12:05:59 +03:00
committed by Junio C Hamano
parent b9a4c9ad24
commit 680e69f60d
4 changed files with 253 additions and 6 deletions

25
hook.h
View File

@@ -35,6 +35,13 @@ struct hook {
} 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.
*
@@ -72,6 +79,8 @@ struct run_hooks_opt {
*
* 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;
@@ -152,7 +161,23 @@ struct run_hooks_opt {
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, \