Skip to content

Commit e17bd99

Browse files
10ne1gitster
authored andcommitted
hook: show disabled hooks in "git hook list"
Disabled hooks were filtered out of the cache entirely, making them invisible to "git hook list". Keep them in the cache with a new "disabled" flag which is propagated to the respective struct hook. "git hook list" now shows disabled hooks as tab-separated columns, with the status as a prefix before the name (like scope with --show-scope). With --show-scope it looks like: $ git hook list --show-scope pre-commit global linter local disabled no-leaks hook from hookdir A disabled hook without a command issues a warning instead of the fatal "hook.X.command must be configured" error. We could also throw an error, however it seemd a bit excessive to me in this case. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent b66efad commit e17bd99

4 files changed

Lines changed: 80 additions & 28 deletions

File tree

builtin/hook.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,20 @@ static int list(int argc, const char **argv, const char *prefix,
7272
case HOOK_TRADITIONAL:
7373
printf("%s%c", _("hook from hookdir"), line_terminator);
7474
break;
75-
case HOOK_CONFIGURED:
76-
if (show_scope)
77-
printf("%s\t%s%c",
78-
config_scope_name(h->u.configured.scope),
79-
h->u.configured.friendly_name,
80-
line_terminator);
75+
case HOOK_CONFIGURED: {
76+
const char *name = h->u.configured.friendly_name;
77+
const char *scope = show_scope ?
78+
config_scope_name(h->u.configured.scope) : NULL;
79+
if (scope)
80+
printf("%s\t%s%s%c", scope,
81+
h->u.configured.disabled ? "disabled\t" : "",
82+
name, line_terminator);
8183
else
82-
printf("%s%c", h->u.configured.friendly_name,
83-
line_terminator);
84+
printf("%s%s%c",
85+
h->u.configured.disabled ? "disabled\t" : "",
86+
name, line_terminator);
8487
break;
88+
}
8589
default:
8690
BUG("unknown hook kind");
8791
}

