Skip to content

Commit 91e8285

Browse files
committed
git: add --no-hooks global option
Git has several hooks which are executed during certain events as long as those hooks are enabled in the hooks directory (possibly moved from .git/hooks via the core.hooksPath config option). These are configured by the user, or perhaps by tooling the user has agreed to, and are not required to operate a Git repository. In some situations, these hooks have poor performance and expert users may want to skip the hooks as they don't seem to affect the current situation. One example is a pre-commit hook that checks for certain structures in the local changes, but expert users are likely to have done the right thing in advance. I have come across users who have disabled hooks themselves either by deleting hooks (supported, safe) or setting 'core.hooksPath' to some bogus path (seems unsafe). The supported process is painful to swap between the hook-enabled scenario and the hook-disabled scenario. To that end, add a new --no-hooks global option to allow users to disable hooks quickly. This option is modeled similarly to the --no-advice option in b79deeb (advice: add --no-advice global option, 2024-05-03). This uses a GIT_HOOKS environment variable to communicate to subprocesses as well as making this a backwards-compatible way for tools to signal that they want to disable hooks. The critical piece is that all hooks pass through run_hooks_opt() where a static int will evaluate the environment variable and store that the variable is initialized for faster repeated runs. Signed-off-by: Derrick Stolee <[email protected]>
1 parent 5b97a56 commit 91e8285

File tree

5 files changed

+64
-2
lines changed

5 files changed

+64
-2
lines changed

Documentation/git.adoc

+12-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ SYNOPSIS
1414
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
1515
[--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
1616
[--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
17-
<command> [<args>]
17+
[--no-hooks] <command> [<args>]
1818

1919
DESCRIPTION
2020
-----------
@@ -230,6 +230,12 @@ If you just want to run git as if it was started in `<path>` then use
230230
linkgit:gitattributes[5]. This is equivalent to setting the
231231
`GIT_ATTR_SOURCE` environment variable.
232232

233+
--no-hooks::
234+
Skip running local Git hooks, even if configured locally. Hooks
235+
are an opt-in feature, so be sure that you know the impact of
236+
ignoring hooks when running with this option. This is equivalent
237+
to setting `GIT_HOOKS=0` environment variable.
238+
233239
GIT COMMANDS
234240
------------
235241
@@ -771,6 +777,11 @@ for further details.
771777
not set, Git will choose buffered or record-oriented flushing
772778
based on whether stdout appears to be redirected to a file or not.
773779

780+
`GIT_HOOKS`::
781+
If this Boolean environment variable is set to false, then commands
782+
will ignore any configured hooks as if the `--no-hooks` option was
783+
provided.
784+
774785
`GIT_TRACE`::
775786
Enables general trace messages, e.g. alias expansion, built-in
776787
command execution and external command execution.

environment.h

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@
5050
*/
5151
#define GIT_ADVICE_ENVIRONMENT "GIT_ADVICE"
5252

53+
/*
54+
* Environment variable used to propagate the --no-hooks global option to
55+
* the hooks layer and to any child processes.
56+
*/
57+
#define GIT_HOOKS "GIT_HOOKS"
58+
5359
/*
5460
* Environment variable used in handshaking the wire protocol.
5561
* Contains a colon ':' separated list of keys with optional values

git.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const char git_usage_string[] =
4141
" [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
4242
" [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
4343
" [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
44-
" <command> [<args>]");
44+
" [--no-hooks] <command> [<args>]");
4545

4646
const char git_more_info_string[] =
4747
N_("'git help -a' and 'git help -g' list available subcommands and some\n"
@@ -349,6 +349,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
349349
setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
350350
if (envchanged)
351351
*envchanged = 1;
352+
} else if (!strcmp(cmd, "--no-hooks")) {
353+
setenv(GIT_HOOKS, "0", 1);
354+
if (envchanged)
355+
*envchanged = 1;
352356
} else {
353357
fprintf(stderr, _("unknown option: %s\n"), cmd);
354358
usage(git_usage_string);

hook.c

+7
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
144144

145145
.data = &cb_data,
146146
};
147+
static int do_run_hooks = -1;
148+
149+
if (do_run_hooks < 0)
150+
do_run_hooks = git_env_bool(GIT_HOOKS, 1);
151+
152+
if (!do_run_hooks)
153+
goto cleanup;
147154

148155
if (!options)
149156
BUG("a struct run_hooks_opt must be provided to run_hooks");

t/t1350-config-hooks-path.sh

+34
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,38 @@ test_expect_success 'core.hooksPath=/dev/null' '
4848
{ test /dev/null = "$value" || test nul = "$value"; }
4949
'
5050

51+
test_expect_success '--no-hooks' '
52+
rm -f actual &&
53+
test_might_fail git config --unset core.hooksPath &&
54+
55+
write_script .git/hooks/pre-commit <<-\EOF &&
56+
echo HOOK >>actual
57+
EOF
58+
59+
echo HOOK >expect &&
60+
61+
git commit --allow-empty -m "A" &&
62+
test_cmp expect actual &&
63+
64+
git --no-hooks commit --allow-empty -m "B" &&
65+
test_cmp expect actual
66+
'
67+
68+
test_expect_success 'GIT_HOOKS' '
69+
rm -f actual &&
70+
test_might_fail git config --unset core.hooksPath &&
71+
72+
write_script .git/hooks/pre-commit <<-\EOF &&
73+
echo HOOK >>actual
74+
EOF
75+
76+
echo HOOK >expect &&
77+
78+
GIT_HOOKS=1 git commit --allow-empty -m "A" &&
79+
test_cmp expect actual &&
80+
81+
GIT_HOOKS=0 git commit --allow-empty -m "B" &&
82+
test_cmp expect actual
83+
'
84+
5185
test_done

0 commit comments

Comments
 (0)