@@ -39,10 +39,20 @@ 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) {
+ /*
+ * 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);
+ }
}
clear_hook_list(head);
@@ -58,6 +68,8 @@ int cmd_hook(int argc, const char **argv, const char *prefix)
if (argc < 2)
usage_with_options(builtin_hook_usage, builtin_hook_options);
+ git_config(git_default_config, NULL);
+
if (!strcmp(argv[1], "list"))
return list(argc - 1, argv + 1, prefix);
@@ -2,6 +2,7 @@
#include "hook.h"
#include "config.h"
+#include "run-command.h"
void free_hook(struct hook *ptr)
{
@@ -35,6 +36,7 @@ static void append_or_move_hook(struct list_head *head, const char *command)
to_add = xmalloc(sizeof(*to_add));
strbuf_init(&to_add->command, 0);
strbuf_addstr(&to_add->command, command);
+ to_add->from_hookdir = 0;
}
/* re-set the scope so we show where an override was specified */
@@ -115,6 +117,21 @@ 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);
+
+ /* Unconditionally add legacy hook, but annotate it. */
+ if (legacy_hook_path) {
+ struct hook *legacy_hook;
+
+ append_or_move_hook(hook_head,
+ absolute_path(legacy_hook_path));
+ legacy_hook = list_entry(hook_head->prev, struct hook,
+ list);
+ legacy_hook->from_hookdir = 1;
+ }
+ }
+
strbuf_release(&hook_key);
return hook_head;
}
@@ -11,6 +11,7 @@ struct hook {
enum config_scope origin;
/* The literal command to run. */
struct strbuf command;
+ unsigned from_hookdir : 1;
};
/*
@@ -23,6 +23,14 @@ setup_hookcmd () {
test_config_global hookcmd.abc.command "/path/abc" --add
}
+setup_hookdir () {
+ mkdir .git/hooks
+ write_script .git/hooks/pre-commit <<-EOF
+ echo \"Legacy Hook\"
+ EOF
+ test_when_finished rm -rf .git/hooks
+}
+
test_expect_success 'git hook rejects commands without a mode' '
test_must_fail git hook pre-commit
'
@@ -85,4 +93,15 @@ test_expect_success 'git hook list reorders on duplicate commands' '
test_cmp expected actual
'
+test_expect_success 'git hook list shows hooks from the hookdir' '
+ setup_hookdir &&
+
+ cat >expected <<-EOF &&
+ hookdir: $(pwd)/.git/hooks/pre-commit
+ EOF
+
+ git hook list pre-commit >actual &&
+ test_cmp expected actual
+'
+
test_done
Historically, hooks are declared by placing an executable into $GIT_DIR/hooks/$HOOKNAME (or $HOOKDIR/$HOOKNAME). Although hooks taken from the config are more featureful than hooks placed in the $HOOKDIR, those hooks should not stop working for users who already have them. Let's list them to the user, but instead of displaying a config scope (e.g. "global: blah") we can prefix them with "hookdir:". Signed-off-by: Emily Shaffer <emilyshaffer@google.com> --- builtin/hook.c | 18 +++++++++++++++--- hook.c | 17 +++++++++++++++++ hook.h | 1 + t/t1360-config-based-hooks.sh | 19 +++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-)