hook.c

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static void list_hooks_add_default(struct repository *r, const char *hookname,
115115
struct hook_config_cache_entry {
116116
char *command;
117117
enum config_scope scope;
118+
bool disabled;
118119
};
119120

120121
/*
@@ -213,8 +214,10 @@ static int hook_config_lookup_all(const char *key, const char *value,
213214
* every item's string is the hook's friendly-name and its util pointer is
214215
* the corresponding command string. Both strings are owned by the map.
215216
*
216-
* Disabled hooks and hooks missing a command are already filtered out at
217-
* parse time, so callers can iterate the list directly.
217+
* Disabled hooks are kept in the cache with entry->disabled set, so that
218+
* "git hook list" can display them. A non-disabled hook missing a command
219+
* is fatal; a disabled hook missing a command emits a warning and is kept
220+
* in the cache with entry->command = NULL.
218221
*/
219222
void hook_cache_clear(struct strmap *cache)
220223
{
@@ -263,21 +266,26 @@ static void build_hook_config_map(struct repository *r, struct strmap *cache)
263266
struct hook_config_cache_entry *entry;
264267
char *command;
265268

266-
/* filter out disabled hooks */
267-
if (unsorted_string_list_lookup(&cb_data.disabled_hooks,
268-
hname))
269-
continue;
269+
bool is_disabled =
270+
!!unsorted_string_list_lookup(
271+
&cb_data.disabled_hooks, hname);
270272

271273
command = strmap_get(&cb_data.commands, hname);
272-
if (!command)
273-
die(_("'hook.%s.command' must be configured or "
274-
"'hook.%s.event' must be removed;"
275-
" aborting."), hname, hname);
274+
if (!command) {
275+
if (is_disabled)
276+
warning(_("disabled hook '%s' has no "
277+
"command configured"), hname);
278+
else
279+
die(_("'hook.%s.command' must be configured or "
280+
"'hook.%s.event' must be removed;"
281+
" aborting."), hname, hname);
282+
}
276283

277284
/* util stores a cache entry; owned by the cache. */
278285
CALLOC_ARRAY(entry, 1);
279-
entry->command = xstrdup(command);
286+
entry->command = xstrdup_or_null(command);
280287
entry->scope = scope;
288+
entry->disabled = is_disabled;
281289
string_list_append(hooks, hname)->util = entry;
282290
}
283291

@@ -358,8 +366,10 @@ static void list_hooks_add_configured(struct repository *r,
358366

359367
hook->kind = HOOK_CONFIGURED;
360368
hook->u.configured.friendly_name = xstrdup(friendly_name);
361-
hook->u.configured.command = xstrdup(entry->command);
369+
hook->u.configured.command =
370+
entry->command ? xstrdup(entry->command) : NULL;
362371
hook->u.configured.scope = entry->scope;
372+
hook->u.configured.disabled = entry->disabled;
363373

364374
string_list_append(list, friendly_name)->util = hook;
365375
}
@@ -397,7 +407,16 @@ struct string_list *list_hooks(struct repository *r, const char *hookname,
397407
int hook_exists(struct repository *r, const char *name)
398408
{
399409
struct string_list *hooks = list_hooks(r, name, NULL);
400-
int exists = hooks->nr > 0;
410+
int exists = 0;
411+
412+
for (size_t i = 0; i < hooks->nr; i++) {
413+
struct hook *h = hooks->items[i].util;
414+
if (h->kind == HOOK_TRADITIONAL ||
415+
!h->u.configured.disabled) {
416+
exists = 1;
417+
break;
418+
}
419+
}
401420
string_list_clear_func(hooks, hook_free);
402421
free(hooks);
403422
return exists;
@@ -412,10 +431,11 @@ static int pick_next_hook(struct child_process *cp,
412431
struct string_list *hook_list = hook_cb->hook_command_list;
413432
struct hook *h;
414433

415-
if (hook_cb->hook_to_run_index >= hook_list->nr)
416-
return 0;
417-
418-
h = hook_list->items[hook_cb->hook_to_run_index++].util;
434+
do {
435+
if (hook_cb->hook_to_run_index >= hook_list->nr)
436+
return 0;
437+
h = hook_list->items[hook_cb->hook_to_run_index++].util;
438+
} while (h->kind == HOOK_CONFIGURED && h->u.configured.disabled);
419439

420440
cp->no_stdin = 1;
421441
strvec_pushv(&cp->env, hook_cb->options->env.v);

hook.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct hook {
3131
const char *friendly_name;
3232
const char *command;
3333
enum config_scope scope;
34+
bool disabled;
3435
} configured;
3536
} u;
3637

t/t1800-hook.sh

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,16 +357,43 @@ test_expect_success 'disabled hook is not run' '
357357
test_must_be_empty actual
358358
'
359359

360-
test_expect_success 'disabled hook does not appear in git hook list' '
360+
test_expect_success 'disabled hook with no command warns' '
361+
test_config hook.nocommand.event "pre-commit" &&
362+
test_config hook.nocommand.enabled false &&
363+
364+
git hook list pre-commit 2>actual &&
365+
test_grep "disabled hook.*nocommand.*no command configured" actual
366+
'
367+
368+
test_expect_success 'disabled hook appears as disabled in git hook list' '
361369
test_config hook.active.event "pre-commit" &&
362370
test_config hook.active.command "echo active" &&
363371
test_config hook.inactive.event "pre-commit" &&
364372
test_config hook.inactive.command "echo inactive" &&
365373
test_config hook.inactive.enabled false &&
366374
367375
git hook list pre-commit >actual &&
368-
test_grep "active" actual &&
369-
test_grep ! "inactive" actual
376+
test_grep "^active$" actual &&
377+
test_grep "^disabled inactive$" actual
378+
'
379+
380+
test_expect_success 'disabled hook shows scope with --show-scope' '
381+
test_config hook.myhook.event "pre-commit" &&
382+
test_config hook.myhook.command "echo hi" &&
383+
test_config hook.myhook.enabled false &&
384+
385+
git hook list --show-scope pre-commit >actual &&
386+
test_grep "^local disabled myhook$" actual
387+
'
388+
389+
test_expect_success 'disabled configured hook is not reported as existing by hook_exists' '
390+
test_when_finished "rm -f git-bugreport-hook-exists-test.txt" &&
391+
test_config hook.linter.event "pre-commit" &&
392+
test_config hook.linter.command "echo lint" &&
393+
test_config hook.linter.enabled false &&
394+
395+
git bugreport -s hook-exists-test &&
396+
test_grep ! "pre-commit" git-bugreport-hook-exists-test.txt
370397
'
371398

372399
test_expect_success 'globally disabled hook can be re-enabled locally' '

0 commit comments

Comments
 (0)