Skip to content

Commit

Permalink
revert: allow cherry-pick --continue to commit before resuming
Browse files Browse the repository at this point in the history
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.

This improves the analogy with "git rebase" (in olden days --continue
was the way to preserve authorship when a rebase encountered
conflicts) and fits well with a general UI goal of making "git cmd
--continue" save humans the trouble of deciding what to do next.

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(-)

This is done for both codepaths to pick multiple commits and a single
commit.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jonathan Nieder authored and Junio C Hamano committed Dec 12, 2011
1 parent 1df9bf4 commit 093a309
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 6 deletions.
23 changes: 20 additions & 3 deletions builtin/revert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

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);
}

Expand Down
139 changes: 136 additions & 3 deletions t/t3510-cherry-pick-sequence.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 &&
Expand All @@ -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
'

test_expect_success 'cherry-pick persists data on failure' '
Expand Down Expand Up @@ -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
'

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 &&
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
'

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
'

test_expect_success '--continue after resolving conflicts and committing' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
echo "c" >foo &&
Expand All @@ -270,6 +331,29 @@ test_expect_success '--continue continues after conflicts are resolved' '
test_cmp expect actual
'

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
'

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
'

test_expect_success '--continue respects opts' '
pristine_detach initial &&
test_must_fail git cherry-pick -x base..anotherpick &&
Expand All @@ -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
'

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
'

test_expect_success '--signoff is not automatically propagated to resolved conflict' '
pristine_detach initial &&
test_must_fail git cherry-pick --signoff base..anotherpick &&
Expand All @@ -306,6 +413,32 @@ test_expect_success '--signoff is not automatically propagated to resolved confl
grep "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
'

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
'

test_expect_success 'malformed instruction sheet 1' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
Expand Down

0 comments on commit 093a309

Please sign in to comment.