threads / discuss / 41579

Bypassing hooks while cherry-picking

Subject: Bypassing hooks while cherry-picking

## tl;dr

10 messages between Mar 1, 2016 and Mar 3, 2016.

replies: 9people: 4as markdown or json

greg0ire· Mar 1, 2016, 11:01 UTC · lore
Hello,

using git 2.1.4 here, and it seems there is no option to bypass pre-commit hooks while cherry-picking, while git commit provides a --no-verify option. I ended up doing this to disable hooks while cherry picking :

     test -f "$GIT_DIR"/CHERRY_PICK_HEAD && exit 0

Wouldn't it be best to add the --no-verify option to cherry-pick too? I had a conflict when cherry-picking the commit, maybe this does not happen otherwise?

Steps to reproduce :
1. create a pre-commit hook
2. create a commit that fails the hook, and bypass the hook
3. checkout another branch
4. might be optional : create a conflicting change with the previously 
created commit
5. cherry-pick the commit
6. might be optional : solve the conflick and use git cherry-pick --continue
Regards,

-- greg0ire

Kevin Daudt· Mar 1, 2016, 20:40 UTC · re: greg0ire · lore

[PATCH] cherry-pick: add --no-verify option

git commit has a --no-verify option to prevent the pre-commit hook from running. When continuing a conflicted cherry-pick, git commit gets executed which also causes the pre-commit hook to be run.

Add --no-verify and pass that through to the git commit command so that the can prevent that from happening

Signed-off-by: Kevin Daudt <me@ikke.info>
---
 builtin/revert.c                |  2 ++
 sequencer.c                     | 21 ++++++++++++++++-----
 sequencer.h                     |  1 +
 t/t3510-cherry-pick-sequence.sh | 12 ++++++++++++
 4 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 56a2c36..81d9c85 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -97,6 +97,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 		OPT_END(),
 		OPT_END(),
 		OPT_END(),
+		OPT_END(),
 	};
 
 	if (opts->action == REPLAY_PICK) {
@@ -106,6 +107,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
 			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "no-verify", &opts->no_verify, N_("don't run pre-commit hook when continuing cherry-pick")),
 			OPT_END(),
 		};
 		if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
diff --git a/sequencer.c b/sequencer.c
index e66f2fe..657a381 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -978,14 +978,25 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 	return 0;
 }
 
-static int continue_single_pick(void)
+static int continue_single_pick(struct replay_opts *opts)
 {
-	const char *argv[] = { "commit", NULL };
+	struct argv_array array;
+	int rc;
+
+	argv_array_init(&array);
+	argv_array_push(&array, "commit");
 
 	if (!file_exists(git_path_cherry_pick_head()) &&
 	    !file_exists(git_path_revert_head()))
 		return error(_("no cherry-pick or revert in progress"));
-	return run_command_v_opt(argv, RUN_GIT_CMD);
+
+	if (opts->no_verify)
+		argv_array_push(&array, "--no-verify");
+
+	rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+	argv_array_clear(&array);
+
+	return rc;
 }
 
 static int sequencer_continue(struct replay_opts *opts)
@@ -993,14 +1004,14 @@ static int sequencer_continue(struct replay_opts *opts)
 	struct commit_list *todo_list = NULL;
 
 	if (!file_exists(git_path_todo_file()))
-		return continue_single_pick();
+		return continue_single_pick(opts);
 	read_populate_opts(&opts);
 	read_populate_todo(&todo_list, opts);
 
 	/* Verify that the conflict has been resolved */
 	if (file_exists(git_path_cherry_pick_head()) ||
 	    file_exists(git_path_revert_head())) {
-		int ret = continue_single_pick();
+		int ret = continue_single_pick(opts);
 		if (ret)
 			return ret;
 	}
