Signed-off-by: Derrick Stolee <stolee@xxxxxxxxx>
---
git: add --no-hooks global option
This is hopefully a helpful feature to more than just the experts I've
been hearing from.
Thanks,
* Stolee
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1899%2Fderrickstolee%2Fno-hooks-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1899/derrickstolee/no-hooks-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/1899
Documentation/git.adoc | 13 ++++++++++++-
environment.h | 6 ++++++
git.c | 6 +++++-
hook.c | 7 +++++++
t/t1350-config-hooks-path.sh | 34 ++++++++++++++++++++++++++++++++++
5 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/Documentation/git.adoc b/Documentation/git.adoc
index 743b7b00e4d..a34c8cfbe78 100644
--- a/Documentation/git.adoc
+++ b/Documentation/git.adoc
@@ -14,7 +14,7 @@ SYNOPSIS
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
[--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
[--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
- <command> [<args>]
+ [--no-hooks] <command> [<args>]
DESCRIPTION
-----------
@@ -230,6 +230,12 @@ If you just want to run git as if it was started in `<path>` then use
linkgit:gitattributes[5]. This is equivalent to setting the
`GIT_ATTR_SOURCE` environment variable.
+--no-hooks::
+ Skip running local Git hooks, even if configured locally. Hooks
+ are an opt-in feature, so be sure that you know the impact of
+ ignoring hooks when running with this option. This is equivalent
+ to setting `GIT_HOOKS=0` environment variable.
+
GIT COMMANDS
------------
@@ -771,6 +777,11 @@ for further details.
not set, Git will choose buffered or record-oriented flushing
based on whether stdout appears to be redirected to a file or not.
+`GIT_HOOKS`::
+ If this Boolean environment variable is set to false, then commands
+ will ignore any configured hooks as if the `--no-hooks` option was
+ provided.
+
`GIT_TRACE`::
Enables general trace messages, e.g. alias expansion, built-in
command execution and external command execution.
diff --git a/environment.h b/environment.h
index 45e690f203f..22ddf201144 100644
--- a/environment.h
+++ b/environment.h
@@ -50,6 +50,12 @@
*/
#define GIT_ADVICE_ENVIRONMENT "GIT_ADVICE"
+/*
+ * Environment variable used to propagate the --no-hooks global option to
+ * the hooks layer and to any child processes.
+ */
+#define GIT_HOOKS "GIT_HOOKS"
+
/*
* Environment variable used in handshaking the wire protocol.
* Contains a colon ':' separated list of keys with optional values
diff --git a/git.c b/git.c
index 77c43595223..d7ebcf60947 100644
--- a/git.c
+++ b/git.c
@@ -41,7 +41,7 @@ const char git_usage_string[] =
" [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
" [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
" [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
- " <command> [<args>]");
+ " [--no-hooks] <command> [<args>]");
const char git_more_info_string[] =
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)
setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--no-hooks")) {
+ setenv(GIT_HOOKS, "0", 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, _("unknown option: %s\n"), cmd);
usage(git_usage_string);
diff --git a/hook.c b/hook.c
index b3de1048bf4..b209553d7a8 100644
--- a/hook.c
+++ b/hook.c
@@ -144,6 +144,13 @@ int run_hooks_opt(struct repository *r, const char *hook_name,
.data = &cb_data,
};
+ static int do_run_hooks = -1;
+
+ if (do_run_hooks < 0)
+ do_run_hooks = git_env_bool(GIT_HOOKS, 1);
+
+ if (!do_run_hooks)
+ goto cleanup;
if (!options)
BUG("a struct run_hooks_opt must be provided to run_hooks");
diff --git a/t/t1350-config-hooks-path.sh b/t/t1350-config-hooks-path.sh
index 45a04929170..4c6a0eafe4e 100755
--- a/t/t1350-config-hooks-path.sh
+++ b/t/t1350-config-hooks-path.sh
@@ -48,4 +48,38 @@ test_expect_success 'core.hooksPath=/dev/null' '
{ test /dev/null = "$value" || test nul = "$value"; }
'
+test_expect_success '--no-hooks' '
+ rm -f actual &&
+ test_might_fail git config --unset core.hooksPath &&
+
+ write_script .git/hooks/pre-commit <<-\EOF &&
+ echo HOOK >>actual
+ EOF
+
+ echo HOOK >expect &&
+
+ git commit --allow-empty -m "A" &&
+ test_cmp expect actual &&
+
+ git --no-hooks commit --allow-empty -m "B" &&
+ test_cmp expect actual
+'
+
+test_expect_success 'GIT_HOOKS' '
+ rm -f actual &&
+ test_might_fail git config --unset core.hooksPath &&
+
+ write_script .git/hooks/pre-commit <<-\EOF &&
+ echo HOOK >>actual
+ EOF
+
+ echo HOOK >expect &&
+
+ GIT_HOOKS=1 git commit --allow-empty -m "A" &&
+ test_cmp expect actual &&
+
+ GIT_HOOKS=0 git commit --allow-empty -m "B" &&
+ test_cmp expect actual
+'
+
test_done
base-commit: 5b97a56fa0e7d580dc8865b73107407c9b3f0eff