Introduce the git-commit command line options `--no-pre-commit` and
`--no-commit-msg` to disable the pre-commit and commit-msg hooks,
respectively. Make `--no-verify` a synonym for specifying both at the
same time.

This change is motivated by an internal usage of git-commit in
git-rebase--interactive to disable pre-commit while keeping
commit-msg enabled when rewording a commit.

Make `test_commit` forward unknown options to git-commit instead of
teaching it all possible options. In order to support leading double
dashes in `<message>`, stop interpreting `test_commit` arguments
following a `--` argument as options. This wasn't a problem before
because the first unknown option would be used as `<message>`.

Allow disabling tag creation to avoid name clashes when using
`test_commit` with the same arguments several times from the same
test suite. By default, `test_commit` tags successful commits using
git-tag for easy reference. The `--notag` option skips this step.

Add tests.

Signed-off-by: Fabian Ruch <baf...@gmail.com>
---
 Documentation/git-commit.txt |  8 ++++-
 builtin/commit.c             | 32 ++++++++++++++---
 t/t7503-pre-commit-hook.sh   | 65 ++++++++++++++++++++++++++++-----
 t/t7504-commit-msg-hook.sh   | 85 ++++++++++++++++++++++++++++++++++----------
 t/test-lib-functions.sh      | 23 ++++++++----
 5 files changed, 176 insertions(+), 37 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 0bbc8f5..28a2c5c 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -158,7 +158,7 @@ OPTIONS
 
 -n::
 --no-verify::
-       This option bypasses the pre-commit and commit-msg hooks.
+       A synonym for `--no-pre-commit --no-commit-msg`.
        See also linkgit:githooks[5].
 
 --allow-empty::
