Skip to content

Commit a6c2654

Browse files
phillipwoodgitster
authored andcommitted
rebase -m: fix --signoff with conflicts
When rebasing with "--signoff" the commit created by "rebase --continue" after resolving conflicts or editing a commit fails to add the "Signed-off-by:" trailer. This happens because the message from the original commit is reused instead of the one that would have been used if the sequencer had not stopped for the user interaction. The correct message is stored in ctx->message and so with a couple of exceptions this is written to rebase_path_message() when stopping for user interaction instead. The exceptions are (i) "fixup" and "squash" commands where the file is written by error_failed_squash() and (ii) "edit" commands that are fast-forwarded where the original message is still reused. The latter is safe because "--signoff" will never fast-forward. Note this introduces a change in behavior as the message file now contains conflict comments. This is safe because commit_staged_changes() passes an explicit cleanup flag when not editing the message and when the message is being edited it will be cleaned up automatically. This means user now sees the same message comments in editor with "rebase --continue" as they would if they ran "git commit" themselves before continuing the rebase. It also matches the behavior of "git cherry-pick", "git merge" etc. which all list the files with merge conflicts. The tests are extended to check that all commits made after continuing a rebase have a "Signed-off-by:" trailer. Sadly there are a couple of leaks in apply.c which I've not been able to track down that mean this test file is no-longer leak free when testing "git rebase --apply --signoff" with conflicts. Reported-by: David Bimmler <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 53f6746 commit a6c2654

File tree

3 files changed

+94
-21
lines changed

3 files changed

+94
-21
lines changed

