Browse Source

rebase -r: make 'merge -c' behave like reword

If the user runs git log while rewording a commit it is confusing if
sometimes we're amending the commit that's being reworded and at other
times we're creating a new commit depending on whether we could
fast-forward or not[1]. For this reason the reword command ensures
that there are no uncommitted changes when rewording. The reword
command also allows the user to edit the todo list while the rebase is
paused. As 'merge -c' also rewords commits make it behave like reword
and add a test.

[1] https://lore.kernel.org/git/xmqqlfvu4be3.fsf@gitster-ct.c.googlers.com/T/#m133009cb91cf0917bcf667300f061178be56680a

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Phillip Wood 4 years ago committed by Junio C Hamano
parent
commit
2be6b6f411
  1. 24
      sequencer.c
  2. 50
      t/lib-rebase.sh
  3. 20
      t/t3430-rebase-merges.sh

24
sequencer.c

@ -3739,10 +3739,9 @@ static struct commit *lookup_label(const char *label, int len, @@ -3739,10 +3739,9 @@ static struct commit *lookup_label(const char *label, int len,
static int do_merge(struct repository *r,
struct commit *commit,
const char *arg, int arg_len,
int flags, struct replay_opts *opts)
int flags, int *check_todo, struct replay_opts *opts)
{
int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ?
EDIT_MSG | VERIFY_MSG : 0;
int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j, *reversed = NULL;
@ -3898,10 +3897,9 @@ static int do_merge(struct repository *r, @@ -3898,10 +3897,9 @@ static int do_merge(struct repository *r,
rollback_lock_file(&lock);
ret = fast_forward_to(r, &commit->object.oid,
&head_commit->object.oid, 0, opts);
if (flags & TODO_EDIT_MERGE_MSG) {
run_commit_flags |= AMEND_MSG;
if (flags & TODO_EDIT_MERGE_MSG)
goto fast_forward_edit;
}

goto leave_merge;
}

@ -4035,10 +4033,17 @@ static int do_merge(struct repository *r, @@ -4035,10 +4033,17 @@ static int do_merge(struct repository *r,
* value (a negative one would indicate that the `merge`
* command needs to be rescheduled).
*/
fast_forward_edit:
ret = !!run_git_commit(git_path_merge_msg(r), opts,
run_commit_flags);

if (!ret && flags & TODO_EDIT_MERGE_MSG) {
fast_forward_edit:
*check_todo = 1;
run_commit_flags |= AMEND_MSG | EDIT_MSG | VERIFY_MSG;
ret = !!run_git_commit(NULL, opts, run_commit_flags);
}


leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
@ -4405,9 +4410,8 @@ static int pick_commits(struct repository *r, @@ -4405,9 +4410,8 @@ static int pick_commits(struct repository *r,
if ((res = do_reset(r, arg, item->arg_len, opts)))
reschedule = 1;
} else if (item->command == TODO_MERGE) {
if ((res = do_merge(r, item->commit,
arg, item->arg_len,
item->flags, opts)) < 0)
if ((res = do_merge(r, item->commit, arg, item->arg_len,
item->flags, &check_todo, opts)) < 0)
reschedule = 1;
else if (item->commit)
record_in_rewritten(&item->commit->object.oid,

50
t/lib-rebase.sh

@ -151,3 +151,53 @@ test_editor_unchanged () { @@ -151,3 +151,53 @@ test_editor_unchanged () {
EOF
test_cmp expect actual
}

# Set up an editor for testing reword commands
# Checks that there are no uncommitted changes when rewording and that the
# todo-list is reread after each
set_reword_editor () {
>reword-actual &&
>reword-oid &&

# Check rewording keeps the original authorship
GIT_AUTHOR_NAME="Reword Author"
GIT_AUTHOR_EMAIL="reword.author@example.com"
GIT_AUTHOR_DATE=@123456

write_script reword-sequence-editor.sh <<-\EOF &&
todo="$(cat "$1")" &&
echo "exec git log -1 --pretty=format:'%an <%ae> %at%n%B%n' \
>>reword-actual" >"$1" &&
printf "%s\n" "$todo" >>"$1"
EOF

write_script reword-editor.sh <<-EOF &&
# Save the oid of the first reworded commit so we can check rebase
# fast-forwards to it
if ! test -s reword-oid
then
git rev-parse HEAD >reword-oid
fi &&
# There should be no uncommited changes
git diff --exit-code HEAD &&
# The todo-list should be re-read after a reword
GIT_SEQUENCE_EDITOR="\"$PWD/reword-sequence-editor.sh\"" \
git rebase --edit-todo &&
echo edited >>"\$1"
EOF

test_set_editor "$PWD/reword-editor.sh"
}

# Check the results of a rebase after calling set_reword_editor
# Pass the commits that were reworded in the order that they were picked
# Expects the first pick to be a fast-forward
check_reworded_commits () {
test_cmp_rev "$(cat reword-oid)" "$1^{commit}" &&
git log --format="%an <%ae> %at%n%B%nedited%n" --no-walk=unsorted "$@" \
>reword-expected &&
test_cmp reword-expected reword-actual &&
git log --format="%an <%ae> %at%n%B" -n $# --first-parent --reverse \
>reword-log &&
test_cmp reword-expected reword-log
}

20
t/t3430-rebase-merges.sh

@ -172,17 +172,19 @@ test_expect_success 'failed `merge <branch>` does not crash' ' @@ -172,17 +172,19 @@ test_expect_success 'failed `merge <branch>` does not crash' '
grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
'

test_expect_success 'fast-forward merge -c still rewords' '
git checkout -b fast-forward-merge-c H &&
test_expect_success 'merge -c commits before rewording and reloads todo-list' '
cat >script-from-scratch <<-\EOF &&
merge -c E B
merge -c H G
EOF

git checkout -b merge-c H &&
(
set_fake_editor &&
FAKE_COMMIT_MESSAGE=edited \
GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
git rebase -ir @^
set_reword_editor &&
GIT_SEQUENCE_EDITOR="\"$PWD/replace-editor.sh\"" \
git rebase -i -r D
) &&
echo edited >expected &&
git log --pretty=format:%B -1 >actual &&
test_cmp expected actual
check_reworded_commits E H
'

test_expect_success 'with a branch tip that was cherry-picked already' '

Loading…
Cancel
Save