]> git.ipfire.org Git - thirdparty/git.git/commitdiff
tests: extend "test_hook" for "rm" and "chmod -x", convert "$HOOK"
authorÆvar Arnfjörð Bjarmason <avarab@gmail.com>
Thu, 17 Mar 2022 10:13:16 +0000 (11:13 +0100)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Mar 2022 21:42:14 +0000 (14:42 -0700)
Extend the "test_hook" function to take options to disable and remove
hooks. Using the wrapper instead of getting the path and running
"chmod -x" or "rm" will make it easier to eventually emulate the same
behavior with config-based hooks.

Not all of these tests need that new mode, but since the rest are
either closely related or use the same "$HOOK" pattern let's convert
them too.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t2400-worktree-add.sh
t/t5543-atomic-push.sh
t/t5571-pre-push-hook.sh
t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
t/t7504-commit-msg-hook.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7520-ignored-hook-warning.sh
t/test-lib-functions.sh

index 43139af08fc951a69cf31d98d184f3d24697ceeb..2f564d533d05965fff88f51bc0f1280abae43518 100755 (executable)
@@ -559,10 +559,7 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
 '
 
 post_checkout_hook () {
-       gitdir=${1:-.git}
-       test_when_finished "rm -f $gitdir/hooks/post-checkout" &&
-       mkdir -p $gitdir/hooks &&
-       write_script $gitdir/hooks/post-checkout <<-\EOF
+       test_hook -C "$1" post-checkout <<-\EOF
        {
                echo $*
                git rev-parse --git-dir --show-toplevel
index bfee461861e456b091ea4c68025a0127d09dbee6..70431122a40360c265dc28627c4208dc360c000c 100755 (executable)
@@ -162,16 +162,10 @@ test_expect_success 'atomic push obeys update hook preventing a branch to be pus
                test_commit two &&
                git push --mirror up
        ) &&
-       (
-               cd upstream &&
-               HOOKDIR="$(git rev-parse --git-dir)/hooks" &&
-               HOOK="$HOOKDIR/update" &&
-               mkdir -p "$HOOKDIR" &&
-               write_script "$HOOK" <<-\EOF
-                       # only allow update to main from now on
-                       test "$1" = "refs/heads/main"
-               EOF
-       ) &&
+       test_hook -C upstream update <<-\EOF &&
+       # only allow update to main from now on
+       test "$1" = "refs/heads/main"
+       EOF
        (
                cd workbench &&
                git checkout main &&
index 96d6ecc0af7b433788124a261990ca4ec520970b..a11b20e378223ea30242e70d5befcfeed889a2ed 100755 (executable)
@@ -6,16 +6,11 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
-# Setup hook that always succeeds
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/pre-push"
-mkdir -p "$HOOKDIR"
-write_script "$HOOK" <<EOF
-cat >actual
-exit 0
-EOF
-
 test_expect_success 'setup' '
+       test_hook pre-push <<-\EOF &&
+       cat >actual
+       EOF
+
        git config push.default upstream &&
        git init --bare repo1 &&
        git remote add parent1 repo1 &&
@@ -28,15 +23,16 @@ test_expect_success 'setup' '
        git push parent1 HEAD:foreign &&
        test_cmp expect actual
 '
-write_script "$HOOK" <<EOF
-cat >actual
-exit 1
-EOF
 
 COMMIT1="$(git rev-parse HEAD)"
 export COMMIT1
 
 test_expect_success 'push with failing hook' '
+       test_hook pre-push <<-\EOF &&
+       cat >actual &&
+       exit 1
+       EOF
+
        test_commit two &&
        cat >expect <<-EOF &&
        HEAD $(git rev-parse HEAD) refs/heads/main $(test_oid zero)
@@ -55,13 +51,13 @@ test_expect_success '--no-verify bypasses hook' '
 COMMIT2="$(git rev-parse HEAD)"
 export COMMIT2
 
-write_script "$HOOK" <<'EOF'
-echo "$1" >actual
-echo "$2" >>actual
-cat >>actual
-EOF
-
 test_expect_success 'push with hook' '
+       test_hook --setup pre-push <<-\EOF &&
+       echo "$1" >actual
+       echo "$2" >>actual
+       cat >>actual
+       EOF
+
        cat >expect <<-EOF &&
        parent1
        repo1
@@ -136,7 +132,9 @@ test_expect_success 'set up many-ref tests' '
 '
 
 test_expect_success 'sigpipe does not cause pre-push hook failure' '
-       echo "exit 0" | write_script "$HOOK" &&
+       test_hook --clobber pre-push <<-\EOF &&
+       exit 0
+       EOF
        git push parent1 "refs/heads/b/*:refs/heads/b/*"
 '
 
index 606d8d0f08909622d7f916ba4fd55bdfb3d820f1..ad1eb64ba0db16d4f1452961c0a226fdf2e5cce0 100755 (executable)
@@ -7,37 +7,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-PRECOMMIT="$HOOKDIR/pre-commit"
-PREMERGE="$HOOKDIR/pre-merge-commit"
-
-# Prepare sample scripts that write their $0 to actual_hooks
-test_expect_success 'sample script setup' '
-       mkdir -p "$HOOKDIR" &&
-       write_script "$HOOKDIR/success.sample" <<-\EOF &&
-       echo $0 >>actual_hooks
-       exit 0
-       EOF
-       write_script "$HOOKDIR/fail.sample" <<-\EOF &&
-       echo $0 >>actual_hooks
-       exit 1
-       EOF
-       write_script "$HOOKDIR/non-exec.sample" <<-\EOF &&
-       echo $0 >>actual_hooks
-       exit 1
-       EOF
-       chmod -x "$HOOKDIR/non-exec.sample" &&
-       write_script "$HOOKDIR/require-prefix.sample" <<-\EOF &&
-       echo $0 >>actual_hooks
-       test $GIT_PREFIX = "success/"
-       EOF
-       write_script "$HOOKDIR/check-author.sample" <<-\EOF
-       echo $0 >>actual_hooks
-       test "$GIT_AUTHOR_NAME" = "New Author" &&
-       test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
-       EOF
-'
-
 test_expect_success 'root commit' '
        echo "root" >file &&
        git add file &&
@@ -96,10 +65,16 @@ test_expect_success '--no-verify with no hook (merge)' '
        test_path_is_missing actual_hooks
 '
 
+setup_success_hook () {
+       test_when_finished "rm -f actual_hooks expected_hooks" &&
+       echo "$1" >expected_hooks &&
+       test_hook "$1" <<-EOF
+       echo $1 >>actual_hooks
+       EOF
+}
+
 test_expect_success 'with succeeding hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
-       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
-       echo "$PRECOMMIT" >expected_hooks &&
+       setup_success_hook "pre-commit" &&
        echo "more" >>file &&
        git add file &&
        git commit -m "more" &&
@@ -107,9 +82,7 @@ test_expect_success 'with succeeding hook' '
 '
 
 test_expect_success 'with succeeding hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
-       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
-       echo "$PREMERGE" >expected_hooks &&
+       setup_success_hook "pre-merge-commit" &&
        git checkout side &&
        git merge -m "merge main" main &&
        git checkout main &&
@@ -117,17 +90,14 @@ test_expect_success 'with succeeding hook (merge)' '
 '
 
 test_expect_success 'automatic merge fails; both hooks are available' '
-       test_when_finished "rm -f \"$PREMERGE\" \"$PRECOMMIT\"" &&
-       test_when_finished "rm -f expected_hooks actual_hooks" &&
-       test_when_finished "git checkout main" &&
-       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
-       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+       setup_success_hook "pre-commit" &&
+       setup_success_hook "pre-merge-commit" &&
 
        git checkout conflicting-a &&
        test_must_fail git merge -m "merge conflicting-b" conflicting-b &&
        test_path_is_missing actual_hooks &&
 
-       echo "$PRECOMMIT" >expected_hooks &&
+       echo "pre-commit" >expected_hooks &&
        echo a+b >conflicting &&
        git add conflicting &&
        git commit -m "resolve conflict" &&
@@ -135,8 +105,7 @@ test_expect_success 'automatic merge fails; both hooks are available' '
 '
 
 test_expect_success '--no-verify with succeeding hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
-       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+       setup_success_hook "pre-commit" &&
        echo "even more" >>file &&
        git add file &&
        git commit --no-verify -m "even more" &&
@@ -144,8 +113,7 @@ test_expect_success '--no-verify with succeeding hook' '
 '
 
 test_expect_success '--no-verify with succeeding hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
-       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
+       setup_success_hook "pre-merge-commit" &&
        git branch -f side side-orig &&
        git checkout side &&
        git merge --no-verify -m "merge main" main &&
@@ -153,10 +121,19 @@ test_expect_success '--no-verify with succeeding hook (merge)' '
        test_path_is_missing actual_hooks
 '
 
+setup_failing_hook () {
+       test_when_finished "rm -f actual_hooks" &&
+       test_hook "$1" <<-EOF
+       echo $1-failing-hook >>actual_hooks
+       exit 1
+       EOF
+}
+
 test_expect_success 'with failing hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
-       cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
-       echo "$PRECOMMIT" >expected_hooks &&
+       setup_failing_hook "pre-commit" &&
+       test_when_finished "rm -f expected_hooks" &&
+       echo "pre-commit-failing-hook" >expected_hooks &&
+
        echo "another" >>file &&
        git add file &&
        test_must_fail git commit -m "another" &&
@@ -164,8 +141,7 @@ test_expect_success 'with failing hook' '
 '
 
 test_expect_success '--no-verify with failing hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
-       cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
+       setup_failing_hook "pre-commit" &&
        echo "stuff" >>file &&
        git add file &&
        git commit --no-verify -m "stuff" &&
@@ -173,9 +149,8 @@ test_expect_success '--no-verify with failing hook' '
 '
 
 test_expect_success 'with failing hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
-       cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
-       echo "$PREMERGE" >expected_hooks &&
+       setup_failing_hook "pre-merge-commit" &&
+       echo "pre-merge-commit-failing-hook" >expected_hooks &&
        git checkout side &&
        test_must_fail git merge -m "merge main" main &&
        git checkout main &&
@@ -183,8 +158,8 @@ test_expect_success 'with failing hook (merge)' '
 '
 
 test_expect_success '--no-verify with failing hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
-       cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
+       setup_failing_hook "pre-merge-commit" &&
+
        git branch -f side side-orig &&
        git checkout side &&
        git merge --no-verify -m "merge main" main &&
@@ -192,9 +167,18 @@ test_expect_success '--no-verify with failing hook (merge)' '
        test_path_is_missing actual_hooks
 '
 
+setup_non_exec_hook () {
+       test_when_finished "rm -f actual_hooks" &&
+       test_hook "$1" <<-\EOF &&
+       echo non-exec >>actual_hooks
+       exit 1
+       EOF
+       test_hook --disable "$1"
+}
+
+
 test_expect_success POSIXPERM 'with non-executable hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
-       cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+       setup_non_exec_hook "pre-commit" &&
        echo "content" >>file &&
        git add file &&
        git commit -m "content" &&
@@ -202,8 +186,7 @@ test_expect_success POSIXPERM 'with non-executable hook' '
 '
 
 test_expect_success POSIXPERM '--no-verify with non-executable hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
-       cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+       setup_non_exec_hook "pre-commit" &&
        echo "more content" >>file &&
        git add file &&
        git commit --no-verify -m "more content" &&
@@ -211,8 +194,7 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook' '
 '
 
 test_expect_success POSIXPERM 'with non-executable hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
-       cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+       setup_non_exec_hook "pre-merge" &&
        git branch -f side side-orig &&
        git checkout side &&
        git merge -m "merge main" main &&
@@ -221,8 +203,7 @@ test_expect_success POSIXPERM 'with non-executable hook (merge)' '
 '
 
 test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
-       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
-       cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+       setup_non_exec_hook "pre-merge" &&
        git branch -f side side-orig &&
        git checkout side &&
        git merge --no-verify -m "merge main" main &&
@@ -230,10 +211,18 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
        test_path_is_missing actual_hooks
 '
 
+setup_require_prefix_hook () {
+       test_when_finished "rm -f expected_hooks" &&
+       echo require-prefix >expected_hooks &&
+       test_hook pre-commit <<-\EOF
+       echo require-prefix >>actual_hooks
+       test $GIT_PREFIX = "success/"
+       EOF
+}
+
 test_expect_success 'with hook requiring GIT_PREFIX' '
-       test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" &&
-       cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
-       echo "$PRECOMMIT" >expected_hooks &&
+       test_when_finished "rm -rf actual_hooks success" &&
+       setup_require_prefix_hook &&
        echo "more content" >>file &&
        git add file &&
        mkdir success &&
@@ -245,9 +234,8 @@ test_expect_success 'with hook requiring GIT_PREFIX' '
 '
 
 test_expect_success 'with failing hook requiring GIT_PREFIX' '
-       test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks fail" &&
-       cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
-       echo "$PRECOMMIT" >expected_hooks &&
+       test_when_finished "rm -rf actual_hooks fail" &&
+       setup_require_prefix_hook &&
        echo "more content" >>file &&
        git add file &&
        mkdir fail &&
@@ -259,13 +247,23 @@ test_expect_success 'with failing hook requiring GIT_PREFIX' '
        test_cmp expected_hooks actual_hooks
 '
 
+setup_require_author_hook () {
+       test_when_finished "rm -f expected_hooks actual_hooks" &&
+       echo check-author >expected_hooks &&
+       test_hook pre-commit <<-\EOF
+       echo check-author >>actual_hooks
+       test "$GIT_AUTHOR_NAME" = "New Author" &&
+       test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+       EOF
+}
+
+
 test_expect_success 'check the author in hook' '
-       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
-       cp "$HOOKDIR/check-author.sample" "$PRECOMMIT" &&
+       setup_require_author_hook &&
        cat >expected_hooks <<-EOF &&
-       $PRECOMMIT
-       $PRECOMMIT
-       $PRECOMMIT
+       check-author
+       check-author
+       check-author
        EOF
        test_must_fail git commit --allow-empty -m "by a.u.thor" &&
        (
index bba58f04806fdfc247c1b72112ed6805340934c1..a39de8c112674f070f8dc19745e27980b7b52c9c 100755 (executable)
@@ -54,15 +54,11 @@ test_expect_success '--no-verify with no hook (editor)' '
 
 '
 
-# now install hook that always succeeds
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/commit-msg"
-mkdir -p "$HOOKDIR"
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 0
-EOF
-chmod +x "$HOOK"
+test_expect_success 'setup: commit-msg hook that always succeeds' '
+       test_hook --setup commit-msg <<-\EOF
+       exit 0
+       EOF
+'
 
 test_expect_success 'with succeeding hook' '
 
@@ -98,11 +94,11 @@ test_expect_success '--no-verify with succeeding hook (editor)' '
 
 '
 
-# now a hook that fails
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 1
-EOF
+test_expect_success 'setup: commit-msg hook that always fails' '
+       test_hook --clobber commit-msg <<-\EOF
+       exit 1
+       EOF
+'
 
 commit_msg_is () {
        test "$(git log --pretty=format:%s%b -1)" = "$1"
@@ -176,8 +172,12 @@ test_expect_success 'merge bypasses failing hook with --no-verify' '
        commit_msg_is "Merge branch '\''main'\'' into newbranch"
 '
 
+test_expect_success 'setup: commit-msg hook made non-executable' '
+       git_dir="$(git rev-parse --git-dir)" &&
+       chmod -x "$git_dir/hooks/commit-msg"
+'
+
 
-chmod -x "$HOOK"
 test_expect_success POSIXPERM 'with non-executable hook' '
 
        echo "content" >file &&
@@ -212,13 +212,12 @@ test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
 
 '
 
-# now a hook that edits the commit message
-cat > "$HOOK" <<'EOF'
-#!/bin/sh
-echo "new message" > "$1"
-exit 0
-EOF
-chmod +x "$HOOK"
+test_expect_success 'setup: commit-msg hook that edits the commit message' '
+       test_hook --clobber commit-msg <<-\EOF
+       echo "new message" >"$1"
+       exit 0
+       EOF
+'
 
 test_expect_success 'hook edits commit message' '
 
index e39c809ca42f04dbda578689e627b4aceb67d044..2128142a61c60da918381f6b6f74ca612ac9227b 100755 (executable)
@@ -47,25 +47,19 @@ test_expect_success 'with no hook' '
 
 '
 
-# set up fake editor for interactive editing
-cat > fake-editor <<'EOF'
-#!/bin/sh
-exit 0
-EOF
-chmod +x fake-editor
-
-## Not using test_set_editor here so we can easily ensure the editor variable
-## is only set for the editor tests
-FAKE_EDITOR="$(pwd)/fake-editor"
-export FAKE_EDITOR
+test_expect_success 'setup fake editor for interactive editing' '
+       write_script fake-editor <<-\EOF &&
+       exit 0
+       EOF
 
-# now install hook that always succeeds and adds a message
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/prepare-commit-msg"
-mkdir -p "$HOOKDIR"
-echo "#!$SHELL_PATH" > "$HOOK"
-cat >> "$HOOK" <<'EOF'
+       ## Not using test_set_editor here so we can easily ensure the editor variable
+       ## is only set for the editor tests
+       FAKE_EDITOR="$(pwd)/fake-editor" &&
+       export FAKE_EDITOR
+'
 
+test_expect_success 'setup prepare-commit-msg hook' '
+       test_hook --setup prepare-commit-msg <<\EOF
 GIT_DIR=$(git rev-parse --git-dir)
 if test -d "$GIT_DIR/rebase-merge"
 then
@@ -103,7 +97,7 @@ else
 fi
 exit 0
 EOF
-chmod +x "$HOOK"
+'
 
 echo dummy template > "$(git rev-parse --git-dir)/template"
 
@@ -265,10 +259,11 @@ test_expect_success 'with hook and editor (cherry-pick)' '
        test "$(git log -1 --pretty=format:%s)" = merge
 '
 
-cat > "$HOOK" <<'EOF'
-#!/bin/sh
-exit 1
-EOF
+test_expect_success 'setup: commit-msg hook that always fails' '
+       test_hook --setup --clobber prepare-commit-msg <<-\EOF
+       exit 1
+       EOF
+'
 
 test_expect_success 'with failing hook' '
 
@@ -296,9 +291,9 @@ test_expect_success 'with failing hook (merge)' '
        git checkout -B other HEAD@{1} &&
        echo "more" >> file &&
        git add file &&
-       rm -f "$HOOK" &&
+       test_hook --remove prepare-commit-msg &&
        git commit -m other &&
-       write_script "$HOOK" <<-EOF &&
+       test_hook --setup prepare-commit-msg <<-\EOF &&
        exit 1
        EOF
        git checkout - &&
index 634fb7f23a040be8e6afbf7e3946048f7b3dc163..dc57526e6f1f7af741a4cd6c1ce4800563be5c37 100755 (executable)
@@ -5,10 +5,7 @@ test_description='ignored hook warning'
 . ./test-lib.sh
 
 test_expect_success setup '
-       hookdir="$(git rev-parse --git-dir)/hooks" &&
-       hook="$hookdir/pre-commit" &&
-       mkdir -p "$hookdir" &&
-       write_script "$hook" <<-\EOF
+       test_hook --setup pre-commit <<-\EOF
        exit 0
        EOF
 '
@@ -19,20 +16,20 @@ test_expect_success 'no warning if hook is not ignored' '
 '
 
 test_expect_success POSIXPERM 'warning if hook is ignored' '
-       chmod -x "$hook" &&
+       test_hook --disable pre-commit &&
        git commit --allow-empty -m "even more" 2>message &&
        test_i18ngrep -e "hook was ignored" message
 '
 
 test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
        test_config advice.ignoredHook false &&
-       chmod -x "$hook" &&
+       test_hook --disable pre-commit &&
        git commit --allow-empty -m "even more" 2>message &&
        test_i18ngrep ! -e "hook was ignored" message
 '
 
 test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
-       rm -f "$hook" &&
+       test_hook --remove pre-commit &&
        test_unconfig advice.ignoredHook &&
        git commit --allow-empty -m "even more" 2>message &&
        test_i18ngrep ! -e "hook was ignored" message
index 6c9c61c79c2dd3b813c460a49297d5f46b5bcfe7..a027f0c409e3e43abbc3b98dc3b30604a411ef41 100644 (file)
@@ -562,9 +562,15 @@ write_script () {
 #      Overwrite an existing <hook-name>, if it exists. Implies
 #      --setup (i.e. the "test_when_finished" is assumed to have been
 #      set up already).
+#    --disable
+#      Disable (chmod -x) an existing <hook-name>, which must exist.
+#    --remove
+#      Remove (rm -f) an existing <hook-name>, which must exist.
 test_hook () {
        setup= &&
        clobber= &&
+       disable= &&
+       remove= &&
        indir= &&
        while test $# != 0
        do
@@ -579,6 +585,12 @@ test_hook () {
                --clobber)
                        clobber=t
                        ;;
+               --disable)
+                       disable=t
+                       ;;
+               --remove)
+                       remove=t
+                       ;;
                -*)
                        BUG "invalid argument: $1"
                        ;;
@@ -592,6 +604,18 @@ test_hook () {
        git_dir=$(git -C "$indir" rev-parse --absolute-git-dir) &&
        hook_dir="$git_dir/hooks" &&
        hook_file="$hook_dir/$1" &&
+       if test -n "$disable$remove"
+       then
+               test_path_is_file "$hook_file" &&
+               if test -n "$disable"
+               then
+                       chmod -x "$hook_file"
+               elif test -n "$remove"
+               then
+                       rm -f "$hook_file"
+               fi &&
+               return 0
+       fi &&
        if test -z "$clobber"
        then
                test_path_is_missing "$hook_file"