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

Re: [PATCH v2 01/13] hook: add 'run' subcommand

From
Emily Shaffer <emilyshaffer@google.com>
Date
Oct 15, 2021, 16:24 UTC
Message-ID
<YWmrL/POUE8P8Od1@google.com>
In-Reply-To
<patch-v2-01.13-ba64faf0580-20211015T093918Z-avarab@gmail.com>
On Tue, Oct 12, 2021 at 03:30:26PM +0200, Ævar Arnfjörð Bjarmason wrote:
Show 41 quoted lines
> 
> 
> In order to enable hooks to be run as an external process, by a
> standalone Git command, or by tools which wrap Git, provide an external
> means to run all configured hook commands for a given hook event.
> 
> Most of our hooks require more complex functionality than this, but
> let's start with the bare minimum required to support our simplest
> hooks.
> 
> In terms of implementation the usage_with_options() and "goto usage"
> pattern here mirrors that of
> builtin/{commit-graph,multi-pack-index}.c.
> 
> Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
> diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt
> new file mode 100644
> index 00000000000..660d6a992a0
> --- /dev/null
> +++ b/Documentation/git-hook.txt
> @@ -0,0 +1,38 @@
> +git-hook(1)
> +===========
> +
> +NAME
> +----
> +git-hook - run git hooks
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'git hook' run <hook-name> [-- <hook-args>]
> +
> +DESCRIPTION
> +-----------
> +
> +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.

s/git/Git/g nit, no? I thought we were somewhat picky about capitalizing Git when we don't mean `git`?

Show 20 quoted lines
> +
> +SUBCOMMANDS
> +-----------
> +
> +run::
> +	Run the `<hook-name>` hook. See linkgit:githooks[5] for
> +	the hook names we support.
> ++
> +Any positional arguments to the hook should be passed after an
> +optional `--` (or `--end-of-options`, see linkgit:gitcli[7]). The
> +arguments (if any) differ by hook name, see linkgit:githooks[5] for
> +what those are.
> +
> +SEE ALSO
> +--------
> +linkgit:githooks[5]
> +
> +GIT
> +---
> +Part of the linkgit:git[1] suite
Show 26 quoted lines
> diff --git a/builtin/hook.c b/builtin/hook.c
> new file mode 100644
> index 00000000000..012a2973b38
> --- /dev/null
> +++ b/builtin/hook.c
> @@ -0,0 +1,90 @@
> +#include "cache.h"
> +#include "builtin.h"
> +#include "config.h"
> +#include "hook.h"
> +#include "parse-options.h"
> +#include "strbuf.h"
> +#include "strvec.h"
> +
> +#define BUILTIN_HOOK_RUN_USAGE \
> +	N_("git hook run <hook-name> [-- <hook-args>]")
> +
> +static const char * const builtin_hook_usage[] = {
> +	BUILTIN_HOOK_RUN_USAGE,
> +	NULL
> +};
> +
> +static const char * const builtin_hook_run_usage[] = {
> +	BUILTIN_HOOK_RUN_USAGE,
> +	NULL
> +};

While some reviewers might think this looks untidy, it does save us some repetition later on adding other subcommands to 'git hook'. Thanks.

