Skip to content

Commit a9279c6

Browse files
phillipwoodgitster
authored andcommitted
sequencer: do not squash 'reword' commits when we hit conflicts
Ever since commit 18633e1 ("rebase -i: use the rebase--helper builtin", 2017-02-09), when a commit marked as 'reword' in an interactive rebase has conflicts and fails to apply, when the rebase is resumed that commit will be squashed into its parent with its commit message taken. The issue can be understood better by looking at commit 56dc3ab ("sequencer (rebase -i): implement the 'edit' command", 2017-01-02), which introduced error_with_patch() for the edit command. For the edit command, it needs to stop the rebase whether or not the patch applies cleanly. If the patch does apply cleanly, then when it resumes it knows it needs to amend all changes into the previous commit. If it does not apply cleanly, then the changes should not be amended. Thus, it passes !res (success of applying the 'edit' commit) to error_with_patch() for the to_amend flag. The problematic line of code actually came from commit 04efc8b ("sequencer (rebase -i): implement the 'reword' command", 2017-01-02). Note that to get to this point in the code: * !!res (i.e. patch application failed) * item->command < TODO_SQUASH * item->command != TODO_EDIT * !is_fixup(item->command) [i.e. not squash or fixup] So that means this can only be a failed patch application that is either a pick, revert, or reword. We only need to amend HEAD when rewording the root commit or a commit that has been fast-forwarded, for any of the other cases we want a new commit, so we should not set the to_amend flag. Helped-by: Johannes Schindelin <[email protected]> Original-patch-by: Elijah Newren <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f0ac6e3 commit a9279c6

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

sequencer.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,10 +3214,27 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
32143214
intend_to_amend();
32153215
return error_failed_squash(item->commit, opts,
32163216
item->arg_len, item->arg);
3217-
} else if (res && is_rebase_i(opts) && item->commit)
3217+
} else if (res && is_rebase_i(opts) && item->commit) {
3218+
int to_amend = 0;
3219+
struct object_id oid;
3220+
3221+
/*
3222+
* If we are rewording and have either
3223+
* fast-forwarded already, or are about to
3224+
* create a new root commit, we want to amend,
3225+
* otherwise we do not.
3226+
*/
3227+
if (item->command == TODO_REWORD &&
3228+
!get_oid("HEAD", &oid) &&
3229+
(!oidcmp(&item->commit->object.oid, &oid) ||
3230+
(opts->have_squash_onto &&
3231+
!oidcmp(&opts->squash_onto, &oid))))
3232+
to_amend = 1;
3233+
32183234
return res | error_with_patch(item->commit,
3219-
item->arg, item->arg_len, opts, res,
3220-
item->command == TODO_REWORD);
3235+
item->arg, item->arg_len, opts,
3236+
res, to_amend);
3237+
}
32213238
} else if (item->command == TODO_EXEC) {
32223239
char *end_of_arg = (char *)(item->arg + item->arg_len);
32233240
int saved = *end_of_arg;

t/t3404-rebase-interactive.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,35 @@ test_expect_success 'rebase -i --root reword root commit' '
981981
test -z "$(git show -s --format=%p HEAD^)"
982982
'
983983

984+
test_expect_success 'rebase -i --root when root has untracked file confilct' '
985+
test_when_finished "reset_rebase" &&
986+
git checkout -b failing-root-pick A &&
987+
echo x >file2 &&
988+
git rm file1 &&
989+
git commit -m "remove file 1 add file 2" &&
990+
echo z >file1 &&
991+
set_fake_editor &&
992+
test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
993+
rm file1 &&
994+
git rebase --continue &&
995+
test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
996+
test "$(git rev-list --count HEAD)" = 2
997+
'
998+
999+
test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
1000+
test_when_finished "reset_rebase" &&
1001+
echo z>file1 &&
1002+
set_fake_editor &&
1003+
test_must_fail env FAKE_LINES="reword 1 2" \
1004+
FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
1005+
rm file1 &&
1006+
FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
1007+
test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
1008+
test "$(git rev-list --count HEAD)" = 2
1009+
'
1010+
9841011
test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
1012+
git checkout reword-root-branch &&
9851013
git reset --hard &&
9861014
git checkout conflict-branch &&
9871015
set_fake_editor &&

t/t3423-rebase-reword.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/sh
2+
3+
test_description='git rebase interactive with rewording'
4+
5+
. ./test-lib.sh
6+
7+
. "$TEST_DIRECTORY"/lib-rebase.sh
8+
9+
test_expect_success 'setup' '
10+
test_commit master file-1 test &&
11+
12+
git checkout -b stuff &&
13+
14+
test_commit feature_a file-2 aaa &&
15+
test_commit feature_b file-2 ddd
16+
'
17+
18+
test_expect_success 'reword without issues functions as intended' '
19+
test_when_finished "reset_rebase" &&
20+
21+
git checkout stuff^0 &&
22+
23+
set_fake_editor &&
24+
FAKE_LINES="pick 1 reword 2" FAKE_COMMIT_MESSAGE="feature_b_reworded" \
25+
git rebase -i -v master &&
26+
27+
test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
28+
test $(git rev-list --count HEAD) = 3
29+
'
30+
31+
test_expect_success 'reword after a conflict preserves commit' '
32+
test_when_finished "reset_rebase" &&
33+
34+
git checkout stuff^0 &&
35+
36+
set_fake_editor &&
37+
test_must_fail env FAKE_LINES="reword 2" \
38+
git rebase -i -v master &&
39+
40+
git checkout --theirs file-2 &&
41+
git add file-2 &&
42+
FAKE_COMMIT_MESSAGE="feature_b_reworded" git rebase --continue &&
43+
44+
test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
45+
test $(git rev-list --count HEAD) = 2
46+
'
47+
48+
test_done

0 commit comments

Comments
 (0)