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

[PATCH v9 00/37] propose config-based hooks

From
Emily Shaffer <emilyshaffer@google.com>
Date
May 27, 2021, 00:08 UTC
Message-ID
<20210527000856.695702-1-emilyshaffer@google.com>
In-Reply-To
<20210311021037.3001235-1-emilyshaffer@google.com>
After much delay and $DAYJOB, here is v9.
- Addressed nits in reviews on v8
- sendemail-validate hook becomes non-parallelized; updated to use
  Ævar's updated system_or_die() function
- changed strbuf to char* in hooks_list
  - Attempted to do so in run_command's stdout callback, but this made
    length protection difficult, so stuck with strbuf there.
- test_i18ncmp -> test_cmp
- Stop doing i18n lego in run_hooks()
- Checked that run_hooks_opt_init() is always separated by a space from
  variable decl blocks
- Checked for early returns which may skip run_hooks_opt_clear(); this
  resulted in minimizing the scope of run_hooks_opt in most places
- Got rid of native-hooks.txt. It was a nice idea, but not attached to a
  large and slow series like this one.
- In traces, log the name of the hook (e.g. "pre-commit") instead of the
  name of the executable (e.g. "/home/emily/check-for-debug-strings");
  the executable name is tracelogged as part of argv anyways, and we
  want to be able to tell which hook was responsible for invoking the
  executable in question.
Thanks.
 - Emily
