Skip to content

Commit b51e238

Browse files
nasamuffingitster
authored andcommitted
hook: allow out-of-repo 'git hook' invocations
Since hooks can now be supplied via the config, and a config can be present without a gitdir via the global and system configs, we can start to allow 'git hook run' to occur without a gitdir. This enables us to do things like run sendemail-validate hooks when running 'git send-email' from a nongit directory. It still doesn't make sense to look for hooks in the hookdir in nongit repos, though, as there is no hookdir. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent d084fa2 commit b51e238

File tree

3 files changed

+40
-8
lines changed

3 files changed

+40
-8
lines changed

git.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ static struct cmd_struct commands[] = {
586586
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
587587
{ "hash-object", cmd_hash_object },
588588
{ "help", cmd_help },
589-
{ "hook", cmd_hook, RUN_SETUP },
589+
{ "hook", cmd_hook, RUN_SETUP_GENTLY },
590590
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
591591
{ "init", cmd_init_db },
592592
{ "init-db", cmd_init_db },

hook.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const char *find_hook(struct repository *r, const char *name)
1818

1919
int found_hook;
2020

21+
if (!r || !r->gitdir)
22+
return NULL;
23+
2124
repo_git_path_replace(r, &path, "hooks/%s", name);
2225
found_hook = access(path.buf, X_OK) >= 0;
2326
#ifdef STRIP_EXTENSION
@@ -268,12 +271,18 @@ static void build_hook_config_map(struct repository *r, struct strmap *cache)
268271
strmap_clear(&cb_data.event_hooks, 0);
269272
}
270273

271-
/* Return the hook config map for `r`, populating it first if needed. */
274+
/*
275+
* Return the hook config map for `r`, populating it first if needed.
276+
*
277+
* Out-of-repo calls (r->gitdir == NULL) allocate and return a temporary
278+
* cache map; the caller is responsible for freeing it with
279+
* hook_cache_clear() + free().
280+
*/
272281
static struct strmap *get_hook_config_cache(struct repository *r)
273282
{
274283
struct strmap *cache = NULL;
275284

276-
if (r) {
285+
if (r && r->gitdir) {
277286
/*
278287
* For in-repo calls, the map is stored in r->hook_config_cache,
279288
* so repeated invocations don't parse the configs, so allocate
@@ -285,6 +294,14 @@ static struct strmap *get_hook_config_cache(struct repository *r)
285294
build_hook_config_map(r, r->hook_config_cache);
286295
}
287296
cache = r->hook_config_cache;
297+
} else {
298+
/*
299+
* Out-of-repo calls (no gitdir) allocate and return a temporary
300+
* map cache which gets free'd immediately by the caller.
301+
*/
302+
cache = xcalloc(1, sizeof(*cache));
303+
strmap_init(cache);
304+
build_hook_config_map(r, cache);
288305
}
289306

290307
return cache;
@@ -315,6 +332,15 @@ static void list_hooks_add_configured(struct repository *r,
315332

316333
string_list_append(list, friendly_name)->util = hook;
317334
}
335+
336+
/*
337+
* Cleanup temporary cache for out-of-repo calls since they can't be
338+
* stored persistently. Next out-of-repo calls will have to re-parse.
339+
*/
340+
if (!r || !r->gitdir) {
341+
hook_cache_clear(cache);
342+
free(cache);
343+
}
318344
}
319345

320346
struct string_list *list_hooks(struct repository *r, const char *hookname,

t/t1800-hook.sh

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,18 @@ test_expect_success 'git hook run -- pass arguments' '
131131
test_cmp expect actual
132132
'
133133

134-
test_expect_success 'git hook run -- out-of-repo runs excluded' '
135-
test_hook test-hook <<-EOF &&
136-
echo Test hook
137-
EOF
134+
test_expect_success 'git hook run: out-of-repo runs execute global hooks' '
135+
test_config_global hook.global-hook.event test-hook --add &&
136+
test_config_global hook.global-hook.command "echo no repo no problems" --add &&
138137
139-
nongit test_must_fail git hook run test-hook
138+
echo "global-hook" >expect &&
139+
nongit git hook list test-hook >actual &&
140+
test_cmp expect actual &&
141+
142+
echo "no repo no problems" >expect &&
143+
144+
nongit git hook run test-hook 2>actual &&
145+
test_cmp expect actual
140146
'
141147

142148
test_expect_success 'git -c core.hooksPath=<PATH> hook run' '

0 commit comments

Comments
 (0)