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

[PATCH v4 00/17] hook.[ch]: new library to run hooks + simple hook conversion

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Nov 1, 2021, 18:56 UTC
Message-ID
<cover-v4-00.17-00000000000-20211101T184938Z-avarab@gmail.com>
In-Reply-To
<cover-v3-00.13-00000000000-20211019T231647Z-avarab@gmail.com>

Part 2 of the greater configurable hook saga, starting by converting some existing simple hooks to the new hook.[ch] library and "git hook run" utility. See [1] for the last iteration.

This re-roll should address Junio's comments on v3, i.e. there's now a run_hooks_l() API similar to run_hook_le(). I also simplified the callers of run_hooks(), which is now called run_hooks_opt().

Since no callers needed anything but a one-shot call the memory public memory management API is gone, that'll eventually be needed for a "git hook list" with configurable hooks, but let's introduce those API bits then, not now.

This grew to 17 patches from 13 due to splitting up some commints for ease of review, the overall diffstat got smaller. I picked a function with the same width as the old function, so many of the diffs are now simply a s/run_hook_le(NULL, /run_hooks_l(/.

1. https://lore.kernel.org/git/cover-v3-00.13-00000000000-20211019T231647Z-avarab@gmail.com/
Emily Shaffer (14):
  hook: add 'run' subcommand
  gc: use hook library for pre-auto-gc hook
  am: convert {pre,post}-applypatch to use hook.h
  rebase: convert pre-rebase to use hook.h
  am: convert applypatch-msg to use hook.h
  merge: convert post-merge to use hook.h
  hooks: convert non-worktree 'post-checkout' hook to hook library
  hooks: convert worktree 'post-checkout' hook to hook library
  send-email: use 'git hook run' for 'sendemail-validate'
  git-p4: use 'git hook' to run hooks
  commit: convert {pre-commit,prepare-commit-msg} hook to hook.h
  read-cache: convert post-index-change to use hook.h
  receive-pack: convert push-to-checkout hook to hook.h
  run-command: remove old run_hook_{le,ve}() hook API
Ævar Arnfjörð Bjarmason (3):
  hook API: add a run_hooks() wrapper
  hook API: add a run_hooks_l() wrapper
  git hook run: add an --ignore-missing flag
 .gitignore                 |   1 +
 Documentation/git-hook.txt |  45 +++++++++++++
 Documentation/githooks.txt |   4 ++
 Makefile                   |   1 +
 builtin.h                  |   1 +
 builtin/am.c               |   6 +-
 builtin/checkout.c         |   3 +-
 builtin/clone.c            |   3 +-
 builtin/gc.c               |   3 +-
 builtin/hook.c             |  84 +++++++++++++++++++++++
 builtin/merge.c            |   2 +-
 builtin/rebase.c           |   3 +-
 builtin/receive-pack.c     |   7 +-
 builtin/worktree.c         |  27 +++-----
 command-list.txt           |   1 +
 commit.c                   |  15 +++--
 git-p4.py                  |  70 ++-----------------
 git-send-email.perl        |  22 +++---
 git.c                      |   1 +
 hook.c                     | 131 ++++++++++++++++++++++++++++++++++++
 hook.h                     |  57 ++++++++++++++++
 read-cache.c               |   3 +-
 reset.c                    |   3 +-
 run-command.c              |  32 ---------
 run-command.h              |  17 -----
 t/t1800-hook.sh            | 134 +++++++++++++++++++++++++++++++++++++
 t/t9001-send-email.sh      |   4 +-
 27 files changed, 522 insertions(+), 158 deletions(-)
 create mode 100644 Documentation/git-hook.txt
 create mode 100644 builtin/hook.c
 create mode 100755 t/t1800-hook.sh
Range-diff against v3:
 1:  02fd699e699 !  1:  d97d6734961 hook: add 'run' subcommand
    @@ Commit message
         builtin/{commit-graph,multi-pack-index}.c.
     
         Some of the implementation here, such as a function being named
    -    run_hooks() when it's tasked with running one hook, to using the
    +    run_hooks_opt() when it's tasked with running one hook, to using the
         run_processes_parallel_tr2() API to run with jobs=1 is somewhere
         between a bit odd and and an overkill for the current features of this
         "hook run" command and the hook.[ch] API.
    @@ builtin/hook.c (new)
     +	int i;
     +	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
     +	const char *hook_name;
    -+	const char *hook_path;
     +	struct option run_options[] = {
     +		OPT_END(),
     +	};
    @@ builtin/hook.c (new)
     +	git_config(git_default_config, NULL);
     +
     +	hook_name = argv[0];
    -+	hook_path = find_hook(hook_name);
    -+	if (!hook_path) {
    -+		error("cannot find a hook named %s", hook_name);
    -+		return 1;
    -+	}
    -+
    -+	ret = run_hooks(hook_name, hook_path, &opt);
    -+	run_hooks_opt_clear(&opt);
    ++	opt.error_if_missing = 1;
    ++	ret = run_hooks_opt(hook_name, &opt);
    ++	if (ret < 0) /* error() return */
    ++		ret = 1;
     +	return ret;
     +usage:
     +	usage_with_options(builtin_hook_run_usage, run_options);
    @@ hook.c: int hook_exists(const char *name)
      	return !!find_hook(name);
      }
     +
    -+void run_hooks_opt_clear(struct run_hooks_opt *o)
    -+{
    -+	strvec_clear(&o->env);
    -+	strvec_clear(&o->args);
    -+}
    -+
     +static int pick_next_hook(struct child_process *cp,
     +			  struct strbuf *out,
     +			  void *pp_cb,
    @@ hook.c: int hook_exists(const char *name)
     +	return 0;
     +}
     +
    -+int run_hooks(const char *hook_name, const char *hook_path,
    -+	      struct run_hooks_opt *options)
    ++static void run_hooks_opt_clear(struct run_hooks_opt *options)
    ++{
    ++	strvec_clear(&options->env);
    ++	strvec_clear(&options->args);
    ++}
    ++
    ++int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
     +{
     +	struct hook_cb_data cb_data = {
     +		.rc = 0,
     +		.hook_name = hook_name,
    -+		.hook_path = hook_path,
     +		.options = options,
     +	};
    ++	const char *const hook_path = find_hook(hook_name);
     +	int jobs = 1;
    ++	int ret = 0;
     +
     +	if (!options)
     +		BUG("a struct run_hooks_opt must be provided to run_hooks");
     +
    ++	if (!hook_path && !options->error_if_missing)
    ++		goto cleanup;
    ++
    ++	if (!hook_path) {
    ++		ret = error("cannot find a hook named %s", hook_name);
    ++		goto cleanup;
    ++	}
    ++
    ++	cb_data.hook_path = hook_path;
     +	run_processes_parallel_tr2(jobs,
     +				   pick_next_hook,
     +				   notify_start_failure,
    @@ hook.c: int hook_exists(const char *name)
     +				   &cb_data,
     +				   "hook",
     +				   hook_name);
    -+
    -+	return cb_data.rc;
    ++	ret = cb_data.rc;
    ++cleanup:
    ++	run_hooks_opt_clear(options);
    ++	return ret;
     +}
     
      ## hook.h ##
    @@ hook.h
     +
     +	/* Args to be passed to each hook */
     +	struct strvec args;
    ++
    ++	/* Emit an error if the hook is missing */
    ++	unsigned int error_if_missing:1;
     +};
     +
     +#define RUN_HOOKS_OPT_INIT { \
    @@ hook.h: const char *find_hook(const char *name);
      int hook_exists(const char *hookname);
      
     +/**
    -+ * Clear data from an initialized "struct run_hooks_opt".
    -+ */
    -+void run_hooks_opt_clear(struct run_hooks_opt *o);
    -+
    -+/**
    -+ * Takes an already resolved hook found via find_hook() and runs
    -+ * it. Does not call run_hooks_opt_clear() for you.
    ++ * Takes a `hook_name`, resolves it to a path with find_hook(), and
    ++ * runs the hook for you with the options specified in "struct
    ++ * run_hooks opt". Will free memory associated with the "struct run_hooks_opt".
    ++ *
    ++ * Returns the status code of the run hook, or a negative value on
    ++ * error().
     + */
    -+int run_hooks(const char *hookname, const char *hook_path,
    -+	      struct run_hooks_opt *options);
    ++int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options);
      #endif
     
      ## t/t1800-hook.sh (new) ##
 -:  ----------- >  2:  ca6464f7d5e hook API: add a run_hooks() wrapper
 2:  42cc4d2c3c6 !  3:  173860afca1 gc: use hook library for pre-auto-gc hook
    @@ Commit message
         gc: use hook library for pre-auto-gc hook
     
         Move the pre-auto-gc hook away from run-command.h to and over to the
    -    new hook.h library.
    -
    -    To do this introduce a simple run_hooks_oneshot() wrapper, we'll be
    -    using it extensively for these simple cases of wanting to run a single
    -    hook under a given name, and having it free the memory we allocate for
    -    us.
    +    new hook.h library. This uses the new run_hooks() wrapper.
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
    @@ builtin/gc.c: static int need_to_gc(void)
      		return 0;
      
     -	if (run_hook_le(NULL, "pre-auto-gc", NULL))
    -+	if (run_hooks_oneshot("pre-auto-gc", NULL))
    ++	if (run_hooks("pre-auto-gc"))
      		return 0;
      	return 1;
      }
    -
    - ## hook.c ##
    -@@ hook.c: int run_hooks(const char *hook_name, const char *hook_path,
    - 
    - 	return cb_data.rc;
    - }
    -+
    -+int run_hooks_oneshot(const char *hook_name, struct run_hooks_opt *options)
    -+{
    -+	const char *hook_path;
    -+	struct run_hooks_opt hook_opt_scratch = RUN_HOOKS_OPT_INIT;
    -+	int ret = 0;
    -+
    -+	if (!options)
    -+		options = &hook_opt_scratch;
    -+
    -+	hook_path = find_hook(hook_name);
    -+	if (!hook_path)
    -+		goto cleanup;
    -+
    -+	ret = run_hooks(hook_name, hook_path, options);
    -+cleanup:
    -+	run_hooks_opt_clear(options);
    -+
    -+	return ret;
    -+}
    -
    - ## hook.h ##
    -@@ hook.h: void run_hooks_opt_clear(struct run_hooks_opt *o);
    - /**
    -  * Takes an already resolved hook found via find_hook() and runs
    -  * it. Does not call run_hooks_opt_clear() for you.
    -+ *
    -+ * See run_hooks_oneshot() for the simpler one-shot API.
    -  */
    - int run_hooks(const char *hookname, const char *hook_path,
    - 	      struct run_hooks_opt *options);
    -+
    -+/**
    -+ * Calls find_hook() on your "hook_name" and runs the hooks (if any)
    -+ * with run_hooks().
    -+ *
    -+ * If "options" is provided calls run_hooks_opt_clear() on it for
    -+ * you. If "options" is NULL the default options from
    -+ * RUN_HOOKS_OPT_INIT will be used.
    -+ */
    -+int run_hooks_oneshot(const char *hook_name, struct run_hooks_opt *options);
    -+
    - #endif
 4:  b26cef24f39 !  4:  80a2171ddaf am: convert applypatch to use hook.h
    @@ Metadata
     Author: Emily Shaffer <emilyshaffer@google.com>
     
      ## Commit message ##
    -    am: convert applypatch to use hook.h
    +    am: convert {pre,post}-applypatch to use hook.h
     
    -    Teach pre-applypatch, post-applypatch, and applypatch-msg to use the
    -    hook.h library instead of the run-command.h library.
    +    Teach pre-applypatch and post-applypatch to use the hook.h library
    +    instead of the run-command.h library.
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/am.c ##
    -@@ builtin/am.c: static void am_destroy(const struct am_state *state)
    - static int run_applypatch_msg_hook(struct am_state *state)
    - {
    - 	int ret;
    -+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
    - 
    - 	assert(state->msg);
    --	ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
    -+	strvec_push(&opt.args, am_path(state, "final-commit"));
    -+	ret = run_hooks_oneshot("applypatch-msg", &opt);
    - 
    - 	if (!ret) {
    - 		FREE_AND_NULL(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;
      
     -	if (run_hook_le(NULL, "pre-applypatch", NULL))
    -+	if (run_hooks_oneshot("pre-applypatch", NULL))
    ++	if (run_hooks("pre-applypatch"))
      		exit(1);
      
      	if (write_cache_as_tree(&tree, 0, NULL))
    @@ builtin/am.c: static void do_commit(const struct am_state *state)
      	}
      
     -	run_hook_le(NULL, "post-applypatch", NULL);
    -+	run_hooks_oneshot("post-applypatch", NULL);
    ++	run_hooks("post-applypatch");
      
      	strbuf_release(&sb);
      }
 -:  ----------- >  5:  74f459db287 hook API: add a run_hooks_l() wrapper
 3:  cbbfd77a4f6 !  6:  1fd70c0e88a rebase: convert pre-rebase to use hook.h
    @@ Commit message
         Move the pre-rebase hook away from run-command.h to and over to the
         new hook.h library.
     
    +    Since this hook needs arguments introduce a run_hooksl() wrapper, like
    +    run_hooks(), but it takes varargs.
    +
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    @@ builtin/rebase.c
      #define DEFAULT_REFLOG_ACTION "rebase"
      
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 	int reschedule_failed_exec = -1;
    - 	int allow_preemptive_ff = 1;
    - 	int preserve_merges_selected = 0;
    -+	struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
    - 	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)
    - 	}
      
      	/* If a hook exists, give it a chance to interrupt*/
    -+	strvec_push(&hook_opt.args, options.upstream_arg);
    -+	if (argc)
    -+		strvec_push(&hook_opt.args, argv[0]);
      	if (!ok_to_skip_pre_rebase &&
     -	    run_hook_le(NULL, "pre-rebase", options.upstream_arg,
    --			argc ? argv[0] : NULL, NULL))
    -+	    run_hooks_oneshot("pre-rebase", &hook_opt))
    ++	    run_hooks_l("pre-rebase", options.upstream_arg,
    + 			argc ? argv[0] : NULL, NULL))
      		die(_("The pre-rebase hook refused to rebase."));
      
    - 	if (options.flags & REBASE_DIFFSTAT) {
 -:  ----------- >  7:  ccba3ddf52e am: convert applypatch-msg to use hook.h
 6:  7a9fd8627cd !  8:  2c23e8645ec merge: convert post-merge to use hook.h
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/merge.c ##
    -@@ builtin/merge.c: static void finish(struct commit *head_commit,
    - 		   const struct object_id *new_head, const char *msg)
    - {
    - 	struct strbuf reflog_message = STRBUF_INIT;
    -+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
    - 	const struct object_id *head = &head_commit->object.oid;
    - 
    - 	if (!msg)
     @@ 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);
    -+	strvec_push(&opt.args, squash ? "1" : "0");
    -+	run_hooks_oneshot("post-merge", &opt);
    ++	run_hooks_l("post-merge", squash ? "1" : "0", NULL);
      
      	apply_autostash(git_path_merge_autostash(the_repository));
      	strbuf_release(&reflog_message);
 -:  ----------- >  9:  cb95c79093b hooks: convert non-worktree 'post-checkout' hook to hook library
 5:  2a747a65829 ! 10:  f330600fec8 hooks: convert 'post-checkout' hook to hook library
    @@ Metadata
     Author: Emily Shaffer <emilyshaffer@google.com>
     
      ## Commit message ##
    -    hooks: convert 'post-checkout' hook to hook library
    +    hooks: convert worktree 'post-checkout' hook to hook library
     
         Move the running of the 'post-checkout' hook away from run-command.h
    -    to the new hook.h library. For "worktree" this requires a change to it
    -    to run the hooks from a given directory.
    +    to the new hook.h library in builtin/worktree.c. For this special case
    +    we need a change to the hook API to teach it to run the hook from a
    +    given directory.
     
    -    We could strictly speaking skip the "absolute_path" flag and just
    -    check if "dir" is specified, but let's split them up for clarity, as
    -    well as for any future user who'd like to set "dir" but not implicitly
    -    change the argument to an absolute path.
    +    We cannot skip the "absolute_path" flag and just check if "dir" is
    +    specified as we'd then fail to find our hook in the new dir we'd
    +    chdir() to. We currently don't have a use-case for running a hook not
    +    in our "base" repository at a given absolute path, so let's have "dir"
    +    imply absolute_path(find_hook(hook_name)).
     
         Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    - ## builtin/checkout.c ##
    -@@
    - #include "config.h"
    - #include "diff.h"
    - #include "dir.h"
    -+#include "hook.h"
    - #include "ll-merge.h"
    - #include "lockfile.h"
    - #include "merge-recursive.h"
    -@@ builtin/checkout.c: struct branch_info {
    - static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
    - 			      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()),
    --			   changed ? "1" : "0", NULL);
    -+	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
    -+
    - 	/* "new_commit" can be NULL when checking out from the index before
    - 	   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()),
    -+		     changed ? "1" : "0",
    -+		     NULL);
    -+	return run_hooks_oneshot("post-checkout", &opt);
    - }
    - 
    - static int update_some(const struct object_id *oid, struct strbuf *base,
    -
    - ## builtin/clone.c ##
    -@@
    - #include "connected.h"
    - #include "packfile.h"
    - #include "list-objects-filter-options.h"
    -+#include "hook.h"
    - 
    - /*
    -  * Overall FIXMEs:
    -@@ builtin/clone.c: static int checkout(int submodule_progress)
    - 	struct tree *tree;
    - 	struct tree_desc t;
    - 	int err = 0;
    -+	struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
    - 
    - 	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()),
    --			   oid_to_hex(&oid), "1", NULL);
    -+	strvec_pushl(&hook_opt.args, oid_to_hex(null_oid()), oid_to_hex(&oid),
    -+		     "1", NULL);
    -+	err |= run_hooks_oneshot("post-checkout", &hook_opt);
    - 
    - 	if (!err && (option_recurse_submodules.nr > 0)) {
    - 		struct strvec args = STRVEC_INIT;
    -
      ## builtin/worktree.c ##
     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
      	 * is_junk is cleared, but do return appropriate code when hook fails.
    @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
     +			     "1",
     +			     NULL);
     +		opt.dir = path;
    -+		opt.absolute_path = 1;
     +
    -+		ret = run_hooks_oneshot("post-checkout", &opt);
    ++		ret = run_hooks_opt("post-checkout", &opt);
      	}
      
      	strvec_clear(&child_env);
    @@ hook.c: static int pick_next_hook(struct child_process *cp,
      
      	strvec_push(&cp->args, hook_path);
      	strvec_pushv(&cp->args, hook_cb->options->args.v);
    -@@ hook.c: static int notify_hook_finished(int result,
    - int run_hooks(const char *hook_name, const char *hook_path,
    - 	      struct run_hooks_opt *options)
    +@@ hook.c: static void run_hooks_opt_clear(struct run_hooks_opt *options)
    + 
    + int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
      {
     +	struct strbuf abs_path = STRBUF_INIT;
      	struct hook_cb_data cb_data = {
      		.rc = 0,
      		.hook_name = hook_name,
    --		.hook_path = hook_path,
    - 		.options = options,
    - 	};
    - 	int jobs = 1;
    -@@ hook.c: int run_hooks(const char *hook_name, const char *hook_path,
    - 	if (!options)
    - 		BUG("a struct run_hooks_opt must be provided to run_hooks");
    +@@ hook.c: int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
    + 	}
      
    -+	if (options->absolute_path) {
    + 	cb_data.hook_path = hook_path;
    ++	if (options->dir) {
     +		strbuf_add_absolute_path(&abs_path, hook_path);
    -+		hook_path = abs_path.buf;
    ++		cb_data.hook_path = abs_path.buf;
     +	}
    -+	cb_data.hook_path = hook_path;
     +
      	run_processes_parallel_tr2(jobs,
      				   pick_next_hook,
      				   notify_start_failure,
    -@@ hook.c: int run_hooks(const char *hook_name, const char *hook_path,
    - 				   "hook",
    +@@ hook.c: int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
      				   hook_name);
    - 
    -+	if (options->absolute_path)
    -+		strbuf_release(&abs_path);
    -+
    - 	return cb_data.rc;
    + 	ret = cb_data.rc;
    + cleanup:
    ++	strbuf_release(&abs_path);
    + 	run_hooks_opt_clear(options);
    + 	return ret;
      }
    - 
     
      ## hook.h ##
     @@ hook.h: struct run_hooks_opt
      
    - 	/* Args to be passed to each hook */
    - 	struct strvec args;
    + 	/* Emit an error if the hook is missing */
    + 	unsigned int error_if_missing:1;
     +
    -+	/*
    -+	 * Resolve and run the "absolute_path(hook)" instead of
    -+	 * "hook". Used for "git worktree" hooks
    ++	/**
    ++	 * An optional initial working directory for the hook,
    ++	 * translates to "struct child_process"'s "dir" member.
     +	 */
    -+	int absolute_path;
    -+
    -+	/* Path to initial working directory for subprocess */
     +	const char *dir;
      };
      
      #define RUN_HOOKS_OPT_INIT { \
    -
    - ## reset.c ##
    -@@
    - #include "tree-walk.h"
    - #include "tree.h"
    - #include "unpack-trees.h"
    -+#include "hook.h"
    - 
    - int reset_head(struct repository *r, struct object_id *oid, const char *action,
    - 	       const char *switch_to_branch, unsigned flags,
    -@@ reset.c: int reset_head(struct repository *r, struct object_id *oid, const char *action,
    - 			ret = create_symref("HEAD", switch_to_branch,
    - 					    reflog_head);
    - 	}
    --	if (run_hook)
    --		run_hook_le(NULL, "post-checkout",
    --			    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;
    -+		strvec_pushl(&opt.args,
    -+			     oid_to_hex(orig ? orig : null_oid()),
    -+			     oid_to_hex(oid),
    -+			     "1",
    -+			     NULL);
    -+		run_hooks_oneshot("post-checkout", &opt);
    -+	}
    - 
    - leave_reset_head:
    - 	strbuf_release(&msg);
 7:  840fb530df3 ! 11:  a0b6818c766 git hook run: add an --ignore-missing flag
    @@ Commit message
         git hook run: add an --ignore-missing flag
     
         For certain one-shot hooks we'd like to optimistically run them, and
    -    not complain if they don't exist. This will be used by send-email in a
    -    subsequent commit.
    +    not complain if they don't exist.
    +
    +    This was already supported by the underlying hook.c library, but had
    +    not been exposed via "git hook run". The command version of this will
    +    be used by send-email in a subsequent commit.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    @@ builtin/hook.c: static int run(int argc, const char **argv, const char *prefix)
      	struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
     +	int ignore_missing = 0;
      	const char *hook_name;
    - 	const char *hook_path;
      	struct option run_options[] = {
     +		OPT_BOOL(0, "ignore-missing", &ignore_missing,
     +			 N_("silently ignore missing requested <hook-name>")),
    @@ builtin/hook.c: static int run(int argc, const char **argv, const char *prefix)
      	git_config(git_default_config, NULL);
      
      	hook_name = argv[0];
    -+	if (ignore_missing)
    -+		return run_hooks_oneshot(hook_name, &opt);
    - 	hook_path = find_hook(hook_name);
    - 	if (!hook_path) {
    - 		error("cannot find a hook named %s", hook_name);
    +-	opt.error_if_missing = 1;
    ++	if (!ignore_missing)
    ++		opt.error_if_missing = 1;
    + 	ret = run_hooks_opt(hook_name, &opt);
    + 	if (ret < 0) /* error() return */
    + 		ret = 1;
     
      ## t/t1800-hook.sh ##
     @@ t/t1800-hook.sh: test_expect_success 'git hook run: nonexistent hook' '
 8:  716ebabd794 = 12:  efa35971e9f send-email: use 'git hook run' for 'sendemail-validate'
 9:  95782109270 = 13:  98e0e3330fb git-p4: use 'git hook' to run hooks
10:  706426c8a79 ! 14:  79ea5a2a4f5 commit: convert {pre-commit,prepare-commit-msg} hook to hook.h
    @@ commit.c: size_t ignore_non_trailer(const char *buf, size_t len)
     -	strvec_clear(&hook_env);
      
     -	return ret;
    -+	return run_hooks_oneshot(name, &opt);
    ++	return run_hooks_opt(name, &opt);
      }
11:  39069a9c3ff ! 15:  81612f94707 read-cache: convert post-index-change to use hook.h
    @@ read-cache.c
      
      /* 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
    - {
    - 	int ret;
    - 	int was_full = !istate->sparse_index;
    -+	struct run_hooks_opt hook_opt = RUN_HOOKS_OPT_INIT;
    - 
    - 	ret = convert_to_sparse(istate, 0);
    - 
     @@ read-cache.c: static int do_write_locked_index(struct index_state *istate, struct lock_file *l
      	else
      		ret = close_lock_file_gently(lock);
      
     -	run_hook_le(NULL, "post-index-change",
    --			istate->updated_workdir ? "1" : "0",
    --			istate->updated_skipworktree ? "1" : "0", NULL);
    -+	strvec_pushl(&hook_opt.args,
    -+		     istate->updated_workdir ? "1" : "0",
    -+		     istate->updated_skipworktree ? "1" : "0",
    -+		     NULL);
    -+	run_hooks_oneshot("post-index-change", &hook_opt);
    -+
    ++	run_hooks_l("post-index-change",
    + 			istate->updated_workdir ? "1" : "0",
    + 			istate->updated_skipworktree ? "1" : "0", NULL);
      	istate->updated_workdir = 0;
    - 	istate->updated_skipworktree = 0;
    - 
     
      ## run-command.c ##
     @@ run-command.c: int async_with_fork(void)
12:  9818078f1e5 ! 16:  43ecd6697e0 receive-pack: convert push-to-checkout hook to hook.h
    @@ builtin/receive-pack.c: static const char *push_to_checkout(unsigned char *hash,
     -			hash_to_hex(hash), NULL))
     +	strvec_pushv(&opt.env, env->v);
     +	strvec_push(&opt.args, hash_to_hex(hash));
    -+	if (run_hooks_oneshot(push_to_checkout_hook, &opt))
    ++	if (run_hooks_opt(push_to_checkout_hook, &opt))
      		return "push-to-checkout hook declined";
      	else
      		return NULL;
13:  1bc080d3611 = 17:  9ef574fa30c run-command: remove old run_hook_{le,ve}() hook API
-- 
2.33.1.1570.g069344fdd45
Previous: Junio C HamanoNext: Ævar Arnfjörð Bjarmason
Message 53 of 115 in “hook.[ch]: new library to run hooks + simple hook conversion”
  1. 00/13 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Oct 15, 2021
  2. 01/13 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Oct 15, 2021
  3. Emily ShafferOct 15, 2021
  4. Eric SunshineOct 15, 2021
  5. Ævar Arnfjörð BjarmasonOct 19, 2021
  6. 02/13 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Oct 15, 2021
  7. Emily ShafferOct 15, 2021
  8. 04/13 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  9. Emily ShafferOct 15, 2021
  10. 05/13 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Oct 15, 2021
  11. Emily ShafferOct 15, 2021
  12. 03/13 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  13. Emily ShafferOct 15, 2021
  14. 06/13 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  15. Emily ShafferOct 15, 2021
  16. 07/13 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Oct 15, 2021
  17. Emily ShafferOct 15, 2021
  18. 08/13 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Oct 15, 2021
  19. Emily ShafferOct 15, 2021
  20. 09/13 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Oct 15, 2021
  21. Emily ShafferOct 15, 2021
  22. 10/13 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  23. Emily ShafferOct 15, 2021
  24. 11/13 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  25. Emily ShafferOct 15, 2021
  26. 12/13 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Oct 15, 2021
  27. 13/13 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Oct 15, 2021
  28. Emily ShafferOct 15, 2021
  29. Emily ShafferOct 15, 2021
  30. Junio C HamanoOct 16, 2021
  31. Eric SunshineOct 16, 2021
  32. Ævar Arnfjörð BjarmasonOct 16, 2021
  33. Junio C HamanoOct 16, 2021
  34. Ævar Arnfjörð BjarmasonOct 16, 2021
  35. 00/13 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Oct 19, 2021
  36. 01/13 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Oct 19, 2021
  37. 02/13 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Oct 19, 2021
  38. 03/13 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  39. Junio C HamanoOct 29, 2021
  40. 04/13 am: convert applypatch to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  41. 05/13 hooks: convert 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Oct 19, 2021
  42. Junio C HamanoOct 29, 2021
  43. 06/13 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  44. 07/13 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Oct 19, 2021
  45. Junio C HamanoOct 29, 2021
  46. 08/13 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Oct 19, 2021
  47. 09/13 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Oct 19, 2021
  48. 10/13 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  49. 11/13 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  50. 12/13 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Oct 19, 2021
  51. 13/13 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Oct 19, 2021
  52. Junio C HamanoOct 29, 2021
  53. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Nov 1, 2021
  54. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Nov 1, 2021
  55. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 1, 2021
  56. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Nov 1, 2021
  57. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Nov 1, 2021
  58. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  59. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Nov 1, 2021
  60. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  61. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  62. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 1, 2021
  63. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  64. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  65. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Nov 1, 2021
  66. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Nov 1, 2021
  67. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  68. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Nov 1, 2021
  69. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Nov 1, 2021
  70. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Nov 1, 2021
  71. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Nov 23, 2021
  72. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Nov 23, 2021
  73. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Nov 23, 2021
  74. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Nov 23, 2021
  75. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  76. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  77. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Nov 23, 2021
  78. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  79. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  80. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 23, 2021
  81. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  82. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Nov 23, 2021
  83. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  84. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Nov 23, 2021
  85. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Nov 23, 2021
  86. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Nov 23, 2021
  87. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Nov 23, 2021
  88. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Nov 23, 2021
  89. 00/17 hook.[ch]: new library to run hooks + simple hook conversionÆvar Arnfjörð Bjarmason, Dec 22, 2021
  90. 01/17 hook: add 'run' subcommandÆvar Arnfjörð Bjarmason, Dec 22, 2021
  91. Emily ShafferJan 7, 2022
  92. Junio C HamanoJan 7, 2022
  93. 03/17 gc: use hook library for pre-auto-gc hookÆvar Arnfjörð Bjarmason, Dec 22, 2021
  94. 04/17 am: convert {pre,post}-applypatch to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  95. 06/17 rebase: convert pre-rebase to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  96. 05/17 hook API: add a run_hooks_l() wrapperÆvar Arnfjörð Bjarmason, Dec 22, 2021
  97. Glen ChooJan 6, 2022
  98. Emily ShafferJan 7, 2022
  99. 02/17 hook API: add a run_hooks() wrapperÆvar Arnfjörð Bjarmason, Dec 22, 2021
  100. Glen ChooJan 6, 2022
  101. 07/17 am: convert applypatch-msg to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  102. 10/17 hooks: convert worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Dec 22, 2021
  103. 09/17 hooks: convert non-worktree 'post-checkout' hook to hook libraryÆvar Arnfjörð Bjarmason, Dec 22, 2021
  104. 08/17 merge: convert post-merge to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  105. 11/17 git hook run: add an --ignore-missing flagÆvar Arnfjörð Bjarmason, Dec 22, 2021
  106. Glen ChooJan 6, 2022
  107. 12/17 send-email: use 'git hook run' for 'sendemail-validate'Ævar Arnfjörð Bjarmason, Dec 22, 2021
  108. 13/17 git-p4: use 'git hook' to run hooksÆvar Arnfjörð Bjarmason, Dec 22, 2021
  109. 14/17 commit: convert {pre-commit,prepare-commit-msg} hook to hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  110. 15/17 read-cache: convert post-index-change to use hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  111. 16/17 receive-pack: convert push-to-checkout hook to hook.hÆvar Arnfjörð Bjarmason, Dec 22, 2021
  112. 17/17 run-command: remove old run_hook_{le,ve}() hook APIÆvar Arnfjörð Bjarmason, Dec 22, 2021
  113. Glen ChooJan 6, 2022
  114. Emily ShafferJan 7, 2022
  115. Emily ShafferJan 7, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.