@@ -238,6 +238,12 @@ You should understand the implications of rewriting 
history if you
 amend a commit that has already been published.  (See the "RECOVERING
 FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 
+--no-pre-commit::
+       This option bypasses the pre-commit hook.
+
+--no-commit-msg::
+       This option bypasses the commit-msg hook.
+
 --no-post-rewrite::
        Bypass the post-rewrite hook.
 
diff --git a/builtin/commit.c b/builtin/commit.c
index 5ed6036..dfd354e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -98,12 +98,27 @@ static char *edit_message, *use_message;
 static char *fixup_message, *squash_message;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
 static int edit_flag = -1; /* unspecified */
-static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
+static int quiet, verbose, allow_empty, dry_run, renew_authorship;
 static int no_post_rewrite, allow_empty_message;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
 static char *sign_commit;
 
 /*
+ * The verify variable is interpreted as a bitmap of enabled commit
+ * verification hooks according to the legend below.
+ *
+ * By default, the pre-commit and commit-msg hooks are enabled. This
+ * is represented by both the PRE_COMMIT and COMMIT_MSG bits being
+ * set.
+ *
+ * The bitmap is changed through the command line options
+ * --no-verify, --no-pre-commit and --no-commit-msg.
+ */
+#define PRE_COMMIT (1<<0)
+#define COMMIT_MSG (1<<1)
+static int verify = PRE_COMMIT | COMMIT_MSG;
+
+/*
  * The default commit message cleanup mode will remove the lines
  * beginning with # (shell comments) and leading and trailing
  * whitespaces (empty lines or containing only whitespaces)
@@ -661,7 +676,8 @@ static int prepare_to_commit(const char *index_file, const 
char *prefix,
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
 
-       if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", 
NULL))
+       if (verify & PRE_COMMIT &&
+           run_commit_hook(use_editor, index_file, "pre-commit", NULL))
                return 0;
 
        if (squash_message) {
@@ -962,7 +978,7 @@ static int prepare_to_commit(const char *index_file, const 
char *prefix,
                }
        }
 
-       if (!no_verify &&
+       if (verify & COMMIT_MSG &&
            run_commit_hook(use_editor, index_file, "commit-msg", 
git_path(commit_editmsg), NULL)) {
                return 0;
        }
@@ -1590,7 +1606,9 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
                OPT_BOOL(0, "interactive", &interactive, N_("interactively add 
files")),
                OPT_BOOL('p', "patch", &patch_interactive, N_("interactively 
add changes")),
                OPT_BOOL('o', "only", &only, N_("commit only specified files")),
-               OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit 
hook")),
+               OPT_NEGBIT('n', "no-verify", &verify,
+                          N_("synonym for --no-pre-commit --no-commit-msg"),
+                          PRE_COMMIT | COMMIT_MSG),
                OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be 
committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status 
concisely"),
                            STATUS_FORMAT_SHORT),
@@ -1603,6 +1621,12 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
                OPT_BOOL('z', "null", &s.null_termination,
                         N_("terminate entries with NUL")),
                OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
+               OPT_NEGBIT(0, "no-pre-commit", &verify,
+                          N_("bypass pre-commit hook"),
+                          PRE_COMMIT),
+               OPT_NEGBIT(0, "no-commit-msg", &verify,
+                          N_("bypass commit-msg hook"),
+                          COMMIT_MSG),
                OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass 
post-rewrite hook")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, 
N_("mode"), N_("show untracked files, optional modes: all, normal, no. 
(Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
                /* end commit contents options */
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
index 984889b..db5de1c 100755
--- a/t/t7503-pre-commit-hook.sh
+++ b/t/t7503-pre-commit-hook.sh
@@ -12,11 +12,11 @@ test_expect_success 'with no hook' '
 
 '
 
-test_expect_success '--no-verify with no hook' '
+test_expect_success '--no-pre-commit with no hook' '
 
        echo "bar" > file &&
        git add file &&
-       git commit --no-verify -m "bar"
+       git commit --no-pre-commit -m "bar"
 
 '
 
@@ -38,11 +38,11 @@ test_expect_success 'with succeeding hook' '
 
 '
 
-test_expect_success '--no-verify with succeeding hook' '
+test_expect_success '--no-pre-commit with succeeding hook' '
 
        echo "even more" >> file &&
        git add file &&
-       git commit --no-verify -m "even more"
+       git commit --no-pre-commit -m "even more"
 
 '
 
@@ -60,11 +60,11 @@ test_expect_success 'with failing hook' '
 
 '
 
-test_expect_success '--no-verify with failing hook' '
+test_expect_success '--no-pre-commit with failing hook' '
 
        echo "stuff" >> file &&
        git add file &&
-       git commit --no-verify -m "stuff"
+       git commit --no-pre-commit -m "stuff"
 
 '
 
@@ -77,15 +77,64 @@ test_expect_success POSIXPERM 'with non-executable hook' '
 
 '
 
-test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-pre-commit with non-executable hook' '
 
        echo "more content" >> file &&
        git add file &&
-       git commit --no-verify -m "more content"
+       git commit --no-pre-commit -m "more content"
 
 '
 chmod +x "$HOOK"
 
+test_hook_enabled () {
+       git checkout --detach master &&
+       output="running failing pre-commit hook with ${*:-(none)}..." &&
+       >actual.output &&
+       cat >"$HOOK" <<-EOF &&
+       #!/bin/sh
+       echo "$output" >>actual.output
+       exit 1
+       EOF
+       chmod +x "$HOOK" &&
+       echo "$output" >expected.output &&
+       test_must_fail test_commit $* file &&
+       test_cmp expected.output actual.output
+}
+
+test_hook_disabled () {
+       git checkout --detach master &&
+       output="running failing pre-commit hook with ${*:-(none)}..." &&
+       >actual.output &&
+       cat >"$HOOK" <<-EOF &&
+       #!/bin/sh
+       echo "$output" >>actual.output
+       exit 1
+       EOF
+       chmod +x "$HOOK" &&
+       test_commit --notag $* file &&
+       test_must_be_empty actual.output
+}
+
+test_expect_success 'command line options combinations' '
+       test_hook_enabled &&
+       test_hook_enabled --pre-commit &&
+       test_hook_enabled --no-pre-commit --pre-commit &&
+       test_hook_enabled --no-verify --pre-commit &&
+       test_hook_enabled --verify &&
+       test_hook_enabled --no-pre-commit --verify &&
+       test_hook_enabled --no-verify --verify &&
+       test_hook_enabled --verify --no-commit-msg &&
+       test_hook_enabled --verify --commit-msg &&
+       test_hook_disabled --no-pre-commit &&
+       test_hook_disabled --pre-commit --no-pre-commit &&
+       test_hook_disabled --pre-commit --no-verify &&
+       test_hook_disabled --no-verify &&
+       test_hook_disabled --verify --no-pre-commit &&
+       test_hook_disabled --verify --no-verify &&
+       test_hook_disabled --no-verify --no-commit-msg &&
+       test_hook_disabled --no-verify --commit-msg
+'
+
 # a hook that checks $GIT_PREFIX and succeeds inside the
 # success/ subdirectory only
 cat > "$HOOK" <<EOF
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 1f53ea8..59642a3 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -34,20 +34,20 @@ test_expect_success 'with no hook (editor)' '
 
 '
 
-test_expect_success '--no-verify with no hook' '
+test_expect_success '--no-commit-msg with no hook' '
 
        echo "bar" > file &&
        git add file &&
-       git commit --no-verify -m "bar"
+       git commit --no-commit-msg -m "bar"
 
 '
 
-test_expect_success '--no-verify with no hook (editor)' '
+test_expect_success '--no-commit-msg with no hook (editor)' '
 
        echo "more bar" > file &&
        git add file &&
        echo "more bar" > FAKE_MSG &&
-       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-commit-msg
 
 '
 
@@ -78,20 +78,20 @@ test_expect_success 'with succeeding hook (editor)' '
 
 '
 
-test_expect_success '--no-verify with succeeding hook' '
+test_expect_success '--no-commit-msg with succeeding hook' '
 
        echo "even more" >> file &&
        git add file &&
-       git commit --no-verify -m "even more"
+       git commit --no-commit-msg -m "even more"
 
 '
 
-test_expect_success '--no-verify with succeeding hook (editor)' '
+test_expect_success '--no-commit-msg with succeeding hook (editor)' '
 
        echo "even more more" >> file &&
        git add file &&
        echo "even more more" > FAKE_MSG &&
-       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-commit-msg
 
 '
 
@@ -118,20 +118,20 @@ test_expect_success 'with failing hook (editor)' '
 
 '
 
-test_expect_success '--no-verify with failing hook' '
+test_expect_success '--no-commit-msg with failing hook' '
 
        echo "stuff" >> file &&
        git add file &&
-       git commit --no-verify -m "stuff"
+       git commit --no-commit-msg -m "stuff"
 
 '
 
-test_expect_success '--no-verify with failing hook (editor)' '
+test_expect_success '--no-commit-msg with failing hook (editor)' '
 
        echo "more stuff" >> file &&
        git add file &&
        echo "more stuff" > FAKE_MSG &&
-       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-commit-msg
 
 '
 
@@ -153,23 +153,72 @@ test_expect_success POSIXPERM 'with non-executable hook 
(editor)' '
 
 '
 
-test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+test_expect_success POSIXPERM '--no-commit-msg with non-executable hook' '
 
        echo "more content" >> file &&
        git add file &&
-       git commit --no-verify -m "more content"
+       git commit --no-commit-msg -m "more content"
 
 '
 
-test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
+test_expect_success POSIXPERM '--no-commit-msg with non-executable hook 
(editor)' '
 
        echo "even more content" >> file &&
        git add file &&
        echo "even more content" > FAKE_MSG &&
-       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-commit-msg
 
 '
 
+test_hook_enabled () {
+       git checkout --detach master &&
+       output="running failing commit-msg hook with ${*:-(none)}..." &&
+       >actual.output &&
+       cat >"$HOOK" <<-EOF &&
+       #!/bin/sh
+       echo "$output" >>actual.output
+       exit 1
+       EOF
+       chmod +x "$HOOK" &&
+       echo "$output" >expected.output &&
+       test_must_fail test_commit $* file &&
+       test_cmp expected.output actual.output
+}
+
+test_hook_disabled () {
+       git checkout --detach master &&
+       output="running failing commit-msg hook with ${*:-(none)}..." &&
+       >actual.output &&
+       cat >"$HOOK" <<-EOF &&
+       #!/bin/sh
+       echo "$output" >>actual.output
+       exit 1
+       EOF
+       chmod +x "$HOOK" &&
+       test_commit --notag $* file &&
+       test_must_be_empty actual.output
+}
+
+test_expect_success 'command line options combinations' '
+       test_hook_enabled &&
+       test_hook_enabled --commit-msg &&
+       test_hook_enabled --no-commit-msg --commit-msg &&
+       test_hook_enabled --no-verify --commit-msg &&
+       test_hook_enabled --verify &&
+       test_hook_enabled --no-commit-msg --verify &&
+       test_hook_enabled --no-verify --verify &&
+       test_hook_enabled --verify --no-pre-commit &&
+       test_hook_enabled --verify --pre-commit &&
+       test_hook_disabled --no-commit-msg &&
+       test_hook_disabled --commit-msg --no-commit-msg &&
+       test_hook_disabled --commit-msg --no-verify &&
+       test_hook_disabled --no-verify &&
+       test_hook_disabled --verify --no-commit-msg &&
+       test_hook_disabled --verify --no-verify &&
+       test_hook_disabled --no-verify --no-pre-commit &&
+       test_hook_disabled --no-verify --pre-commit
+'
+
 # now a hook that edits the commit message
 cat > "$HOOK" <<'EOF'
 #!/bin/sh
@@ -205,7 +254,7 @@ test_expect_success "hook doesn't edit commit message" '
 
        echo "plus" >> file &&
        git add file &&
-       git commit --no-verify -m "plus" &&
+       git commit --no-commit-msg -m "plus" &&
        commit_msg_is "plus"
 
 '
@@ -215,7 +264,7 @@ test_expect_success "hook doesn't edit commit message 
(editor)" '
        echo "more plus" >> file &&
        git add file &&
        echo "more plus" > FAKE_MSG &&
-       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-commit-msg &&
        commit_msg_is "more plus"
 
 '
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index dafd6ad..a107073 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -154,20 +154,28 @@ test_pause () {
 
 test_commit () {
        notick= &&
-       signoff= &&
+       notag= &&
+       commit_opts= &&
        while test $# != 0
        do
                case "$1" in
                --notick)
                        notick=yes
                        ;;
-               --signoff)
-                       signoff="$1"
+               --notag)
+                       notag=yes
+                       ;;
+               --)
+                       shift &&
+                       break
+                       ;;
+               -*)
+                       commit_opts="$commit_opts $1"
                        ;;
                *)
                        break
                        ;;
-               esac
+               esac &&
                shift
        done &&
        file=${2:-"$1.t"} &&
@@ -177,8 +185,11 @@ test_commit () {
        then
                test_tick
        fi &&
-       git commit $signoff -m "$1" &&
-       git tag "${4:-$1}"
+       git commit $commit_opts -m "$1" &&
+       if test -z "$notag"
+       then
+               git tag "${4:-$1}"
+       fi
 }
 
 # Call test_merge with the arguments "<message> <commit>", where <commit>
-- 
2.0.1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to