Re: [PATCH 2/7] revert: allow cherry-pick --continue to commit before resuming
From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:52:38
Hi again, Jonathan Nieder wrote:
When "git cherry-pick ..bar" encounters conflicts, permit the operator to use cherry-pick --continue after resolving them as a shortcut for "git commit && git cherry-pick --continue" to record the resolution and carry on with the rest of the sequence.
Sounds good. I remember my implementation being quite complicated; let's see how you've done this.
Example: after encountering a conflict from running "git cherry-pick foo bar baz": CONFLICT (content): Merge conflict in main.c error: could not apply f78a8d98c... bar! hint: after resolving the conflicts, mark the corrected paths hint: with 'git add <paths>' or 'git rm <paths>' hint: and commit the result with 'git commit' We edit main.c to resolve the conflict, mark it acceptable with "git add main.c", and can run "cherry-pick --continue" to resume the sequence. $ git cherry-pick --continue [editor opens to confirm commit message] [master 78c8a8c98] bar! 1 files changed, 1 insertions(+), 1 deletions(-) [master 87ca8798c] baz! 1 files changed, 1 insertions(+), 1 deletions(-)
I like the presentation of this example: much clearer than my examples.
builtin/revert.c | 23 ++++++- t/t3510-cherry-pick-sequence.sh | 139 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 6 deletions(-)
Oh, good -- lots of new tests :)
quoted hunk ↗ jump to hunk
diff --git a/builtin/revert.c b/builtin/revert.c index 9f6c85c1..a43b4d85 100644 --- a/builtin/revert.c +++ b/builtin/revert.c@@ -1038,18 +1038,35 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)return 0; } +static int continue_single_pick(void) +{ + const char *argv[] = { "commit", NULL }; + + 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); +}
Very nice! I can see how the introduction of REVERT_HEAD simplifies things :) I'm totally embarrassed by the horribly convoluted logic in the "New sequencer workflow!" I posted earlier. Note to self: don't capitalize error() messages.
static int sequencer_continue(struct replay_opts *opts)
{
struct commit_list *todo_list = NULL;
if (!file_exists(git_path(SEQ_TODO_FILE)))
- return error(_("No %s in progress"), action_name(opts));
+ return continue_single_pick();
read_populate_opts(&opts);
read_populate_todo(&todo_list, opts);
/* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
+ if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
+ file_exists(git_path("REVERT_HEAD"))) {
+ int ret = continue_single_pick();
+ if (ret)
+ return ret;
+ }
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ todo_list = todo_list->next;
return pick_commits(todo_list, opts);
}Very nicely done. I can see why 1/7 makes so much sense now: it helps think of different operations independently.
quoted hunk ↗ jump to hunk
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 2c4c1c85..4d1883b7 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh@@ -2,6 +2,7 @@test_description='Test cherry-pick continuation features + + conflicting: rewrites unrelated to conflicting + yetanotherpick: rewrites foo to e + anotherpick: rewrites foo to d + picked: rewrites foo to c
Note to self: this list of commits is becoming quite unwieldy. We should probably refactor these sometime.
quoted hunk ↗ jump to hunk
@@ -27,6 +28,7 @@ test_cmp_rev () {} test_expect_success setup ' + git config advice.detachedhead false echo unrelated >unrelated && git add unrelated && test_commit initial foo a &&
Huh, why are you moving this line up? Oh, right: there are "test_commit" statements in the setup- good catch. This is unrelated to your patch and should be a separate commit though.
quoted hunk ↗ jump to hunk
@@ -35,8 +37,8 @@ test_expect_success setup 'test_commit picked foo c && test_commit anotherpick foo d && test_commit yetanotherpick foo e && - git config advice.detachedhead false - + pristine_detach initial && + test_commit conflicting unrelated '
Looks fishy- I wonder why you're doing this. Let's read ahead and find out.
quoted hunk ↗ jump to hunk
@@ -243,7 +245,66 @@ test_expect_success '--continue complains when there are unresolved conflicts' 'test_must_fail git cherry-pick --continue ' -test_expect_success '--continue continues after conflicts are resolved' ' +test_expect_success '--continue of single cherry-pick' ' + pristine_detach initial && + echo c >expect && + test_must_fail git cherry-pick picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + + test_cmp expect foo && + test_cmp_rev initial HEAD^ && + git diff --exit-code HEAD && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD +'
Beautiful. The tests I wrote are ugly in comparison :\
+test_expect_success '--continue of single revert' ' + pristine_detach initial && + echo resolved >expect && + echo "Revert \"picked\"" >expect.msg && + test_must_fail git revert picked && + echo resolved >foo && + git add foo && + git cherry-pick --continue &&
Huh? You're continuing a "git revert" with a a "git cherry-pick --continue"? The current 'master' still uses a commit_list, and doesn't allow mixed "pick" and "revert" instructions yet.
+ git diff --exit-code HEAD && + test_cmp expect foo && + test_cmp_rev initial HEAD^ && + git diff-tree -s --pretty=tformat:%s HEAD >msg && + test_cmp expect.msg msg && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD && + test_must_fail git rev-parse --verify REVERT_HEAD +'
A couple of notes:
1. I haven't used the "-s" flag of "git diff-tree" before, so I opened
up the documentation to find this:
By default, 'git diff-tree --stdin' shows differences,
either in machine-readable form (without '-p') or in patch
form (with '-p'). This output can be suppressed. It is
only useful with '-v' flag.
Very misleading. TODO: Fix this.
2. Why did you use "diff-tree" to get the commit message? Isn't
"cat-file commit" much more straightforward?
+test_expect_success '--continue after resolving conflicts' '
+ pristine_detach initial &&
+ echo d >expect &&
+ cat >expect.log <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M foo
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo c >foo &&
+ git add foo &&
+ git cherry-pick --continue &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual.log &&
+ test_cmp expect foo &&
+ test_cmp expect.log actual.log
+'Unchanged from the original: I suspect you've moved the generation of expectation messages up to produce a clean diff.
+test_expect_success '--continue after resolving conflicts and committing' ' pristine_detach initial && test_must_fail git cherry-pick base..anotherpick && echo "c" >foo &&
Okay, the diff isn't all that clean :P
+test_expect_success '--continue asks for help after resolving patch to nil' ' + pristine_detach conflicting && + test_must_fail git cherry-pick initial..picked && + + test_cmp_rev unrelatedpick CHERRY_PICK_HEAD && + git checkout HEAD -- unrelated && + test_must_fail git cherry-pick --continue 2>msg && + test_i18ngrep "The previous cherry-pick is now empty" msg +'
I thought it was a bad idea to grep for specific output messages, because of their volatile nature? Remind me what this test has to do with the rest of your patch?
+test_expect_failure 'follow advice and skip nil patch' ' + pristine_detach conflicting && + test_must_fail git cherry-pick initial..picked && + + git checkout HEAD -- unrelated && + test_must_fail git cherry-pick --continue && + git reset && + git cherry-pick --continue && + + git rev-list initial..HEAD >commits && + test_line_count = 3 commits +'
Again, what does this test have to do with the rest of your patch?
quoted hunk ↗ jump to hunk
test_expect_success '--continue respects opts' ' pristine_detach initial && test_must_fail git cherry-pick -x base..anotherpick &&@@ -288,6 +372,29 @@ test_expect_success '--continue respects opts' 'grep "cherry picked from" anotherpick_msg ' +test_expect_success '--continue of single-pick respects -x' ' + pristine_detach initial && + test_must_fail git cherry-pick -x picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + test_path_is_missing .git/sequencer && + git cat-file commit HEAD >msg && + grep "cherry picked from" msg +'
I'd have liked s/respects -x/respects opts/ here for symmetry with the previous test.
+test_expect_success '--continue respects -x in first commit in multi-pick' ' + pristine_detach initial && + test_must_fail git cherry-pick -x picked anotherpick && + echo c >foo && + git add foo && + git cherry-pick --continue && + test_path_is_missing .git/sequencer && + git cat-file commit HEAD^ >msg && + picked=$(git rev-parse --verify picked) && + grep "cherry picked from.*$picked" msg +'
Can you explain why "first commit in a multi-pick" is a special case?
quoted hunk ↗ jump to hunk
@@ -306,6 +413,32 @@ test_expect_success '--signoff is not automatically propagated to resolved conflgrep "Signed-off-by:" anotherpick_msg ' +test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked anotherpick && + echo c >foo && + git add foo && + git cherry-pick --continue && + + git diff --exit-code HEAD && + test_cmp_rev initial HEAD^^ && + git cat-file commit HEAD^ >msg && + ! grep Signed-off-by: msg +'
Unrelated.
+test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked && + echo c >foo && + git add foo && + git cherry-pick --continue && + + git diff --exit-code HEAD && + test_cmp_rev initial HEAD^ && + git cat-file commit HEAD >msg && + ! grep Signed-off-by: msg +'
If the previous test were a separate patch preceding this one, this'd belong here. Thanks for working on this. -- Ram