aboutsummaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-03-10 14:23:18 -0700
committerJunio C Hamano <gitster@pobox.com>2026-03-10 14:23:18 -0700
commitf330d46deeb143b6109143f37a47d025475d11d6 (patch)
treec2b93a2ec12c0e9c8227be1a362c0fd261d6376a /builtin
parent9a8aebae972de22ecd5adb92fec9d77147949c8a (diff)
parentec1c4d974ac74afb4f0574d29f7bbb30c1c46431 (diff)
downloadgit-f330d46deeb143b6109143f37a47d025475d11d6.tar.xz
Merge branch 'ar/config-hooks'
Allow hook commands to be defined (possibly centrally) in the configuration files, and run multiple of them for the same hook event. * ar/config-hooks: hook: add -z option to "git hook list" hook: allow out-of-repo 'git hook' invocations hook: allow event = "" to overwrite previous values hook: allow disabling config hooks hook: include hooks from the config hook: add "git hook list" command hook: run a list of hooks to prepare for multihook support hook: add internal state alloc/free callbacks
Diffstat (limited to 'builtin')
-rw-r--r--builtin/hook.c66
-rw-r--r--builtin/receive-pack.c33
2 files changed, 92 insertions, 7 deletions
diff --git a/builtin/hook.c b/builtin/hook.c
index 7afec380d2..83020dfb4f 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -6,12 +6,16 @@
#include "hook.h"
#include "parse-options.h"
#include "strvec.h"
+#include "abspath.h"
#define BUILTIN_HOOK_RUN_USAGE \
N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]")
+#define BUILTIN_HOOK_LIST_USAGE \
+ N_("git hook list [-z] <hook-name>")
static const char * const builtin_hook_usage[] = {
BUILTIN_HOOK_RUN_USAGE,
+ BUILTIN_HOOK_LIST_USAGE,
NULL
};
@@ -20,6 +24,67 @@ static const char * const builtin_hook_run_usage[] = {
NULL
};
+static int list(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ static const char *const builtin_hook_list_usage[] = {
+ BUILTIN_HOOK_LIST_USAGE,
+ NULL
+ };
+ struct string_list *head;
+ struct string_list_item *item;
+ const char *hookname = NULL;
+ int line_terminator = '\n';
+ int ret = 0;
+
+ struct option list_options[] = {
+ OPT_SET_INT('z', NULL, &line_terminator,
+ N_("use NUL as line terminator"), '\0'),
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, prefix, list_options,
+ builtin_hook_list_usage, 0);
+
+ /*
+ * The only unnamed argument provided should be the hook-name; if we add
+ * arguments later they probably should be caught by parse_options.
+ */
+ if (argc != 1)
+ usage_msg_opt(_("You must specify a hook event name to list."),
+ builtin_hook_list_usage, list_options);
+
+ hookname = argv[0];
+
+ head = list_hooks(repo, hookname, NULL);
+
+ if (!head->nr) {
+ warning(_("No hooks found for event '%s'"), hookname);
+ ret = 1; /* no hooks found */
+ goto cleanup;
+ }
+
+ for_each_string_list_item(item, head) {
+ struct hook *h = item->util;
+
+ switch (h->kind) {
+ case HOOK_TRADITIONAL:
+ printf("%s%c", _("hook from hookdir"), line_terminator);
+ break;
+ case HOOK_CONFIGURED:
+ printf("%s%c", h->u.configured.friendly_name, line_terminator);
+ break;
+ default:
+ BUG("unknown hook kind");
+ }
+ }
+
+cleanup:
+ hook_list_clear(head, NULL);
+ free(head);
+ return ret;
+}
+
static int run(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
@@ -77,6 +142,7 @@ int cmd_hook(int argc,
parse_opt_subcommand_fn *fn = NULL;
struct option builtin_hook_options[] = {
OPT_SUBCOMMAND("run", &fn, run),
+ OPT_SUBCOMMAND("list", &fn, list),
OPT_END(),
};
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 55d39daa62..d6225df890 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -901,6 +901,26 @@ static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_
return state->cmd ? 0 : 1; /* 0 = more to come, 1 = EOF */
}
+static void *receive_hook_feed_state_alloc(void *feed_pipe_ctx)
+{
+ struct receive_hook_feed_state *init_state = feed_pipe_ctx;
+ struct receive_hook_feed_state *data = xcalloc(1, sizeof(*data));
+ data->report = init_state->report;
+ data->cmd = init_state->cmd;
+ data->skip_broken = init_state->skip_broken;
+ strbuf_init(&data->buf, 0);
+ return data;
+}
+
+static void receive_hook_feed_state_free(void *data)
+{
+ struct receive_hook_feed_state *d = data;
+ if (!d)
+ return;
+ strbuf_release(&d->buf);
+ free(d);
+}
+
static int run_receive_hook(struct command *commands,
const char *hook_name,
int skip_broken,
@@ -908,7 +928,7 @@ static int run_receive_hook(struct command *commands,
{
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
struct command *iter = commands;
- struct receive_hook_feed_state feed_state;
+ struct receive_hook_feed_state feed_init_state = { 0 };
struct async sideband_async;
int sideband_async_started = 0;
int saved_stderr = -1;
@@ -941,16 +961,15 @@ static int run_receive_hook(struct command *commands,
prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started);
/* set up stdin callback */
- feed_state.cmd = commands;
- feed_state.skip_broken = skip_broken;
- feed_state.report = NULL;
- strbuf_init(&feed_state.buf, 0);
- opt.feed_pipe_cb_data = &feed_state;
+ feed_init_state.cmd = commands;
+ feed_init_state.skip_broken = skip_broken;
+ opt.feed_pipe_ctx = &feed_init_state;
opt.feed_pipe = feed_receive_hook_cb;
+ opt.feed_pipe_cb_data_alloc = receive_hook_feed_state_alloc;
+ opt.feed_pipe_cb_data_free = receive_hook_feed_state_free;
ret = run_hooks_opt(the_repository, hook_name, &opt);
- strbuf_release(&feed_state.buf);
finish_sideband_async(&sideband_async, saved_stderr, sideband_async_started);
return ret;