Show 15 quoted lines
> +
> +static int run(int argc, const char **argv, const char *prefix)
> +{
> +	int i;
> +	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
> +	const char *hook_name;
> +	const char *hook_path;
> +	struct option run_options[] = {
> +		OPT_END(),
> +	};
> +	int ret;
> +
> +	argc = parse_options(argc, argv, prefix, run_options,
> +			     builtin_hook_run_usage,
> +			     PARSE_OPT_KEEP_DASHDASH);

We KEEP_DASHDASH so we can ensure someone did not send us some args they meant for 'git hook' instead of for the hook script itself. Ok.

Show 10 quoted lines
> +
> +	if (!argc)
> +		goto usage;
> +
> +	/*
> +	 * Having a -- for "run" when providing <hook-args> is
> +	 * mandatory.
> +	 */
> +	if (argc > 1 && strcmp(argv[1], "--") &&
> +	    strcmp(argv[1], "--end-of-options"))

--end-of-options isn't documented anywhere, right? Fine, I don't mind, but it's interesting to support it "secretly".

Show 8 quoted lines
> +		goto usage;
> +
> +	/* Add our arguments, start after -- */
> +	for (i = 2 ; i < argc; i++)
> +		strvec_push(&opt.args, argv[i]);
> +
> +	/* Need to take into account core.hooksPath */
> +	git_config(git_default_config, NULL);
..and git_default_config() sets it up for use in find_hook(). Great.
Show 6 quoted lines
> +
> +	/*
> +	 * We are not using a plain run_hooks() because we'd like to
> +	 * detect missing hooks. Let's find it ourselves and call
> +	 * run_hooks() instead.
> +	 */

Hmm. In practice, I wonder if this means that callers like 'git-send-email' still need to check whether there is a 'sendemail-validate' hook before they can call 'git hook run sendemail-validate'? I guess I'll have a look at the work there and see.

Show 31 quoted lines
> +	hook_name = argv[0];
> +	hook_path = find_hook(hook_name);
> +	if (!hook_path) {
> +		error("cannot find a hook named %s", hook_name);
> +		return 1;
> +	}
> +
> +	ret = run_hooks(hook_name, hook_path, &opt);
> +	run_hooks_opt_clear(&opt);
> +	return ret;
> +usage:
> +	usage_with_options(builtin_hook_run_usage, run_options);
> +}
> +
> +int cmd_hook(int argc, const char **argv, const char *prefix)
> +{
> +	struct option builtin_hook_options[] = {
> +		OPT_END(),
> +	};
> +
> +	argc = parse_options(argc, argv, NULL, builtin_hook_options,
> +			     builtin_hook_usage, PARSE_OPT_STOP_AT_NON_OPTION);
> +	if (!argc)
> +		goto usage;
> +
> +	if (!strcmp(argv[0], "run"))
> +		return run(argc, argv, prefix);
> +
> +usage:
> +	usage_with_options(builtin_hook_usage, builtin_hook_options);
> +}
The rest of it is very straightforward boilerplate for a builtin. Thanks.
Show 21 quoted lines
> diff --git a/command-list.txt b/command-list.txt
> index a289f09ed6f..9ccd8e5aebe 100644
> --- a/command-list.txt
> +++ b/command-list.txt
> @@ -103,6 +103,7 @@ git-grep                                mainporcelain           info
>  git-gui                                 mainporcelain
>  git-hash-object                         plumbingmanipulators
>  git-help                                ancillaryinterrogators          complete
> +git-hook                                mainporcelain
>  git-http-backend                        synchingrepositories
>  git-http-fetch                          synchelpers
>  git-http-push                           synchelpers
> diff --git a/git.c b/git.c
> index 60c2784be45..e5891e02021 100644
> --- a/git.c
> +++ b/git.c
> @@ -538,6 +538,7 @@ static struct cmd_struct commands[] = {
>  	{ "grep", cmd_grep, RUN_SETUP_GENTLY },
>  	{ "hash-object", cmd_hash_object },
>  	{ "help", cmd_help },
> +	{ "hook", cmd_hook, RUN_SETUP },

Ok. For now this is still requiring a gitdir. That makes sense to me, because we are still getting our hooks from $GITDIR/hooks/. Fine.

Show 36 quoted lines
>  	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
>  	{ "init", cmd_init_db },
>  	{ "init-db", cmd_init_db },
> diff --git a/hook.c b/hook.c
> index 55e1145a4b7..240270db64e 100644
> --- a/hook.c
> +++ b/hook.c
> @@ -1,6 +1,7 @@
>  #include "cache.h"
>  #include "hook.h"
>  #include "run-command.h"
> +#include "config.h"
>  
>  const char *find_hook(const char *name)
>  {
> @@ -40,3 +41,103 @@ int hook_exists(const char *name)
>  {
>  	return !!find_hook(name);
>  }
> +
> +void run_hooks_opt_clear(struct run_hooks_opt *o)
> +{
> +	strvec_clear(&o->env);
> +	strvec_clear(&o->args);
> +}
> +
> +static int pick_next_hook(struct child_process *cp,
> +			  struct strbuf *out,
> +			  void *pp_cb,
> +			  void **pp_task_cb)
> +{
> +	struct hook_cb_data *hook_cb = pp_cb;
> +	struct hook *run_me = hook_cb->run_me;
> +
> +	if (!run_me)
> +		return 0;

Signal the run_processes_parallel() machinery if we don't have any hooks left to run. Right now the linked list is always going to be of size 1, but that's ok :)

Show 5 quoted lines
> +
> +	cp->no_stdin = 1;
> +	cp->env = hook_cb->options->env.v;
> +	cp->stdout_to_stderr = 1;
> +	cp->trace2_hook_name = hook_cb->hook_name;
These args match identically the ones set up in run_hook_ve. Ok.
Show 31 quoted lines
> +
> +	/* add command */
> +	strvec_push(&cp->args, run_me->hook_path);
> +
> +	/*
> +	 * add passed-in argv, without expanding - let the user get back
> +	 * exactly what they put in
> +	 */
> +	strvec_pushv(&cp->args, hook_cb->options->args.v);
> +
> +	/* Provide context for errors if necessary */
> +	*pp_task_cb = run_me;
> +
> +	/*
> +	 * This pick_next_hook() will be called again, we're only
> +	 * running one hook, so indicate that no more work will be
> +	 * done.
> +	 */
> +	hook_cb->run_me = NULL;
> +
> +	return 1;
> +}
> +
> +static int notify_start_failure(struct strbuf *out,
> +				void *pp_cb,
> +				void *pp_task_cp)
> +{
> +	struct hook_cb_data *hook_cb = pp_cb;
> +	struct hook *attempted = pp_task_cp;
> +
> +	hook_cb->rc |= 1;

The OR means this will continue to work if the list of hooks is greater than 1. Ok.

Show 5 quoted lines
> +
> +	strbuf_addf(out, _("Couldn't start hook '%s'\n"),
> +		    attempted->hook_path);
> +
> +	return 1;

By returning 1, we ask the run_processes_parallel() machinery to give up on the rest of the hooks. I think that is okay, because the Git operation will not complete successfully anyway, and because "failed to start process" has more serious implications for Git's ability to continue starting tasks, compared to a hook validation failure. Ok.

Show 12 quoted lines
> +}
> +
> +static int notify_hook_finished(int result,
> +				struct strbuf *out,
> +				void *pp_cb,
> +				void *pp_task_cb)
> +{
> +	struct hook_cb_data *hook_cb = pp_cb;
> +
> +	hook_cb->rc |= result;
> +
> +	return 0;

Conversely, here we could decide to terminate the task list early if we heard an unhappy hook's return code. However, it's probably more useful for a user to hear *all* the reasons that their operation failed, rather than just one, so they don't need to rerun their commit operation 5 times for 5 different hook failures... :) Fine.

Show 14 quoted lines
> +}
> +
> +int run_hooks(const char *hook_name, const char *hook_path,
> +	      struct run_hooks_opt *options)
> +{
> +	struct hook my_hook = {
> +		.hook_path = hook_path,
> +	};
> +	struct hook_cb_data cb_data = {
> +		.rc = 0,
> +		.hook_name = hook_name,
> +		.options = options,
> +	};
> +	int jobs = 1;

Even though this is a constant, I find the throwaway variable easier to read than using a magic 1 in the run_processes_parallel call. I imagine the compiler gets rid of it, anyways. Thanks.

Show 60 quoted lines
> +
> +	if (!options)
> +		BUG("a struct run_hooks_opt must be provided to run_hooks");
> +
> +	cb_data.run_me = &my_hook;
> +
> +	run_processes_parallel_tr2(jobs,
> +				   pick_next_hook,
> +				   notify_start_failure,
> +				   notify_hook_finished,
> +				   &cb_data,
> +				   "hook",
> +				   hook_name);
> +
> +	return cb_data.rc;
> +}
> diff --git a/hook.h b/hook.h
> index 6aa36fc7ff9..111c5cf9296 100644
> --- a/hook.h
> +++ b/hook.h
> @@ -1,5 +1,33 @@
>  #ifndef HOOK_H
>  #define HOOK_H
> +#include "strvec.h"
> +
> +struct hook {
> +	/* The path to the hook */
> +	const char *hook_path;
> +};
> +
> +struct run_hooks_opt
> +{
> +	/* Environment vars to be set for each hook */
> +	struct strvec env;
> +
> +	/* Args to be passed to each hook */
> +	struct strvec args;
> +};
> +
> +#define RUN_HOOKS_OPT_INIT { \
> +	.env = STRVEC_INIT, \
> +	.args = STRVEC_INIT, \
> +}
> +
> +struct hook_cb_data {
> +	/* rc reflects the cumulative failure state */
> +	int rc;
> +	const char *hook_name;
> +	struct hook *run_me;
> +	struct run_hooks_opt *options;
> +};
>  
>  /*
>   * Returns the path to the hook file, or NULL if the hook is missing
> @@ -13,4 +41,15 @@ const char *find_hook(const char *name);
>   */
>  int hook_exists(const char *hookname);
>  
> +/**
> + * Clear data from an initialized "struct run_hooks-opt".
Nit, s/run_hooks-opt/run_hooks_opt/
Show 37 quoted lines
> + */
> +void run_hooks_opt_clear(struct run_hooks_opt *o);
> +
> +/**
> + * Takes an already resolved hook found via find_hook() and runs
> + * it. Does not call run_hooks_opt_clear() for you.
> + */
> +int run_hooks(const char *hookname, const char *hook_path,
> +	      struct run_hooks_opt *options);
>  #endif
> diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
> new file mode 100755
> index 00000000000..3aea1b105f0
> --- /dev/null
> +++ b/t/t1800-hook.sh
> @@ -0,0 +1,129 @@
> +#!/bin/sh
> +
> +test_description='git-hook command'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
> +. ./test-lib.sh
> +
> +test_expect_success 'git hook usage' '
> +	test_expect_code 129 git hook &&
> +	test_expect_code 129 git hook run &&
> +	test_expect_code 129 git hook run -h &&
> +	test_expect_code 129 git hook run --unknown 2>err &&
> +	grep "unknown option" err
> +'
> +
> +test_expect_success 'git hook run: nonexistent hook' '
> +	cat >stderr.expect <<-\EOF &&
> +	error: cannot find a hook named test-hook
> +	EOF
> +	test_expect_code 1 git hook run test-hook 2>stderr.actual &&
> +	test_cmp stderr.expect stderr.actual
See my earlier worry about this meaning callers still have to check the hookdir.
Show 110 quoted lines
> +'
> +
> +test_expect_success 'git hook run: basic' '
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	echo Test hook
> +	EOF
> +
> +	cat >expect <<-\EOF &&
> +	Test hook
> +	EOF
> +	git hook run test-hook 2>actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'git hook run: stdout and stderr both write to our stderr' '
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	echo >&1 Will end up on stderr
> +	echo >&2 Will end up on stderr
> +	EOF
> +
> +	cat >stderr.expect <<-\EOF &&
> +	Will end up on stderr
> +	Will end up on stderr
> +	EOF
> +	git hook run test-hook >stdout.actual 2>stderr.actual &&
> +	test_cmp stderr.expect stderr.actual &&
> +	test_must_be_empty stdout.actual
> +'
> +
> +test_expect_success 'git hook run: exit codes are passed along' '
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	exit 1
> +	EOF
> +
> +	test_expect_code 1 git hook run test-hook &&
> +
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	exit 2
> +	EOF
> +
> +	test_expect_code 2 git hook run test-hook &&
> +
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	exit 128
> +	EOF
> +
> +	test_expect_code 128 git hook run test-hook &&
> +
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	exit 129
> +	EOF
> +
> +	test_expect_code 129 git hook run test-hook
> +'
> +
> +test_expect_success 'git hook run arg u ments without -- is not allowed' '
> +	test_expect_code 129 git hook run test-hook arg u ments
> +'
> +
> +test_expect_success 'git hook run -- pass arguments' '
> +	write_script .git/hooks/test-hook <<-\EOF &&
> +	echo $1
> +	echo $2
> +	EOF
> +
> +	cat >expect <<-EOF &&
> +	arg
> +	u ments
> +	EOF
> +
> +	git hook run test-hook -- arg "u ments" 2>actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'git hook run -- out-of-repo runs excluded' '
> +	write_script .git/hooks/test-hook <<-EOF &&
> +	echo Test hook
> +	EOF
> +
> +	nongit test_must_fail git hook run test-hook
> +'
> +
> +test_expect_success 'git -c core.hooksPath=<PATH> hook run' '
> +	mkdir my-hooks &&
> +	write_script my-hooks/test-hook <<-\EOF &&
> +	echo Hook ran $1 >>actual
> +	EOF
> +
> +	cat >expect <<-\EOF &&
> +	Test hook
> +	Hook ran one
> +	Hook ran two
> +	Hook ran three
> +	Hook ran four
> +	EOF
> +
> +	# Test various ways of specifying the path. See also
> +	# t1350-config-hooks-path.sh
> +	>actual &&
> +	git hook run test-hook -- ignored 2>>actual &&
> +	git -c core.hooksPath=my-hooks hook run test-hook -- one 2>>actual &&
> +	git -c core.hooksPath=my-hooks/ hook run test-hook -- two 2>>actual &&
> +	git -c core.hooksPath="$PWD/my-hooks" hook run test-hook -- three 2>>actual &&
> +	git -c core.hooksPath="$PWD/my-hooks/" hook run test-hook -- four 2>>actual &&
> +	test_cmp expect actual
> +'
> +
> +test_done
> -- 
> 2.33.0.1567.g7b23ce7ed9e