Emily Shaffer (37):
  doc: propose hooks managed by the config
  hook: introduce git-hook subcommand
  hook: include hookdir hook in list
  hook: teach hook.runHookDir
  hook: implement hookcmd.<name>.skip
  parse-options: parse into strvec
  hook: add 'run' subcommand
  hook: introduce hook_exists()
  hook: support passing stdin to hooks
  run-command: allow stdin for run_processes_parallel
  hook: allow parallel hook execution
  hook: allow specifying working directory for hooks
  run-command: add stdin callback for parallelization
  hook: provide stdin by string_list or callback
  run-command: allow capturing of collated output
  hooks: allow callers to capture output
  commit: use config-based hooks
  am: convert applypatch hooks to use config
  merge: use config-based hooks for post-merge hook
  gc: use hook library for pre-auto-gc hook
  rebase: teach pre-rebase to use hook.h
  read-cache: convert post-index-change hook to use config
  receive-pack: convert push-to-checkout hook to hook.h
  git-p4: use 'git hook' to run hooks
  hooks: convert 'post-checkout' hook to hook library
  hook: convert 'post-rewrite' hook to config
  transport: convert pre-push hook to use config
  reference-transaction: look for hooks in config
  receive-pack: convert 'update' hook to hook.h
  proc-receive: acquire hook list from hook.h
  post-update: use hook.h library
  receive-pack: convert receive hooks to hook.h
  bugreport: use hook_exists instead of find_hook
  git-send-email: use 'git hook run' for 'sendemail-validate'
  run-command: stop thinking about hooks
  doc: clarify fsmonitor-watchman specification
  docs: link githooks and git-hook manpages
 .gitignore                                    |   1 +
 Documentation/Makefile                        |   1 +
 Documentation/config/hook.txt                 |  27 +
 Documentation/git-hook.txt                    | 162 ++++++
 Documentation/githooks.txt                    |  77 ++-
 Documentation/technical/api-parse-options.txt |   7 +
 .../technical/config-based-hooks.txt          | 369 +++++++++++++
 Makefile                                      |   2 +
 builtin.h                                     |   1 +
 builtin/am.c                                  |  39 +-
 builtin/bugreport.c                           |   4 +-
 builtin/checkout.c                            |  19 +-
 builtin/clone.c                               |   8 +-
 builtin/commit.c                              |  11 +-
 builtin/fetch.c                               |   1 +
 builtin/gc.c                                  |   9 +-
 builtin/hook.c                                | 190 +++++++
 builtin/merge.c                               |  15 +-
 builtin/rebase.c                              |  10 +-
 builtin/receive-pack.c                        | 326 ++++++------
 builtin/submodule--helper.c                   |   2 +-
 builtin/worktree.c                            |  32 +-
 command-list.txt                              |   1 +
 commit.c                                      |  22 +-
 commit.h                                      |   3 +-
 git-p4.py                                     |  67 +--
 git-send-email.perl                           |  26 +-
 git.c                                         |   1 +
 hook.c                                        | 483 ++++++++++++++++++
 hook.h                                        | 139 +++++
 parse-options-cb.c                            |  16 +
 parse-options.h                               |   4 +
 read-cache.c                                  |  13 +-
 refs.c                                        |  43 +-
 reset.c                                       |  17 +-
 run-command.c                                 | 156 +++---
 run-command.h                                 |  55 +-
 sequencer.c                                   |  92 ++--
 submodule.c                                   |   1 +
 t/helper/test-parse-options.c                 |   6 +
 t/helper/test-run-command.c                   |  46 +-
 t/t0040-parse-options.sh                      |  27 +
 t/t0061-run-command.sh                        |  37 ++
 t/t1360-config-based-hooks.sh                 | 329 ++++++++++++
 t/t1416-ref-transaction-hooks.sh              |  12 +-
 t/t5411/test-0015-too-many-hooks-error.sh     |  47 ++
 ...3-pre-commit-and-pre-merge-commit-hooks.sh |  17 +-
 t/t9001-send-email.sh                         |  13 +-
 transport.c                                   |  58 +--
 49 files changed, 2505 insertions(+), 539 deletions(-)
 create mode 100644 Documentation/config/hook.txt
 create mode 100644 Documentation/git-hook.txt
 create mode 100644 Documentation/technical/config-based-hooks.txt
 create mode 100644 builtin/hook.c
 create mode 100644 hook.c
 create mode 100644 hook.h
 create mode 100755 t/t1360-config-based-hooks.sh
 create mode 100644 t/t5411/test-0015-too-many-hooks-error.sh
 1:  85b99369f1 !  1:  d2b7ee8317 doc: propose hooks managed by the config
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Since v6, checked for inconsistencies with implementation and added lots of
    +    caveats about whether 'git hook add' and 'git hook edit' will ever materialize.
    +
    +    Hopefully this reflects reality now; please review accordingly.
    +
    +    Since v6, checked for inconsistencies with implementation and added lots of
    +    caveats about whether 'git hook add' and 'git hook edit' will ever materialize.
    +
    +    Hopefully this reflects reality now; please review accordingly.
    +
    +    Since v4, addressed comments from Jonathan Tan about wording. However, I have
    +    not addressed AEvar's comments or done a full re-review of this document.
    +    I wanted to get the rest of the series out for initial review first.
    +
    +     - Emily
    +
    +    Since v4, addressed comments from Jonathan Tan about wording.
    +
      ## Documentation/Makefile ##
     @@ Documentation/Makefile: SP_ARTICLES += $(API_DOCS)
      TECH_DOCS += MyFirstContribution
 2:  1d19f1477c <  -:  ---------- hook: scaffolding for git-hook subcommand
 3:  c125c63880 !  2:  112a809f02 hook: add list command
    @@ Metadata
     Author: Emily Shaffer <emilyshaffer@google.com>
     
      ## Commit message ##
    -    hook: add list command
    +    hook: introduce git-hook subcommand
     
    -    Teach 'git hook list <hookname>', which checks the known configs in
    +    Add a new subcommand, git-hook, which will be used to ease config-based
    +    hook management. This command will handle parsing configs to compose a
    +    list of hooks to run for a given event, as well as adding or modifying
    +    hook configs in an interactive fashion.
    +
    +    Start with 'git hook list <hookname>', which checks the known configs in
         order to create an ordered list of hooks to run on a given hook event.
     
         Multiple commands can be specified for a given hook by providing
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Since v4, mainly changed to RUN_SETUP_GENTLY so that 'git hook list' can
    +    be executed outside of a repo.
    +
    + ## .gitignore ##
    +@@
    + /git-grep
    + /git-hash-object
    + /git-help
    ++/git-hook
    + /git-http-backend
    + /git-http-fetch
    + /git-http-push
    +
      ## Documentation/config/hook.txt (new) ##
     @@
     +hook.<command>.command::
    @@ Documentation/config/hook.txt (new)
     +	as a command. This can be an executable on your device or a oneliner for
     +	your shell. See linkgit:git-hook[1].
     
    - ## Documentation/git-hook.txt ##
    -@@ Documentation/git-hook.txt: git-hook - Manage configured hooks
    - SYNOPSIS
    - --------
    - [verse]
    --'git hook'
    + ## Documentation/git-hook.txt (new) ##
    +@@
    ++git-hook(1)
    ++===========
    ++
    ++NAME
    ++----
    ++git-hook - Manage configured hooks
    ++
    ++SYNOPSIS
    ++--------
    ++[verse]
     +'git hook' list <hook-name>
    - 
    - DESCRIPTION
    - -----------
    --A placeholder command. Later, you will be able to list, add, and modify hooks
    --with this command.
    ++
    ++DESCRIPTION
    ++-----------
     +You can list configured hooks with this command. Later, you will be able to run,
     +add, and modify hooks with this command.
     +
    @@ Documentation/git-hook.txt: git-hook - Manage configured hooks
     +CONFIGURATION
     +-------------
     +include::config/hook.txt[]
    - 
    - GIT
    - ---
    ++
    ++GIT
    ++---
    ++Part of the linkgit:git[1] suite
     
      ## Makefile ##
     @@ Makefile: LIB_OBJS += hash-lookup.o
    @@ Makefile: LIB_OBJS += hash-lookup.o
      LIB_OBJS += ident.o
      LIB_OBJS += json-writer.o
      LIB_OBJS += kwset.o
    +@@ Makefile: BUILTIN_OBJS += builtin/get-tar-commit-id.o
    + BUILTIN_OBJS += builtin/grep.o
    + BUILTIN_OBJS += builtin/hash-object.o
    + BUILTIN_OBJS += builtin/help.o
    ++BUILTIN_OBJS += builtin/hook.o
    + BUILTIN_OBJS += builtin/index-pack.o
    + BUILTIN_OBJS += builtin/init-db.o
    + BUILTIN_OBJS += builtin/interpret-trailers.o
     
    - ## builtin/hook.c ##
    + ## builtin.h ##
    +@@ builtin.h: int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
    + int cmd_grep(int argc, const char **argv, const char *prefix);
    + int cmd_hash_object(int argc, const char **argv, const char *prefix);
    + int cmd_help(int argc, const char **argv, const char *prefix);
    ++int cmd_hook(int argc, const char **argv, const char *prefix);
    + int cmd_index_pack(int argc, const char **argv, const char *prefix);
    + int cmd_init_db(int argc, const char **argv, const char *prefix);
    + int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
    +
    + ## builtin/hook.c (new) ##
     @@
    - #include "cache.h"
    --
    - #include "builtin.h"
    ++#include "cache.h"
    ++#include "builtin.h"
     +#include "config.h"
     +#include "hook.h"
    - #include "parse-options.h"
    ++#include "parse-options.h"
     +#include "strbuf.h"
    - 
    - static const char * const builtin_hook_usage[] = {
    --	N_("git hook"),
    ++
    ++static const char * const builtin_hook_usage[] = {
     +	N_("git hook list <hookname>"),
    - 	NULL
    - };
    - 
    --int cmd_hook(int argc, const char **argv, const char *prefix)
    ++	NULL
    ++};
    ++
     +static int list(int argc, const char **argv, const char *prefix)
    - {
    --	struct option builtin_hook_options[] = {
    ++{
     +	struct list_head *head, *pos;
    -+	struct strbuf hookname = STRBUF_INIT;
    ++	const char *hookname = NULL;
     +
     +	struct option list_options[] = {
    - 		OPT_END(),
    - 	};
    - 
    --	argc = parse_options(argc, argv, prefix, builtin_hook_options,
    ++		OPT_END(),
    ++	};
    ++
     +	argc = parse_options(argc, argv, prefix, list_options,
    - 			     builtin_hook_usage, 0);
    - 
    ++			     builtin_hook_usage, 0);
    ++
     +	if (argc < 1) {
     +		usage_msg_opt(_("You must specify a hook event name to list."),
     +			      builtin_hook_usage, list_options);
     +	}
     +
    -+	strbuf_addstr(&hookname, argv[0]);
    ++	hookname = argv[0];
     +
    -+	head = hook_list(&hookname);
    ++	head = hook_list(hookname);
     +
     +	if (list_empty(head)) {
     +		printf(_("no commands configured for hook '%s'\n"),
    -+		       hookname.buf);
    -+		strbuf_release(&hookname);
    ++		       hookname);
     +		return 0;
     +	}
     +
    @@ builtin/hook.c
     +	}
     +
     +	clear_hook_list(head);
    -+	strbuf_release(&hookname);
     +
    - 	return 0;
    - }
    ++	return 0;
    ++}
     +
     +int cmd_hook(int argc, const char **argv, const char *prefix)
     +{
    @@ builtin/hook.c
     +	usage_with_options(builtin_hook_usage, builtin_hook_options);
     +}
     
    + ## command-list.txt ##
    +@@ command-list.txt: 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
    +
    + ## git.c ##
    +@@ git.c: static struct cmd_struct commands[] = {
    + 	{ "grep", cmd_grep, RUN_SETUP_GENTLY },
    + 	{ "hash-object", cmd_hash_object },
    + 	{ "help", cmd_help },
    ++	{ "hook", cmd_hook, RUN_SETUP_GENTLY },
    + 	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
    + 	{ "init", cmd_init_db },
    + 	{ "init-db", cmd_init_db },
    +
      ## hook.c (new) ##
     @@
     +#include "cache.h"
    @@ hook.c (new)
     +	return 0;
     +}
     +
    -+struct list_head* hook_list(const struct strbuf* hookname)
    ++struct list_head* hook_list(const char* hookname)
     +{
     +	struct strbuf hook_key = STRBUF_INIT;
     +	struct list_head *hook_head = xmalloc(sizeof(struct list_head));
    @@ hook.c (new)
     +	if (!hookname)
     +		return NULL;
     +
    -+	strbuf_addf(&hook_key, "hook.%s.command", hookname->buf);
    ++	strbuf_addf(&hook_key, "hook.%s.command", hookname);
     +
     +	git_config(hook_config_lookup, &cb_data);
     +
    @@ hook.h (new)
     + * Provides a linked list of 'struct hook' detailing commands which should run
     + * in response to the 'hookname' event, in execution order.
     + */
    -+struct list_head* hook_list(const struct strbuf *hookname);
    ++struct list_head* hook_list(const char *hookname);
     +
     +/* Free memory associated with a 'struct hook' */
     +void free_hook(struct hook *ptr);
     +/* Empties the list at 'head', calling 'free_hook()' on each entry */
     +void clear_hook_list(struct list_head *head);
     
    - ## t/t1360-config-based-hooks.sh ##
    -@@ t/t1360-config-based-hooks.sh: test_description='config-managed multihooks, including git-hook command'
    - 
    - . ./test-lib.sh
    - 
    --test_expect_success 'git hook command does not crash' '
    --	git hook
    + ## t/t1360-config-based-hooks.sh (new) ##
    +@@
    ++#!/bin/bash
    ++
    ++test_description='config-managed multihooks, including git-hook command'
    ++
    ++. ./test-lib.sh
    ++
     +ROOT=
     +if test_have_prereq MINGW
     +then
    @@ t/t1360-config-based-hooks.sh: test_description='config-managed multihooks, incl
     +
     +	git hook list pre-commit >actual &&
     +	test_cmp expected actual
    - '
    - 
    - test_done
    ++'
    ++
    ++test_done
 4:  0b8cd46ff9 !  3:  3114306368 hook: include hookdir hook in list
    @@ builtin/hook.c: static int list(int argc, const char **argv, const char *prefix)
      	list_for_each(pos, head) {
      		struct hook *item = list_entry(pos, struct hook, list);
     -		if (item)
    +-			printf("%s: %s\n",
    +-			       config_scope_name(item->origin),
     +		item = list_entry(pos, struct hook, list);
     +		if (item) {
    -+			/* Don't translate 'hookdir' - it matches the config */
    - 			printf("%s: %s\n",
    --			       config_scope_name(item->origin),
    ++			/*
    ++			 * TRANSLATORS: "<config scope>: <path>". Both fields
    ++			 * should be left untranslated; config scope matches the
    ++			 * output of 'git config --show-scope'. Marked for
    ++			 * translation to provide better RTL support later.
    ++			 */
    ++			printf(_("%s: %s\n"),
     +			       (item->from_hookdir
     +				? "hookdir"
     +				: config_scope_name(item->origin)),
    @@ hook.c: static void append_or_move_hook(struct list_head *head, const char *comm
      	}
      
      	/* re-set the scope so we show where an override was specified */
    -@@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
    +@@ hook.c: struct list_head* hook_list(const char* hookname)
      
      	git_config(hook_config_lookup, &cb_data);
      
     +	if (have_git_dir()) {
    -+		const char *legacy_hook_path = find_hook(hookname->buf);
    ++		const char *legacy_hook_path = find_hook(hookname);
     +
     +		/* Unconditionally add legacy hook, but annotate it. */
     +		if (legacy_hook_path) {
 5:  05c503fbe1 !  4:  681013c32a hook: teach hook.runHookDir
    @@ Commit message
         list'. Later on, though, we will pay attention to this enum when running
         the hooks.
     
    +
    + ## Notes ##
    +    Since v7, tidied up the behavior of the HOOK_UNKNOWN flag and added a test to
    +    enforce it - now it matches the design doc much better.
    +
    +    Also, thanks Jonathan Tan for pointing out that the commit message made no sense
    +    and was targeted for a different change. Rewrote the commit message now.
    +
    +    Plus, added HOOK_ERROR flag per Junio and Jonathan Nieder.
    +
    +    Newly split into its own commit since v4, and taking place much sooner.
    +
    +    An unfortunate side effect of adding this support *before* the
    +    hook.runHookDir support is that the labels on the list are not clear -
    +    because we aren't yet flagging which hooks are from the hookdir versus
    +    the config. I suppose we could move the addition of that field to the
    +    struct hook up to this patch, but it didn't make a lot of sense to me to
    +    do it just for cosmetic purposes.
    +
    +    Since v7, tidied up the behavior of the HOOK_UNKNOWN flag and added a test to
    +    enforce it - now it matches the design doc much better.
    +
    +    Also, thanks Jonathan Tan for pointing out that the commit message made no sense
    +    and was targeted for a different change. Rewrote the commit message now.
    +
    +    Newly split into its own commit since v4, and taking place much sooner.
    +
    +    An unfortunate side effect of adding this support *before* the
    +    hook.runHookDir support is that the labels on the list are not clear -
    +    because we aren't yet flagging which hooks are from the hookdir versus
    +    the config. I suppose we could move the addition of that field to the
    +    struct hook up to this patch, but it didn't make a lot of sense to me to
    +    do it just for cosmetic purposes.
    +
      ## Documentation/config/hook.txt ##
     @@ Documentation/config/hook.txt: hookcmd.<name>.command::
      	A command to execute during a hook for which <name> has been specified
    @@ builtin/hook.c: static const char * const builtin_hook_usage[] = {
      static int list(int argc, const char **argv, const char *prefix)
      {
      	struct list_head *head, *pos;
    - 	struct strbuf hookname = STRBUF_INIT;
    + 	const char *hookname = NULL;
     +	struct strbuf hookdir_annotation = STRBUF_INIT;
      
      	struct option list_options[] = {
      		OPT_END(),
     @@ builtin/hook.c: static int list(int argc, const char **argv, const char *prefix)
    - 		return 0;
    - 	}
    - 
    -+	switch (should_run_hookdir) {
    -+		case HOOKDIR_NO:
    -+			strbuf_addstr(&hookdir_annotation, _(" (will not run)"));
    -+			break;
    -+		case HOOKDIR_ERROR:
    -+			strbuf_addstr(&hookdir_annotation, _(" (will error and not run)"));
    -+			break;
    -+		case HOOKDIR_INTERACTIVE:
    -+			strbuf_addstr(&hookdir_annotation, _(" (will prompt)"));
    -+			break;
    -+		case HOOKDIR_WARN:
    -+			strbuf_addstr(&hookdir_annotation, _(" (will warn but run)"));
    -+			break;
    -+		case HOOKDIR_YES:
    -+		/*
    -+		 * The default behavior should agree with
    -+		 * hook.c:configured_hookdir_opt(). HOOKDIR_UNKNOWN should just
    -+		 * do the default behavior.
    -+		 */
    -+		case HOOKDIR_UNKNOWN:
    -+		default:
    -+			break;
    -+	}
    -+
    - 	list_for_each(pos, head) {
      		struct hook *item = list_entry(pos, struct hook, list);
      		item = list_entry(pos, struct hook, list);
      		if (item) {
    - 			/* Don't translate 'hookdir' - it matches the config */
    --			printf("%s: %s\n",
    -+			printf("%s: %s%s\n",
    - 			       (item->from_hookdir
    - 				? "hookdir"
    - 				: config_scope_name(item->origin)),
    +-			/*
    +-			 * TRANSLATORS: "<config scope>: <path>". Both fields
    +-			 * should be left untranslated; config scope matches the
    +-			 * output of 'git config --show-scope'. Marked for
    +-			 * translation to provide better RTL support later.
    +-			 */
    +-			printf(_("%s: %s\n"),
    +-			       (item->from_hookdir
    +-				? "hookdir"
    +-				: config_scope_name(item->origin)),
     -			       item->command.buf);
    -+			       item->command.buf,
    -+			       (item->from_hookdir
    -+				? hookdir_annotation.buf
    -+				: ""));
    ++			if (item->from_hookdir) {
    ++				/*
    ++				 * TRANSLATORS: do not translate 'hookdir' as
    ++				 * it matches the config setting.
    ++				 */
    ++				switch (should_run_hookdir) {
    ++				case HOOKDIR_NO:
    ++					printf(_("hookdir: %s (will not run)\n"),
    ++					       item->command.buf);
    ++					break;
    ++				case HOOKDIR_ERROR:
    ++					printf(_("hookdir: %s (will error and not run)\n"),
    ++					       item->command.buf);
    ++					break;
    ++				case HOOKDIR_INTERACTIVE:
    ++					printf(_("hookdir: %s (will prompt)\n"),
    ++					       item->command.buf);
    ++					break;
    ++				case HOOKDIR_WARN:
    ++					printf(_("hookdir: %s (will warn but run)\n"),
    ++					       item->command.buf);
    ++					break;
    ++				case HOOKDIR_YES:
    ++				/*
    ++				 * The default behavior should agree with
    ++				 * hook.c:configured_hookdir_opt(). HOOKDIR_UNKNOWN should just
    ++				 * do the default behavior.
    ++				 */
    ++				case HOOKDIR_UNKNOWN:
    ++				default:
    ++					printf(_("hookdir: %s\n"),
    ++						 item->command.buf);
    ++					break;
    ++				}
    ++			} else {
    ++				/*
    ++				 * TRANSLATORS: "<config scope>: <path>". Both fields
    ++				 * should be left untranslated; config scope matches the
    ++				 * output of 'git config --show-scope'. Marked for
    ++				 * translation to provide better RTL support later.
    ++				 */
    ++				printf(_("%s: %s\n"),
    ++					config_scope_name(item->origin),
    ++					item->command.buf);
    ++			}
      		}
      	}
      
      	clear_hook_list(head);
     +	strbuf_release(&hookdir_annotation);
    - 	strbuf_release(&hookname);
      
      	return 0;
    -@@ builtin/hook.c: static int list(int argc, const char **argv, const char *prefix)
    + }
      
      int cmd_hook(int argc, const char **argv, const char *prefix)
      {
    @@ builtin/hook.c: static int list(int argc, const char **argv, const char *prefix)
     +		else if (!strcmp(run_hookdir, "interactive"))
     +			should_run_hookdir = HOOKDIR_INTERACTIVE;
     +		else
    ++			/*
    ++			 * TRANSLATORS: leave "yes/warn/interactive/no"
    ++			 * untranslated; the strings are compared literally.
    ++			 */
     +			die(_("'%s' is not a valid option for --run-hookdir "
     +			      "(yes, warn, interactive, no)"), run_hookdir);
     +	else
    @@ hook.c: static int hook_config_lookup(const char *key, const char *value, void *
     +	return HOOKDIR_UNKNOWN;
     +}
     +
    - struct list_head* hook_list(const struct strbuf* hookname)
    + struct list_head* hook_list(const char* hookname)
      {
      	struct strbuf hook_key = STRBUF_INIT;
     
      ## hook.h ##
     @@ hook.h: struct hook {
       */
    - struct list_head* hook_list(const struct strbuf *hookname);
    + struct list_head* hook_list(const char *hookname);
      
     +enum hookdir_opt
     +{
    @@ hook.h: struct hook {
     + * command line arguments.
     + */
     +enum hookdir_opt configured_hookdir_opt(void);
    ++
    ++/*
    ++ * Provides the hookdir_opt specified in the config without consulting any
    ++ * command line arguments.
    ++ */
    ++enum hookdir_opt configured_hookdir_opt(void);
     +
      /* Free memory associated with a 'struct hook' */
      void free_hook(struct hook *ptr);
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list shows hooks fr
     +
     +	git hook list pre-commit >actual &&
     +	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'hook.runHookDir = error is respected by list' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list shows hooks fr
     +
     +	git hook list pre-commit >actual &&
     +	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'hook.runHookDir = warn is respected by list' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list shows hooks fr
     +
     +	git hook list pre-commit >actual &&
     +	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list shows hooks fr
     +
     +	git hook list pre-commit >actual &&
     +	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'hook.runHookDir is tolerant to unknown values' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list shows hooks fr
     +
     +	git hook list pre-commit >actual &&
     +	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
      test_done
 6:  e86025853a !  5:  0a4b9f27b3 hook: implement hookcmd.<name>.skip
    @@ hook.c: static int hook_config_lookup(const char *key, const char *value, void *
     
      ## t/t1360-config-based-hooks.sh ##
     @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = warn is respected by list' '
    - 	test_i18ncmp expected actual
    + 	test_cmp expected actual
      '
      
     +test_expect_success 'git hook list removes skipped hookcmd' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = warn is re
     +	EOF
     +
     +	git hook list pre-commit >actual &&
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'git hook list ignores skip referring to unused hookcmd' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = warn is re
     +	EOF
     +
     +	git hook list pre-commit >actual &&
    -+	test_i18ncmp expected actual
    ++	test_cmp expected actual
     +'
     +
     +test_expect_success 'git hook list removes skipped inlined hook' '
 7:  6e10593d75 !  6:  2ad4f44d08 parse-options: parse into strvec
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Since v7, updated the reference doc to make the intended usage for OPT_STRVEC
    +    more clear.
    +
    +    Since v4, fixed one or two more places where I missed the argv_array->strvec
    +    rename.
    +
      ## Documentation/technical/api-parse-options.txt ##
     @@ Documentation/technical/api-parse-options.txt: There are some macros to easily define options:
      	The string argument is stored as an element in `string_list`.
    @@ parse-options.h: int parse_opt_commits(const struct option *, const char *, int)
      int parse_opt_noop_cb(const struct option *, const char *, int);
      enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
      					   const struct option *,
    +
    + ## t/helper/test-parse-options.c ##
    +@@
    + #include "cache.h"
    + #include "parse-options.h"
    + #include "string-list.h"
    ++#include "strvec.h"
    + #include "trace2.h"
    + 
    + static int boolean = 0;
    +@@ t/helper/test-parse-options.c: static char *string = NULL;
    + static char *file = NULL;
    + static int ambiguous;
    + static struct string_list list = STRING_LIST_INIT_NODUP;
    ++static struct strvec vector = STRVEC_INIT;
    + 
    + static struct {
    + 	int called;
    +@@ t/helper/test-parse-options.c: int cmd__parse_options(int argc, const char **argv)
    + 		OPT_STRING('o', NULL, &string, "str", "get another string"),
    + 		OPT_NOOP_NOARG(0, "obsolete"),
    + 		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
    ++		OPT_STRVEC(0, "vector", &vector, "str", "add str to strvec"),
    + 		OPT_GROUP("Magic arguments"),
    + 		OPT_ARGUMENT("quux", NULL, "means --quux"),
    + 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
    +@@ t/helper/test-parse-options.c: int cmd__parse_options(int argc, const char **argv)
    + 	for (i = 0; i < list.nr; i++)
    + 		show(&expect, &ret, "list: %s", list.items[i].string);
    + 
    ++	for (i = 0; i < vector.nr; i++)
    ++		show(&expect, &ret, "vector: %s", vector.v[i]);
    ++
    + 	for (i = 0; i < argc; i++)
    + 		show(&expect, &ret, "arg %02d: %s", i, argv[i]);
    + 
    +
    + ## t/t0040-parse-options.sh ##
    +@@ t/t0040-parse-options.sh: String options
    +     --st <st>             get another string (pervert ordering)
    +     -o <str>              get another string
    +     --list <str>          add str to list
    ++    --vector <str>        add str to strvec
    + 
    + Magic arguments
    +     --quux                means --quux
    +@@ t/t0040-parse-options.sh: test_expect_success '--no-list resets list' '
    + 	test_cmp expect output
    + '
    + 
    ++cat >expect <<\EOF
    ++boolean: 0
    ++integer: 0
    ++magnitude: 0
    ++timestamp: 0
    ++string: (not set)
    ++abbrev: 7
    ++verbose: -1
    ++quiet: 0
    ++dry run: no
    ++file: (not set)
    ++vector: foo
    ++vector: bar
    ++vector: baz
    ++EOF
    ++test_expect_success '--vector keeps list of strings' '
    ++	test-tool parse-options --vector foo --vector=bar --vector=baz >output &&
    ++	test_cmp expect output
    ++'
    ++
    ++test_expect_success '--no-vector resets list' '
    ++	test-tool parse-options --vector=other --vector=irrelevant --vector=options \
    ++		--no-vector --vector=foo --vector=bar --vector=baz >output &&
    ++	test_cmp expect output
    ++'
    ++
    + test_expect_success 'multiple quiet levels' '
    + 	test-tool parse-options --expect="quiet: 3" -q -q -q
    + '
 8:  0dc9284057 !  7:  27dd8e3edf hook: add 'run' subcommand
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Since v7, added support for "error" hook.runHookDir setting.
    +
    +    Since v4, updated the docs, and did less local application of single
    +    quotes. In order for hookdir hooks to run successfully with a space in
    +    the path, though, they must not be run with 'sh -c'. So we can treat the
    +    hookdir hooks specially, and warn users via doc about special
    +    considerations for configured hooks with spaces in their path.
    +
      ## Documentation/git-hook.txt ##
     @@ Documentation/git-hook.txt: SYNOPSIS
      --------
    @@ hook.c
      
      void free_hook(struct hook *ptr)
      {
    +-	if (ptr) {
    ++	if (ptr)
    + 		strbuf_release(&ptr->command);
    +-		free(ptr);
    +-	}
    ++	free(ptr);
    + }
    + 
    + static struct hook * find_hook_by_command(struct list_head *head, const char *command)
     @@ hook.c: enum hookdir_opt configured_hookdir_opt(void)
      	return HOOKDIR_UNKNOWN;
      }
    @@ hook.c: enum hookdir_opt configured_hookdir_opt(void)
     +
     +	switch (cfg)
     +	{
    -+		case HOOKDIR_ERROR:
    -+			fprintf(stderr, _("Skipping legacy hook at '%s'\n"),
    -+				path);
    -+			/* FALLTHROUGH */
    -+		case HOOKDIR_NO:
    -+			return 0;
    -+		case HOOKDIR_WARN:
    -+			fprintf(stderr, _("Running legacy hook at '%s'\n"),
    -+				path);
    -+			return 1;
    -+		case HOOKDIR_INTERACTIVE:
    -+			do {
    -+				/*
    -+				 * TRANSLATORS: Make sure to include [Y] and [n]
    -+				 * in your translation. Only English input is
    -+				 * accepted. Default option is "yes".
    -+				 */
    -+				fprintf(stderr, _("Run '%s'? [Yn] "), path);
    -+				git_read_line_interactively(&prompt);
    -+				strbuf_tolower(&prompt);
    -+				if (starts_with(prompt.buf, "n")) {
    -+					strbuf_release(&prompt);
    -+					return 0;
    -+				} else if (starts_with(prompt.buf, "y")) {
    -+					strbuf_release(&prompt);
    -+					return 1;
    -+				}
    -+				/* otherwise, we didn't understand the input */
    -+			} while (prompt.len); /* an empty reply means "Yes" */
    -+			strbuf_release(&prompt);
    -+			return 1;
    -+		/*
    -+		 * HOOKDIR_UNKNOWN should match the default behavior, but let's
    -+		 * give a heads up to the user.
    -+		 */
    -+		case HOOKDIR_UNKNOWN:
    -+			fprintf(stderr,
    -+				_("Unrecognized value for 'hook.runHookDir'. "
    -+				  "Is there a typo? "));
    -+			/* FALLTHROUGH */
    -+		case HOOKDIR_YES:
    -+		default:
    -+			return 1;
    ++	case HOOKDIR_ERROR:
    ++		fprintf(stderr, _("Skipping legacy hook at '%s'\n"),
    ++			path);
    ++		/* FALLTHROUGH */
    ++	case HOOKDIR_NO:
    ++		return 0;
    ++	case HOOKDIR_WARN:
    ++		fprintf(stderr, _("Running legacy hook at '%s'\n"),
    ++			path);
    ++		return 1;
    ++	case HOOKDIR_INTERACTIVE:
    ++		do {
    ++			/*
    ++			 * TRANSLATORS: Make sure to include [Y] and [n]
    ++			 * in your translation. Only English input is
    ++			 * accepted. Default option is "yes".
    ++			 */
    ++			fprintf(stderr, _("Run '%s'? [Y/n] "), path);
    ++			git_read_line_interactively(&prompt);
    ++			/*
    ++			 * In case of prompt = '' - that is, user hit enter,
    ++			 * saying "yes I want the default" - strncasecmp will
    ++			 * return 0 regardless. So list the default first.
    ++			 *
    ++			 * Case insensitively, accept "y", "ye", or "yes" as
    ++			 * "yes"; accept "n" or "no" as "no".
    ++			 */
    ++			if (!strncasecmp(prompt.buf, "yes", prompt.len)) {
    ++				strbuf_release(&prompt);
    ++				return 1;
    ++			} else if (!strncasecmp(prompt.buf, "no", prompt.len)) {
    ++				strbuf_release(&prompt);
    ++				return 0;
    ++			}
    ++			/* otherwise, we didn't understand the input */
    ++		} while (prompt.len); /* an empty reply means default (yes) */
    ++		return 1;
    ++	/*
    ++	 * HOOKDIR_UNKNOWN should match the default behavior, but let's
    ++	 * give a heads up to the user.
    ++	 */
    ++	case HOOKDIR_UNKNOWN:
    ++		fprintf(stderr,
    ++			_("Unrecognized value for 'hook.runHookDir'. "
    ++			  "Is there a typo? "));
    ++		/* FALLTHROUGH */
    ++	case HOOKDIR_YES:
    ++	default:
    ++		return 1;
     +	}
     +}
     +
    - struct list_head* hook_list(const struct strbuf* hookname)
    + struct list_head* hook_list(const char* hookname)
      {
      	struct strbuf hook_key = STRBUF_INIT;
    -@@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
    +@@ hook.c: struct list_head* hook_list(const char* hookname)
      	strbuf_release(&hook_key);
      	return hook_head;
      }
    @@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
     +	strvec_clear(&o->args);
     +}
     +
    -+static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options,
    ++static void prepare_hook_cp(const char *hookname, struct hook *hook,
    ++			    struct run_hooks_opt *options,
     +			    struct child_process *cp)
     +{
     +	if (!hook)
    @@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
     +	cp->no_stdin = 1;
     +	cp->env = options->env.v;
     +	cp->stdout_to_stderr = 1;
    -+	cp->trace2_hook_name = hook->command.buf;
    ++	cp->trace2_hook_name = hookname;
     +
     +	/*
     +	 * Commands from the config could be oneliners, but we know
    @@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
     +
     +int run_hooks(const char *hookname, struct run_hooks_opt *options)
     +{
    -+	struct strbuf hookname_str = STRBUF_INIT;
     +	struct list_head *to_run, *pos = NULL, *tmp = NULL;
     +	int rc = 0;
     +
     +	if (!options)
     +		BUG("a struct run_hooks_opt must be provided to run_hooks");
     +
    -+	strbuf_addstr(&hookname_str, hookname);
    -+
    -+	to_run = hook_list(&hookname_str);
    ++	to_run = hook_list(hookname);
     +
     +	list_for_each_safe(pos, tmp, to_run) {
     +		struct child_process hook_proc = CHILD_PROCESS_INIT;
    @@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
     +		    !should_include_hookdir(hook->command.buf, options->run_hookdir))
     +			continue;
     +
    -+		prepare_hook_cp(hook, options, &hook_proc);
    ++		prepare_hook_cp(hookname, hook, options, &hook_proc);
     +
     +		rc |= run_command(&hook_proc);
     +	}
    @@ hook.h: enum hookdir_opt
     +void run_hooks_opt_init(struct run_hooks_opt *o);
     +void run_hooks_opt_clear(struct run_hooks_opt *o);
     +
    -+/*
    + /*
    +- * Provides the hookdir_opt specified in the config without consulting any
    +- * command line arguments.
     + * Runs all hooks associated to the 'hookname' event in order. Each hook will be
     + * passed 'env' and 'args'.
    -+ */
    +  */
    +-enum hookdir_opt configured_hookdir_opt(void);
     +int run_hooks(const char *hookname, struct run_hooks_opt *options);
    -+
    + 
      /* Free memory associated with a 'struct hook' */
      void free_hook(struct hook *ptr);
    - /* Empties the list at 'head', calling 'free_hook()' on each entry */
     
      ## t/t1360-config-based-hooks.sh ##
     @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = no is respected by list' '
      
      	git hook list pre-commit >actual &&
      	# the hookdir annotation is translated
    --	test_i18ncmp expected actual
    -+	test_i18ncmp expected actual &&
    +-	test_cmp expected actual
    ++	test_cmp expected actual &&
     +
     +	git hook run pre-commit 2>actual &&
     +	test_must_be_empty actual
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = error is r
      
      	git hook list pre-commit >actual &&
      	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual &&
    ++	test_cmp expected actual &&
     +
     +	cat >expected <<-EOF &&
     +	Skipping legacy hook at '\''$(pwd)/.git/hooks/pre-commit'\''
     +	EOF
     +
     +	git hook run pre-commit 2>actual &&
    - 	test_i18ncmp expected actual
    + 	test_cmp expected actual
      '
      
     @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = warn is respected by list' '
      
      	git hook list pre-commit >actual &&
      	# the hookdir annotation is translated
    -+	test_i18ncmp expected actual &&
    ++	test_cmp expected actual &&
     +
     +	cat >expected <<-EOF &&
     +	Running legacy hook at '\''$(pwd)/.git/hooks/pre-commit'\''
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = warn is re
     +	EOF
     +
     +	git hook run pre-commit 2>actual &&
    - 	test_i18ncmp expected actual
    + 	test_cmp expected actual
      '
      
     @@ t/t1360-config-based-hooks.sh: test_expect_success 'git hook list removes skipped inlined hook' '
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = interactiv
      
      	git hook list pre-commit >actual &&
      	# the hookdir annotation is translated
    --	test_i18ncmp expected actual
    -+	test_i18ncmp expected actual &&
    ++	test_cmp expected actual &&
     +
     +	test_write_lines n | git hook run pre-commit 2>actual &&
     +	! grep "Legacy Hook" actual &&
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = interactiv
     +	test_cmp expected actual
     +'
     +
    ++test_expect_success 'git hook run can pass args and env vars' '
    ++	write_script sample-hook.sh <<-\EOF &&
    ++	echo $1
    ++	echo $2
    ++	echo $TEST_ENV_1
    ++	echo $TEST_ENV_2
    ++	EOF
    ++
    ++	test_config hook.pre-commit.command "\"$(pwd)/sample-hook.sh\"" &&
    ++
    ++	cat >expected <<-EOF &&
    ++	arg1
    ++	arg2
    ++	env1
    ++	env2
    ++	EOF
    ++
    ++	git hook run --arg arg1 \
    ++		--env TEST_ENV_1=env1 \
    ++		-a arg2 \
    ++		-e TEST_ENV_2=env2 \
    ++		pre-commit 2>actual &&
    ++
    + 	test_cmp expected actual
    + '
    + 
     +test_expect_success 'hookdir hook included in git hook run' '
     +	setup_hookdir &&
     +
    @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir = interactiv
     +	setup_hooks &&
     +
     +	nongit test_must_fail git hook run pre-commit
    - '
    - 
    ++'
    ++
      test_expect_success 'hook.runHookDir is tolerant to unknown values' '
    + 	setup_hookdir &&
    + 
 9:  92c67ed9da !  8:  46975c11c8 hook: introduce hook_exists()
    @@ hook.c: void run_hooks_opt_init(struct run_hooks_opt *o)
      	strvec_clear(&o->env);
     
      ## hook.h ##
    -@@ hook.h: struct list_head* hook_list(const struct strbuf *hookname);
    +@@ hook.h: struct list_head* hook_list(const char *hookname);
      
      enum hookdir_opt
      {
10:  9b3bb0b655 !  9:  e11f9e28a3 hook: support passing stdin to hooks
    @@ hook.c: void run_hooks_opt_init(struct run_hooks_opt *o)
      	o->run_hookdir = configured_hookdir_opt();
      }
      
    -@@ hook.c: static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options,
    +@@ hook.c: static void prepare_hook_cp(const char *hookname, struct hook *hook,
      	if (!hook)
      		return;
      
    @@ hook.c: static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *opt
     +
      	cp->env = options->env.v;
      	cp->stdout_to_stderr = 1;
    - 	cp->trace2_hook_name = hook->command.buf;
    + 	cp->trace2_hook_name = hookname;
     
      ## hook.h ##
     @@ hook.h: struct run_hooks_opt
    @@ hook.h: int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdi
     
      ## t/t1360-config-based-hooks.sh ##
     @@ t/t1360-config-based-hooks.sh: test_expect_success 'hook.runHookDir is tolerant to unknown values' '
    - 	test_i18ncmp expected actual
    + 	test_cmp expected actual
      '
      
     +test_expect_success 'stdin to multiple hooks' '
11:  9933985e78 = 10:  3ceb4156fd run-command: allow stdin for run_processes_parallel
12:  43caafe656 ! 11:  93a47f5242 hook: allow parallel hook execution
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Per AEvar's request - parallel hook execution on day zero.
    +
    +    In most ways run_processes_parallel() worked great for me - but it didn't
    +    have great support for hooks where we pipe to and from. I had to add this
    +    support later in the series.
    +
    +    Since I modified an existing and in-use library I'd appreciate a keen look on
    +    these patches.
    +
    +     - Emily
    +
      ## Documentation/config/hook.txt ##
     @@ Documentation/config/hook.txt: hook.runHookDir::
      	Controls how hooks contained in your hookdir are executed. Can be any of
    @@ hook.c: enum hookdir_opt configured_hookdir_opt(void)
      static int should_include_hookdir(const char *path, enum hookdir_opt cfg)
      {
      	struct strbuf prompt = STRBUF_INIT;
    -@@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
    +@@ hook.c: struct list_head* hook_list(const char* hookname)
      	return hook_head;
      }
      
    @@ hook.c: void run_hooks_opt_clear(struct run_hooks_opt *o)
      	strvec_clear(&o->args);
      }
      
    --static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options,
    +-static void prepare_hook_cp(const char *hookname, struct hook *hook,
    +-			    struct run_hooks_opt *options,
     -			    struct child_process *cp)
     +static int pick_next_hook(struct child_process *cp,
     +			  struct strbuf *out,
    @@ hook.c: void run_hooks_opt_clear(struct run_hooks_opt *o)
     -	cp->env = options->env.v;
     +	cp->env = hook_cb->options->env.v;
      	cp->stdout_to_stderr = 1;
    - 	cp->trace2_hook_name = hook->command.buf;
    +-	cp->trace2_hook_name = hookname;
    ++	cp->trace2_hook_name = hook_cb->hookname;
      
    -@@ hook.c: static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options,
    + 	/*
    + 	 * Commands from the config could be oneliners, but we know
    +@@ hook.c: static void prepare_hook_cp(const char *hookname, struct hook *hook,
      	 * add passed-in argv, without expanding - let the user get back
      	 * exactly what they put in
      	 */
    @@ hook.c: static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *opt
      
      int run_hooks(const char *hookname, struct run_hooks_opt *options)
      {
    - 	struct strbuf hookname_str = STRBUF_INIT;
      	struct list_head *to_run, *pos = NULL, *tmp = NULL;
     -	int rc = 0;
    -+	struct hook_cb_data cb_data = { 0, NULL, NULL, options };
    ++	struct hook_cb_data cb_data = { 0, hookname, NULL, NULL, options };
      
      	if (!options)
      		BUG("a struct run_hooks_opt must be provided to run_hooks");
     @@ hook.c: int run_hooks(const char *hookname, struct run_hooks_opt *options)
    - 	to_run = hook_list(&hookname_str);
    + 	to_run = hook_list(hookname);
      
      	list_for_each_safe(pos, tmp, to_run) {
     -		struct child_process hook_proc = CHILD_PROCESS_INIT;
    @@ hook.c: int run_hooks(const char *hookname, struct run_hooks_opt *options)
     +	if (list_empty(to_run))
     +		return 0;
      
    --		prepare_hook_cp(hook, options, &hook_proc);
    +-		prepare_hook_cp(hookname, hook, options, &hook_proc);
     +	cb_data.head = to_run;
     +	cb_data.run_me = list_entry(to_run->next, struct hook, list);
      
    @@ hook.h: struct run_hooks_opt
     + */
     +struct hook_cb_data {
     +	int rc;
    ++	const char *hookname;
     +	struct list_head *head;
     +	struct hook *run_me;
     +	struct run_hooks_opt *options;
13:  2e189a7566 ! 12:  7f8c886d3f hook: allow specifying working directory for hooks
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Needed later for "post-checkout" conversion.
    +
      ## hook.c ##
     @@ hook.c: void run_hooks_opt_init_sync(struct run_hooks_opt *o)
      	o->path_to_stdin = NULL;
    @@ hook.c: void run_hooks_opt_init_sync(struct run_hooks_opt *o)
     @@ hook.c: static int pick_next_hook(struct child_process *cp,
      	cp->env = hook_cb->options->env.v;
      	cp->stdout_to_stderr = 1;
    - 	cp->trace2_hook_name = hook->command.buf;
    + 	cp->trace2_hook_name = hook_cb->hookname;
     +	cp->dir = hook_cb->options->dir;
      
      	/*
14:  07899ad346 = 13:  8930baa9db run-command: add stdin callback for parallelization
15:  d3f18e433f ! 14:  d0f362591a hook: provide stdin by string_list or callback
    @@ Commit message
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
      ## hook.c ##
    -@@ hook.c: void free_hook(struct hook *ptr)
    +@@
    + 
    + void free_hook(struct hook *ptr)
      {
    - 	if (ptr) {
    +-	if (ptr)
    ++	if (ptr) {
      		strbuf_release(&ptr->command);
     +		free(ptr->feed_pipe_cb_data);
    - 		free(ptr);
    - 	}
    ++	}
    + 	free(ptr);
      }
    + 
     @@ hook.c: static void append_or_move_hook(struct list_head *head, const char *command)
      		strbuf_init(&to_add->command, 0);
      		strbuf_addstr(&to_add->command, command);
    @@ hook.c: int run_hooks(const char *hookname, struct run_hooks_opt *options)
     +	if (options->path_to_stdin && options->feed_pipe)
     +		BUG("choose only one method to populate stdin");
     +
    - 	strbuf_addstr(&hookname_str, hookname);
    + 	to_run = hook_list(hookname);
      
    - 	to_run = hook_list(&hookname_str);
    + 	list_for_each_safe(pos, tmp, to_run) {
     @@ hook.c: int run_hooks(const char *hookname, struct run_hooks_opt *options)
      	run_processes_parallel_tr2(options->jobs,
      				   pick_next_hook,
16:  417c3f054e ! 15:  83bbb405a5 run-command: allow capturing of collated output
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Originally when writing this patch I attempted to use a pipe in memory -
    +    but managing its lifetime was actually pretty tricky, and I found I could
    +    achieve the same thing with less code by doing it this way. Critique welcome,
    +    including "no, you really need to do it with a pipe".
    +
      ## builtin/fetch.c ##
     @@ builtin/fetch.c: static int fetch_multiple(struct string_list *list, int max_children)
      		result = run_processes_parallel_tr2(max_children,
17:  c0f3471bd1 ! 16:  73ed5de54c hooks: allow callers to capture output
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    You can see this in practice in the conversions for some of the push hooks,
    +    like 'receive-pack'.
    +
      ## hook.c ##
     @@ hook.c: void run_hooks_opt_init_sync(struct run_hooks_opt *o)
      	o->dir = NULL;
18:  13446e4273 = 17:  900c4d8963 commit: use config-based hooks
19:  9380c43180 ! 18:  a562120e22 am: convert applypatch hooks to use config
    @@ builtin/am.c: static void am_destroy(const struct am_state *state)
      {
      	int ret;
     +	struct run_hooks_opt opt;
    ++
     +	run_hooks_opt_init_sync(&opt);
      
      	assert(state->msg);
    @@ builtin/am.c: static void do_commit(const struct am_state *state)
      	const char *reflog_msg, *author, *committer = NULL;
      	struct strbuf sb = STRBUF_INIT;
     +	struct run_hooks_opt hook_opt;
    ++
     +	run_hooks_opt_init_async(&hook_opt);
      
     -	if (run_hook_le(NULL, "pre-applypatch", NULL))
    -+	if (run_hooks("pre-applypatch", &hook_opt))
    ++	if (run_hooks("pre-applypatch", &hook_opt)) {
    ++		run_hooks_opt_clear(&hook_opt);
      		exit(1);
    ++	}
    ++
    ++	run_hooks_opt_clear(&hook_opt);
      
      	if (write_cache_as_tree(&tree, 0, NULL))
    + 		die(_("git write-tree failed to write a tree"));
     @@ builtin/am.c: static void do_commit(const struct am_state *state)
      		fclose(fp);
      	}
      
     -	run_hook_le(NULL, "post-applypatch", NULL);
    ++	run_hooks_opt_init_async(&hook_opt);
     +	run_hooks("post-applypatch", &hook_opt);
      
     +	run_hooks_opt_clear(&hook_opt);
20:  316a605606 ! 19:  e841ed4384 merge: use config-based hooks for post-merge hook
    @@ builtin/merge.c: static void finish(struct commit *head_commit,
      	struct strbuf reflog_message = STRBUF_INIT;
     +	struct run_hooks_opt opt;
      	const struct object_id *head = &head_commit->object.oid;
    -+	run_hooks_opt_init_async(&opt);
      
      	if (!msg)
    - 		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
     @@ builtin/merge.c: static void finish(struct commit *head_commit,
      	}
      
      	/* Run a post-merge hook */
     -	run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
    ++	run_hooks_opt_init_async(&opt);
     +	strvec_push(&opt.args, squash ? "1" : "0");
     +	run_hooks("post-merge", &opt);
     +	run_hooks_opt_clear(&opt);
21:  a5132f14b3 ! 20:  7e99398f7d gc: use hook library for pre-auto-gc hook
    @@ builtin/gc.c: static void add_repack_incremental_option(void)
      static int need_to_gc(void)
      {
     +	struct run_hooks_opt hook_opt;
    -+	run_hooks_opt_init_async(&hook_opt);
    ++
      	/*
      	 * Setting gc.auto to 0 or negative can disable the
      	 * automatic gc.
    @@ builtin/gc.c: static int need_to_gc(void)
      		return 0;
      
     -	if (run_hook_le(NULL, "pre-auto-gc", NULL))
    -+	if (run_hooks("pre-auto-gc", &hook_opt))
    ++	run_hooks_opt_init_async(&hook_opt);
    ++	if (run_hooks("pre-auto-gc", &hook_opt)) {
    ++		run_hooks_opt_clear(&hook_opt);
      		return 0;
    ++	}
    ++	run_hooks_opt_clear(&hook_opt);
      	return 1;
      }
    + 
22:  01f1331cc9 ! 21:  5423217ef2 rebase: teach pre-rebase to use hook.h
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      	struct option builtin_rebase_options[] = {
      		OPT_STRING(0, "onto", &options.onto_name,
      			   N_("revision"),
    -@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 	};
    - 	int i;
    - 
    -+	run_hooks_opt_init_async(&hook_opt);
    -+
    - 	if (argc == 2 && !strcmp(argv[1], "-h"))
    - 		usage_with_options(builtin_rebase_usage,
    - 				   builtin_rebase_options);
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
      	}
      
      	/* If a hook exists, give it a chance to interrupt*/
    ++	run_hooks_opt_init_async(&hook_opt);
     +	strvec_pushl(&hook_opt.args, options.upstream_arg, argc ? argv[0] : NULL, NULL);
      	if (!ok_to_skip_pre_rebase &&
     -	    run_hook_le(NULL, "pre-rebase", options.upstream_arg,
     -			argc ? argv[0] : NULL, NULL))
    -+	    run_hooks("pre-rebase", &hook_opt))
    ++	    run_hooks("pre-rebase", &hook_opt)) {
    ++		run_hooks_opt_clear(&hook_opt);
      		die(_("The pre-rebase hook refused to rebase."));
    ++	}
    ++	run_hooks_opt_clear(&hook_opt);
      
      	if (options.flags & REBASE_DIFFSTAT) {
    -@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 	ret = !!run_specific_rebase(&options, action);
    - 
    - cleanup:
    -+	run_hooks_opt_clear(&hook_opt);
    - 	strbuf_release(&buf);
    - 	strbuf_release(&revisions);
    - 	free(options.head_name);
    + 		struct diff_options opts;
23:  85a7721adc ! 22:  1c0c5ad129 read-cache: convert post-index-change hook to use config
    @@ Documentation/githooks.txt: and "0" meaning they were not.
     
      ## read-cache.c ##
     @@
    - #include "fsmonitor.h"
      #include "thread-utils.h"
      #include "progress.h"
    + #include "sparse-index.h"
     +#include "hook.h"
    ++>>>>>>> 9524a9d29d (read-cache: convert post-index-change hook to use config)
      
      /* Mask for the name length in ce_flags in the on-disk index */
      
     @@ read-cache.c: static int do_write_locked_index(struct index_state *istate, struct lock_file *l
    - 				 unsigned flags)
      {
      	int ret;
    + 	int was_full = !istate->sparse_index;
     +	struct run_hooks_opt hook_opt;
    -+	run_hooks_opt_init_async(&hook_opt);
      
    - 	/*
    - 	 * TODO trace2: replace "the_repository" with the actual repo instance
    + 	ret = convert_to_sparse(istate);
    + 
     @@ read-cache.c: static int do_write_locked_index(struct index_state *istate, struct lock_file *l
      	else
      		ret = close_lock_file_gently(lock);
    @@ read-cache.c: static int do_write_locked_index(struct index_state *istate, struc
     -	run_hook_le(NULL, "post-index-change",
     -			istate->updated_workdir ? "1" : "0",
     -			istate->updated_skipworktree ? "1" : "0", NULL);
    ++	run_hooks_opt_init_async(&hook_opt);
     +	strvec_pushl(&hook_opt.args,
     +		     istate->updated_workdir ? "1" : "0",
     +		     istate->updated_skipworktree ? "1" : "0",
24:  21ec3e1a9d ! 23:  1193e856e6 receive-pack: convert push-to-checkout hook to hook.h
    @@ builtin/receive-pack.c: static const char *push_to_checkout(unsigned char *hash,
      				    const char *work_tree)
      {
     +	struct run_hooks_opt opt;
    ++
     +	run_hooks_opt_init_sync(&opt);
     +
      	strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
25:  e0405e96ad ! 24:  1817b6851b git-p4: use 'git hook' to run hooks
    @@ Commit message
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
     
    +
    + ## Notes ##
    +    Maybe there is a better way to do this - I had a hard time getting this to run
    +    locally, and Python is not my forte, so if anybody has a better approach I'd
    +    love to just take that patch instead :)
    +
    +    Since v6, removed the developer debug print statements.... :X
    +
    +    Maybe there is a better way to do this - I had a hard time getting this to run
    +    locally, and Python is not my forte, so if anybody has a better approach I'd
    +    love to just take that patch instead :)
    +
      ## git-p4.py ##
     @@ git-p4.py: def decode_path(path):
      
26:  c52578e078 ! 25:  b3a354e4a8 hooks: convert 'post-checkout' hook to hook library
    @@ builtin/checkout.c: struct branch_info {
      			      int changed)
      {
     -	return run_hook_le(NULL, "post-checkout",
    --			   oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid),
    --			   oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid),
    +-			   oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),
    +-			   oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),
     -			   changed ? "1" : "0", NULL);
     +	struct run_hooks_opt opt;
     +	int rc;
    @@ builtin/checkout.c: struct branch_info {
      	   a commit exists. */
     -
     +	strvec_pushl(&opt.args,
    -+		     oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid),
    -+		     oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid),
    ++		     oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),
    ++		     oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),
     +		     changed ? "1" : "0",
     +		     NULL);
     +	rc = run_hooks("post-checkout", &opt);
    @@ builtin/clone.c: static int checkout(int submodule_progress)
      	struct tree_desc t;
      	int err = 0;
     +	struct run_hooks_opt hook_opt;
    -+	run_hooks_opt_init_sync(&hook_opt);
      
      	if (option_no_checkout)
      		return 0;
    @@ builtin/clone.c: static int checkout(int submodule_progress)
      	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
      		die(_("unable to write new index file"));
      
    --	err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
    +-	err |= run_hook_le(NULL, "post-checkout", oid_to_hex(null_oid()),
     -			   oid_to_hex(&oid), "1", NULL);
    -+	strvec_pushl(&hook_opt.args, oid_to_hex(&null_oid), oid_to_hex(&oid), "1", NULL);
    ++	run_hooks_opt_init_sync(&hook_opt);
    ++	strvec_pushl(&hook_opt.args, oid_to_hex(null_oid()), oid_to_hex(&oid), "1", NULL);
     +	err |= run_hooks("post-checkout", &hook_opt);
     +	run_hooks_opt_clear(&hook_opt);
      
    @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
     -			cp.argv = NULL;
     -			cp.trace2_hook_name = "post-checkout";
     -			strvec_pushl(&cp.args, absolute_path(hook),
    --				     oid_to_hex(&null_oid),
    +-				     oid_to_hex(null_oid()),
     -				     oid_to_hex(&commit->object.oid),
     -				     "1", NULL);
     -			ret = run_command(&cp);
     -		}
     +		struct run_hooks_opt opt;
    ++
     +		run_hooks_opt_init_sync(&opt);
     +
     +		strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
     +		strvec_pushl(&opt.args,
    -+			     oid_to_hex(&null_oid),
    ++			     oid_to_hex(null_oid()),
     +			     oid_to_hex(&commit->object.oid),
     +			     "1",
     +			     NULL);
    @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
      
      	strvec_clear(&child_env);
     
    + ## read-cache.c ##
    +@@
    + #include "progress.h"
    + #include "sparse-index.h"
    + #include "hook.h"
    +->>>>>>> 9524a9d29d (read-cache: convert post-index-change hook to use config)
    + 
    + /* Mask for the name length in ce_flags in the on-disk index */
    + 
    +
      ## reset.c ##
     @@
      #include "tree-walk.h"
    @@ reset.c: int reset_head(struct repository *r, struct object_id *oid, const char
      	}
     -	if (run_hook)
     -		run_hook_le(NULL, "post-checkout",
    --			    oid_to_hex(orig ? orig : &null_oid),
    +-			    oid_to_hex(orig ? orig : null_oid()),
     -			    oid_to_hex(oid), "1", NULL);
     +	if (run_hook) {
     +		struct run_hooks_opt opt;
    ++
     +		run_hooks_opt_init_sync(&opt);
     +		strvec_pushl(&opt.args,
    -+			     oid_to_hex(orig ? orig : &null_oid),
    ++			     oid_to_hex(orig ? orig : null_oid()),
     +			     oid_to_hex(oid),
     +			     "1",
     +			     NULL);
27:  316cb6f584 ! 26:  692352f9aa hook: convert 'post-rewrite' hook to config
    @@ builtin/am.c: static int run_applypatch_msg_hook(struct am_state *state)
     -	const char *hook = find_hook("post-rewrite");
     +	struct run_hooks_opt opt;
      	int ret;
    -+	run_hooks_opt_init_async(&opt);
      
     -	if (!hook)
     -		return 0;
    --
    ++	run_hooks_opt_init_async(&opt);
    + 
     -	strvec_push(&cp.args, hook);
     -	strvec_push(&cp.args, "rebase");
    --
    --	cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
    --	cp.stdout_to_stderr = 1;
    --	cp.trace2_hook_name = "post-rewrite";
     +	strvec_push(&opt.args, "rebase");
     +	opt.path_to_stdin = am_path(state, "rewritten");
      
    --	ret = run_command(&cp);
    +-	cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
    +-	cp.stdout_to_stderr = 1;
    +-	cp.trace2_hook_name = "post-rewrite";
     +	ret = run_hooks("post-rewrite", &opt);
      
    +-	ret = run_command(&cp);
    +-
     -	close(cp.in);
     +	run_hooks_opt_clear(&opt);
      	return ret;
    @@ sequencer.c: int update_head_with_reflog(const struct commit *old_head,
     +	struct string_list to_stdin = STRING_LIST_INIT_DUP;
      	int code;
     -	struct strbuf sb = STRBUF_INIT;
    -+	run_hooks_opt_init_async(&opt);
      
     -	argv[0] = find_hook("post-rewrite");
     -	if (!argv[0])
     -		return 0;
    -+	strvec_push(&opt.args, "amend");
    ++	run_hooks_opt_init_async(&opt);
      
     -	argv[1] = "amend";
     -	argv[2] = NULL;
    @@ sequencer.c: int update_head_with_reflog(const struct commit *old_head,
     -	strbuf_release(&sb);
     -	sigchain_pop(SIGPIPE);
     -	return finish_command(&proc);
    ++	strvec_push(&opt.args, "amend");
    ++
     +	strbuf_addf(&tmp,
     +		    "%s %s",
     +		    oid_to_hex(oldoid),
    @@ sequencer.c: static int pick_commits(struct repository *r,
     -			strvec_push(&child.args, "--for-rewrite=rebase");
     +			struct child_process notes_cp = CHILD_PROCESS_INIT;
     +			struct run_hooks_opt hook_opt;
    ++
     +			run_hooks_opt_init_async(&hook_opt);
     +
     +			notes_cp.in = open(rebase_path_rewritten_list(), O_RDONLY);
28:  0ab776068d = 27:  964011dfdd transport: convert pre-push hook to use config
29:  601dada804 ! 28:  c04822add9 reference-transaction: look for hooks in config
    @@ t/t1416-ref-transaction-hooks.sh: test_expect_success 'interleaving hook calls s
      	EOF
      
      	git push ./target-repo.git PRE POST &&
    +
    + ## transport.c ##
    +@@ transport.c: static int run_pre_push_hook(struct transport *transport,
    + 	struct strbuf tmp = STRBUF_INIT;
    + 	struct ref *r;
    + 	struct string_list to_stdin = STRING_LIST_INIT_DUP;
    ++
    + 	run_hooks_opt_init_async(&opt);
    + 
    + 	strvec_push(&opt.args, transport->remote->name);
30:  d60f2b146e ! 29:  ddc6f56bec receive-pack: convert 'update' hook to hook.h
    @@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
      	return status;
      }
      
    --static int run_update_hook(struct command *cmd)
     +static void hook_output_to_sideband(struct strbuf *output, void *cb_data)
    - {
    --	const char *argv[5];
    --	struct child_process proc = CHILD_PROCESS_INIT;
    --	int code;
    ++{
     +	int keepalive_active = 0;
    - 
    --	argv[0] = find_hook("update");
    --	if (!argv[0])
    --		return 0;
    ++
     +	if (keepalive_in_sec <= 0)
     +		use_keepalive = KEEPALIVE_NEVER;
     +	if (use_keepalive == KEEPALIVE_ALWAYS)
     +		keepalive_active = 1;
    - 
    --	argv[1] = cmd->ref_name;
    --	argv[2] = oid_to_hex(&cmd->old_oid);
    --	argv[3] = oid_to_hex(&cmd->new_oid);
    --	argv[4] = NULL;
    ++
     +	/* send a keepalive if there is no data to write */
     +	if (keepalive_active && !output->len) {
     +		static const char buf[] = "0005\1";
     +		write_or_die(1, buf, sizeof(buf) - 1);
     +		return;
     +	}
    - 
    --	proc.no_stdin = 1;
    --	proc.stdout_to_stderr = 1;
    --	proc.err = use_sideband ? -1 : 0;
    --	proc.argv = argv;
    --	proc.trace2_hook_name = "update";
    ++
     +	if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) {
     +		const char *first_null = memchr(output->buf, '\0', output->len);
     +		if (first_null) {
    @@ builtin/receive-pack.c: static int run_receive_hook(struct command *commands,
     +	send_sideband(1, 2, output->buf, output->len, use_sideband);
     +}
     +
    -+static int run_update_hook(struct command *cmd)
    -+{
    + static int run_update_hook(struct command *cmd)
    + {
    +-	const char *argv[5];
    +-	struct child_process proc = CHILD_PROCESS_INIT;
     +	struct run_hooks_opt opt;
    -+	int code;
    + 	int code;
    + 
    +-	argv[0] = find_hook("update");
    +-	if (!argv[0])
    +-		return 0;
     +	run_hooks_opt_init_async(&opt);
    -+
    + 
    +-	argv[1] = cmd->ref_name;
    +-	argv[2] = oid_to_hex(&cmd->old_oid);
    +-	argv[3] = oid_to_hex(&cmd->new_oid);
    +-	argv[4] = NULL;
     +	strvec_pushl(&opt.args,
     +		     cmd->ref_name,
     +		     oid_to_hex(&cmd->old_oid),
     +		     oid_to_hex(&cmd->new_oid),
     +		     NULL);
      
    +-	proc.no_stdin = 1;
    +-	proc.stdout_to_stderr = 1;
    +-	proc.err = use_sideband ? -1 : 0;
    +-	proc.argv = argv;
    +-	proc.trace2_hook_name = "update";
    +-
     -	code = start_command(&proc);
     -	if (code)
     -		return code;
31:  1e6898670b ! 30:  e1e810869f proc-receive: acquire hook list from hook.h
    @@ builtin/receive-pack.c: static int run_proc_receive_hook(struct command *command
      
     -	argv[0] = find_hook("proc-receive");
     -	if (!argv[0]) {
    -+	struct strbuf hookname = STRBUF_INIT;
     +	struct hook *proc_receive = NULL;
     +	struct list_head *pos, *hooks;
     +
    -+	strbuf_addstr(&hookname, "proc-receive");
    -+	hooks = hook_list(&hookname);
    ++	hooks = hook_list("proc-receive");
     +
     +	list_for_each(pos, hooks) {
     +		if (proc_receive) {
32:  012e3a7a79 ! 31:  b8be5a2288 post-update: use hook.h library
    @@ builtin/receive-pack.c: static const char *update(struct command *cmd, struct sh
      	struct command *cmd;
     -	struct child_process proc = CHILD_PROCESS_INIT;
     -	const char *hook;
    --
    ++	struct run_hooks_opt opt;
    + 
     -	hook = find_hook("post-update");
     -	if (!hook)
     -		return;
    -+	struct run_hooks_opt opt;
     +	run_hooks_opt_init_async(&opt);
      
      	for (cmd = commands; cmd; cmd = cmd->next) {
33:  2740bcda6d = 32:  1cc1384eae receive-pack: convert receive hooks to hook.h
34:  f201f3af5f = 33:  1bb9a3810c bugreport: use hook_exists instead of find_hook
35:  0956a94cc7 ! 34:  3db7bf3b0d git-send-email: use 'git hook run' for 'sendemail-validate'
    @@ git-send-email.perl: sub unique_email_list {
      	my ($fn, $xfer_encoding) = @_;
      
     -	if ($repo) {
    --		my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'),
    +-		my $validate_hook = catfile($repo->hooks_path(),
     -					    'sendemail-validate');
     -		my $hook_error;
     -		if (-x $validate_hook) {
    @@ git-send-email.perl: sub unique_email_list {
     -			chdir($repo->wc_path() or $repo->repo_path())
     -				or die("chdir: $!");
     -			local $ENV{"GIT_DIR"} = $repo->repo_path();
    --			$hook_error = "rejected by sendemail-validate hook"
    --				if system($validate_hook, $target);
    +-			$hook_error = system_or_msg([$validate_hook, $target]);
     -			chdir($cwd_save) or die("chdir: $!");
     -		}
    --		return $hook_error if $hook_error;
    +-		if ($hook_error) {
    +-			die sprintf(__("fatal: %s: rejected by sendemail-validate hook\n" .
    +-				       "%s\n" .
    +-				       "warning: no patches were sent\n"), $fn, $hook_error);
    +-		}
     -	}
     +	my $target = abs_path($fn);
    -+	return "rejected by sendemail-validate hook"
    -+		if system(("git", "hook", "run", "sendemail-validate", "-a",
    -+				$target));
    ++
    ++	system_or_die(["git", "hook", "run", "sendemail-validate", "-j1", "-a", $target],
    ++		sprintf(__("fatal: %s: rejected by sendemail-validate hook\n" .
    ++			   "warning: no patches were sent\n"),
    ++		        $fn));
      
      	# Any long lines will be automatically fixed if we use a suitable transfer
      	# encoding.
     
      ## t/t9001-send-email.sh ##
    +@@ t/t9001-send-email.sh: test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
    + 	test_path_is_file my-hooks.ran &&
    + 	cat >expect <<-EOF &&
    + 	fatal: longline.patch: rejected by sendemail-validate hook
    +-	fatal: command '"'"'$PWD/my-hooks/sendemail-validate'"'"' died with exit code 1
    + 	warning: no patches were sent
    + 	EOF
    + 	test_cmp expect actual
    +@@ t/t9001-send-email.sh: test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
    + 	test_path_is_file my-hooks.ran &&
    + 	cat >expect <<-EOF &&
    + 	fatal: longline.patch: rejected by sendemail-validate hook
    +-	fatal: command '"'"'$PWD/my-hooks/sendemail-validate'"'"' died with exit code 1
    + 	warning: no patches were sent
    + 	EOF
    + 	test_cmp expect actual
     @@ t/t9001-send-email.sh: test_expect_success $PREREQ 'invoke hook' '
      	mkdir -p .git/hooks &&
      
36:  7f05b25392 ! 35:  d2a477d9e3 run-command: stop thinking about hooks
    @@ hook.c: static int should_include_hookdir(const char *path, enum hookdir_opt cfg
     +}
     +
     +
    - struct list_head* hook_list(const struct strbuf* hookname)
    + struct list_head* hook_list(const char* hookname)
      {
      	struct strbuf hook_key = STRBUF_INIT;
    -@@ hook.c: struct list_head* hook_list(const struct strbuf* hookname)
    +@@ hook.c: struct list_head* hook_list(const char* hookname)
      	git_config(hook_config_lookup, &cb_data);
      
      	if (have_git_dir()) {
    --		const char *legacy_hook_path = find_hook(hookname->buf);
    -+		const char *legacy_hook_path = find_legacy_hook(hookname->buf);
    +-		const char *legacy_hook_path = find_hook(hookname);
    ++		const char *legacy_hook_path = find_legacy_hook(hookname);
      
      		/* Unconditionally add legacy hook, but annotate it. */
      		if (legacy_hook_path) {
37:  e9b1f847f2 <  -:  ---------- docs: unify githooks and git-hook manpages
 -:  ---------- > 36:  62a3e3b419 doc: clarify fsmonitor-watchman specification
 -:  ---------- > 37:  5c864de1aa docs: link githooks and git-hook manpages
-- 
2.31.1.818.g46aad6cb9e-goog
Previous: Ævar Arnfjörð BjarmasonNext: Emily Shaffer
Message 118 of 479 in “config-based hooks”
  1. 00/37 config-based hooksEmily Shaffer, Mar 11, 2021
  2. 04/37 hook: include hookdir hook in listEmily Shaffer, Mar 11, 2021
  3. Ævar Arnfjörð BjarmasonMar 12, 2021
  4. Emily ShafferMar 24, 2021
  5. Junio C HamanoMar 24, 2021
  6. Eric SunshineMar 24, 2021
  7. Emily ShafferMar 24, 2021
  8. 01/37 doc: propose hooks managed by the configEmily Shaffer, Mar 11, 2021
  9. 05/37 hook: teach hook.runHookDirEmily Shaffer, Mar 11, 2021
  10. Ævar Arnfjörð BjarmasonMar 12, 2021
  11. Emily ShafferMar 24, 2021
  12. Ævar Arnfjörð BjarmasonMar 24, 2021
  13. 07/37 parse-options: parse into strvecEmily Shaffer, Mar 11, 2021
  14. Ævar Arnfjörð BjarmasonMar 12, 2021
  15. Emily ShafferMar 24, 2021
  16. 09/37 hook: introduce hook_exists()Emily Shaffer, Mar 11, 2021
  17. 11/37 run-command: allow stdin for run_processes_parallelEmily Shaffer, Mar 11, 2021
  18. 02/37 hook: scaffolding for git-hook subcommandEmily Shaffer, Mar 11, 2021
  19. 08/37 hook: add 'run' subcommandEmily Shaffer, Mar 11, 2021
  20. Ævar Arnfjörð BjarmasonMar 12, 2021
  21. Emily ShafferMar 24, 2021
  22. Junio C HamanoMar 12, 2021
  23. 10/37 hook: support passing stdin to hooksEmily Shaffer, Mar 11, 2021
  24. Ævar Arnfjörð BjarmasonMar 12, 2021
  25. Junio C HamanoMar 12, 2021
  26. 03/37 hook: add list commandEmily Shaffer, Mar 11, 2021
  27. Ævar Arnfjörð BjarmasonMar 12, 2021
  28. Emily ShafferMar 24, 2021
  29. Ævar Arnfjörð BjarmasonMar 25, 2021
  30. 13/37 hook: allow specifying working directory for hooksEmily Shaffer, Mar 11, 2021
  31. 06/37 hook: implement hookcmd.<name>.skipEmily Shaffer, Mar 11, 2021
  32. Ævar Arnfjörð BjarmasonMar 12, 2021
  33. 14/37 run-command: add stdin callback for parallelizationEmily Shaffer, Mar 11, 2021
  34. 18/37 commit: use config-based hooksEmily Shaffer, Mar 11, 2021
  35. Junio C HamanoMar 12, 2021
  36. 12/37 hook: allow parallel hook executionEmily Shaffer, Mar 11, 2021
  37. 15/37 hook: provide stdin by string_list or callbackEmily Shaffer, Mar 11, 2021
  38. 17/37 hooks: allow callers to capture outputEmily Shaffer, Mar 11, 2021
  39. Ævar Arnfjörð BjarmasonMar 12, 2021
  40. Emily ShafferMar 24, 2021
  41. 21/37 gc: use hook library for pre-auto-gc hookEmily Shaffer, Mar 11, 2021
  42. 19/37 am: convert applypatch hooks to use configEmily Shaffer, Mar 11, 2021
  43. Junio C HamanoMar 12, 2021
  44. Emily ShafferMar 29, 2021
  45. 20/37 merge: use config-based hooks for post-merge hookEmily Shaffer, Mar 11, 2021
  46. 16/37 run-command: allow capturing of collated outputEmily Shaffer, Mar 11, 2021
  47. 22/37 rebase: teach pre-rebase to use hook.hEmily Shaffer, Mar 11, 2021
  48. Junio C HamanoMar 12, 2021
  49. 26/37 hooks: convert 'post-checkout' hook to hook libraryEmily Shaffer, Mar 11, 2021
  50. 23/37 read-cache: convert post-index-change hook to use configEmily Shaffer, Mar 11, 2021
  51. Junio C HamanoMar 12, 2021
  52. Emily ShafferMar 29, 2021
  53. 29/37 reference-transaction: look for hooks in configEmily Shaffer, Mar 11, 2021
  54. 28/37 transport: convert pre-push hook to use configEmily Shaffer, Mar 11, 2021
  55. 27/37 hook: convert 'post-rewrite' hook to configEmily Shaffer, Mar 11, 2021
  56. 30/37 receive-pack: convert 'update' hook to hook.hEmily Shaffer, Mar 11, 2021
  57. 24/37 receive-pack: convert push-to-checkout hook to hook.hEmily Shaffer, Mar 11, 2021
  58. Junio C HamanoMar 12, 2021
  59. Emily ShafferMar 29, 2021
  60. Junio C HamanoMar 30, 2021
  61. 25/37 git-p4: use 'git hook' to run hooksEmily Shaffer, Mar 11, 2021
  62. 32/37 post-update: use hook.h libraryEmily Shaffer, Mar 11, 2021
  63. Ævar Arnfjörð BjarmasonMar 12, 2021
  64. Emily ShafferMar 30, 2021
  65. 31/37 proc-receive: acquire hook list from hook.hEmily Shaffer, Mar 11, 2021
  66. 34/37 bugreport: use hook_exists instead of find_hookEmily Shaffer, Mar 11, 2021
  67. 33/37 receive-pack: convert receive hooks to hook.hEmily Shaffer, Mar 11, 2021
  68. 35/37 git-send-email: use 'git hook run' for 'sendemail-validate'Emily Shaffer, Mar 11, 2021
  69. Ævar Arnfjörð BjarmasonMar 12, 2021
  70. Emily ShafferMar 30, 2021
  71. Emily ShafferMar 31, 2021
  72. Junio C HamanoMar 31, 2021
  73. Emily ShafferApr 1, 2021
  74. Junio C HamanoApr 1, 2021
  75. 0/2 git-send-email: refactor duplicate $? checks into a functionÆvar Arnfjörð Bjarmason, Apr 2, 2021
  76. 2/2 git-send-email: refactor duplicate $? checks into a functionÆvar Arnfjörð Bjarmason, Apr 2, 2021
  77. Junio C HamanoApr 2, 2021
  78. 1/2 git-send-email: replace "map" in void context with "for"Ævar Arnfjörð Bjarmason, Apr 2, 2021
  79. Junio C HamanoApr 2, 2021
  80. Emily ShafferApr 2, 2021
  81. 0/4 refactor duplicate $? checks into a function + improve errorsÆvar Arnfjörð Bjarmason, Apr 4, 2021
  82. 1/4 git-send-email: replace "map" in void context with "for"Ævar Arnfjörð Bjarmason, Apr 4, 2021
  83. 3/4 git-send-email: test full --validate outputÆvar Arnfjörð Bjarmason, Apr 4, 2021
  84. 4/4 git-send-email: improve --validate error outputÆvar Arnfjörð Bjarmason, Apr 4, 2021
  85. Junio C HamanoApr 5, 2021
  86. 2/4 git-send-email: refactor duplicate $? checks into a functionÆvar Arnfjörð Bjarmason, Apr 4, 2021
  87. Junio C HamanoApr 5, 2021
  88. Junio C HamanoApr 5, 2021
  89. Junio C HamanoApr 8, 2021
  90. Junio C HamanoApr 8, 2021
  91. Ævar Arnfjörð BjarmasonApr 8, 2021
  92. Junio C HamanoApr 9, 2021
  93. Emily ShafferMay 3, 2021
  94. 0/3 refactor duplicate $? checks into a function + improve errorsÆvar Arnfjörð Bjarmason, Apr 6, 2021
  95. 1/3 git-send-email: test full --validate outputÆvar Arnfjörð Bjarmason, Apr 6, 2021
  96. 2/3 git-send-email: refactor duplicate $? checks into a functionÆvar Arnfjörð Bjarmason, Apr 6, 2021
  97. 3/3 git-send-email: improve --validate error outputÆvar Arnfjörð Bjarmason, Apr 6, 2021
  98. Junio C HamanoApr 6, 2021
  99. Emily ShafferMar 12, 2021
  100. 36/37 run-command: stop thinking about hooksEmily Shaffer, Mar 11, 2021
  101. Ævar Arnfjörð BjarmasonMar 12, 2021
  102. Emily ShafferMar 30, 2021
  103. 37/37 docs: unify githooks and git-hook manpagesEmily Shaffer, Mar 11, 2021
  104. Ævar Arnfjörð BjarmasonMar 12, 2021
  105. Emily ShafferMar 30, 2021
  106. Junio C HamanoApr 7, 2021
  107. Jeff HostetlerApr 8, 2021
  108. Junio C HamanoApr 8, 2021
  109. Emily ShafferApr 8, 2021
  110. Junio C HamanoApr 9, 2021
  111. Junio C HamanoMar 11, 2021
  112. Emily ShafferMar 12, 2021
  113. Ævar Arnfjörð BjarmasonMar 12, 2021
  114. Emily ShafferMar 17, 2021
  115. Emily ShafferMar 17, 2021
  116. Ævar Arnfjörð BjarmasonMar 12, 2021
  117. Ævar Arnfjörð BjarmasonMar 25, 2021
  118. 00/37 propose config-based hooksEmily Shaffer, May 27, 2021
  119. 01/37 doc: propose hooks managed by the configEmily Shaffer, May 27, 2021
  120. 02/37 hook: introduce git-hook subcommandEmily Shaffer, May 27, 2021
  121. Junio C HamanoMay 27, 2021
  122. 03/37 hook: include hookdir hook in listEmily Shaffer, May 27, 2021
  123. 04/37 hook: teach hook.runHookDirEmily Shaffer, May 27, 2021
  124. 05/37 hook: implement hookcmd.<name>.skipEmily Shaffer, May 27, 2021
  125. 06/37 parse-options: parse into strvecEmily Shaffer, May 27, 2021
  126. 08/37 hook: introduce hook_exists()Emily Shaffer, May 27, 2021
  127. 07/37 hook: add 'run' subcommandEmily Shaffer, May 27, 2021
  128. Ævar Arnfjörð BjarmasonJun 3, 2021
  129. Junio C HamanoJun 3, 2021
  130. 09/37 hook: support passing stdin to hooksEmily Shaffer, May 27, 2021
  131. 10/37 run-command: allow stdin for run_processes_parallelEmily Shaffer, May 27, 2021
  132. 11/37 hook: allow parallel hook executionEmily Shaffer, May 27, 2021
  133. 12/37 hook: allow specifying working directory for hooksEmily Shaffer, May 27, 2021
  134. 13/37 run-command: add stdin callback for parallelizationEmily Shaffer, May 27, 2021
  135. 14/37 hook: provide stdin by string_list or callbackEmily Shaffer, May 27, 2021
  136. 15/37 run-command: allow capturing of collated outputEmily Shaffer, May 27, 2021
  137. 16/37 hooks: allow callers to capture outputEmily Shaffer, May 27, 2021
  138. 17/37 commit: use config-based hooksEmily Shaffer, May 27, 2021
  139. 18/37 am: convert applypatch hooks to use configEmily Shaffer, May 27, 2021
  140. 19/37 merge: use config-based hooks for post-merge hookEmily Shaffer, May 27, 2021
  141. 20/37 gc: use hook library for pre-auto-gc hookEmily Shaffer, May 27, 2021
  142. 21/37 rebase: teach pre-rebase to use hook.hEmily Shaffer, May 27, 2021
  143. 22/37 read-cache: convert post-index-change hook to use configEmily Shaffer, May 27, 2021
  144. Ævar Arnfjörð BjarmasonMay 27, 2021
  145. Taylor BlauMay 28, 2021
  146. Felipe ContrerasMay 31, 2021
  147. 23/37 receive-pack: convert push-to-checkout hook to hook.hEmily Shaffer, May 27, 2021
  148. 24/37 git-p4: use 'git hook' to run hooksEmily Shaffer, May 27, 2021
  149. 25/37 hooks: convert 'post-checkout' hook to hook libraryEmily Shaffer, May 27, 2021
  150. 26/37 hook: convert 'post-rewrite' hook to configEmily Shaffer, May 27, 2021
  151. 27/37 transport: convert pre-push hook to use configEmily Shaffer, May 27, 2021
  152. 28/37 reference-transaction: look for hooks in configEmily Shaffer, May 27, 2021
  153. 29/37 receive-pack: convert 'update' hook to hook.hEmily Shaffer, May 27, 2021
  154. 30/37 proc-receive: acquire hook list from hook.hEmily Shaffer, May 27, 2021
  155. 31/37 post-update: use hook.h libraryEmily Shaffer, May 27, 2021
  156. Ævar Arnfjörð BjarmasonJun 14, 2021
  157. 32/37 receive-pack: convert receive hooks to hook.hEmily Shaffer, May 27, 2021
  158. 33/37 bugreport: use hook_exists instead of find_hookEmily Shaffer, May 27, 2021
  159. 34/37 git-send-email: use 'git hook run' for 'sendemail-validate'Emily Shaffer, May 27, 2021
  160. Ævar Arnfjörð BjarmasonMay 27, 2021
  161. 35/37 run-command: stop thinking about hooksEmily Shaffer, May 27, 2021
  162. 36/37 doc: clarify fsmonitor-watchman specificationEmily Shaffer, May 27, 2021
  163. 37/37 docs: link githooks and git-hook manpagesEmily Shaffer, May 27, 2021
  164. Ævar Arnfjörð BjarmasonJun 3, 2021
  165. Ævar Arnfjörð BjarmasonMay 27, 2021
  166. Ævar Arnfjörð BjarmasonMay 27, 2021
  167. Felipe ContrerasMay 27, 2021
  168. 00/31 minimal restart of "config-based-hooks"Ævar Arnfjörð Bjarmason, May 28, 2021
  169. 01/31 hooks tests: don't leave "actual" nonexisting on failureÆvar Arnfjörð Bjarmason, May 28, 2021
  170. 02/31 gc tests: add a test for the "pre-auto-gc" hookÆvar Arnfjörð Bjarmason, May 28, 2021
  171. 03/31 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, May 28, 2021
  172. 04/31 run-command.h: move find_hook() to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  173. 05/31 hook.c: add a hook_exists() wrapper and use it in bugreport.cÆvar Arnfjörð Bjarmason, May 28, 2021
  174. 06/31 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, May 28, 2021
  175. 08/31 am: convert applypatch hooks to use configÆvar Arnfjörð Bjarmason, May 28, 2021
  176. 07/31 rebase: teach pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  177. 09/31 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, May 28, 2021
  178. 10/31 merge: use config-based hooks for post-merge hookÆvar Arnfjörð Bjarmason, May 28, 2021
  179. 11/31 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, May 28, 2021
  180. 12/31 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, May 28, 2021
  181. 13/31 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, May 28, 2021
  182. 14/31 commit: use hook.h to execute hooksÆvar Arnfjörð Bjarmason, May 28, 2021
  183. 15/31 read-cache: convert post-index-change hook to use configÆvar Arnfjörð Bjarmason, May 28, 2021
  184. 16/31 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  185. Felipe ContrerasJun 2, 2021
  186. 19/31 am: convert 'post-rewrite' hook to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  187. 17/31 run-command: allow stdin for run_processes_parallelÆvar Arnfjörð Bjarmason, May 28, 2021
  188. 18/31 hook: support passing stdin to hooksÆvar Arnfjörð Bjarmason, May 28, 2021
  189. 20/31 run-command: add stdin callback for parallelizationÆvar Arnfjörð Bjarmason, May 28, 2021
  190. 21/31 hook: provide stdin by string_list or callbackÆvar Arnfjörð Bjarmason, May 28, 2021
  191. Felipe ContrerasJun 2, 2021
  192. 22/31 hook: convert 'post-rewrite' hook in sequencer.c to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  193. 23/31 transport: convert pre-push hook to use configÆvar Arnfjörð Bjarmason, May 28, 2021
  194. 24/31 reference-transaction: use hook.h to run hooksÆvar Arnfjörð Bjarmason, May 28, 2021
  195. 25/31 run-command: allow capturing of collated outputÆvar Arnfjörð Bjarmason, May 28, 2021
  196. 26/31 hooks: allow callers to capture outputÆvar Arnfjörð Bjarmason, May 28, 2021
  197. 28/31 post-update: use hook.h libraryÆvar Arnfjörð Bjarmason, May 28, 2021
  198. 27/31 receive-pack: convert 'update' hook to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  199. Felipe ContrerasJun 2, 2021
  200. 29/31 receive-pack: convert receive hooks to hook.hÆvar Arnfjörð Bjarmason, May 28, 2021
  201. 30/31 hooks: fix a TOCTOU in "did we run a hook?" heuristicÆvar Arnfjörð Bjarmason, May 28, 2021
  202. Bagas SanjayaMay 28, 2021
  203. Ævar Arnfjörð BjarmasonMay 28, 2021
  204. 31/31 hook-list.h: add a generated list of hooks, like config-list.hÆvar Arnfjörð Bjarmason, May 28, 2021
  205. Ævar Arnfjörð BjarmasonJun 1, 2021
  206. Emily ShafferJun 1, 2021
  207. Derrick StoleeJun 1, 2021
  208. Felipe ContrerasJun 2, 2021
  209. Ævar Arnfjörð BjarmasonJun 2, 2021
  210. Ævar Arnfjörð BjarmasonJun 2, 2021
  211. Felipe ContrerasJun 2, 2021
  212. Ævar Arnfjörð BjarmasonJun 2, 2021
  213. Ævar Arnfjörð BjarmasonJun 2, 2021
  214. Felipe ContrerasJun 25, 2021
  215. 00/30 Minimal restart of "config-based-hooks"Ævar Arnfjörð Bjarmason, Jun 14, 2021
  216. 03/30 hook.c: add a hook_exists() wrapper and use it in bugreport.cÆvar Arnfjörð Bjarmason, Jun 14, 2021
  217. 01/30 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Jun 14, 2021
  218. Emily ShafferJun 14, 2021
  219. Ævar Arnfjörð BjarmasonJun 15, 2021
  220. Emily ShafferJun 18, 2021
  221. Ævar Arnfjörð BjarmasonJun 20, 2021
  222. Junio C HamanoJun 21, 2021
  223. Emily ShafferJun 22, 2021
  224. Junio C HamanoJun 29, 2021
  225. Felipe ContrerasJun 25, 2021
  226. Felipe ContrerasJun 25, 2021
  227. 02/30 run-command.h: move find_hook() to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  228. 12/30 commit: use hook.h to execute hooksÆvar Arnfjörð Bjarmason, Jun 14, 2021
  229. 13/30 read-cache: convert post-index-change hook to use configÆvar Arnfjörð Bjarmason, Jun 14, 2021
  230. Felipe ContrerasJun 25, 2021
  231. 09/30 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Jun 14, 2021
  232. 07/30 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Jun 14, 2021
  233. 06/30 am: convert applypatch hooks to use configÆvar Arnfjörð Bjarmason, Jun 14, 2021
  234. 05/30 rebase: teach pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  235. 04/30 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Jun 14, 2021
  236. Emily ShafferJun 14, 2021
  237. 08/30 merge: use config-based hooks for post-merge hookÆvar Arnfjörð Bjarmason, Jun 14, 2021
  238. 11/30 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Jun 14, 2021
  239. 16/30 run-command: allow stdin for run_processes_parallelÆvar Arnfjörð Bjarmason, Jun 14, 2021
  240. 17/30 hook: support passing stdin to hooksÆvar Arnfjörð Bjarmason, Jun 14, 2021
  241. 10/30 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Jun 14, 2021
  242. 26/30 receive-pack: convert 'update' hook to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  243. 15/30 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Jun 14, 2021
  244. Felipe ContrerasJun 25, 2021
  245. 29/30 hooks: fix a TOCTOU in "did we run a hook?" heuristicÆvar Arnfjörð Bjarmason, Jun 14, 2021
  246. 14/30 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  247. 18/30 am: convert 'post-rewrite' hook to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  248. 23/30 reference-transaction: use hook.h to run hooksÆvar Arnfjörð Bjarmason, Jun 14, 2021
  249. 20/30 hook: provide stdin by string_list or callbackÆvar Arnfjörð Bjarmason, Jun 14, 2021
  250. 22/30 transport: convert pre-push hook to use configÆvar Arnfjörð Bjarmason, Jun 14, 2021
  251. 19/30 run-command: add stdin callback for parallelizationÆvar Arnfjörð Bjarmason, Jun 14, 2021
  252. 21/30 hook: convert 'post-rewrite' hook in sequencer.c to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  253. 30/30 hook-list.h: add a generated list of hooks, like config-list.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  254. Ævar Arnfjörð BjarmasonJun 15, 2021
  255. 27/30 post-update: use hook.h libraryÆvar Arnfjörð Bjarmason, Jun 14, 2021
  256. 25/30 hooks: allow callers to capture outputÆvar Arnfjörð Bjarmason, Jun 14, 2021
  257. 24/30 run-command: allow capturing of collated outputÆvar Arnfjörð Bjarmason, Jun 14, 2021
  258. 28/30 receive-pack: convert receive hooks to hook.hÆvar Arnfjörð Bjarmason, Jun 14, 2021
  259. Emily ShafferJun 14, 2021
  260. Junio C HamanoJun 16, 2021
  261. 00/27 Base for "config-based-hooks"Ævar Arnfjörð Bjarmason, Jun 17, 2021
  262. 01/27 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Jun 17, 2021
  263. 04/27 am: convert applypatch hooks to use configÆvar Arnfjörð Bjarmason, Jun 17, 2021
  264. 06/27 merge: use config-based hooks for post-merge hookÆvar Arnfjörð Bjarmason, Jun 17, 2021
  265. 02/27 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Jun 17, 2021
  266. Emily ShafferJul 22, 2021
  267. 03/27 rebase: teach pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  268. 05/27 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Jun 17, 2021
  269. 09/27 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Jun 17, 2021
  270. 07/27 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Jun 17, 2021
  271. Emily ShafferJul 2, 2021
  272. 08/27 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Jun 17, 2021
  273. 10/27 commit: use hook.h to execute hooksÆvar Arnfjörð Bjarmason, Jun 17, 2021
  274. 12/27 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  275. 13/27 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Jun 17, 2021
  276. 14/27 run-command: allow stdin for run_processes_parallelÆvar Arnfjörð Bjarmason, Jun 17, 2021
  277. 15/27 hook: support passing stdin to hooksÆvar Arnfjörð Bjarmason, Jun 17, 2021
  278. 11/27 read-cache: convert post-index-change hook to use configÆvar Arnfjörð Bjarmason, Jun 17, 2021
  279. 16/27 am: convert 'post-rewrite' hook to hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  280. 17/27 run-command: add stdin callback for parallelizationÆvar Arnfjörð Bjarmason, Jun 17, 2021
  281. 18/27 hook: provide stdin by string_list or callbackÆvar Arnfjörð Bjarmason, Jun 17, 2021
  282. 19/27 hook: convert 'post-rewrite' hook in sequencer.c to hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  283. 20/27 transport: convert pre-push hook to use configÆvar Arnfjörð Bjarmason, Jun 17, 2021
  284. 22/27 run-command: allow capturing of collated outputÆvar Arnfjörð Bjarmason, Jun 17, 2021
  285. 21/27 reference-transaction: use hook.h to run hooksÆvar Arnfjörð Bjarmason, Jun 17, 2021
  286. 23/27 hooks: allow callers to capture outputÆvar Arnfjörð Bjarmason, Jun 17, 2021
  287. 24/27 receive-pack: convert 'update' hook to hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  288. 25/27 post-update: use hook.h libraryÆvar Arnfjörð Bjarmason, Jun 17, 2021
  289. 26/27 receive-pack: convert receive hooks to hook.hÆvar Arnfjörð Bjarmason, Jun 17, 2021
  290. 27/27 hooks: fix a TOCTOU in "did we run a hook?" heuristicÆvar Arnfjörð Bjarmason, Jun 17, 2021
  291. Emily ShafferJun 18, 2021
  292. 0/9 config-based hooks restartedEmily Shaffer, Jul 15, 2021
  293. 1/9 hook: run a list of hooks insteadEmily Shaffer, Jul 15, 2021
  294. 2/9 hook: allow parallel hook executionEmily Shaffer, Jul 15, 2021
  295. Ævar Arnfjörð BjarmasonJul 16, 2021
  296. Emily ShafferJul 22, 2021
  297. Ævar Arnfjörð BjarmasonJul 23, 2021
  298. 5/9 hook: allow running non-native hooksEmily Shaffer, Jul 15, 2021
  299. 4/9 hook: treat hookdir hook speciallyEmily Shaffer, Jul 15, 2021
  300. Ævar Arnfjörð BjarmasonJul 16, 2021
  301. Emily ShafferJul 22, 2021
  302. Ævar Arnfjörð BjarmasonJul 23, 2021
  303. Felipe ContrerasJul 23, 2021
  304. Eric SunshineJul 23, 2021
  305. Felipe ContrerasJul 23, 2021
  306. 3/9 hook: introduce "git hook list"Emily Shaffer, Jul 15, 2021
  307. Ævar Arnfjörð BjarmasonJul 16, 2021
  308. Emily ShafferJul 22, 2021
  309. Ævar Arnfjörð BjarmasonJul 23, 2021
  310. 6/9 hook: include hooks from the configEmily Shaffer, Jul 15, 2021
  311. Ævar Arnfjörð BjarmasonJul 16, 2021
  312. Emily ShafferJul 22, 2021
  313. Ævar Arnfjörð BjarmasonJul 23, 2021
  314. 8/9 hook: teach 'hookcmd' config to alias hook scriptsEmily Shaffer, Jul 15, 2021
  315. Ævar Arnfjörð BjarmasonJul 16, 2021
  316. Emily ShafferJul 22, 2021
  317. Ævar Arnfjörð BjarmasonJul 23, 2021
  318. Emily ShafferAug 4, 2021
  319. Ævar Arnfjörð BjarmasonAug 5, 2021
  320. Emily ShafferAug 5, 2021
  321. Ævar Arnfjörð BjarmasonAug 5, 2021
  322. Emily ShafferAug 6, 2021
  323. Jonathan TanAug 4, 2021
  324. 7/9 hook: allow out-of-repo 'git hook' invocationsEmily Shaffer, Jul 15, 2021
  325. Ævar Arnfjörð BjarmasonJul 16, 2021
  326. Emily ShafferJul 22, 2021
  327. Ævar Arnfjörð BjarmasonJul 23, 2021
  328. 9/9 hook: implement hookcmd.<name>.skipEmily Shaffer, Jul 15, 2021
  329. Emily ShafferJul 28, 2021
  330. 00/36 Run hooks via "git run hook" & hook libraryÆvar Arnfjörð Bjarmason, Aug 3, 2021
  331. 01/36 Makefile: mark "check" target as .PHONYÆvar Arnfjörð Bjarmason, Aug 3, 2021
  332. Emily ShafferAug 20, 2021
  333. 02/36 Makefile: stop hardcoding {command,config}-list.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  334. Emily ShafferAug 20, 2021
  335. Ævar Arnfjörð BjarmasonAug 24, 2021
  336. 03/36 Makefile: remove an out-of-date commentÆvar Arnfjörð Bjarmason, Aug 3, 2021
  337. Emily ShafferAug 20, 2021
  338. 04/36 hook.[ch]: move find_hook() to this new libraryÆvar Arnfjörð Bjarmason, Aug 3, 2021
  339. Emily ShafferAug 20, 2021
  340. 05/36 hook.c: add a hook_exists() wrapper and use it in bugreport.cÆvar Arnfjörð Bjarmason, Aug 3, 2021
  341. Emily ShafferAug 20, 2021
  342. 07/36 hook-list.h: add a generated list of hooks, like config-list.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  343. 06/36 hook.c users: use "hook_exists()" insted of "find_hook()"Ævar Arnfjörð Bjarmason, Aug 3, 2021
  344. Emily ShafferAug 20, 2021
  345. 08/36 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Aug 3, 2021
  346. Ævar Arnfjörð BjarmasonAug 4, 2021
  347. 12/36 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Aug 3, 2021
  348. 09/36 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Aug 3, 2021
  349. 10/36 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  350. 11/36 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  351. 13/36 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  352. 14/36 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Aug 3, 2021
  353. 15/36 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Aug 3, 2021
  354. 16/36 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Aug 3, 2021
  355. 17/36 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  356. 18/36 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  357. 19/36 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  358. 20/36 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Aug 3, 2021
  359. 21/36 run-command: allow stdin for run_processes_parallelÆvar Arnfjörð Bjarmason, Aug 3, 2021
  360. 22/36 hook: support passing stdin to hooksÆvar Arnfjörð Bjarmason, Aug 3, 2021
  361. 24/36 run-command: add stdin callback for parallelizationÆvar Arnfjörð Bjarmason, Aug 3, 2021
  362. 23/36 am: convert 'post-rewrite' hook to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  363. 26/36 hook: convert 'post-rewrite' hook in sequencer.c to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  364. 25/36 hook: provide stdin by string_list or callbackÆvar Arnfjörð Bjarmason, Aug 3, 2021
  365. 27/36 transport: convert pre-push hook to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  366. 28/36 hook tests: test for exact "pre-push" hook inputÆvar Arnfjörð Bjarmason, Aug 3, 2021
  367. Emily ShafferAug 20, 2021
  368. 29/36 hook tests: use a modern style for "pre-push" testsÆvar Arnfjörð Bjarmason, Aug 3, 2021
  369. Emily ShafferAug 20, 2021
  370. 30/36 reference-transaction: use hook.h to run hooksÆvar Arnfjörð Bjarmason, Aug 3, 2021
  371. 32/36 hooks: allow callers to capture outputÆvar Arnfjörð Bjarmason, Aug 3, 2021
  372. 31/36 run-command: allow capturing of collated outputÆvar Arnfjörð Bjarmason, Aug 3, 2021
  373. 33/36 receive-pack: convert 'update' hook to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  374. 36/36 hooks: fix a TOCTOU in "did we run a hook?" heuristicÆvar Arnfjörð Bjarmason, Aug 3, 2021
  375. 34/36 post-update: use hook.h libraryÆvar Arnfjörð Bjarmason, Aug 3, 2021
  376. 35/36 receive-pack: convert receive hooks to hook.hÆvar Arnfjörð Bjarmason, Aug 3, 2021
  377. 0/6 config-based hooks restartedEmily Shaffer, Aug 12, 2021
  378. 1/6 hook: run a list of hooks insteadEmily Shaffer, Aug 12, 2021
  379. Junio C HamanoAug 12, 2021
  380. Emily ShafferAug 16, 2021
  381. 2/6 hook: allow parallel hook executionEmily Shaffer, Aug 12, 2021
  382. Junio C HamanoAug 12, 2021
  383. Emily ShafferAug 16, 2021
  384. 3/6 hook: introduce "git hook list"Emily Shaffer, Aug 12, 2021
  385. Junio C HamanoAug 12, 2021
  386. Emily ShafferAug 17, 2021
  387. 4/6 hook: allow running non-native hooksEmily Shaffer, Aug 12, 2021
  388. Junio C HamanoAug 12, 2021
  389. Emily ShafferAug 18, 2021
  390. Emily ShafferAug 18, 2021
  391. Junio C HamanoAug 18, 2021
  392. 5/6 hook: include hooks from the configEmily Shaffer, Aug 12, 2021
  393. Junio C HamanoAug 12, 2021
  394. Emily ShafferAug 19, 2021
  395. 6/6 hook: allow out-of-repo 'git hook' invocationsEmily Shaffer, Aug 12, 2021
  396. Junio C HamanoAug 12, 2021
  397. Junio C HamanoAug 12, 2021
  398. Emily ShafferAug 16, 2021
  399. 0/6 config-based hooks restartedEmily Shaffer, Aug 19, 2021
  400. 1/6 hook: run a list of hooks insteadEmily Shaffer, Aug 19, 2021
  401. Ævar Arnfjörð BjarmasonAug 24, 2021
  402. Emily ShafferAug 26, 2021
  403. Ævar Arnfjörð BjarmasonAug 27, 2021
  404. 2/6 hook: allow parallel hook executionEmily Shaffer, Aug 19, 2021
  405. Ævar Arnfjörð BjarmasonAug 24, 2021
  406. Eric SunshineAug 24, 2021
  407. Emily ShafferAug 26, 2021
  408. 3/6 hook: introduce "git hook list"Emily Shaffer, Aug 19, 2021
  409. Ævar Arnfjörð BjarmasonAug 24, 2021
  410. Emily ShafferAug 26, 2021
  411. Ævar Arnfjörð BjarmasonAug 24, 2021
  412. Emily ShafferAug 26, 2021
  413. 4/6 hook: allow running non-native hooksEmily Shaffer, Aug 19, 2021
  414. Ævar Arnfjörð BjarmasonAug 24, 2021
  415. Emily ShafferAug 26, 2021
  416. Junio C HamanoAug 27, 2021
  417. 5/6 hook: include hooks from the configEmily Shaffer, Aug 19, 2021
  418. Junio C HamanoAug 19, 2021
  419. Emily ShafferAug 19, 2021
  420. Junio C HamanoAug 19, 2021
  421. Ævar Arnfjörð BjarmasonAug 24, 2021
  422. Emily ShafferAug 31, 2021
  423. 6/6 hook: allow out-of-repo 'git hook' invocationsEmily Shaffer, Aug 19, 2021
  424. Ævar Arnfjörð BjarmasonAug 24, 2021
  425. Randall S. BeckerAug 24, 2021
  426. Ævar Arnfjörð BjarmasonAug 24, 2021
  427. Emily ShafferAug 31, 2021
  428. Ævar Arnfjörð BjarmasonAug 24, 2021
  429. Emily ShafferSep 2, 2021
  430. 0/5 config-based hooks restartedÆvar Arnfjörð Bjarmason, Sep 9, 2021
  431. 1/5 hook: run a list of hooks insteadÆvar Arnfjörð Bjarmason, Sep 9, 2021
  432. fixup! hook: run a list of hooks insteadÆvar Arnfjörð Bjarmason, Sep 9, 2021
  433. 2/5 hook: allow parallel hook executionÆvar Arnfjörð Bjarmason, Sep 9, 2021
  434. 3/5 hook: introduce "git hook list"Ævar Arnfjörð Bjarmason, Sep 9, 2021
  435. 5/5 hook: allow out-of-repo 'git hook' invocationsÆvar Arnfjörð Bjarmason, Sep 9, 2021
  436. 4/5 hook: include hooks from the configÆvar Arnfjörð Bjarmason, Sep 9, 2021
  437. Emily ShafferAug 19, 2021
  438. Emily ShafferAug 19, 2021
  439. Ævar Arnfjörð BjarmasonSep 2, 2021
  440. 00/36 Run hooks via "git run hook" & hook libraryÆvar Arnfjörð Bjarmason, Sep 2, 2021
  441. 01/36 Makefile: mark "check" target as .PHONYÆvar Arnfjörð Bjarmason, Sep 2, 2021
  442. 02/36 Makefile: stop hardcoding {command,config}-list.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  443. 03/36 Makefile: remove an out-of-date commentÆvar Arnfjörð Bjarmason, Sep 2, 2021
  444. 04/36 hook.[ch]: move find_hook() from run-command.c to hook.cÆvar Arnfjörð Bjarmason, Sep 2, 2021
  445. 05/36 hook.c: add a hook_exists() wrapper and use it in bugreport.cÆvar Arnfjörð Bjarmason, Sep 2, 2021
  446. 06/36 hook.c users: use "hook_exists()" instead of "find_hook()"Ævar Arnfjörð Bjarmason, Sep 2, 2021
  447. 07/36 hook-list.h: add a generated list of hooks, like config-list.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  448. 08/36 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Sep 2, 2021
  449. 09/36 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Sep 2, 2021
  450. 10/36 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  451. 11/36 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  452. 12/36 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Sep 2, 2021
  453. 13/36 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  454. 14/36 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Sep 2, 2021
  455. 15/36 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Sep 2, 2021
  456. 16/36 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Sep 2, 2021
  457. 17/36 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  458. 18/36 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  459. 19/36 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  460. 20/36 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Sep 2, 2021
  461. 21/36 run-command: allow stdin for run_processes_parallelÆvar Arnfjörð Bjarmason, Sep 2, 2021
  462. 22/36 hook: support passing stdin to hooksÆvar Arnfjörð Bjarmason, Sep 2, 2021
  463. 23/36 am: convert 'post-rewrite' hook to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  464. 24/36 run-command: add stdin callback for parallelizationÆvar Arnfjörð Bjarmason, Sep 2, 2021
  465. ab/config-based-hooks-N status (was Re: [PATCH v5 24/36] run-command: add stdin callback for parallelization)Ævar Arnfjörð Bjarmason, Oct 6, 2021
  466. Ævar Arnfjörð BjarmasonOct 12, 2021
  467. 25/36 hook: provide stdin by string_list or callbackÆvar Arnfjörð Bjarmason, Sep 2, 2021
  468. 26/36 hook: convert 'post-rewrite' hook in sequencer.c to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  469. 28/36 hook tests: test for exact "pre-push" hook inputÆvar Arnfjörð Bjarmason, Sep 2, 2021
  470. 27/36 transport: convert pre-push hook to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  471. 31/36 run-command: allow capturing of collated outputÆvar Arnfjörð Bjarmason, Sep 2, 2021
  472. 32/36 hooks: allow callers to capture outputÆvar Arnfjörð Bjarmason, Sep 2, 2021
  473. 30/36 reference-transaction: use hook.h to run hooksÆvar Arnfjörð Bjarmason, Sep 2, 2021
  474. 29/36 hook tests: use a modern style for "pre-push" testsÆvar Arnfjörð Bjarmason, Sep 2, 2021
  475. 33/36 receive-pack: convert 'update' hook to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  476. 34/36 post-update: use hook.h libraryÆvar Arnfjörð Bjarmason, Sep 2, 2021
  477. 35/36 receive-pack: convert receive hooks to hook.hÆvar Arnfjörð Bjarmason, Sep 2, 2021
  478. 36/36 hooks: fix a TOCTOU in "did we run a hook?" heuristicÆvar Arnfjörð Bjarmason, Sep 2, 2021
  479. Felipe ContrerasJun 25, 2021

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.