To enable fine-grained options which apply to a single hook executable, and to make it easier for a single executable to be run on multiple hook events, teach "hookcmd.<alias>.config". These can be configured as follows: [hookcmd.my-linter] command = ~/my-linter.sh [hook.pre-commit] command = my-linter During the config parse, we can attempt to dereference the 'hook.pre-commit.command' string 'my-linter' and check if it matches any hookcmd names; if so, we can run the command associated with that hookcmd alias instead. Signed-off-by: Emily Shaffer <emilyshaffer@xxxxxxxxxx> --- Documentation/config/hook.txt | 5 +++++ Documentation/git-hook.txt | 42 +++++++++++++++++++++++++++++++---- hook.c | 9 +++++++- t/t1360-config-based-hooks.sh | 19 ++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Documentation/config/hook.txt b/Documentation/config/hook.txt index a97b980cca..5b35170664 100644 --- a/Documentation/config/hook.txt +++ b/Documentation/config/hook.txt @@ -3,6 +3,11 @@ hook.<command>.command:: executable on your device, a oneliner for your shell, or the name of a hookcmd. See linkgit:git-hook[1]. +hookcmd.<name>.command:: + A command to execute during a hook for which <name> has been specified + as a command. This can be an executable on your device or a oneliner for + your shell. See linkgit:git-hook[1]. + hook.jobs:: Specifies how many hooks can be run simultaneously during parallelized hook execution. If unspecified, defaults to the number of processors on diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 8e2ab724e8..1a4d22fd90 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -18,10 +18,44 @@ This command is an interface to git hooks (see linkgit:githooks[5]). Currently it only provides a convenience wrapper for running hooks for use by git itself. In the future it might gain other functionality. -This command parses the default configuration files for sections like `hook -"<hook name>"`. `hook` is used to describe the commands which will be run during -a particular hook event; commands are run in the order Git encounters them -during the configuration parse (see linkgit:git-config[1]). +This command parses the default configuration files for sections `hook` and +`hookcmd`. `hook` is used to describe the commands which will be run during a +particular hook event; commands are run in the order Git encounters them during +the configuration parse (see linkgit:git-config[1]). `hookcmd` is used to +describe attributes of a specific command. If additional attributes don't need +to be specified, a command to run can be specified directly in the `hook` +section; if a `hookcmd` by that name isn't found, Git will attempt to run the +provided value directly. For example: + +Global config +---- + [hook "post-commit"] + command = "linter" + command = "~/typocheck.sh" + + [hookcmd "linter"] + command = "/bin/linter --c" +---- + +Local config +---- + [hook "prepare-commit-msg"] + command = "linter" + [hook "post-commit"] + command = "python ~/run-test-suite.py" +---- + +With these configs, you'd then run post-commit hooks in this order: + + /bin/linter --c + ~/typocheck.sh + python ~/run-test-suite.py + .git/hooks/post-commit (if present) + +and prepare-commit-msg hooks in this order: + + /bin/linter --c + .git/hooks/prepare-commit-msg (if present) In general, when instructions suggest adding a script to `.git/hooks/<something>`, you can specify it in the config instead by running diff --git a/hook.c b/hook.c index b08b876d5d..21904d90f6 100644 --- a/hook.c +++ b/hook.c @@ -184,7 +184,14 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data) /* TODO: implement skipping hooks */ - /* TODO: immplement hook aliases */ + /* + * Check if a hookcmd with that name exists. If it doesn't, + * 'git_config_get_value()' is documented not to touch &command, + * so we don't need to do anything. + */ + strbuf_reset(&hookcmd_name); + strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command); + git_config_get_value(hookcmd_name.buf, &command); /* * TODO: implement an option-getting callback, e.g. diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index e4a7b06ad1..50ee824f05 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -18,6 +18,11 @@ setup_hooks () { test_config_global hook.pre-commit.command "/path/def" --add } +setup_hookcmd () { + test_config hook.pre-commit.command "abc" --add + test_config_global hookcmd.abc.command "/path/abc" --add +} + setup_hookdir () { mkdir .git/hooks write_script .git/hooks/pre-commit <<-EOF @@ -59,6 +64,20 @@ test_expect_success 'git hook list orders by config order' ' test_cmp expected actual ' +test_expect_success 'git hook list dereferences a hookcmd' ' + setup_hooks && + setup_hookcmd && + + cat >expected <<-EOF && + $ROOT/path/def + $ROOT/path/ghi + $ROOT/path/abc + EOF + + git hook list pre-commit >actual && + test_cmp expected actual +' + test_expect_success 'git hook list reorders on duplicate commands' ' setup_hooks && -- 2.32.0.402.g57bb445576-goog