diff --git a/sequencer.h b/sequencer.h
index 5ed5cb1..d868a50 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -34,6 +34,7 @@ struct replay_opts {
 	int allow_empty;
 	int allow_empty_message;
 	int keep_redundant_commits;
+	int no_verify;
 
 	int mainline;
 
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 7b7a89d..29a06f8 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -340,6 +340,18 @@ test_expect_success '--continue after resolving conflicts and committing' '
 	test_cmp expect actual
 '
 
+test_expect_success '--continue --no-verify does not run pre-commit hook ' '
+	pristine_detach initial &&
+	mkdir -p .git/hooks &&
+	echo -e "#!/bin/sh\nexit 1" >.git/hooks/pre-commit &&
+	chmod u+x .git/hooks/pre-commit &&
+	test_when_finished "rm -r .git/hooks/" &&
+
+	test_must_fail git cherry-pick picked &&
+	git add foo &&
+	git cherry-pick --continue --no-verify
+'
+
 test_expect_success '--continue asks for help after resolving patch to nil' '
 	pristine_detach conflicting &&
 	test_must_fail git cherry-pick initial..picked &&
-- 
2.7.2
Eric Sunshine· Mar 2, 2016, 00:33 UTC · re: Kevin Daudt · lore

Re: [PATCH] cherry-pick: add --no-verify option

On Tue, Mar 1, 2016 at 3:40 PM, Kevin Daudt <me@ikke.info> wrote:
Show 15 quoted lines
> git commit has a --no-verify option to prevent the pre-commit hook from
> running. When continuing a conflicted cherry-pick, git commit gets
> executed which also causes the pre-commit hook to be run.
>
> Add --no-verify and pass that through to the git commit command so that
> the can prevent that from happening
>
> Signed-off-by: Kevin Daudt <me@ikke.info>
> ---
> diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
> @@ -340,6 +340,18 @@ test_expect_success '--continue after resolving conflicts and committing' '
> +test_expect_success '--continue --no-verify does not run pre-commit hook ' '
> +       pristine_detach initial &&
> +       mkdir -p .git/hooks &&
> +       echo -e "#!/bin/sh\nexit 1" >.git/hooks/pre-commit &&

Non-portable 'echo'. You could use printf instead, however, even better would be to use write_script() along with a here-doc (then you could drop the 'chmod' also).

Show 7 quoted lines
> +       chmod u+x .git/hooks/pre-commit &&
> +       test_when_finished "rm -r .git/hooks/" &&
> +
> +       test_must_fail git cherry-pick picked &&
> +       git add foo &&
> +       git cherry-pick --continue --no-verify
> +'
Kevin Daudt· Mar 2, 2016, 11:58 UTC · re: Kevin Daudt · lore

[PATCH v2] cherry-pick: add --no-verify option

git commit has a --no-verify option to prevent the pre-commit hook from running. When continuing a conflicted cherry-pick, git commit gets executed which also causes the pre-commit hook to be run.

Add --no-verify and pass that through to the git commit command.
Signed-off-by: Kevin Daudt <me@ikke.info>
---
Changes since v1:
- Use write_script to create hook
- Add option to cherry-pick man page
 Documentation/git-cherry-pick.txt |  5 +++++
 builtin/revert.c                  |  2 ++
 sequencer.c                       | 21 ++++++++++++++++-----
 sequencer.h                       |  1 +
 t/t3510-cherry-pick-sequence.sh   | 14 ++++++++++++++
 5 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 6154e57..30bbdbd 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -148,6 +148,11 @@ effect to your index in a row.
 	Pass the merge strategy-specific option through to the
 	merge strategy.  See linkgit:git-merge[1] for details.
 
+--no-verify::
+	Pass --no-verify to the commit command when continuing after a
+	conflicted cherry-pick to disable any pre-commit hooks that
+	might get run.
+
 SEQUENCER SUBCOMMANDS
 ---------------------
 include::sequencer.txt[]
diff --git a/builtin/revert.c b/builtin/revert.c
index 56a2c36..81d9c85 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -97,6 +97,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 		OPT_END(),
 		OPT_END(),
 		OPT_END(),
+		OPT_END(),
 	};
 
 	if (opts->action == REPLAY_PICK) {
@@ -106,6 +107,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 			OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
 			OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
 			OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+			OPT_BOOL(0, "no-verify", &opts->no_verify, N_("don't run pre-commit hook when continuing cherry-pick")),
 			OPT_END(),
 		};
 		if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
diff --git a/sequencer.c b/sequencer.c
index e66f2fe..657a381 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -978,14 +978,25 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 	return 0;
 }
 
-static int continue_single_pick(void)
+static int continue_single_pick(struct replay_opts *opts)
 {
-	const char *argv[] = { "commit", NULL };
+	struct argv_array array;
+	int rc;
+
+	argv_array_init(&array);
+	argv_array_push(&array, "commit");
 
 	if (!file_exists(git_path_cherry_pick_head()) &&
 	    !file_exists(git_path_revert_head()))
 		return error(_("no cherry-pick or revert in progress"));
-	return run_command_v_opt(argv, RUN_GIT_CMD);
+
+	if (opts->no_verify)
+		argv_array_push(&array, "--no-verify");
+
+	rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+	argv_array_clear(&array);
+
+	return rc;
 }
 
 static int sequencer_continue(struct replay_opts *opts)
@@ -993,14 +1004,14 @@ static int sequencer_continue(struct replay_opts *opts)
 	struct commit_list *todo_list = NULL;
 
 	if (!file_exists(git_path_todo_file()))
-		return continue_single_pick();
+		return continue_single_pick(opts);
 	read_populate_opts(&opts);
 	read_populate_todo(&todo_list, opts);
 
 	/* Verify that the conflict has been resolved */
 	if (file_exists(git_path_cherry_pick_head()) ||
 	    file_exists(git_path_revert_head())) {
-		int ret = continue_single_pick();
+		int ret = continue_single_pick(opts);
 		if (ret)
 			return ret;
 	}
