diff options
| author | Adrian Ratiu <adrian.ratiu@collabora.com> | 2026-02-19 00:23:48 +0200 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2026-02-19 13:23:41 -0800 |
| commit | 03b4043b9182bd3d36541371fa39f04d6d038286 (patch) | |
| tree | 46af18d57733c40b3438b40b6029d22124c95a30 /Documentation | |
| parent | 9fdaa6788924d4bb5ffc3a5908dae8a50e072f77 (diff) | |
| download | git-03b4043b9182bd3d36541371fa39f04d6d038286.tar.xz | |
hook: include hooks from the config
Teach the hook.[hc] library to parse configs to populate the list of
hooks to run for a given event.
Multiple commands can be specified for a given hook by providing
"hook.<friendly-name>.command = <path-to-hook>" and
"hook.<friendly-name>.event = <hook-event>" lines.
Hooks will be started in config order of the "hook.<name>.event"
lines and will be run sequentially (.jobs == 1) like before.
Running the hooks in parallel will be enabled in a future patch.
The "traditional" hook from the hookdir is run last, if present.
A strmap cache is added to struct repository to avoid re-reading
the configs on each rook run. This is useful for hooks like the
ref-transaction which gets executed multiple times per process.
Examples:
$ git config --get-regexp "^hook\."
hook.bar.command=~/bar.sh
hook.bar.event=pre-commit
# Will run ~/bar.sh, then .git/hooks/pre-commit
$ git hook run pre-commit
Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'Documentation')
| -rw-r--r-- | Documentation/config/hook.adoc | 15 | ||||
| -rw-r--r-- | Documentation/git-hook.adoc | 128 |
2 files changed, 141 insertions, 2 deletions
diff --git a/Documentation/config/hook.adoc b/Documentation/config/hook.adoc new file mode 100644 index 0000000000..9faafe3016 --- /dev/null +++ b/Documentation/config/hook.adoc @@ -0,0 +1,15 @@ +hook.<name>.command:: + The command to execute for `hook.<name>`. `<name>` is a unique + "friendly" name that identifies this hook. (The hook events that + trigger the command are configured with `hook.<name>.event`.) The + value can be an executable path or a shell oneliner. If more than + one value is specified for the same `<name>`, only the last value + parsed is used. See linkgit:git-hook[1]. + +hook.<name>.event:: + The hook events that trigger `hook.<name>`. The value is the name + of a hook event, like "pre-commit" or "update". (See + linkgit:githooks[5] for a complete list of hook events.) On the + specified event, the associated `hook.<name>.command` is executed. + This is a multi-valued key. To run `hook.<name>` on multiple + events, specify the key more than once. See linkgit:git-hook[1]. diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc index eb0ffcb8a9..7e4259e4f0 100644 --- a/Documentation/git-hook.adoc +++ b/Documentation/git-hook.adoc @@ -17,12 +17,96 @@ DESCRIPTION A command interface for running git hooks (see linkgit: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.<name>.event` configs during the configuration parse (see +linkgit: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.<name>.event` to, +and how hooks are invoked during those events, see linkgit:githooks[5]. + +Git will ignore any `hook.<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:: - Run the `<hook-name>` hook. See linkgit:githooks[5] for - supported hook names. + 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 linkgit:githooks[5] for supported + hook names. + Any positional arguments to the hook should be passed after a @@ -46,6 +130,46 @@ OPTIONS tools that want to do a blind one-shot run of a hook that may or may not be present. +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 mywrapper-start-tests \ + # providing something to stdin + --stdin some-tempfile-123 \ + # execute hooks in serial + # 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 linkgit: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 +------------- +include::config/hook.adoc[] + SEE ALSO -------- linkgit:githooks[5] |
