--------
[verse]
'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]
+'git hook' list <hook-name>
DESCRIPTION
-----------
mandatory `--` (or `--end-of-options`, see linkgit:gitcli[7]). See
linkgit:githooks[5] for arguments hooks might expect (if any).
+list::
+ Print a list of hooks which will be run on `<hook-name>` event. If no
+ hooks are configured for that event, print a warning and return 1.
+
OPTIONS
-------
#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 <hook-name>")
static const char * const builtin_hook_usage[] = {
BUILTIN_HOOK_RUN_USAGE,
+ BUILTIN_HOOK_LIST_USAGE,
NULL
};
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 ret = 0;
+
+ struct option list_options[] = {
+ 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\n", _("hook from hookdir"));
+ 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)
{
parse_opt_subcommand_fn *fn = NULL;
struct option builtin_hook_options[] = {
OPT_SUBCOMMAND("run", &fn, run),
+ OPT_SUBCOMMAND("list", &fn, list),
OPT_END(),
};
free(h);
}
-static void hook_list_clear(struct string_list *hooks, cb_data_free_fn cb_data_free)
+void hook_list_clear(struct string_list *hooks, cb_data_free_fn cb_data_free)
{
struct string_list_item *item;
string_list_append(hook_list, hook_path)->util = h;
}
-/*
- * Provides a list of hook commands to run for the 'hookname' event.
- *
- * This function consolidates hooks from two sources:
- * 1. The config-based hooks (not yet implemented).
- * 2. The "traditional" hook found in the repository hooks directory
- * (e.g., .git/hooks/pre-commit).
- *
- * The list is ordered by execution priority.
- *
- * The caller is responsible for freeing the memory of the returned list
- * using string_list_clear() and free().
- */
-static struct string_list *list_hooks(struct repository *r, const char *hookname,
+struct string_list *list_hooks(struct repository *r, const char *hookname,
struct run_hooks_opt *options)
{
struct string_list *hook_head;
struct run_hooks_opt *options;
};
-/*
+/**
+ * Provides a list of hook commands to run for the 'hookname' event.
+ *
+ * This function consolidates hooks from two sources:
+ * 1. The config-based hooks (not yet implemented).
+ * 2. The "traditional" hook found in the repository hooks directory
+ * (e.g., .git/hooks/pre-commit).
+ *
+ * The list is ordered by execution priority.
+ *
+ * The caller is responsible for freeing the memory of the returned list
+ * using string_list_clear() and free().
+ */
+struct string_list *list_hooks(struct repository *r, const char *hookname,
+ struct run_hooks_opt *options);
+
+/**
+ * Frees the memory allocated for the hook list, including the `struct hook`
+ * items and their internal state.
+ */
+void hook_list_clear(struct string_list *hooks, cb_data_free_fn cb_data_free);
+
+/**
* Returns the path to the hook file, or NULL if the hook is missing
* or disabled. Note that this points to static storage that will be
* overwritten by further calls to find_hook and run_hook_*.
test_expect_code 129 git hook run &&
test_expect_code 129 git hook run -h &&
test_expect_code 129 git hook run --unknown 2>err &&
+ test_expect_code 129 git hook list &&
+ test_expect_code 129 git hook list -h &&
grep "unknown option" err
'
+test_expect_success 'git hook list: nonexistent hook' '
+ cat >stderr.expect <<-\EOF &&
+ warning: No hooks found for event '\''test-hook'\''
+ EOF
+ test_expect_code 1 git hook list test-hook 2>stderr.actual &&
+ test_cmp stderr.expect stderr.actual
+'
+
+test_expect_success 'git hook list: traditional hook from hookdir' '
+ test_hook test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ cat >expect <<-\EOF &&
+ hook from hookdir
+ EOF
+ git hook list test-hook >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git hook run: nonexistent hook' '
cat >stderr.expect <<-\EOF &&
error: cannot find a hook named test-hook