rebase -i: Retain user-edited commit messages after squash/fixup conflicts
authorMichael Haggerty <mhagger@alum.mit.edu>
Thu, 14 Jan 2010 05:54:57 +0000 (06:54 +0100)
committerJunio C Hamano <gitster@pobox.com>
Thu, 14 Jan 2010 08:27:57 +0000 (00:27 -0800)
When a squash/fixup fails due to a conflict, the user is required to
edit the commit message.  Previously, if further squash/fixup commands
followed the conflicting squash/fixup, this user-edited message was
discarded and a new automatically-generated commit message was
suggested.

Change the handling of conflicts within squash/fixup command series:
Whenever the user is required to intervene, consider the resulting
commit to be a new basis for the following squash/fixups and use its
commit message in later suggested combined commit messages.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-rebase--interactive.sh
t/t3404-rebase-interactive.sh

index 1569a7ad915988cfc14be71eb550debcd134f864..b835a2975987861cdea59c271aee1b3dea63cee6 100755 (executable)
@@ -408,6 +408,21 @@ peek_next_command () {
        sed -n -e "/^#/d" -e "/^$/d" -e "s/ .*//p" -e "q" < "$TODO"
 }
 
+# A squash/fixup has failed.  Prepare the long version of the squash
+# commit message, then die_with_patch.  This code path requires the
+# user to edit the combined commit message for all commits that have
+# been squashed/fixedup so far.  So also erase the old squash
+# messages, effectively causing the combined commit to be used as the
+# new basis for any further squash/fixups.  Args: sha1 rest
+die_failed_squash() {
+       mv "$SQUASH_MSG" "$MSG" || exit
+       rm -f "$FIXUP_MSG"
+       cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit
+       warn
+       warn "Could not apply $1... $2"
+       die_with_patch $1 ""
+}
+
 do_next () {
        rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
        read command sha1 rest < "$TODO"
@@ -465,58 +480,31 @@ do_next () {
 
                mark_action_done
                update_squash_messages $squash_style $sha1
-               failed=f
                author_script=$(get_author_ident_from_commit HEAD)
                echo "$author_script" > "$AUTHOR_SCRIPT"
                eval "$author_script"
                output git reset --soft HEAD^
-               pick_one -n $sha1 || failed=t
+               pick_one -n $sha1 || die_failed_squash $sha1 "$rest"
                case "$(peek_next_command)" in
                squash|s|fixup|f)
                        # This is an intermediate commit; its message will only be
                        # used in case of trouble.  So use the long version:
-                       if test $failed = f
-                       then
-                               do_with_author output git commit --no-verify -F "$SQUASH_MSG" ||
-                                       failed=t
-                       fi
-                       if test $failed = t
-                       then
-                               cp "$SQUASH_MSG" "$MSG" || exit
-                               # After any kind of hiccup, prevent committing without
-                               # opening the commit message editor:
-                               rm -f "$FIXUP_MSG"
-                               cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit
-                               warn
-                               warn "Could not apply $sha1... $rest"
-                               die_with_patch $sha1 ""
-                       fi
+                       do_with_author output git commit --no-verify -F "$SQUASH_MSG" ||
+                               die_failed_squash $sha1 "$rest"
                        ;;
                *)
                        # This is the final command of this squash/fixup group
-                       if test $failed = f
+                       if test -f "$FIXUP_MSG"
                        then
-                               if test -f "$FIXUP_MSG"
-                               then
-                                       do_with_author git commit --no-verify -F "$FIXUP_MSG" ||
-                                               failed=t
-                               else
-                                       cp "$SQUASH_MSG" "$GIT_DIR"/SQUASH_MSG || exit
-                                       rm -f "$GIT_DIR"/MERGE_MSG
-                                       do_with_author git commit --no-verify -e ||
-                                               failed=t
-                               fi
-                       fi
-                       rm -f "$FIXUP_MSG"
-                       if test $failed = t
-                       then
-                               mv "$SQUASH_MSG" "$MSG" || exit
-                               cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit
-                               warn
-                               warn "Could not apply $sha1... $rest"
-                               die_with_patch $sha1 ""
+                               do_with_author git commit --no-verify -F "$FIXUP_MSG" ||
+                                       die_failed_squash $sha1 "$rest"
+                       else
+                               cp "$SQUASH_MSG" "$GIT_DIR"/SQUASH_MSG || exit
+                               rm -f "$GIT_DIR"/MERGE_MSG
+                               do_with_author git commit --no-verify -e ||
+                                       die_failed_squash $sha1 "$rest"
                        fi
-                       rm -f "$SQUASH_MSG"
+                       rm -f "$SQUASH_MSG" "$FIXUP_MSG"
                        ;;
                esac
                ;;
index a119ce0d37abef0068176b91ae8ee772401ecaaf..4e3513709eb121769f87501c1862c996184a6d05 100755 (executable)
@@ -264,6 +264,42 @@ test_expect_success 'multi-fixup does not fire up editor' '
        git branch -D multi-fixup
 '
 
+test_expect_success 'commit message used after conflict' '
+       git checkout -b conflict-fixup conflict-branch &&
+       base=$(git rev-parse HEAD~4) &&
+       (
+               FAKE_LINES="1 fixup 3 fixup 4" &&
+               export FAKE_LINES &&
+               test_must_fail git rebase -i $base
+       ) &&
+       echo three > conflict &&
+       git add conflict &&
+       FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
+               git rebase --continue &&
+       test $base = $(git rev-parse HEAD^) &&
+       test 1 = $(git show | grep ONCE | wc -l) &&
+       git checkout to-be-rebased &&
+       git branch -D conflict-fixup
+'
+
+test_expect_success 'commit message retained after conflict' '
+       git checkout -b conflict-squash conflict-branch &&
+       base=$(git rev-parse HEAD~4) &&
+       (
+               FAKE_LINES="1 fixup 3 squash 4" &&
+               export FAKE_LINES &&
+               test_must_fail git rebase -i $base
+       ) &&
+       echo three > conflict &&
+       git add conflict &&
+       FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
+               git rebase --continue &&
+       test $base = $(git rev-parse HEAD^) &&
+       test 2 = $(git show | grep TWICE | wc -l) &&
+       git checkout to-be-rebased &&
+       git branch -D conflict-squash
+'
+
 cat > expect-squash-fixup << EOF
 B