sequencer.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3636,13 +3636,24 @@ static int error_with_patch(struct repository *r,
36363636
struct replay_opts *opts,
36373637
int exit_code, int to_amend)
36383638
{
3639-
if (commit) {
3640-
if (make_patch(r, commit, opts))
3639+
struct replay_ctx *ctx = opts->ctx;
3640+
3641+
/*
3642+
* Write the commit message to be used by "git rebase
3643+
* --continue". If a "fixup" or "squash" command has conflicts
3644+
* then we will have already written rebase_path_message() in
3645+
* error_failed_squash(). If an "edit" command was
3646+
* fast-forwarded then we don't have a message in ctx->message
3647+
* and rely on make_patch() to write rebase_path_message()
3648+
* instead.
3649+
*/
3650+
if (ctx->have_message && !file_exists(rebase_path_message()) &&
3651+
write_message(ctx->message.buf, ctx->message.len,
3652+
rebase_path_message(), 0))
3653+
return error(_("could not write commit message file"));
3654+
3655+
if (commit && make_patch(r, commit, opts))
36413656
return -1;
3642-
} else if (copy_file(rebase_path_message(),
3643-
git_path_merge_msg(r), 0666))
3644-
return error(_("unable to copy '%s' to '%s'"),
3645-
git_path_merge_msg(r), rebase_path_message());
36463657

36473658
if (to_amend) {
36483659
if (intend_to_amend())

t/t3428-rebase-signoff.sh

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ test_description='git rebase --signoff
55
This test runs git rebase --signoff and make sure that it works.
66
'
77

8-
TEST_PASSES_SANITIZE_LEAK=true
98
. ./test-lib.sh
9+
. "$TEST_DIRECTORY"/lib-rebase.sh
1010

1111
test_expect_success 'setup' '
1212
git commit --allow-empty -m "Initial empty commit" &&
1313
test_commit first file a &&
14+
test_commit second file &&
15+
git checkout -b conflict-branch first &&
16+
test_commit file-2 file-2 &&
17+
test_commit conflict file &&
18+
test_commit third file &&
1419
1520
ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
1621
@@ -28,6 +33,22 @@ test_expect_success 'setup' '
2833
Signed-off-by: $ident
2934
EOF
3035
36+
# Expected commit message after conflict resolution for rebase --signoff
37+
cat >expected-signed-conflict <<-EOF &&
38+
third
39+
40+
Signed-off-by: $ident
41+
42+
conflict
43+
44+
Signed-off-by: $ident
45+
46+
file-2
47+
48+
Signed-off-by: $ident
49+
50+
EOF
51+
3152
# Expected commit message after rebase without --signoff (or with --no-signoff)
3253
cat >expected-unsigned <<-EOF &&
3354
first
@@ -39,8 +60,12 @@ test_expect_success 'setup' '
3960
# We configure an alias to do the rebase --signoff so that
4061
# on the next subtest we can show that --no-signoff overrides the alias
4162
test_expect_success 'rebase --apply --signoff adds a sign-off line' '
42-
git rbs --apply HEAD^ &&
43-
test_commit_message HEAD expected-signed
63+
test_must_fail git rbs --apply second third &&
64+
git checkout --theirs file &&
65+
git add file &&
66+
git rebase --continue &&
67+
git log --format=%B -n3 >actual &&
68+
test_cmp expected-signed-conflict actual
4469
'
4570

4671
test_expect_success 'rebase --no-signoff does not add a sign-off line' '
@@ -51,28 +76,65 @@ test_expect_success 'rebase --no-signoff does not add a sign-off line' '
5176

5277
test_expect_success 'rebase --exec --signoff adds a sign-off line' '
5378
test_when_finished "rm exec" &&
54-
git commit --amend -m "first" &&
55-
git rebase --exec "touch exec" --signoff HEAD^ &&
79+
git rebase --exec "touch exec" --signoff first^ first &&
5680
test_path_is_file exec &&
5781
test_commit_message HEAD expected-signed
5882
'
5983

6084
test_expect_success 'rebase --root --signoff adds a sign-off line' '
61-
git commit --amend -m "first" &&
85+
git checkout first &&
6286
git rebase --root --keep-empty --signoff &&
6387
test_commit_message HEAD^ expected-initial-signed &&
6488
test_commit_message HEAD expected-signed
6589
'
6690

67-
test_expect_success 'rebase -i --signoff fails' '
68-
git commit --amend -m "first" &&
69-
git rebase -i --signoff HEAD^ &&
70-
test_commit_message HEAD expected-signed
91+
test_expect_success 'rebase -m --signoff adds a sign-off line' '
92+
test_must_fail git rebase -m --signoff second third &&
93+
git checkout --theirs file &&
94+
git add file &&
95+
GIT_EDITOR="sed -n /Conflicts:/,/^\\\$/p >actual" \
96+
git rebase --continue &&
97+
cat >expect <<-\EOF &&
98+
# Conflicts:
99+
# file
100+
101+
EOF
102+
test_cmp expect actual &&
103+
git log --format=%B -n3 >actual &&
104+
test_cmp expected-signed-conflict actual
71105
'
72106

73-
test_expect_success 'rebase -m --signoff fails' '
74-
git commit --amend -m "first" &&
75-
git rebase -m --signoff HEAD^ &&
76-
test_commit_message HEAD expected-signed
107+
test_expect_success 'rebase -i --signoff adds a sign-off line when editing commit' '
108+
(
109+
set_fake_editor &&
110+
FAKE_LINES="edit 1 edit 3 edit 2" \
111+
git rebase -i --signoff first third
112+
) &&
113+
echo a >a &&
114+
git add a &&
115+
test_must_fail git rebase --continue &&
116+
git checkout --ours file &&
117+
echo b >a &&
118+
git add a file &&
119+
git rebase --continue &&
120+
echo c >a &&
121+
git add a &&
122+
git log --format=%B -n3 >actual &&
123+
cat >expect <<-EOF &&
124+
conflict
125+
126+
Signed-off-by: $ident
127+
128+
third
129+
130+
Signed-off-by: $ident
131+
132+
file-2
133+
134+
Signed-off-by: $ident
135+
136+
EOF
137+
test_cmp expect actual
77138
'
139+
78140
test_done

t/t3434-rebase-i18n.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ test_rebase_continue_update_encode () {
7171
git config i18n.commitencoding $new &&
7272
test_must_fail git rebase -m main &&
7373
test -f .git/rebase-merge/message &&
74-
git stripspace <.git/rebase-merge/message >two.t &&
74+
git stripspace -s <.git/rebase-merge/message >two.t &&
7575
git add two.t &&
7676
git rebase --continue &&
7777
compare_msg $msgfile $old $new &&

0 commit comments

Comments
 (0)