[PATCH v4 0/4] pre-merge-commit hook
- From
Josh Steadmon <steadmon@google.com>
- Date
- Aug 5, 2019, 22:43 UTC
- Message-ID
- <cover.1565044345.git.steadmon@google.com>
- In-Reply-To
- <cover.1564695892.git.steadmon@google.com>
This series adds a new pre-merge-commit hook, similar in usage to pre-commit. It also improves hook testing in t7503, by verifying that the correct hooks are run or bypassed as expected.
The original series was done by Michael J Gruber <git@grubix.eu>. I have addressed the outstanding review comments, and noted my changes in the commit messages in "[js: ...]" blocks.
Changes since V3: * Applied several test style fixes suggested by Martin (thanks!). * Clarified the documentation for pre-merge-commit hook. * Fixed a few cases where testing that the merge hook did not run might erroneously succeed if we don't have any merge to actually perform. * Simplified test cleanup by adding a new non-executable sample hook script. * Added test cases for non-executable pre-merge-commit hooks.
Changes since V2: * Renamed the hook from "pre-merge" to "pre-merge-commit". * Added a new patch (1/4) to improve t7503 by verifying that the expected hooks are (or are not) run. * Squashed test changes (from V2's patch 4/4) into patch 3/4. Modified the tests to follow the example set in patch 1/4. * Reworded commit messages to match with the current state of certain flags, which changed in between V1 and V2 of this series.
Josh Steadmon (1): t7503: verify proper hook execution
Michael J Gruber (3): merge: do no-verify like commit git-merge: honor pre-merge-commit hook merge: --no-verify to bypass pre-merge-commit hook
Documentation/git-merge.txt | 2 +- Documentation/githooks.txt | 22 ++ Documentation/merge-options.txt | 4 + builtin/merge.c | 18 +- ...3-pre-commit-and-pre-merge-commit-hooks.sh | 281 ++++++++++++++++++ t/t7503-pre-commit-hook.sh | 139 --------- templates/hooks--pre-merge-commit.sample | 13 + 7 files changed, 336 insertions(+), 143 deletions(-) create mode 100755 t/t7503-pre-commit-and-pre-merge-commit-hooks.sh delete mode 100755 t/t7503-pre-commit-hook.sh create mode 100755 templates/hooks--pre-merge-commit.sample
Range-diff against v3:
1: f0476b2b1e ! 1: 5085729095 t7503: verify proper hook execution
@@ Commit message
write_script() and doing setup inside a test_expect_success block.
+ Improved-by: Martin Ågren <martin.agren@gmail.com>
+ Signed-off-by: Martin Ågren <martin.agren@gmail.com>
## t/t7503-pre-commit-hook.sh ##
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
+ 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/"
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
- echo "foo" > file &&
+test_expect_success 'with no hook' '
-+ test_when_finished "rm -f expected_hooks actual_hooks" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f actual_hooks" &&
+ echo "foo" >file &&
git add file &&
- git commit -m "first"
-
+ git commit -m "first" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
test_expect_success '--no-verify with no hook' '
-
- echo "bar" > file &&
-+ test_when_finished "rm -f expected_hooks actual_hooks" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f actual_hooks" &&
+ echo "bar" >file &&
git add file &&
- git commit --no-verify -m "bar"
-
+ git commit --no-verify -m "bar" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
-# now install hook that always succeeds
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
- echo "more" >> file &&
+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+ ln -s "success.sample" "$PRECOMMIT" &&
-+ touch actual_hooks &&
+ echo "$PRECOMMIT" >expected_hooks &&
+ echo "more" >>file &&
git add file &&
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
test_expect_success '--no-verify with succeeding hook' '
-
- echo "even more" >> file &&
-+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
++ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+ ln -s "success.sample" "$PRECOMMIT" &&
-+ touch expected_hooks actual_hooks &&
+ echo "even more" >>file &&
git add file &&
- git commit --no-verify -m "even more"
-
+ git commit --no-verify -m "even more" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
-# now a hook that fails
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
- echo "another" >> file &&
+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+ ln -s "fail.sample" "$PRECOMMIT" &&
-+ touch actual_hooks &&
+ echo "$PRECOMMIT" >expected_hooks &&
+ echo "another" >>file &&
git add file &&
@@ t/t7503-pre-commit-hook.sh: test_description='pre-commit hook'
test_expect_success '--no-verify with failing hook' '
-
- echo "stuff" >> file &&
-+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
++ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+ ln -s "fail.sample" "$PRECOMMIT" &&
-+ touch expected_hooks actual_hooks &&
+ echo "stuff" >>file &&
git add file &&
- git commit --no-verify -m "stuff"
-
+ git commit --no-verify -m "stuff" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
-chmod -x "$HOOK"
test_expect_success POSIXPERM 'with non-executable hook' '
-
- echo "content" >> file &&
-+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks; chmod +x \"$HOOKDIR/fail.sample\"" &&
-+ ln -s "fail.sample" "$PRECOMMIT" &&
-+ chmod -x "$HOOKDIR/fail.sample" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
++ ln -s "non-exec.sample" "$PRECOMMIT" &&
+ echo "content" >>file &&
git add file &&
- git commit -m "content"
-
+ git commit -m "content" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
test_expect_success POSIXPERM '--no-verify with non-executable hook' '
-
- echo "more content" >> file &&
-+ test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks; chmod +x \"$HOOKDIR/fail.sample\"" &&
-+ ln -s "fail.sample" "$PRECOMMIT" &&
-+ chmod -x "$HOOKDIR/fail.sample" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
++ ln -s "non-exec.sample" "$PRECOMMIT" &&
+ echo "more content" >>file &&
git add file &&
- git commit --no-verify -m "more content"
-
+ git commit --no-verify -m "more content" &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
'
-chmod +x "$HOOK"
-
2: 89ddbf410f = 2: 3b701a5c41 merge: do no-verify like commit
3: 61b989ff16 ! 3: 9210421fbb git-merge: honor pre-merge-commit hook
@@ Commit message
[js: * renamed hook from "pre-merge" to "pre-merge-commit"
* only discard the index if the hook is actually present
+ * expanded githooks documentation entry
* clarified that hook should write messages to stderr
* squashed test changes from the original series' patch 4/4
* modified tests to follow new pattern from this series' patch 1/4
+ * added a test case for non-executable merge hooks
+ * added a test case for failed merges
+ * when testing that the merge hook did not run, make sure we
+ actually have a merge to perform (by resetting the "side" branch
+ to its original state).
* reworded commit message
]
+ Improved-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Michael J Gruber <git@grubix.eu>
+ Signed-off-by: Martin Ågren <martin.agren@gmail.com>
## Documentation/githooks.txt ##
@@ Documentation/githooks.txt: The default 'pre-commit' hook, when enabled--and wit
+pre-merge-commit
+~~~~~~~~~~~~~~~~
+
-+This hook is invoked by 'git merge' when doing an automatic merge
-+commit; it is equivalent to 'pre-commit' for a non-automatic commit
-+for a merge.
++This hook is invoked by linkgit:git-merge[1]. It takes no parameters, and is
++invoked after the merge has been carried out successfully and before
++obtaining the proposed commit log message to
++make a commit. Exiting with a non-zero status from this script
++causes the `git merge` command to abort before creating a commit.
++
++The default 'pre-merge-commit' hook, when enabled, runs the
++'pre-commit' hook, if the latter is enabled.
++
++This hook is invoked with the environment variable
++`GIT_EDITOR=:` if the command will not bring up an editor
++to modify the commit message.
++
++If the merge cannot be carried out automatically, the conflicts
++need to be resolved and the result committed separately (see
++linkgit:git-merge[1]). At that point, this hook will not be executed,
++but the 'pre-commit' hook will, if it is enabled.
+
prepare-commit-msg
~~~~~~~~~~~~~~~~~~
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success 'sample sc
'
+test_expect_success 'root commit' '
-+ echo "root" > file &&
++ echo "root" >file &&
+ git add file &&
+ git commit -m "zeroth" &&
+ git checkout -b side &&
-+ echo "foo" > foo &&
++ echo "foo" >foo &&
+ git add foo &&
+ git commit -m "make it non-ff" &&
++ git branch side-orig side &&
+ git checkout master
+'
++
++test_expect_success 'setup conflicting branches' '
++ test_when_finished "git checkout master" &&
++ git checkout -b conflicting-a master &&
++ echo a >conflicting &&
++ git add conflicting &&
++ git commit -m conflicting-a &&
++ git checkout -b conflicting-b master &&
++ echo b >conflicting &&
++ git add conflicting &&
++ git commit -m conflicting-b
++'
+
test_expect_success 'with no hook' '
- test_when_finished "rm -f expected_hooks actual_hooks" &&
- touch expected_hooks actual_hooks &&
+ test_when_finished "rm -f actual_hooks" &&
+ echo "foo" >file &&
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success 'with no hook' '
- test_cmp expected_hooks actual_hooks
+ test_path_is_missing actual_hooks
'
+test_expect_success 'with no hook (merge)' '
-+ test_when_finished "rm -f expected_hooks actual_hooks" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f actual_hooks" &&
++ git branch -f side side-orig &&
+ git checkout side &&
+ git merge -m "merge master" master &&
+ git checkout master &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
+'
+
test_expect_success '--no-verify with no hook' '
- test_when_finished "rm -f expected_hooks actual_hooks" &&
- touch expected_hooks actual_hooks &&
+ test_when_finished "rm -f actual_hooks" &&
+ echo "bar" >file &&
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success 'with succeeding hook' '
test_cmp expected_hooks actual_hooks
'
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success 'with succ
+test_expect_success 'with succeeding hook (merge)' '
+ test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
+ ln -s "success.sample" "$PREMERGE" &&
-+ touch actual_hooks &&
+ echo "$PREMERGE" >expected_hooks &&
+ git checkout side &&
+ git merge -m "merge master" master &&
+ git checkout master &&
+ test_cmp expected_hooks actual_hooks
+'
++
++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 master" &&
++ ln -s "success.sample" "$PREMERGE" &&
++ ln -s "success.sample" "$PRECOMMIT" &&
++
++ 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 a+b >conflicting &&
++ git add conflicting &&
++ git commit -m "resolve conflict" &&
++ test_cmp expected_hooks actual_hooks
++'
+
test_expect_success '--no-verify with succeeding hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
ln -s "success.sample" "$PRECOMMIT" &&
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success '--no-verify with failing hook' '
- test_cmp expected_hooks actual_hooks
+ test_path_is_missing actual_hooks
'
+test_expect_success 'with failing hook (merge)' '
+ test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
+ ln -s "fail.sample" "$PREMERGE" &&
-+ touch actual_hooks &&
+ echo "$PREMERGE" >expected_hooks &&
+ git checkout side &&
+ test_must_fail git merge -m "merge master" master &&
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success '--no-veri
+'
+
test_expect_success POSIXPERM 'with non-executable hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks; chmod +x \"$HOOKDIR/fail.sample\"" &&
- ln -s "fail.sample" "$PRECOMMIT" &&
+ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+ ln -s "non-exec.sample" "$PRECOMMIT" &&
+@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+ test_path_is_missing actual_hooks
+ '
+
++test_expect_success POSIXPERM 'with non-executable hook (merge)' '
++ test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
++ ln -s "non-exec.sample" "$PREMERGE" &&
++ git branch -f side side-orig &&
++ git checkout side &&
++ git merge -m "merge master" master &&
++ git checkout master &&
++ test_path_is_missing actual_hooks
++'
++
+ test_expect_success 'with hook requiring GIT_PREFIX' '
+ test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" &&
+ ln -s "require-prefix.sample" "$PRECOMMIT" &&
## templates/hooks--pre-merge-commit.sample (new) ##
@@
4: 45828c56fc ! 4: 96c54883d3 merge: --no-verify to bypass pre-merge-commit hook
@@ Commit message
* cleaned up trailing whitespace
* squashed test changes from the original series' patch 4/4
* modified tests to follow pattern from this series' patch 1/4
+ * added a test case for --no-verify with non-executable hook
+ * when testing that the merge hook did not run, make sure we
+ actually have a merge to perform (by resetting the "side" branch
+ to its original state).
+
]
+ Improved-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Michael J Gruber <git@grubix.eu>
+ Signed-off-by: Martin Ågren <martin.agren@gmail.com>
## Documentation/githooks.txt ##
-@@ Documentation/githooks.txt: pre-merge-commit
-
- This hook is invoked by 'git merge' when doing an automatic merge
- commit; it is equivalent to 'pre-commit' for a non-automatic commit
--for a merge.
-+for a merge, and can be bypassed with the `--no-verify` option.
+@@ Documentation/githooks.txt: the use of non-ASCII filenames.
+ pre-merge-commit
+ ~~~~~~~~~~~~~~~~
- prepare-commit-msg
- ~~~~~~~~~~~~~~~~~~
+-This hook is invoked by linkgit:git-merge[1]. It takes no parameters, and is
++This hook is invoked by linkgit:git-merge[1], and can be bypassed
++with the `--no-verify` option. It takes no parameters, and is
+ invoked after the merge has been carried out successfully and before
+ obtaining the proposed commit log message to
+ make a commit. Exiting with a non-zero status from this script
## builtin/merge.c ##
@@ builtin/merge.c: static struct option builtin_merge_options[] = {
@@ builtin/merge.c: static void prepare_to_commit(struct commit_list *remoteheads)
## t/t7503-pre-commit-and-pre-merge-commit-hooks.sh ##
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success '--no-verify with no hook' '
- test_cmp expected_hooks actual_hooks
+ test_path_is_missing actual_hooks
'
+test_expect_success '--no-verify with no hook (merge)' '
-+ test_when_finished "rm -f expected_hooks actual_hooks" &&
-+ touch expected_hooks actual_hooks &&
++ test_when_finished "rm -f actual_hooks" &&
++ git branch -f side side-orig &&
+ git checkout side &&
+ git merge --no-verify -m "merge master" master &&
+ git checkout master &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
+'
+
test_expect_success 'with succeeding hook' '
test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
ln -s "success.sample" "$PRECOMMIT" &&
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success '--no-verify with succeeding hook' '
- test_cmp expected_hooks actual_hooks
+ test_path_is_missing actual_hooks
'
+test_expect_success '--no-verify with succeeding hook (merge)' '
-+ test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
++ test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+ ln -s "success.sample" "$PREMERGE" &&
-+ touch expected_hooks actual_hooks &&
++ git branch -f side side-orig &&
+ git checkout side &&
+ git merge --no-verify -m "merge master" master &&
+ git checkout master &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
+'
+
test_expect_success 'with failing hook' '
@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success 'with fail
'
+test_expect_success '--no-verify with failing hook (merge)' '
-+ test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
++ test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+ ln -s "fail.sample" "$PREMERGE" &&
-+ touch expected_hooks actual_hooks &&
++ git branch -f side side-orig &&
+ git checkout side &&
+ git merge --no-verify -m "merge master" master &&
+ git checkout master &&
-+ test_cmp expected_hooks actual_hooks
++ test_path_is_missing actual_hooks
+'
+
test_expect_success POSIXPERM 'with non-executable hook' '
- test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks; chmod +x \"$HOOKDIR/fail.sample\"" &&
- ln -s "fail.sample" "$PRECOMMIT" &&
+ test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+ ln -s "non-exec.sample" "$PRECOMMIT" &&
+@@ t/t7503-pre-commit-and-pre-merge-commit-hooks.sh: test_expect_success POSIXPERM 'with non-executable hook (merge)' '
+ test_path_is_missing actual_hooks
+ '
+
++test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
++ test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
++ ln -s "non-exec.sample" "$PREMERGE" &&
++ git branch -f side side-orig &&
++ git checkout side &&
++ git merge --no-verify -m "merge master" master &&
++ git checkout master &&
++ test_path_is_missing actual_hooks
++'
++
+ test_expect_success 'with hook requiring GIT_PREFIX' '
+ test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" &&
+ ln -s "require-prefix.sample" "$PRECOMMIT" &&-- 2.22.0.770.g0f2c4a37fd-goog