@@ -12,23 +12,24 @@ void free_hook(struct hook *ptr)
}
}
-static void append_or_move_hook(struct list_head *head, const char *command)
+static struct hook* find_hook_by_command(struct list_head *head, const char *command)
{
struct list_head *pos = NULL, *tmp = NULL;
- struct hook *to_add = NULL;
+ struct hook *found = NULL;
- /*
- * remove the prior entry with this command; we'll replace it at the
- * end.
- */
list_for_each_safe(pos, tmp, head) {
struct hook *it = list_entry(pos, struct hook, list);
if (!strcmp(it->command.buf, command)) {
list_del(pos);
- /* we'll simply move the hook to the end */
- to_add = it;
+ found = it;
}
}
+ return found;
+}
+
+static void append_or_move_hook(struct list_head *head, const char *command)
+{
+ struct hook *to_add = find_hook_by_command(head, command);
if (!to_add) {
/* adding a new hook, not moving an old one */
@@ -41,7 +42,7 @@ static void append_or_move_hook(struct list_head *head, const char *command)
/* re-set the scope so we show where an override was specified */
to_add->origin = current_config_scope();
- list_add_tail(&to_add->list, pos);
+ list_add_tail(&to_add->list, head);
}
static void remove_hook(struct list_head *to_remove)
@@ -73,8 +74,18 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data)
if (!strcmp(key, hook_key)) {
const char *command = value;
struct strbuf hookcmd_name = STRBUF_INIT;
+ int skip = 0;
+
+ /*
+ * Check if we're removing that hook instead. Hookcmds are
+ * removed by name, and inlined hooks are removed by command
+ * content.
+ */
+ strbuf_addf(&hookcmd_name, "hookcmd.%s.skip", command);
+ git_config_get_bool(hookcmd_name.buf, &skip);
/* Check if a hookcmd with that name exists. */
+ strbuf_reset(&hookcmd_name);
strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command);
git_config_get_value(hookcmd_name.buf, &command);
@@ -89,7 +100,13 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data)
* for each key+value, do_callback(key, value, cb_data)
*/
- append_or_move_hook(head, command);
+ if (skip) {
+ struct hook *to_remove = find_hook_by_command(head, command);
+ if (to_remove)
+ remove_hook(&(to_remove->list));
+ } else {
+ append_or_move_hook(head, command);
+ }
strbuf_release(&hookcmd_name);
}
@@ -132,6 +132,29 @@ test_expect_success 'hook.runHookDir = warn is respected by list' '
test_i18ncmp expected actual
'
+test_expect_success 'git hook list removes skipped hookcmd' '
+ setup_hookcmd &&
+ test_config hookcmd.abc.skip "true" --add &&
+
+ cat >expected <<-EOF &&
+ no commands configured for hook '\''pre-commit'\''
+ EOF
+
+ git hook list pre-commit >actual &&
+ test_i18ncmp expected actual
+'
+
+test_expect_success 'git hook list removes skipped inlined hook' '
+ setup_hooks &&
+ test_config hookcmd."$ROOT/path/ghi".skip "true" --add &&
+
+ cat >expected <<-EOF &&
+ global: $ROOT/path/def
+ EOF
+
+ git hook list pre-commit >actual &&
+ test_cmp expected actual
+'
test_expect_success 'hook.runHookDir = interactive is respected by list' '
setup_hookdir &&
If a user wants a specific repo to skip execution of a hook which is set at a global or system level, they can now do so by specifying 'skip' in their repo config: ~/.gitconfig [hook.pre-commit] command = skippable-oneliner command = skippable-hookcmd [hookcmd.skippable-hookcmd] command = foo.sh $GIT_DIR/.git/config [hookcmd.skippable-oneliner] skip = true [hookcmd.skippable-hookcmd] skip = true Later it may make sense to add an option like "hookcmd.<name>.<hook-event>-skip" - but for simplicity, let's start with a universal skip setting like this. Signed-off-by: Emily Shaffer <emilyshaffer@google.com> --- Notes: In addition to being handy for turning off global hooks one project doesn't care about, this setting will be necessary much later for the 'proc-receive' hook, which can only cope with up to one hook being specified. New since v4. During the Google team's review club I was reminded about this whole 'skip' option I never implemented. It's true that it's impossible to exclude a given hook without this; however, I think I have some more work to do on it, so consider it RFC for now and tell me what you think :) - Emily During the Google team's review club this week I was reminded about this whole 'skip' option I never implemented. It's true that it's impossible to exclude a given hook without this; however, I think we have some more work to do on it, so consider it RFC for now and tell me what you think :) - Emily hook.c | 37 +++++++++++++++++++++++++---------- t/t1360-config-based-hooks.sh | 23 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-)