{ "grep", cmd_grep, RUN_SETUP_GENTLY },
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
- { "hook", cmd_hook, RUN_SETUP },
+ { "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 },
int found_hook;
+ if (!r || !r->gitdir)
+ return NULL;
+
repo_git_path_replace(r, &path, "hooks/%s", name);
found_hook = access(path.buf, X_OK) >= 0;
#ifdef STRIP_EXTENSION
strmap_clear(&cb_data.event_hooks, 0);
}
-/* Return the hook config map for `r`, populating it first if needed. */
+/*
+ * Return the hook config map for `r`, populating it first if needed.
+ *
+ * Out-of-repo calls (r->gitdir == NULL) allocate and return a temporary
+ * cache map; the caller is responsible for freeing it with
+ * hook_cache_clear() + free().
+ */
static struct strmap *get_hook_config_cache(struct repository *r)
{
struct strmap *cache = NULL;
- if (r) {
+ if (r && r->gitdir) {
/*
* For in-repo calls, the map is stored in r->hook_config_cache,
* so repeated invocations don't parse the configs, so allocate
build_hook_config_map(r, r->hook_config_cache);
}
cache = r->hook_config_cache;
+ } else {
+ /*
+ * Out-of-repo calls (no gitdir) allocate and return a temporary
+ * map cache which gets free'd immediately by the caller.
+ */
+ cache = xcalloc(1, sizeof(*cache));
+ strmap_init(cache);
+ build_hook_config_map(r, cache);
}
return cache;
string_list_append(list, friendly_name)->util = hook;
}
+
+ /*
+ * Cleanup temporary cache for out-of-repo calls since they can't be
+ * stored persistently. Next out-of-repo calls will have to re-parse.
+ */
+ if (!r || !r->gitdir) {
+ hook_cache_clear(cache);
+ free(cache);
+ }
}
struct string_list *list_hooks(struct repository *r, const char *hookname,
test_cmp expect actual
'
-test_expect_success 'git hook run -- out-of-repo runs excluded' '
- test_hook test-hook <<-EOF &&
- echo Test hook
- EOF
+test_expect_success 'git hook run: out-of-repo runs execute global hooks' '
+ test_config_global hook.global-hook.event test-hook --add &&
+ test_config_global hook.global-hook.command "echo no repo no problems" --add &&
- nongit test_must_fail git hook run test-hook
+ echo "global-hook" >expect &&
+ nongit git hook list test-hook >actual &&
+ test_cmp expect actual &&
+
+ echo "no repo no problems" >expect &&
+
+ nongit git hook run test-hook 2>actual &&
+ test_cmp expect actual
'
test_expect_success 'git -c core.hooksPath=<PATH> hook run' '