git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] git: add --no-hooks global option

From
D. Ben Knoble <ben.knoble@gmail.com>
Date
Apr 11, 2025, 18:53 UTC
Message-ID
<CALnO6CD4ApAC_QqP-XCm0yCo=fx2j8v8Z6ByjUHBYiE=JfZA2w@mail.gmail.com>
In-Reply-To
<pull.1899.git.1743719888430.gitgitgadget@gmail.com>

On Thu, Apr 3, 2025 at 6:38 PM Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com> wrote:

Show 14 quoted lines
>
> From: Derrick Stolee <stolee@gmail.com>
>
> 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.

Or a pre-push hook that runs tests which, for some reason, are very slow to execute but which isn't properly shutdown by a normal SIGINT, messing up my shell.

> 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.

In my particular case, the tool being used (husky) sets core.hooksPath to a directory in the repo, so I can go back to using my preferred hooks simply by unsetting core.hooksPath, which seems safe. But swapping is still a bit painful, as mentioned.

Worse, every "npm install" in this repo re-enables the hooks, so I have to remember to reset core.hooksPath :/ When I don't, I spend a few frustrating minutes getting back to normal. And then I accidentally train myself to use --no-verify often with this project, which is IMO a worse state of affairs.

Obviously, this repo's hooks need fixed, but in the interim something like --no-hooks seems useful to me. Not sure if I count among the experts or not /shrug.

Show 6 quoted lines
> 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 b79deeb554 (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.

On a related note: with client-side hooks always being considered optional, it seems like there ought to be a safe, sane way to turn them off (rather than remember --no-verify for the various commands, which is probably the safe, sane way to bypass them on a one-off basis).

Maybe with this I'll have more ammo to convince folks that their sanity/security/whatever checks belong in server-side hooks and optional developer scripts rather than pre-commit hooks.

Show 172 quoted lines
>
> 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 <stolee@gmail.com>
> ---
>     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
> --
> gitgitgadget
>
-- 
D. Ben Knoble
Previous: brian m. carlsonNext: Derrick Stolee via GitGitGadget
Message 11 of 17 in “git: add --no-hooks global option”
  1. git: add --no-hooks global optionDerrick Stolee via GitGitGadget, Apr 3, 2025
  2. brian m. carlsonApr 3, 2025
  3. Derrick StoleeApr 4, 2025
  4. Phillip WoodApr 4, 2025
  5. Derrick StoleeApr 14, 2025
  6. Phillip WoodApr 16, 2025
  7. Junio C HamanoApr 16, 2025
  8. Derrick StoleeApr 16, 2025
  9. Lucas Seiki OshiroApr 14, 2025
  10. brian m. carlsonApr 14, 2025
  11. D. Ben KnobleApr 11, 2025
  12. docs: document core.hooksPath=/dev/nullDerrick Stolee via GitGitGadget, Apr 16, 2025
  13. Lucas Seiki OshiroApr 16, 2025
  14. Derrick StoleeApr 17, 2025
  15. brian m. carlsonApr 17, 2025
  16. Junio C HamanoApr 17, 2025
  17. Lucas Seiki OshiroApr 17, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.