Skip to content

Commit 9135259

Browse files
committed
Merge branch 'pw/rebase-r-fixes'
Various bugs in "git rebase -r" have been fixed. * pw/rebase-r-fixes: rebase -r: fix merge -c with a merge strategy rebase -r: don't write .git/MERGE_MSG when fast-forwarding rebase -i: add another reword test rebase -r: make 'merge -c' behave like reword
2 parents 0ba5a0b + f2563c9 commit 9135259

File tree

4 files changed

+155
-58
lines changed

4 files changed

+155
-58
lines changed

sequencer.c

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,8 @@ static int run_git_commit(const char *defmsg,
983983

984984
cmd.git_cmd = 1;
985985

986-
if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
986+
if (is_rebase_i(opts) && !(!defmsg && (flags & AMEND_MSG)) &&
987+
read_env_script(&cmd.env_array)) {
987988
const char *gpg_opt = gpg_sign_opt_quoted(opts);
988989

989990
return error(_(staged_changes_advice),
@@ -3739,10 +3740,9 @@ static struct commit *lookup_label(const char *label, int len,
37393740
static int do_merge(struct repository *r,
37403741
struct commit *commit,
37413742
const char *arg, int arg_len,
3742-
int flags, struct replay_opts *opts)
3743+
int flags, int *check_todo, struct replay_opts *opts)
37433744
{
3744-
int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ?
3745-
EDIT_MSG | VERIFY_MSG : 0;
3745+
int run_commit_flags = 0;
37463746
struct strbuf ref_name = STRBUF_INIT;
37473747
struct commit *head_commit, *merge_commit, *i;
37483748
struct commit_list *bases, *j, *reversed = NULL;
@@ -3816,6 +3816,45 @@ static int do_merge(struct repository *r,
38163816
goto leave_merge;
38173817
}
38183818

3819+
/*
3820+
* If HEAD is not identical to the first parent of the original merge
3821+
* commit, we cannot fast-forward.
3822+
*/
3823+
can_fast_forward = opts->allow_ff && commit && commit->parents &&
3824+
oideq(&commit->parents->item->object.oid,
3825+
&head_commit->object.oid);
3826+
3827+
/*
3828+
* If any merge head is different from the original one, we cannot
3829+
* fast-forward.
3830+
*/
3831+
if (can_fast_forward) {
3832+
struct commit_list *p = commit->parents->next;
3833+
3834+
for (j = to_merge; j && p; j = j->next, p = p->next)
3835+
if (!oideq(&j->item->object.oid,
3836+
&p->item->object.oid)) {
3837+
can_fast_forward = 0;
3838+
break;
3839+
}
3840+
/*
3841+
* If the number of merge heads differs from the original merge
3842+
* commit, we cannot fast-forward.
3843+
*/
3844+
if (j || p)
3845+
can_fast_forward = 0;
3846+
}
3847+
3848+
if (can_fast_forward) {
3849+
rollback_lock_file(&lock);
3850+
ret = fast_forward_to(r, &commit->object.oid,
3851+
&head_commit->object.oid, 0, opts);
3852+
if (flags & TODO_EDIT_MERGE_MSG)
3853+
goto fast_forward_edit;
3854+
3855+
goto leave_merge;
3856+
}
3857+
38193858
if (commit) {
38203859
const char *encoding = get_commit_output_encoding();
38213860
const char *message = logmsg_reencode(commit, NULL, encoding);
@@ -3865,46 +3904,6 @@ static int do_merge(struct repository *r,
38653904
}
38663905
}
38673906

3868-
/*
3869-
* If HEAD is not identical to the first parent of the original merge
3870-
* commit, we cannot fast-forward.
3871-
*/
3872-
can_fast_forward = opts->allow_ff && commit && commit->parents &&
3873-
oideq(&commit->parents->item->object.oid,
3874-
&head_commit->object.oid);
3875-
3876-
/*
3877-
* If any merge head is different from the original one, we cannot
3878-
* fast-forward.
3879-
*/
3880-
if (can_fast_forward) {
3881-
struct commit_list *p = commit->parents->next;
3882-
3883-
for (j = to_merge; j && p; j = j->next, p = p->next)
3884-
if (!oideq(&j->item->object.oid,
3885-
&p->item->object.oid)) {
3886-
can_fast_forward = 0;
3887-
break;
3888-
}
3889-
/*
3890-
* If the number of merge heads differs from the original merge
3891-
* commit, we cannot fast-forward.
3892-
*/
3893-
if (j || p)
3894-
can_fast_forward = 0;
3895-
}
3896-
3897-
if (can_fast_forward) {
3898-
rollback_lock_file(&lock);
3899-
ret = fast_forward_to(r, &commit->object.oid,
3900-
&head_commit->object.oid, 0, opts);
3901-
if (flags & TODO_EDIT_MERGE_MSG) {
3902-
run_commit_flags |= AMEND_MSG;
3903-
goto fast_forward_edit;
3904-
}
3905-
goto leave_merge;
3906-
}
3907-
39083907
if (strategy || to_merge->next) {
39093908
/* Octopus merge */
39103909
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -3935,7 +3934,10 @@ static int do_merge(struct repository *r,
39353934
strvec_pushf(&cmd.args,
39363935
"-X%s", opts->xopts[k]);
39373936
}
3938-
strvec_push(&cmd.args, "--no-edit");
3937+
if (!(flags & TODO_EDIT_MERGE_MSG))
3938+
strvec_push(&cmd.args, "--no-edit");
3939+
else
3940+
strvec_push(&cmd.args, "--edit");
39393941
strvec_push(&cmd.args, "--no-ff");
39403942
strvec_push(&cmd.args, "--no-log");
39413943
strvec_push(&cmd.args, "--no-stat");
@@ -4035,10 +4037,17 @@ static int do_merge(struct repository *r,
40354037
* value (a negative one would indicate that the `merge`
40364038
* command needs to be rescheduled).
40374039
*/
4038-
fast_forward_edit:
40394040
ret = !!run_git_commit(git_path_merge_msg(r), opts,
40404041
run_commit_flags);
40414042

4043+
if (!ret && flags & TODO_EDIT_MERGE_MSG) {
4044+
fast_forward_edit:
4045+
*check_todo = 1;
4046+
run_commit_flags |= AMEND_MSG | EDIT_MSG | VERIFY_MSG;
4047+
ret = !!run_git_commit(NULL, opts, run_commit_flags);
4048+
}
4049+
4050+
40424051
leave_merge:
40434052
strbuf_release(&ref_name);
40444053
rollback_lock_file(&lock);
@@ -4405,9 +4414,8 @@ static int pick_commits(struct repository *r,
44054414
if ((res = do_reset(r, arg, item->arg_len, opts)))
44064415
reschedule = 1;
44074416
} else if (item->command == TODO_MERGE) {
4408-
if ((res = do_merge(r, item->commit,
4409-
arg, item->arg_len,
4410-
item->flags, opts)) < 0)
4417+
if ((res = do_merge(r, item->commit, arg, item->arg_len,
4418+
item->flags, &check_todo, opts)) < 0)
44114419
reschedule = 1;
44124420
else if (item->commit)
44134421
record_in_rewritten(&item->commit->object.oid,

t/lib-rebase.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,59 @@ test_editor_unchanged () {
151151
EOF
152152
test_cmp expect actual
153153
}
154+
155+
# Set up an editor for testing reword commands
156+
# Checks that there are no uncommitted changes when rewording and that the
157+
# todo-list is reread after each
158+
set_reword_editor () {
159+
>reword-actual &&
160+
>reword-oid &&
161+
162+
# Check rewording keeps the original authorship
163+
GIT_AUTHOR_NAME="Reword Author"
164+
GIT_AUTHOR_EMAIL="[email protected]"
165+
GIT_AUTHOR_DATE=@123456
166+
167+
write_script reword-sequence-editor.sh <<-\EOF &&
168+
todo="$(cat "$1")" &&
169+
echo "exec git log -1 --pretty=format:'%an <%ae> %at%n%B%n' \
170+
>>reword-actual" >"$1" &&
171+
printf "%s\n" "$todo" >>"$1"
172+
EOF
173+
174+
write_script reword-editor.sh <<-EOF &&
175+
# Save the oid of the first reworded commit so we can check rebase
176+
# fast-forwards to it. Also check that we do not write .git/MERGE_MSG
177+
# when fast-forwarding
178+
if ! test -s reword-oid
179+
then
180+
git rev-parse HEAD >reword-oid &&
181+
if test -f .git/MERGE_MSG
182+
then
183+
echo 1>&2 "error: .git/MERGE_MSG exists"
184+
exit 1
185+
fi
186+
fi &&
187+
# There should be no uncommited changes
188+
git diff --exit-code HEAD &&
189+
# The todo-list should be re-read after a reword
190+
GIT_SEQUENCE_EDITOR="\"$PWD/reword-sequence-editor.sh\"" \
191+
git rebase --edit-todo &&
192+
echo edited >>"\$1"
193+
EOF
194+
195+
test_set_editor "$PWD/reword-editor.sh"
196+
}
197+
198+
# Check the results of a rebase after calling set_reword_editor
199+
# Pass the commits that were reworded in the order that they were picked
200+
# Expects the first pick to be a fast-forward
201+
check_reworded_commits () {
202+
test_cmp_rev "$(cat reword-oid)" "$1^{commit}" &&
203+
git log --format="%an <%ae> %at%n%B%nedited%n" --no-walk=unsorted "$@" \
204+
>reword-expected &&
205+
test_cmp reword-expected reword-actual &&
206+
git log --format="%an <%ae> %at%n%B" -n $# --first-parent --reverse \
207+
>reword-log &&
208+
test_cmp reword-expected reword-log
209+
}

t/t3404-rebase-interactive.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,19 @@ test_expect_success 'reword' '
839839
git show HEAD~2 | grep "C changed"
840840
'
841841

842+
test_expect_success 'no uncommited changes when rewording the todo list is reloaded' '
843+
git checkout E &&
844+
test_when_finished "git checkout @{-1}" &&
845+
(
846+
set_fake_editor &&
847+
GIT_SEQUENCE_EDITOR="\"$PWD/fake-editor.sh\"" &&
848+
export GIT_SEQUENCE_EDITOR &&
849+
set_reword_editor &&
850+
FAKE_LINES="reword 1 reword 2" git rebase -i C
851+
) &&
852+
check_reworded_commits D E
853+
'
854+
842855
test_expect_success 'rebase -i can copy notes' '
843856
git config notes.rewrite.rebase true &&
844857
git config notes.rewriteRef "refs/notes/*" &&

t/t3430-rebase-merges.sh

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,39 @@ test_expect_success 'failed `merge <branch>` does not crash' '
172172
grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
173173
'
174174

175-
test_expect_success 'fast-forward merge -c still rewords' '
176-
git checkout -b fast-forward-merge-c H &&
175+
test_expect_success 'merge -c commits before rewording and reloads todo-list' '
176+
cat >script-from-scratch <<-\EOF &&
177+
merge -c E B
178+
merge -c H G
179+
EOF
180+
181+
git checkout -b merge-c H &&
177182
(
178-
set_fake_editor &&
179-
FAKE_COMMIT_MESSAGE=edited \
180-
GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
181-
git rebase -ir @^
183+
set_reword_editor &&
184+
GIT_SEQUENCE_EDITOR="\"$PWD/replace-editor.sh\"" \
185+
git rebase -i -r D
182186
) &&
183-
echo edited >expected &&
184-
git log --pretty=format:%B -1 >actual &&
185-
test_cmp expected actual
187+
check_reworded_commits E H
186188
'
187189

190+
test_expect_success 'merge -c rewords when a strategy is given' '
191+
git checkout -b merge-c-with-strategy H &&
192+
write_script git-merge-override <<-\EOF &&
193+
echo overridden$1 >G.t
194+
git add G.t
195+
EOF
196+
197+
PATH="$PWD:$PATH" \
198+
GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
199+
GIT_EDITOR="echo edited >>" \
200+
git rebase --no-ff -ir -s override -Xxopt E &&
201+
test_write_lines overridden--xopt >expect &&
202+
test_cmp expect G.t &&
203+
test_write_lines H "" edited "" >expect &&
204+
git log --format=%B -1 >actual &&
205+
test_cmp expect actual
206+
207+
'
188208
test_expect_success 'with a branch tip that was cherry-picked already' '
189209
git checkout -b already-upstream main &&
190210
base="$(git rev-parse --verify HEAD)" &&

0 commit comments

Comments
 (0)