pre-merge-hook
cover pre-merge-hook
The pre-commit hook is often used to ensure certain properties of each comitted tree like formatting or coding standards, validity (lint/make) or code quality (make test). But merges introduce new commits unless they are fast forwards, and therefore they can break these properties because the pre-commit hook is not run by "git merge".
Introduce a pre-merge hook which works for (non ff, automatic) merges like pre-commit does for commits. Typically this will just call the pre-commit hook (like in the sample hook), but it does not need to.
Michael J Gruber (3):
git-merge: Honor pre-merge hook
merge: --no-verify to bypass pre-merge hook
t7503: add tests for pre-merge-hook
Documentation/git-merge.txt | 2 +- Documentation/githooks.txt | 7 +++++ Documentation/merge-options.txt | 4 +++ builtin/merge.c | 15 ++++++++- t/t7503-pre-commit-hook.sh | 66 ++++++++++++++++++++++++++++++++++++++- templates/hooks--pre-merge.sample | 13 ++++++++ 6 files changed, 104 insertions(+), 3 deletions(-) create mode 100755 templates/hooks--pre-merge.sample
Signature · 1 line
1.7.12.406.gafd3f81
PATCH 1/3 git-merge: Honor pre-merge hook
git-merge does not honor the pre-commit hook when doing automatic merge commits, and for compatibility reasons this is going to stay.
Introduce a pre-merge hook which is called for an automatic merge commit just like pre-commit is called for a non-automatic merge commit (or any other commit).
--- Documentation/githooks.txt | 7 +++++++ builtin/merge.c | 13 ++++++++++++- templates/hooks--pre-merge.sample | 13 +++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100755 templates/hooks--pre-merge.sample
Documentation/githooks.txt +7 -0
@@ -86,6 +86,13 @@ All the 'git commit' hooks are invoked with the environment variable `GIT_EDITOR=:` if the command will not bring up an editor to modify the commit message. +pre-merge +~~~~~~~~~ + +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. + prepare-commit-msg ~~~~~~~~~~~~~~~~~~
builtin/merge.c +12 -1
@@ -898,12 +898,23 @@ static void prepare_to_commit(struct commit_list *remoteheads) {struct strbuf msg =STRBUF_INIT ;const char *comment =_ (merge_editor_comment); +const char *index_file =get_index_file (); + +if (run_hook (index_file,"pre-merge" ,NULL )) +abort_commit (remoteheads,NULL ); +/* +* Re-read the index as pre-merge hook could have updated it, +* and write it out as a tree. We must do this before we invoke +* the editor and after we invoke run_status above. +*/ +discard_cache (); +read_cache_from (index_file);strbuf_addbuf (&msg, &merge_msg);strbuf_addch (&msg,'\n' );if (0 < option_edit)strbuf_add_lines (&msg,"# " , comment,strlen (comment));write_merge_msg (&msg); -run_hook (get_index_file (),"prepare-commit-msg" , +run_hook (index_file,"prepare-commit-msg" ,git_path ("MERGE_MSG" ),"merge" ,NULL ,NULL );if (0 < option_edit) {if (launch_editor (git_path ("MERGE_MSG" ),NULL ,NULL ))
templates/hooks--pre-merge.sample new file +13 -0
@@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-merge". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +:
Signature · 1 line
1.7.12.406.gafd3f81
Re: [PATCH 1/3] git-merge: Honor pre-merge hook
Quoted 6 lines · Michael J Gruber
git-merge does not honor the pre-commit hook when doing automatic merge commits, and for compatibility reasons this is going to stay.
Introduce a pre-merge hook which is called for an automatic merge commit just like pre-commit is called for a non-automatic merge commit (or any other commit).
What exactly is an "automatic merge commit"? Is it any merge that doesn't have a conflict? A merge that doesn't invoke the editor? A merge done as part of another operation (e.g., pull)? I don't see the term mentioned in the git-merge or githooks man pages.
I think it would be good if you would define this term in the documentation files that your patch touched, and perhaps in the githooks section about "pre-commit" as well.
Secondly, though it is impossible (for backwards compatibility reasons) for the pre-commit hook to be invoked for automatic merges, no such considerations prohibit the pre-merge commit from being invoked for non-automatic merges. In other words, both hooks, pre-commit *and* pre-merge, could be invoked for non-automatic merges. Would this be preferable?
It depends on what pre-merge scripts are likely to be used for. If they will tend to be used for merge-specific actions, then it might be more convenient for *all* merges to be vetted by them. On the other hand, if they tend to do the same actions as pre-commit hooks, then having non-automatic merge commits go through both hooks would tend to be more annoying than helpful. Specifically, one of the scripts would probably have to check whether the merge is a non-automatic merge, and if so do nothing (i.e., letting the other script take care of it). This would also require an easy way for a script to determine whether a commit is a non-automatic merge commit.
Have you considered this?
Michael
Signature · 3 lines
Michael Haggerty [email protected] http://softwareswirl.blogspot.com/
Re: [PATCH 1/3] git-merge: Honor pre-merge hook
Michael Haggerty venit, vidit, dixit 05.09.2012 17:30:
Quoted 8 lines · Michael J Gruber
Quoted 6 lines
git-merge does not honor the pre-commit hook when doing automatic merge commits, and for compatibility reasons this is going to stay.
Introduce a pre-merge hook which is called for an automatic merge commit just like pre-commit is called for a non-automatic merge commit (or any other commit).
Quoted 8 lines
What exactly is an "automatic merge commit"? Is it any merge that doesn't have a conflict? A merge that doesn't invoke the editor? A merge done as part of another operation (e.g., pull)? I don't see the term mentioned in the git-merge or githooks man pages.
I think it would be good if you would define this term in the documentation files that your patch touched, and perhaps in the githooks section about "pre-commit" as well.
"git merge" can go three ways:
F: fast forward: no commit is created, only a ref is changed
A: automatic: true merge (non-ff) without conflicts (i.e. chosen strategy can perform the merge); a new commit is created
C: merge with conflicts: no commit is created but the index is prepared (partially) for a merge commit
In case F, no commit hook is run (talking only about pre-commit/pre-merge).
In case A, no commit is run so far but my patch proposes pre-merge to be run.
In case C, pre-commit (!) is run so far and after my patch.
Quoted 18 lines
Secondly, though it is impossible (for backwards compatibility reasons) for the pre-commit hook to be invoked for automatic merges, no such considerations prohibit the pre-merge commit from being invoked for non-automatic merges. In other words, both hooks, pre-commit *and* pre-merge, could be invoked for non-automatic merges. Would this be preferable?
It depends on what pre-merge scripts are likely to be used for. If they will tend to be used for merge-specific actions, then it might be more convenient for *all* merges to be vetted by them. On the other hand, if they tend to do the same actions as pre-commit hooks, then having non-automatic merge commits go through both hooks would tend to be more annoying than helpful. Specifically, one of the scripts would probably have to check whether the merge is a non-automatic merge, and if so do nothing (i.e., letting the other script take care of it). This would also require an easy way for a script to determine whether a commit is a non-automatic merge commit.
Quoted 1 line
Have you considered this?
Your second paragraph explains why I did it the way I did. One can easily have pre-merge call pre-commit, or have them be different. One can not easily have only pre-merge called for a non-automatic merge commit, but that is because of backward compatibility. The way *I* would like it is:
- call pre-merge for any non-ff merge commit (automatic or not)
- call pre-commit for any non-merge commit (#parents <=1)
But that would break compatibility.
So I hope my patch is the best approximation to the above which keeps compatibility and is simple to handle in most situations.
Cheers
Michael
Re: [PATCH 1/3] git-merge: Honor pre-merge hook
Quoted 26 lines · Michael J Gruber
Michael Haggerty venit, vidit, dixit 05.09.2012 17:30:
Quoted 8 lines · Michael J Gruber
Quoted 6 lines
git-merge does not honor the pre-commit hook when doing automatic merge commits, and for compatibility reasons this is going to stay.
Introduce a pre-merge hook which is called for an automatic merge commit just like pre-commit is called for a non-automatic merge commit (or any other commit).
Quoted 8 lines
What exactly is an "automatic merge commit"? Is it any merge that doesn't have a conflict? A merge that doesn't invoke the editor? A merge done as part of another operation (e.g., pull)? I don't see the term mentioned in the git-merge or githooks man pages.
I think it would be good if you would define this term in the documentation files that your patch touched, and perhaps in the githooks section about "pre-commit" as well.
"git merge" can go three ways:
F: fast forward: no commit is created, only a ref is changed
A: automatic: true merge (non-ff) without conflicts (i.e. chosen strategy can perform the merge); a new commit is created
C: merge with conflicts: no commit is created but the index is prepared (partially) for a merge commit
Quoted 6 lines
In case F, no commit hook is run (talking only about pre-commit/pre-merge).
In case A, no commit is run so far but my patch proposes pre-merge to be run.
In case C, pre-commit (!) is run so far and after my patch.
Thanks for the explanation. I hope you will explain this briefly in the patch to the docs.
Quoted 26 lines
Quoted 18 lines
Secondly, though it is impossible (for backwards compatibility reasons) for the pre-commit hook to be invoked for automatic merges, no such considerations prohibit the pre-merge commit from being invoked for non-automatic merges. In other words, both hooks, pre-commit *and* pre-merge, could be invoked for non-automatic merges. Would this be preferable?
It depends on what pre-merge scripts are likely to be used for. If they will tend to be used for merge-specific actions, then it might be more convenient for *all* merges to be vetted by them. On the other hand, if they tend to do the same actions as pre-commit hooks, then having non-automatic merge commits go through both hooks would tend to be more annoying than helpful. Specifically, one of the scripts would probably have to check whether the merge is a non-automatic merge, and if so do nothing (i.e., letting the other script take care of it). This would also require an easy way for a script to determine whether a commit is a non-automatic merge commit.
Quoted 1 line
Have you considered this?
Your second paragraph explains why I did it the way I did. One can easily have pre-merge call pre-commit, or have them be different. One can not easily have only pre-merge called for a non-automatic merge commit, but that is because of backward compatibility. The way *I* would like it is:
Quoted 7 lines
- call pre-merge for any non-ff merge commit (automatic or not)
- call pre-commit for any non-merge commit (#parents <=1)But that would break compatibility.
So I hope my patch is the best approximation to the above which keeps compatibility and is simple to handle in most situations.
I can understand your reasoning and won't object. But before I shut up, I will point out a third alternative that is arguably closer to your "ideal":
- For non-merge commits, call pre-commit
- For automatic merge commits, call pre-merge
- For non-automatic merge commits:
if pre-merge exists, call pre-merge (only)
else if pre-commit exists, call pre-commit (for backwards comptibility)
Michael
Signature · 3 lines
Michael Haggerty [email protected] http://softwareswirl.blogspot.com/
PATCH v2 1/4 merge: document prepare-commit-msg hook usage
--- Documentation/git-merge.txt | 5 +++++ Documentation/githooks.txt | 2 ++ 2 files changed, 7 insertions(+)
Documentation/git-merge.txt +5 -0
@@ -308,6 +308,11 @@ branch.<name>.mergeoptions:: supported options are the same as those of 'git merge', but option values containing whitespace characters are currently not supported. +HOOKS +----- +This command can run the `prepare-commit-msg` hook. +See linkgit:githooks[5] for more information. + SEE ALSO -------- linkgit:git-fmt-merge-msg[1], linkgit:git-pull[1],
Documentation/githooks.txt +2 -0
@@ -108,6 +108,8 @@ it is not suppressed by the `--no-verify` option. A non-zero exit means a failure of the hook and aborts the commit. It should not be used as replacement for pre-commit hook. +This hook is also called by 'git merge' when it creates a new commit. + The sample `prepare-commit-msg` hook that comes with git comments out the `Conflicts:` part of a merge's commit message.
Signature · 1 line
1.7.12.406.gafd3f81
PATCH v2 2/4 git-merge: Honor pre-commit hook based on config
In reply to Michael Haggertygit-merge does not honor the pre-commit hook when doing automatic merge commits, and for compatibility reasons this is going to stay.
Introduce a merge.usePreCommitHook which controls whether an automatic merge commit invokes pre-commit.
--- Documentation/git-merge.txt | 2 +- Documentation/githooks.txt | 3 +++ Documentation/merge-config.txt | 5 +++++ builtin/merge.c | 17 ++++++++++++++++- 4 files changed, 25 insertions(+), 2 deletions(-)
Documentation/git-merge.txt +1 -1
@@ -310,7 +310,7 @@ branch.<name>.mergeoptions:: HOOKS ----- -This command can run the `prepare-commit-msg` hook. +This command can run the `prepare-commit-msg` and `pre-commit` hooks. See linkgit:githooks[5] for more information. SEE ALSO
Documentation/githooks.txt +3 -0
@@ -86,6 +86,9 @@ All the 'git commit' hooks are invoked with the environment variable `GIT_EDITOR=:` if the command will not bring up an editor to modify the commit message. +If the configuration option `merge.usePreCommitHook` is set to `true` +then 'git merge' invokes this hook whenever it creates a new commit. + prepare-commit-msg ~~~~~~~~~~~~~~~~~~
Documentation/merge-config.txt +5 -0
@@ -59,6 +59,11 @@ merge.tool:: and "xxdiff". Any other value is treated is custom merge tool and there must be a corresponding mergetool.<tool>.cmd option. +merge.usePreCommitHook:: + Controls whether the pre-commit hook is invoked when 'git merge' + creates a new commit (true merge without conflicts). False by + default. + merge.verbosity:: Controls the amount of output shown by the recursive merge strategy. Level 0 outputs nothing except a final error
builtin/merge.c +16 -1
@@ -64,6 +64,7 @@ static int allow_rerere_auto;static int abort_current_merge;static int show_progress =-1 ;static int default_to_upstream; +static int use_pre_commit_hook;static const char *sign_commit;static struct strategy all_strategy[] = {
@@ -575,6 +576,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"merge.defaulttoupstream" )) { default_to_upstream =git_config_bool (k, v);return 0 ; + }else if (!strcmp (k,"merge.useprecommithook" )) { + use_pre_commit_hook =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);
@@ -898,12 +902,23 @@ static void prepare_to_commit(struct commit_list *remoteheads) {struct strbuf msg =STRBUF_INIT ;const char *comment =_ (merge_editor_comment); +const char *index_file =get_index_file (); + +if (use_pre_commit_hook &&run_hook (index_file,"pre-commit" ,NULL )) +abort_commit (remoteheads,NULL ); +/* +* Re-read the index as pre-commit hook could have updated it, +* and write it out as a tree. We must do this before we invoke +* the editor and after we invoke run_status above. +*/ +discard_cache (); +read_cache_from (index_file);strbuf_addbuf (&msg, &merge_msg);strbuf_addch (&msg,'\n' );if (0 < option_edit)strbuf_add_lines (&msg,"# " , comment,strlen (comment));write_merge_msg (&msg); -run_hook (get_index_file (),"prepare-commit-msg" , +run_hook (index_file,"prepare-commit-msg" ,git_path ("MERGE_MSG" ),"merge" ,NULL ,NULL );if (0 < option_edit) {if (launch_editor (git_path ("MERGE_MSG" ),NULL ,NULL ))
Signature · 1 line
1.7.12.406.gafd3f81
PATCH v2 3/4 merge: --no-verify to bypass pre-commit hook
In reply to Michael HaggertyAnalogous to commit, introduce a '--no-verify' option which bypasses the pre-commit hook. The shorthand '-n' is taken by the (non-existing) '--no-stat' already.
--- Documentation/git-merge.txt | 2 +- Documentation/githooks.txt | 1 + Documentation/merge-options.txt | 4 ++++ builtin/merge.c | 4 +++- 4 files changed, 9 insertions(+), 2 deletions(-)
Documentation/git-merge.txt +1 -1
@@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit] - [-s <strategy>] [-X <strategy-option>] + [--no-verify] [-s <strategy>] [-X <strategy-option>] [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...] 'git merge' <msg> HEAD <commit>... 'git merge' --abort
Documentation/githooks.txt +1 -0
@@ -88,6 +88,7 @@ to modify the commit message. If the configuration option `merge.usePreCommitHook` is set to `true` then 'git merge' invokes this hook whenever it creates a new commit. +It can be bypassed with the `\--no-verify` option. prepare-commit-msg ~~~~~~~~~~~~~~~~~~
Documentation/merge-options.txt +4 -0
@@ -70,6 +70,10 @@ merge. With --no-squash perform the merge and commit the result. This option can be used to override --squash. +--no-verify:: + This option bypasses the pre-commit hook. + See also linkgit:githooks[5]. + -s <strategy>:: --strategy=<strategy>:: Use the given merge strategy; can be supplied more than
builtin/merge.c +3 -1
@@ -60,6 +60,7 @@ static const char *branch;static char *branch_mergeoptions;static int option_renormalize;static int verbosity; +static int no_verify;static int allow_rerere_auto;static int abort_current_merge;static int show_progress =-1 ;
@@ -199,6 +200,7 @@ static struct option builtin_merge_options[] = { N_("allow fast-forward (default)")),OPT_BOOLEAN (0 ,"ff-only" , &fast_forward_only,N_ ("abort if fast-forward is not possible" )), +OPT_BOOLEAN (0 ,"no-verify" , &no_verify,"bypass pre-merge hook" ),OPT_RERERE_AUTOUPDATE (&allow_rerere_auto),OPT_CALLBACK ('s' ,"strategy" , &use_strategies,N_ ("strategy" ), N_("merge strategy to use"), option_parse_strategy),
@@ -904,7 +906,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)const char *comment =_ (merge_editor_comment);const char *index_file =get_index_file (); -if (use_pre_commit_hook &&run_hook (index_file,"pre-commit" ,NULL )) +if (use_pre_commit_hook && !no_verify &&run_hook (index_file,"pre-commit" ,NULL ))abort_commit (remoteheads,NULL );/* * Re-read the index as pre-commit hook could have updated it,
Signature · 1 line
1.7.12.406.gafd3f81
v2 cover pre-commit hook for merges
In reply to Michael HaggertyIn this second iteration I implement Junio's suggestion: 'git merge' invokes the pre-commit hook when merge.usePreCommitHook is set to true.
1/4 documents that 'git merge' invokes the prepare-commit-msg hook unconditionally already.
2-4 are v2 of the previous 1-3.
This leaves aside the issue of commit-msg and post-commit hooks. If we can live with a bit of incompatibility I would rename the config to merge.useCommitHooks and call all of them based on that. This would change the way prepare-commit-msg is treated now.
We could also introduce 4 config variables, 3 defaulting to false, 1 to true, of course...
[I had messed up my alias file when adding mhagger, and it seems that tripped up vger; resending, sorry.]
Michael J Gruber (4):
merge: document prepare-commit-msg hook usage
git-merge: Honor pre-commit hook based on config
merge: --no-verify to bypass pre-commit hook
t7503: add tests for pre-commit hook (merge)
Documentation/git-merge.txt | 7 ++++- Documentation/githooks.txt | 6 ++++ Documentation/merge-config.txt | 5 ++++ Documentation/merge-options.txt | 4 +++ builtin/merge.c | 19 ++++++++++++- t/t7503-pre-commit-hook.sh | 62 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 2 deletions(-)
Signature · 1 line
1.7.12.406.gafd3f81
PATCH v2 4/4 t7503: add tests for pre-commit hook (merge)
In reply to Michael HaggertyAdd tests which make sure that the pre-commit hook is called by 'git merge' when merge.usePreCommitHook is set, allows/disallows merge commits depending on its return value and is suppressed by "--no-verify".
--- t/t7503-pre-commit-hook.sh | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
t/t7503-pre-commit-hook.sh +62 -0
@@ -4,6 +4,19 @@ test_description='pre-commit hook' . ./test-lib.sh +test_expect_success 'root commit' ' + + echo "root" > file && + git add file && + git commit -m "zeroth" && + git checkout -b side && + echo "foo" > foo && + git add foo && + git commit -m "make it non-ff" && + git checkout master + +' + test_expect_success 'with no hook' ' echo "foo" > file &&
@@ -12,6 +25,14 @@ test_expect_success 'with no hook' ' ' +test_expect_success 'with no hook (merge)' ' + + git checkout side && + git merge -m "merge master" master && + git checkout master + +' + test_expect_success '--no-verify with no hook' ' echo "bar" > file &&
@@ -20,6 +41,14 @@ test_expect_success '--no-verify with no hook' ' ' +test_expect_success '--no-verify with no hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + # now install hook that always succeeds HOOKDIR="$(git rev-parse --git-dir)/hooks" HOOK="$HOOKDIR/pre-commit"
@@ -29,6 +58,7 @@ cat > "$HOOK" <<EOF exit 0 EOF chmod +x "$HOOK" +git config merge.usePreCommitHook true test_expect_success 'with succeeding hook' '
@@ -38,6 +68,14 @@ test_expect_success 'with succeeding hook' ' ' +test_expect_success 'with succeeding hook (merge)' ' + + git checkout side && + git merge -m "merge master" master && + git checkout master + +' + test_expect_success '--no-verify with succeeding hook' ' echo "even more" >> file &&
@@ -46,6 +84,14 @@ test_expect_success '--no-verify with succeeding hook' ' ' +test_expect_success '--no-verify with succeeding hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + # now a hook that fails cat > "$HOOK" <<EOF #!/bin/sh
@@ -68,6 +114,22 @@ test_expect_success '--no-verify with failing hook' ' ' +test_expect_success 'with failing hook (merge)' ' + + git checkout side && + test_must_fail git merge -m "merge master" master && + git checkout master + +' + +test_expect_success '--no-verify with failing hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + chmod -x "$HOOK" test_expect_success POSIXPERM 'with non-executable hook' '
Signature · 1 line
1.7.12.406.gafd3f81
Quoted 9 lines · Michael J Gruber
The pre-commit hook is often used to ensure certain properties of each comitted tree like formatting or coding standards, validity (lint/make) or code quality (make test). But merges introduce new commits unless they are fast forwards, and therefore they can break these properties because the pre-commit hook is not run by "git merge".
Introduce a pre-merge hook which works for (non ff, automatic) merges like pre-commit does for commits. Typically this will just call the pre-commit hook (like in the sample hook), but it does not need to.
When your merge asks for a help from you to resolve conflict, you conclude with "git commit", and at that point, pre-commit hook will have a chance to reject it, presumably. That means for any project that wants to audit a merge via hook, their pre-commit hook MUST be prepared to look at and judge a merge. Given that, is a separate hook that "can just call the pre-commit but does not need to" really needed and useful?
I admit that I haven't thought things through, but adding a boolean "merge.usePreCommitHook" smells like a more appropriate approach to me.
I dunno.
Junio C Hamano venit, vidit, dixit 06.09.2012 07:07:
Quoted 20 lines
Quoted 9 lines · Michael J Gruber
The pre-commit hook is often used to ensure certain properties of each comitted tree like formatting or coding standards, validity (lint/make) or code quality (make test). But merges introduce new commits unless they are fast forwards, and therefore they can break these properties because the pre-commit hook is not run by "git merge".
Introduce a pre-merge hook which works for (non ff, automatic) merges like pre-commit does for commits. Typically this will just call the pre-commit hook (like in the sample hook), but it does not need to.
When your merge asks for a help from you to resolve conflict, you conclude with "git commit", and at that point, pre-commit hook will have a chance to reject it, presumably. That means for any project that wants to audit a merge via hook, their pre-commit hook MUST be prepared to look at and judge a merge. Given that, is a separate hook that "can just call the pre-commit but does not need to" really needed and useful?
Quoted 5 lines
I admit that I haven't thought things through, but adding a boolean "merge.usePreCommitHook" smells like a more appropriate approach to me.
I dunno.
That would be an option ;)
Seriously, that would make the case where both hooks are the same even simpler, obviously. On the other hand it would make the other case more difficult. But, really, since non-automatic merges call pre-commit now, and that is probably to stay, catching those with a different (part of the pre-commit) hook will stay difficult anyways. So the question is really more whether we prefer to bloat the config space or the name space by this.
Either works for me, and if we don't change the current behaviour (pre-commit-hook resp. no hook for non-automatic merges resp. automatic merges) the config option is probably less confusing.
Michael
Quoted 21 lines · Michael J Gruber
Junio C Hamano venit, vidit, dixit 06.09.2012 07:07:
Quoted 20 lines
Quoted 9 lines · Michael J Gruber
The pre-commit hook is often used to ensure certain properties of each comitted tree like formatting or coding standards, validity (lint/make) or code quality (make test). But merges introduce new commits unless they are fast forwards, and therefore they can break these properties because the pre-commit hook is not run by "git merge".
Introduce a pre-merge hook which works for (non ff, automatic) merges like pre-commit does for commits. Typically this will just call the pre-commit hook (like in the sample hook), but it does not need to.
When your merge asks for a help from you to resolve conflict, you conclude with "git commit", and at that point, pre-commit hook will have a chance to reject it, presumably. That means for any project that wants to audit a merge via hook, their pre-commit hook MUST be prepared to look at and judge a merge. Given that, is a separate hook that "can just call the pre-commit but does not need to" really needed and useful?
Quoted 7 lines
Quoted 5 lines
I admit that I haven't thought things through, but adding a boolean "merge.usePreCommitHook" smells like a more appropriate approach to me.
I dunno.
That would be an option ;)
I said "I dunno" as others may have opinions on the best direction to go.
Quoted 3 lines
Either works for me, and if we don't change the current behaviour (pre-commit-hook resp. no hook for non-automatic merges resp. automatic merges) the config option is probably less confusing.
If we were to go in the "pre-commit has to be prepared to vet a merge already, so let it handle the automerge case" direction, I have another suggestion.
Because you need to support "merge --no-verify" to override the hook anyway, I think it makes sense to introduce "merge --verify" to force it trigger the hook (and it needs to percolate up to "pull").
People who want it always on may want a boolean merge.verify, but that should come in a separate step. The patch that implements it must make sure all our internal uses of "merge" is updated to pass "--no-verify", unless there is a very good reason.
Another direction to go would be to deprecate the "commit is the way to conclude a merge that stopped in the middle due to a conflict asking for your help" and introduce "merge --continue" [*1*]. That would open a way to a separate "pre/post-merge" hook much cleanly. At that point it would be clear that "pre-commit" won't be involved in "merge" (i.e. the user never will use "git commit" when merging).
I am fairly negative on the current mess imposed on "git commit" that has to know who called it and behave differently (look for "whence" in builtin/commit.c), and would rather not to see it made worse by piling "call pre-merge if exists and in a merge, or call pre-commit" kind of hack on top of it.
[Footnote]
*1* This has been brought up a few times during discussion on sequencer and mergy operations, and I think it makes sense in the longer term. "am" and "rebase" were first to use "--continue", instead of having the user to use "commit" to conclude, and later "cherry-pick" and "revert" have been updated to follow suit, so "merge" may be the only oddball remaining now.
Junio C Hamano venit, vidit, dixit 06.09.2012 20:34:
Quoted 29 lines
Quoted 27 lines · Michael J Gruber
Junio C Hamano venit, vidit, dixit 06.09.2012 07:07:
Quoted 26 lines
Quoted 11 lines · Michael J Gruber
The pre-commit hook is often used to ensure certain properties of each comitted tree like formatting or coding standards, validity (lint/make) or code quality (make test). But merges introduce new commits unless they are fast forwards, and therefore they can break these properties because the pre-commit hook is not run by "git merge".
Introduce a pre-merge hook which works for (non ff, automatic) merges like pre-commit does for commits. Typically this will just call the pre-commit hook (like in the sample hook), but it does not need to.
When your merge asks for a help from you to resolve conflict, you conclude with "git commit", and at that point, pre-commit hook will have a chance to reject it, presumably. That means for any project that wants to audit a merge via hook, their pre-commit hook MUST be prepared to look at and judge a merge. Given that, is a separate hook that "can just call the pre-commit but does not need to" really needed and useful?
I admit that I haven't thought things through, but adding a boolean "merge.usePreCommitHook" smells like a more appropriate approach to me.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
I dunno.
That would be an option ;)
I said "I dunno" as others may have opinions on the best direction to go.
Sure. I meant to make a pun the config option approach being an option.
Quoted 8 lines
Quoted 3 lines
Either works for me, and if we don't change the current behaviour (pre-commit-hook resp. no hook for non-automatic merges resp. automatic merges) the config option is probably less confusing.
If we were to go in the "pre-commit has to be prepared to vet a merge already, so let it handle the automerge case" direction, I have another suggestion.
Quoted 8 lines
Because you need to support "merge --no-verify" to override the hook anyway, I think it makes sense to introduce "merge --verify" to force it trigger the hook (and it needs to percolate up to "pull").
People who want it always on may want a boolean merge.verify, but that should come in a separate step. The patch that implements it must make sure all our internal uses of "merge" is updated to pass "--no-verify", unless there is a very good reason.
Hmm. Nothing calls cmd_merge() nor the relevant parts. "git pull" calls "git merge" and should probably obey that option. All others (am etc.) call git-merge-${strategy} directly and are not affected by the option. Am I overlooking something?
Quoted 7 lines
Another direction to go would be to deprecate the "commit is the way to conclude a merge that stopped in the middle due to a conflict asking for your help" and introduce "merge --continue" [*1*]. That would open a way to a separate "pre/post-merge" hook much cleanly. At that point it would be clear that "pre-commit" won't be involved in "merge" (i.e. the user never will use "git commit" when merging).
Quoted 5 lines
I am fairly negative on the current mess imposed on "git commit" that has to know who called it and behave differently (look for "whence" in
builtin/commit.c), and would rather not to see it made worse by piling "call pre-merge if exists and in a merge, or call pre-commit" kind of hack on top of it.
That is a mess, yes. Part of it is due to the fact that both builtin/{commit,merge}.c do a lot of "commit stuff" in parallel, often with copy & pasted code, and "commit" needs to be aware of other states (in-am, in-rebase) also.
My feeling is that builtin/merge.c should rather get rid of the stuff that parallels things that builtin/commit.c does, so that all is in one place. (I think that redundancy is to blame for the inconsistent hook behaviour for merges even within builtin/merge.c).
Quoted 8 lines
[Footnote]
*1* This has been brought up a few times during discussion on sequencer and mergy operations, and I think it makes sense in the longer term. "am" and "rebase" were first to use "--continue", instead of having the user to use "commit" to conclude, and later "cherry-pick" and "revert" have been updated to follow suit, so "merge" may be the only oddball remaining now.
"git merge --continue" to commit a resolved merge, but code-wise builtin/commit.c taking over because it's just one more case?
I afraid that restructuring is beyond my available Git capacity and my self-imposed constraint to avoid anything with backwards compatibility or migration plan issues. (I took this up because I thought it's within, to share something I've been using for a while.)
I fully agree that a big-picture-solution is preferable.
Michael
PATCH 2/3 merge: --no-verify to bypass pre-merge hook
In reply to Michael J GruberAnalogous to commit, introduce a '--no-verify' option which bypasses the pre-merge hook. The shorthand '-n' is taken by the (non-existing) '--no-stat' already.
--- Documentation/git-merge.txt | 2 +- Documentation/githooks.txt | 2 +- Documentation/merge-options.txt | 4 ++++ builtin/merge.c | 4 +++- 4 files changed, 9 insertions(+), 3 deletions(-)
Documentation/git-merge.txt +1 -1
@@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit] - [-s <strategy>] [-X <strategy-option>] + [--no-verify] [-s <strategy>] [-X <strategy-option>] [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...] 'git merge' <msg> HEAD <commit>... 'git merge' --abort
Documentation/githooks.txt +1 -1
@@ -91,7 +91,7 @@ pre-merge 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. prepare-commit-msg ~~~~~~~~~~~~~~~~~~
Documentation/merge-options.txt +4 -0
@@ -70,6 +70,10 @@ merge. With --no-squash perform the merge and commit the result. This option can be used to override --squash. +--no-verify:: + This option bypasses the pre-merge and commit-msg hooks. + See also linkgit:githooks[5]. + -s <strategy>:: --strategy=<strategy>:: Use the given merge strategy; can be supplied more than
builtin/merge.c +3 -1
@@ -60,6 +60,7 @@ static const char *branch;static char *branch_mergeoptions;static int option_renormalize;static int verbosity; +static int no_verify;static int allow_rerere_auto;static int abort_current_merge;static int show_progress =-1 ;
@@ -198,6 +199,7 @@ static struct option builtin_merge_options[] = { N_("allow fast-forward (default)")),OPT_BOOLEAN (0 ,"ff-only" , &fast_forward_only,N_ ("abort if fast-forward is not possible" )), +OPT_BOOLEAN (0 ,"no-verify" , &no_verify,"bypass pre-merge hook" ),OPT_RERERE_AUTOUPDATE (&allow_rerere_auto),OPT_CALLBACK ('s' ,"strategy" , &use_strategies,N_ ("strategy" ), N_("merge strategy to use"), option_parse_strategy),
@@ -900,7 +902,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)const char *comment =_ (merge_editor_comment);const char *index_file =get_index_file (); -if (run_hook (index_file,"pre-merge" ,NULL )) +if (!no_verify &&run_hook (index_file,"pre-merge" ,NULL ))abort_commit (remoteheads,NULL );/* * Re-read the index as pre-merge hook could have updated it,
Signature · 1 line
1.7.12.406.gafd3f81
PATCH 3/3 t7503: add tests for pre-merge-hook
In reply to Michael J GruberAdd tests which make sure that the pre-merge-hook is called when present, allows/disallows merge commits depending on its return value and is suppressed by "--no-verify".
--- t/t7503-pre-commit-hook.sh | 66 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-)
t/t7503-pre-commit-hook.sh +65 -1
@@ -1,9 +1,22 @@ #!/bin/sh -test_description='pre-commit hook' +test_description='pre-commit and pre-merge hooks' . ./test-lib.sh +test_expect_success 'root commit' ' + + echo "root" > file && + git add file && + git commit -m "zeroth" && + git checkout -b side && + echo "foo" > foo && + git add foo && + git commit -m "make it non-ff" && + git checkout master + +' + test_expect_success 'with no hook' ' echo "foo" > file &&
@@ -12,6 +25,14 @@ test_expect_success 'with no hook' ' ' +test_expect_success 'with no hook (merge)' ' + + git checkout side && + git merge -m "merge master" master && + git checkout master + +' + test_expect_success '--no-verify with no hook' ' echo "bar" > file &&
@@ -20,15 +41,25 @@ test_expect_success '--no-verify with no hook' ' ' +test_expect_success '--no-verify with no hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + # now install hook that always succeeds HOOKDIR="$(git rev-parse --git-dir)/hooks" HOOK="$HOOKDIR/pre-commit" +MERGEHOOK="$HOOKDIR/pre-merge" mkdir -p "$HOOKDIR" cat > "$HOOK" <<EOF #!/bin/sh exit 0 EOF chmod +x "$HOOK" +cp -p "$HOOK" "$MERGEHOOK" test_expect_success 'with succeeding hook' '
@@ -38,6 +69,14 @@ test_expect_success 'with succeeding hook' ' ' +test_expect_success 'with succeeding hook (merge)' ' + + git checkout side && + git merge -m "merge master" master && + git checkout master + +' + test_expect_success '--no-verify with succeeding hook' ' echo "even more" >> file &&
@@ -46,11 +85,20 @@ test_expect_success '--no-verify with succeeding hook' ' ' +test_expect_success '--no-verify with succeeding hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + # now a hook that fails cat > "$HOOK" <<EOF #!/bin/sh exit 1 EOF +cp -p "$HOOK" "$MERGEHOOK" test_expect_success 'with failing hook' '
@@ -68,6 +116,22 @@ test_expect_success '--no-verify with failing hook' ' ' +test_expect_success 'with failing hook (merge)' ' + + git checkout side && + test_must_fail git merge -m "merge master" master && + git checkout master + +' + +test_expect_success '--no-verify with failing hook (merge)' ' + + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master + +' + chmod -x "$HOOK" test_expect_success POSIXPERM 'with non-executable hook' '
Signature · 1 line
1.7.12.406.gafd3f81