Thanks. Other than my concern about erroring when there is no hook executable, it looks fine - but that one is serious enough that I'll withhold the Reviewed-by line.

Previous: Ævar Arnfjörð BjarmasonNext: Eric Sunshine
Message 3 of 115 in “hook.[ch]: new library to run hooks + simple hook conversion”
  1. 00/13 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Oct 15, 2021
  2. 01/13 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Oct 15, 2021
  3. Emily ShafferOct 15, 2021
  4. Eric SunshineOct 15, 2021
  5. Ævar Arnfjörð BjarmasonOct 19, 2021
  6. 02/13 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Oct 15, 2021
  7. Emily ShafferOct 15, 2021
  8. 04/13 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  9. Emily ShafferOct 15, 2021
  10. 05/13 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Oct 15, 2021
  11. Emily ShafferOct 15, 2021
  12. 03/13 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  13. Emily ShafferOct 15, 2021
  14. 06/13 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  15. Emily ShafferOct 15, 2021
  16. 07/13 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Oct 15, 2021
  17. Emily ShafferOct 15, 2021
  18. 08/13 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Oct 15, 2021
  19. Emily ShafferOct 15, 2021
  20. 09/13 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Oct 15, 2021
  21. Emily ShafferOct 15, 2021
  22. 10/13 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  23. Emily ShafferOct 15, 2021
  24. 11/13 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  25. Emily ShafferOct 15, 2021
  26. 12/13 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  27. 13/13 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Oct 15, 2021
  28. Emily ShafferOct 15, 2021
  29. Emily ShafferOct 15, 2021
  30. Junio C HamanoOct 16, 2021
  31. Eric SunshineOct 16, 2021
  32. Ævar Arnfjörð BjarmasonOct 16, 2021
  33. Junio C HamanoOct 16, 2021
  34. Ævar Arnfjörð BjarmasonOct 16, 2021
  35. 00/13 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Oct 19, 2021
  36. 01/13 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Oct 19, 2021
  37. 02/13 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Oct 19, 2021
  38. 03/13 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  39. Junio C HamanoOct 29, 2021
  40. 04/13 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  41. 05/13 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Oct 19, 2021
  42. Junio C HamanoOct 29, 2021
  43. 06/13 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  44. 07/13 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Oct 19, 2021
  45. Junio C HamanoOct 29, 2021
  46. 08/13 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Oct 19, 2021
  47. 09/13 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Oct 19, 2021
  48. 10/13 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  49. 11/13 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  50. 12/13 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  51. 13/13 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Oct 19, 2021
  52. Junio C HamanoOct 29, 2021
  53. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Nov 1, 2021
  54. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Nov 1, 2021
  55. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 1, 2021
  56. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Nov 1, 2021
  57. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Nov 1, 2021
  58. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  59. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Nov 1, 2021
  60. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  61. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  62. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 1, 2021
  63. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  64. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  65. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Nov 1, 2021
  66. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Nov 1, 2021
  67. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  68. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  69. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Nov 1, 2021
  70. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Nov 1, 2021
  71. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Nov 23, 2021
  72. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Nov 23, 2021
  73. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Nov 23, 2021
  74. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Nov 23, 2021
  75. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  76. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  77. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Nov 23, 2021
  78. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  79. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  80. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 23, 2021
  81. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  82. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Nov 23, 2021
  83. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  84. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  85. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Nov 23, 2021
  86. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Nov 23, 2021
  87. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Nov 23, 2021
  88. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 23, 2021
  89. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Dec 22, 2021
  90. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Dec 22, 2021
  91. Emily ShafferJan 7, 2022
  92. Junio C HamanoJan 7, 2022
  93. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Dec 22, 2021
  94. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  95. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  96. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Dec 22, 2021
  97. Glen ChooJan 6, 2022
  98. Emily ShafferJan 7, 2022
  99. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Dec 22, 2021
  100. Glen ChooJan 6, 2022
  101. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  102. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Dec 22, 2021
  103. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Dec 22, 2021
  104. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  105. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Dec 22, 2021
  106. Glen ChooJan 6, 2022
  107. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Dec 22, 2021
  108. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Dec 22, 2021
  109. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  110. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  111. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  112. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Dec 22, 2021
  113. Glen ChooJan 6, 2022
  114. Emily ShafferJan 7, 2022
  115. Emily ShafferJan 7, 2022

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.