diff --git a/sequencer.h b/sequencer.h
index 5ed5cb1..d868a50 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -34,6 +34,7 @@ struct replay_opts {
 	int allow_empty;
 	int allow_empty_message;
 	int keep_redundant_commits;
+	int no_verify;
 
 	int mainline;
 
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 7b7a89d..a97ee52 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -340,6 +340,20 @@ test_expect_success '--continue after resolving conflicts and committing' '
 	test_cmp expect actual
 '
 
+test_expect_success '--continue --no-verify does not run pre-commit hook ' '
+	pristine_detach initial &&
+
+	mkdir -p .git/hooks &&
+	write_script .git/hooks/pre-commit <<-\EOF &&
+		exit 1
+	EOF
+	test_when_finished "rm -r .git/hooks/" &&
+
+	test_must_fail git cherry-pick picked &&
+	git add foo &&
+	git cherry-pick --continue --no-verify
+'
+
 test_expect_success '--continue asks for help after resolving patch to nil' '
 	pristine_detach conflicting &&
 	test_must_fail git cherry-pick initial..picked &&
-- 
2.7.2
Kevin Daudt· Mar 3, 2016, 21:17 UTC · re: greg0ire · lore

Re: Bypassing hooks while cherry-picking

On Tue, Mar 01, 2016 at 12:01:53PM +0100, greg0ire wrote:
Show 26 quoted lines
> Hello,
> 
> using git 2.1.4 here, and it seems there is no option to bypass pre-commit
> hooks while cherry-picking, while git commit provides a --no-verify option.
> I ended up doing this to disable hooks while cherry picking :
> 
>     test -f "$GIT_DIR"/CHERRY_PICK_HEAD && exit 0
> 
> Wouldn't it be best to add the --no-verify option to cherry-pick too?
> I had a conflict when cherry-picking the commit, maybe this does not happen
> otherwise?
> 
> Steps to reproduce :
> 
> 1. create a pre-commit hook
> 2. create a commit that fails the hook, and bypass the hook
> 3. checkout another branch
> 4. might be optional : create a conflicting change with the previously
> created commit
> 5. cherry-pick the commit
> 6. might be optional : solve the conflick and use git cherry-pick --continue
> 
> Regards,
> 
> --
> greg0ire

Forgot to CC you, but I've submitted a patch[0] that adds this option. The conflict does matter because otherwise the pre-commit hook does not run.

Because they're now the rc-cycle, it might take a while before the patch is accepted.

[0]:http://article.gmane.org/gmane.comp.version-control.git/288124
Grégoire PARIS· Mar 3, 2016, 22:18 UTC · re: Kevin Daudt · lore

Re: Bypassing hooks while cherry-picking

Oh great, glad to see that, good job!
greg0ire
Le 03/03/2016 22:17, Kevin Daudt a écrit :
Show 37 quoted lines
> On Tue, Mar 01, 2016 at 12:01:53PM +0100, greg0ire wrote:
>> Hello,
>>
>> using git 2.1.4 here, and it seems there is no option to bypass pre-commit
>> hooks while cherry-picking, while git commit provides a --no-verify option.
>> I ended up doing this to disable hooks while cherry picking :
>>
>>      test -f "$GIT_DIR"/CHERRY_PICK_HEAD && exit 0
>>
>> Wouldn't it be best to add the --no-verify option to cherry-pick too?
>> I had a conflict when cherry-picking the commit, maybe this does not happen
>> otherwise?
>>
>> Steps to reproduce :
>>
>> 1. create a pre-commit hook
>> 2. create a commit that fails the hook, and bypass the hook
>> 3. checkout another branch
>> 4. might be optional : create a conflicting change with the previously
>> created commit
>> 5. cherry-pick the commit
>> 6. might be optional : solve the conflick and use git cherry-pick --continue
>>
>> Regards,
>>
>> --
>> greg0ire
> Forgot to CC you, but I've submitted a patch[0] that adds this option.
> The conflict does matter because otherwise the pre-commit hook does not
> run.
>
> Because they're now the rc-cycle, it might take a while before the patch
> is accepted.
>
>
> [0]:http://article.gmane.org/gmane.comp.version-control.git/288124
>
Junio C Hamano· Mar 3, 2016, 21:30 UTC · re: greg0ire · lore

Re: Bypassing hooks while cherry-picking

greg0ire <postmaster@greg0ire.fr> writes:
Show 9 quoted lines
> Steps to reproduce :
>
> 1. create a pre-commit hook
> 2. create a commit that fails the hook, and bypass the hook
> 3. checkout another branch
> 4. might be optional : create a conflicting change with the previously
> created commit
> 5. cherry-pick the commit
> 6. might be optional : solve the conflick and use git cherry-pick --continue

Wouldn't "commit --no-verify" at that last step, instead of "cherry-pick --continue", just work?

Grégoire PARIS· Mar 3, 2016, 22:20 UTC · re: Junio C Hamano · lore

Re: Bypassing hooks while cherry-picking

Not sure how cherry-picking is managed, and whether commit is able to see that we are doing a cherry-pick, and end it up properly. Anyway, with Kevin's patch, we should be able to happily use cherry-pick

greg0ire
Le 03/03/2016 22:30, Junio C Hamano a écrit :
Show 15 quoted lines
> greg0ire <postmaster@greg0ire.fr> writes:
>
>> Steps to reproduce :
>>
>> 1. create a pre-commit hook
>> 2. create a commit that fails the hook, and bypass the hook
>> 3. checkout another branch
>> 4. might be optional : create a conflicting change with the previously
>> created commit
>> 5. cherry-pick the commit
>> 6. might be optional : solve the conflick and use git cherry-pick --continue
> Wouldn't "commit --no-verify" at that last step, instead of
> "cherry-pick --continue", just work?
>
>
Junio C Hamano· Mar 3, 2016, 22:55 UTC · re: Grégoire PARIS · lore

Re: Bypassing hooks while cherry-picking

Grégoire PARIS <postmaster@greg0ire.fr> writes:
> Not sure how cherry-picking is managed, and whether commit is able to
> see that we are doing a cherry-pick, and end it up properly. Anyway,
> with Kevin's patch, we should be able to happily use cherry-pick
[Please do not top post]
It is somewhat sad to see such a response as that is backwards.

"cherry-pick --continue" originally was "I am done with the commit I was asked to deal with, so please replay the next one", and in order to "be done with" the commit, you would used "git commit" yourself, with whatever necessary options (e.g. --no-verify, -s, -c) you want to affect the behaviour of the "git commit".

It is a later invention that "cherry-pick --continue" additionally creates a commit when the user, even though she claims to "be done with", hasn't actually made the commit before going on to replay the next one. It was accepted as a short-cut as most of the time you do not give any option to your "git commit" invocation, but probably was a misguided invention--it made new people somehow think that they are not allowed to run "git commit" to conclude the conflict resolution, when in reality that is an acceptable and primary way to do so and "--continue" is a mere short-hand.

As "git commit" does have options other than "--no-verify" that affects the way it behaves, and can gain more such options in the future, having to keep adding corresponding options to "cherry-pick --continue --more-options" will not be a good solution in the longer run.

Grégoire PARIS· Mar 3, 2016, 23:18 UTC · re: Junio C Hamano · lore

Re: Bypassing hooks while cherry-picking

Le 03/03/2016 23:55, Junio C Hamano a écrit :
Show 32 quoted lines
> Grégoire PARIS <postmaster@greg0ire.fr> writes:
>
>> Not sure how cherry-picking is managed, and whether commit is able to
>> see that we are doing a cherry-pick, and end it up properly. Anyway,
>> with Kevin's patch, we should be able to happily use cherry-pick
> [Please do not top post]
>
> It is somewhat sad to see such a response as that is backwards.
>
> "cherry-pick --continue" originally was "I am done with the commit I
> was asked to deal with, so please replay the next one", and in order
> to "be done with" the commit, you would used "git commit" yourself,
> with whatever necessary options (e.g. --no-verify, -s, -c) you want
> to affect the behaviour of the "git commit".
>
> It is a later invention that "cherry-pick --continue" additionally
> creates a commit when the user, even though she claims to "be done
> with", hasn't actually made the commit before going on to replay the
> next one.  It was accepted as a short-cut as most of the time you do
> not give any option to your "git commit" invocation, but probably
> was a misguided invention--it made new people somehow think that
> they are not allowed to run "git commit" to conclude the conflict
> resolution, when in reality that is an acceptable and primary way
> to do so and "--continue" is a mere short-hand.
>
> As "git commit" does have options other than "--no-verify" that
> affects the way it behaves, and can gain more such options in the
> future, having to keep adding corresponding options to "cherry-pick
> --continue --more-options" will not be a good solution in the longer
> run.
>
>
Sorry for top-posting, I'll keep in mind to not do this.

You're describing very precisely how using cherry-pick --continue makes me feel. I checked again right now and even though there is a comment to make me understand that commit would do a great job at solving my problem (hint: and commit the result with 'git commit'), I failed to notice it.

I agree with you, adding every commit option is not a good solution… Maybe a good solution would be to display a special message when using cherry-pick --continue with unrecognized options? Like for instance

 > hint: try git commit --no-verify, then git cherry-pick --continue to 
replay the next commit
What do you think?

-- greg0ire

← back to recent threads