rebase -i: handle fixup of root commit correctly

There is a bug with git rebase -i --root when a fixup or squash line is
applied to the new root. We attempt to amend the commit onto which they
apply with git reset --soft HEAD^ followed by a normal commit. Unlike a
real commit --amend, this sequence will fail against a root commit as it
has no parent.

Fix rebase -i to use commit --amend for fixup and squash instead, and
add a test for the case of a fixup of the root commit.

Signed-off-by: Chris Webb <chris@arachsys.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Chris Webb 2012-07-24 13:17:03 +01:00 коммит произвёл Junio C Hamano
Родитель 994fd91d1f
Коммит 2147f844ed
2 изменённых файлов: 21 добавлений и 12 удалений

Просмотреть файл

@ -495,25 +495,28 @@ do_next () {
author_script_content=$(get_author_ident_from_commit HEAD) author_script_content=$(get_author_ident_from_commit HEAD)
echo "$author_script_content" > "$author_script" echo "$author_script_content" > "$author_script"
eval "$author_script_content" eval "$author_script_content"
output git reset --soft HEAD^ if ! pick_one -n $sha1
pick_one -n $sha1 || die_failed_squash $sha1 "$rest" then
git rev-parse --verify HEAD >"$amend"
die_failed_squash $sha1 "$rest"
fi
case "$(peek_next_command)" in case "$(peek_next_command)" in
squash|s|fixup|f) squash|s|fixup|f)
# This is an intermediate commit; its message will only be # This is an intermediate commit; its message will only be
# used in case of trouble. So use the long version: # used in case of trouble. So use the long version:
do_with_author output git commit --no-verify -F "$squash_msg" || do_with_author output git commit --amend --no-verify -F "$squash_msg" ||
die_failed_squash $sha1 "$rest" die_failed_squash $sha1 "$rest"
;; ;;
*) *)
# This is the final command of this squash/fixup group # This is the final command of this squash/fixup group
if test -f "$fixup_msg" if test -f "$fixup_msg"
then then
do_with_author git commit --no-verify -F "$fixup_msg" || do_with_author git commit --amend --no-verify -F "$fixup_msg" ||
die_failed_squash $sha1 "$rest" die_failed_squash $sha1 "$rest"
else else
cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
rm -f "$GIT_DIR"/MERGE_MSG rm -f "$GIT_DIR"/MERGE_MSG
do_with_author git commit --no-verify -e || do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e ||
die_failed_squash $sha1 "$rest" die_failed_squash $sha1 "$rest"
fi fi
rm -f "$squash_msg" "$fixup_msg" rm -f "$squash_msg" "$fixup_msg"
@ -729,7 +732,6 @@ In both case, once you're done, continue with:
fi fi
. "$author_script" || . "$author_script" ||
die "Error trying to find the author identity to amend commit" die "Error trying to find the author identity to amend commit"
current_head=
if test -f "$amend" if test -f "$amend"
then then
current_head=$(git rev-parse --verify HEAD) current_head=$(git rev-parse --verify HEAD)
@ -737,13 +739,12 @@ In both case, once you're done, continue with:
die "\ die "\
You have uncommitted changes in your working tree. Please, commit them You have uncommitted changes in your working tree. Please, commit them
first and then run 'git rebase --continue' again." first and then run 'git rebase --continue' again."
git reset --soft HEAD^ || do_with_author git commit --amend --no-verify -F "$msg" -e ||
die "Cannot rewind the HEAD" die "Could not commit staged changes."
else
do_with_author git commit --no-verify -F "$msg" -e ||
die "Could not commit staged changes."
fi fi
do_with_author git commit --no-verify -F "$msg" -e || {
test -n "$current_head" && git reset --soft $current_head
die "Could not commit staged changes."
}
fi fi
record_in_rewritten "$(cat "$state_dir"/stopped-sha)" record_in_rewritten "$(cat "$state_dir"/stopped-sha)"

Просмотреть файл

@ -786,4 +786,12 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '
git rebase --abort git rebase --abort
' '
test_expect_success 'rebase -i --root fixup root commit' '
git checkout B &&
FAKE_LINES="1 fixup 2" git rebase -i --root &&
test A = $(git cat-file commit HEAD | sed -ne \$p) &&
test B = $(git show HEAD:file1) &&
test 0 = $(git cat-file commit HEAD | grep -c ^parent\ )
'
test_done test_done