git-hook - Man Page

Run Git hooks

Synopsis

git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] [(-j|--jobs) <n>]
        <hook-name> [-- <hook-args>]
git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>

Description

A command interface for running Git hooks (see githooks(5)), for use by other scripted Git commands​.

This command parses the default configuration files for sets of configs like so:

[hook "linter"]
  event = pre-commit
  command = ~/bin/linter --cpp20

In this example, [hook "linter"] represents one script - ~/bin/linter --cpp20 - which can be shared by many repos, and even by many hook events, if appropriate​.

To add an unrelated hook which runs on a different event, for example a spell-checker for your commit messages, you would write a configuration like so:

[hook "linter"]
  event = pre-commit
  command = ~/bin/linter --cpp20
[hook "spellcheck"]
  event = commit-msg
  command = ~/bin/spellchecker

With this config, when you run git commit, first ~/bin/linter --cpp20 will have a chance to check your files to be committed (during the pre-commit hook event), and then ~/bin/spellchecker will have a chance to check your commit message (during the commit-msg hook event)​.

Commands are run in the order Git encounters their associated hook​.<friendly-name>​.event configs during the configuration parse (see git-config(1))​. Although multiple hook​.linter​.event configs can be added, only one hook​.linter​.command event is valid - Git uses "last-one-wins" to determine which command to run​.

So if you wanted your linter to run when you commit as well as when you push, you would configure it like so:

[hook "linter"]
  event = pre-commit
  event = pre-push
  command = ~/bin/linter --cpp20

With this config, ~/bin/linter --cpp20 would be run by Git before a commit is generated (during pre-commit) as well as before a push is performed (during pre-push)​.

And if you wanted to run your linter as well as a secret-leak detector during only the "pre-commit" hook event, you would configure it instead like so:

[hook "linter"]
  event = pre-commit
  command = ~/bin/linter --cpp20
[hook "no-leaks"]
  event = pre-commit
  command = ~/bin/leak-detector

With this config, before a commit is generated (during pre-commit), Git would first start ~/bin/linter --cpp20 and second start ~/bin/leak-detector​. It would evaluate the output of each when deciding whether to proceed with the commit​.

For a full list of hook events which you can set your hook​.<friendly-name>​.event to, and how hooks are invoked during those events, see githooks(5)​.

Git will ignore any hook​.<friendly-name>​.event that specifies an event it doesn’t recognize​. This is intended so that tools which wrap Git can use the hook infrastructure to run their own hooks; see "Wrappers" for more guidance​.

In general, when instructions suggest adding a script to ​.git/hooks/<hook-event>, you can specify it in the config instead by running:

git config set hook​.<some-name>​.command <path-to-script>
git config set --append hook​.<some-name>​.event <hook-event>

This way you can share the script between multiple repos​. That is, cp ~/my-script​.sh ~/project/​.git/hooks/pre-commit would become:

git config set hook​.my-script​.command ~/my-script​.sh
git config set --append hook​.my-script​.event pre-commit

Subcommands

run

Runs hooks configured for <hook-name>, in the order they are discovered during the config parse​. The default <hook-name> from the hookdir is run last​. See githooks(5) for supported hook names​.

Any positional arguments to the hook should be passed after a mandatory -- (or --end-of-options, see gitcli(7))​. See githooks(5) for arguments hooks might expect (if any)​.

list [-z] [--show-scope]

Print a list of hooks which will be run on <hook-name> event​. If no hooks are configured for that event, print a warning and return 1​. Use -z to terminate output lines with NUL instead of newlines​.

Options

--allow-unknown-hook-name

By default git hook run and git hook list will bail out when <hook-name> is not a hook event known to Git (see githooks(5) for the list of known hooks)​. This is meant to help catch typos such as prereceive when pre-receive was intended​. Pass this flag to allow unknown hook names​.

--to-stdin

For "run"; specify a file which will be streamed into the hook’s stdin​. The hook will receive the entire file from beginning to EOF​.

--ignore-missing

Ignore any missing hook by quietly returning zero​. Used for tools that want to do a blind one-shot run of a hook that may or may not be present​.

-z

Terminate "list" output lines with NUL instead of newlines​.

--show-scope

For "list"; prefix each configured hook’s friendly name with a tab-separated config scope (e​.g​. local, global, system), mirroring the output style of git config --show-scope​. Traditional hooks from the hookdir are unaffected​.

-j,  --jobs

Only valid for run​.

Specify how many hooks to run simultaneously​. If this flag is not specified, the value of the hook​.jobs config is used, see git-config(1)​. If neither is specified, defaults to 1 (serial execution)​.

When greater than 1, it overrides the per-hook hook​.<friendly-name>​.parallel setting, allowing all hooks for the event to run concurrently, even if they are not individually marked as parallel​.

Some hooks always run sequentially regardless of this flag or the hook​.jobs config, because Git knows they cannot safely run in parallel: applypatch-msg, pre-commit, prepare-commit-msg, commit-msg, post-commit, post-checkout, and push-to-checkout​.

Wrappers

git hook run has been designed to make it easy for tools which wrap Git to configure and execute hooks using the Git hook infrastructure​. It is possible to provide arguments and stdin via the command line, as well as specifying parallel or series execution if the user has provided multiple hooks​.

Assuming your wrapper wants to support a hook named "mywrapper-start-tests", you can have your users specify their hooks like so:

