hook: allow event = "" to overwrite previous values
Add the ability for empty events to clear previously set multivalue variables, so the newly added "hook.*.event" behave like the other multivalued keys. 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>
Adrian Ratiu committed
Feb 19, 2026 at 00:23 UTC
d084fa2a915784d65257fbaff43f00b3ea5c8a44
3 files changed
+34
-11
Documentation/config/hook.adoc
+3
-1
@@ -12,7 +12,9 @@ hook.<name>.event::
12
linkgit:githooks[5] for a complete list of hook events.) On the
13
specified event, the associated `hook.<name>.command` is executed.
14
This is a multi-valued key. To run `hook.<name>` on multiple
15
- events, specify the key more than once. See linkgit:git-hook[1].
15
+ events, specify the key more than once. An empty value resets
16
+ the list of events, clearing any previously defined events for
17
+ `hook.<name>`. See linkgit:git-hook[1].
18
19
hook.<name>.enabled::
20
Whether the hook `hook.<name>` is enabled. Defaults to `true`.
hook.c
+19
-10
@@ -147,18 +147,27 @@ static int hook_config_lookup_all(const char *key, const char *value,
147
hook_name = xmemdupz(name, name_len);
148
149
if (!strcmp(subkey, "event")) {
150
- struct string_list *hooks =
151
- strmap_get(&data->event_hooks, value);
150
+ if (!*value) {
151
+ /* Empty values reset previous events for this hook. */
152
+ struct hashmap_iter iter;
153
+ struct strmap_entry *e;
154
+
155
+ strmap_for_each_entry(&data->event_hooks, &iter, e)
156
+ unsorted_string_list_remove(e->value, hook_name);
157
+ } else {
158
+ struct string_list *hooks =
159
+ strmap_get(&data->event_hooks, value);
160
+
161
+ if (!hooks) {
162
+ hooks = xcalloc(1, sizeof(*hooks));
163
+ string_list_init_dup(hooks);
164
+ strmap_put(&data->event_hooks, value, hooks);
165
+ }
166
153
- if (!hooks) {
154
- hooks = xcalloc(1, sizeof(*hooks));
155
- string_list_init_dup(hooks);
156
- strmap_put(&data->event_hooks, value, hooks);
167
+ /* Re-insert if necessary to preserve last-seen order. */
168
+ unsorted_string_list_remove(hooks, hook_name);
169
+ string_list_append(hooks, hook_name);
170
}
158
-
159
- /* Re-insert if necessary to preserve last-seen order. */
160
- unsorted_string_list_remove(hooks, hook_name);
161
- string_list_append(hooks, hook_name);
171
} else if (!strcmp(subkey, "command")) {
172
/* Store command overwriting the old value */
173
char *old = strmap_put(&data->commands, hook_name,
t/t1800-hook.sh
+12
@@ -226,6 +226,18 @@ test_expect_success 'git hook list reorders on duplicate event declarations' '
226
test_cmp expected actual
227
'
228
229
+test_expect_success 'git hook list: empty event value resets events' '
230
+ setup_hooks &&
231
+
232
+ # ghi is configured for pre-commit; reset it with an empty value
233
+ test_config hook.ghi.event "" --add &&
234
+
235
+ # only def should remain for pre-commit
236
+ echo "def" >expected &&
237
+ git hook list pre-commit >actual &&
238
+ test_cmp expected actual
239
+'
240
+
241
test_expect_success 'hook can be configured for multiple events' '
242
setup_hooks &&
243