[hook "setup-test-dashboard"]
  event = mywrapper-start-tests
  command = ~/mywrapper/setup-dashboard​.py --tap

Then, in your mywrapper tool, you can invoke any users' configured hooks by running:

git hook run --allow-unknown-hook-name mywrapper-start-tests \
  # providing something to stdin
  --stdin some-tempfile-123 \
  # execute multiple hooks in parallel
  --jobs 3 \
  # plus some arguments of your own​.​.​.
  -- \
  --testname bar \
  baz

Take care to name your wrapper’s hook events in a way which is unlikely to overlap with Git’s native hooks (see githooks(5)) - a hook event named mywrappertool-validate-commit is much less likely to be added to native Git than a hook event named validate-commit​. If Git begins to use a hook event named the same thing as your wrapper hook, it may invoke your users' hooks in unintended and unsupported ways​.

Configuration

hook​.<friendly-name>​.command

The command to execute for hook​.<friendly-name>​. <friendly-name> is a unique name that identifies this hook​. The hook events that trigger the command are configured with hook​.<friendly-name>​.event​. The value can be an executable path or a shell oneliner​. If more than one value is specified for the same <friendly-name>, only the last value parsed is used​.

hook​.<friendly-name>​.event

The hook events that trigger hook​.<friendly-name>​. The value is the name of a hook event, like "pre-commit" or "update"​. (See githooks(5) for a complete list of hook events​.) On the specified event, the associated hook​.<friendly-name>​.command is executed​. This is a multi-valued key​. To run hook​.<friendly-name> on multiple events, specify the key more than once​. An empty value resets the list of events, clearing any previously defined events for hook​.<friendly-name>​.

The <friendly-name> must not be the same as a known hook event name (e​.g​. do not use hook​.pre-commit​.event)​. Using a known event name as a friendly-name is a fatal error because it creates an ambiguity with hook​.<event>​.enabled and hook​.<event>​.jobs​. For unknown event names, a warning is issued when <friendly-name> matches the event value​.

hook​.<friendly-name>​.enabled

Whether the hook hook​.<friendly-name> is enabled​. Defaults to true​. Set to false to disable the hook without removing its configuration​. This is particularly useful when a hook is defined in a system or global config file and needs to be disabled for a specific repository​.

hook​.<friendly-name>​.parallel

Whether the hook hook​.<friendly-name> may run in parallel with other hooks for the same event​. Defaults to false​. Set to true only when the hook script is safe to run concurrently with other hooks for the same event​. If any hook for an event does not have this set to true, all hooks for that event run sequentially regardless of hook​.jobs​. Only configured (named) hooks need to declare this​. Traditional hooks found in the hooks directory do not need to, and run in parallel when the effective job count is greater than 1​.

hook​.<event>​.enabled

Switch to enable or disable all hooks for the <event> hook event​. When set to false, no hooks fire for that event, regardless of any per-hook hook​.<friendly-name>​.enabled settings​. Defaults to true​.

Note on naming: <event> must be the event name (e​.g​. pre-commit), not a hook friendly-name​. Since using a known event name as a friendly-name is disallowed (see hook​.<friendly-name>​.event above), there is no ambiguity between event-level and per-hook ​.enabled settings for known events​. For unknown events, if a friendly-name matches the event name despite the warning, ​.enabled is treated as per-hook only​.

hook​.<event>​.jobs

Specifies how many hooks can be run simultaneously for the <event> hook event (e​.g​. hook​.post-receive​.jobs = 4)​. Overrides hook​.jobs for this specific event​. The same parallelism restrictions apply: this setting has no effect unless all configured hooks for the event have hook​.<friendly-name>​.parallel set to true​. Set to -1 to use the number of available CPU cores​. Must be a positive integer or -1; zero is rejected with a warning​.

Note on naming: although this key resembles hook​.<friendly-name>​.* (a per-hook setting), <event> must be the event name, not a hook friendly name​. The key component is stored literally and looked up by event name at runtime with no translation between the two namespaces​. A key like hook​.my-hook​.jobs is stored under "my-hook" but the lookup at runtime uses the event name (e​.g​. "post-receive"), so hook​.my-hook​.jobs is silently ignored even when my-hook is registered for that event​. Use hook​.post-receive​.jobs or any other valid event name when setting hook​.<event>​.jobs​.

hook​.jobs

Specifies how many hooks can be run simultaneously during parallelized hook execution​. If unspecified, defaults to 1 (serial execution)​. Set to -1 to use the number of available CPU cores​. Can be overridden on a per-event basis with hook​.<event>​.jobs​. Some hooks always run sequentially regardless of this setting because they operate on shared data and cannot safely be parallelized:

applypatch-msg, prepare-commit-msg, commit-msg

Receive a commit message file and may rewrite it in place​.

pre-commit, post-checkout, push-to-checkout, post-commit

Access the working tree, index, or repository state​.

This setting has no effect unless all configured hooks for the event have hook​.<friendly-name>​.parallel set to true​.

For pre-push hooks, which normally keep stdout and stderr separate, setting this to a value greater than 1 (or passing -j) will merge stdout into stderr to allow correct de-interleaving of parallel output​.

See Also

githooks(5)

Git

Part of the git(1) suite

Info

09/28/2026 Git 2.56.0 Git Manual