[PATCH v3 26/27] rebase -i: refuse to commit when resuming with updated head

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If git-rebase--interactive fails to apply the changes introduced by a
commit due to conflicts, it interrupts the rebase process and gives
the user a shell to resolve the conflicts manually. The process is
resumed when the user executes `git rebase --continue`. If the index
has changes, the script assumes that those are to be committed under
the authorship and with the log message of the commit it tried to
replay last. However, that assumption is most likely incorrect if the
user has already committed the resolved changes herself. To prevent
committing unrelated changes under the wrong authorship and with the
wrong log message, at least check that HEAD is still at the same
commit by tracking the hash of the last replayed commit. A similar
check already happens before `git rebase --continue` amends the
previous commit after an `edit` or a conflicted `squash` command.

Signed-off-by: Fabian Ruch <bafain@xxxxxxxxx>
---
 git-rebase--interactive.sh    | 35 +++++++++++++++++++++--------------
 t/t3404-rebase-interactive.sh | 12 ++++++++++++
 2 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8fbfe6d..51ee80c 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -62,13 +62,18 @@ msgnum="$state_dir"/msgnum
 # being rebased.
 author_script="$state_dir"/author-script
 
-# When an "edit" rebase command is being processed, the SHA1 of the
-# commit to be edited is recorded in this file.  The same happens when
-# rewriting a root commit fails, for instance "reword".  When "git
-# rebase --continue" is executed, if there are any staged changes then
-# they will be amended to the HEAD commit, but only provided the HEAD
-# commit is still the commit to be edited.  When any other rebase
-# command is processed, this file is deleted.
+# This file keeps track of the SHA1 of the last replayed commit, the
+# new parent of the next commit being replayed. It is used to make
+# sure that "git rebase --continue" only commits resolved conflicts
+# or "edit" changes automatically.
+last_head="$state_dir"/last_head
+
+# When an "edit" or a "squash" rebase command is being processed, the
+# file 'amend' is created. When "git rebase --continue" is executed,
+# if there are any staged changes then they will be amended to the
+# HEAD commit, but only provided the HEAD commit is still the commit
+# to be edited or the squash commit. When any other rebase command is
+# processed, these files are deleted.
 amend="$state_dir"/amend
 
 # For the post-rewrite hook, we make a list of rewritten commits and
@@ -179,7 +184,8 @@ die_with_patch () {
 exit_with_patch () {
 	echo "$1" > "$state_dir"/stopped-sha
 	make_patch $1
-	git rev-parse --verify HEAD > "$amend"
+	>"$amend"
+	git rev-parse --verify HEAD >"$last_head"
 	gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
 	warn "You can amend the commit now, with"
 	warn
@@ -535,7 +541,7 @@ do_pick () {
 			fi
 			rewrite=y
 			rewrite_amend=y
-			git rev-parse --verify HEAD >"$amend"
+			>"$amend"
 			;;
 		-F|--file)
 			if test $# -eq 0
@@ -572,7 +578,7 @@ do_pick () {
 	then
 		rewrite=y
 		rewrite_amend=y
-		git rev-parse --verify HEAD >"$amend"
+		>"$amend"
 
 		# Set the correct commit message and author info on the
 		# sentinel root before cherry-picking the original changes
@@ -734,6 +740,7 @@ do_replay () {
 
 do_next () {
 	rm -f "$msg" "$author_script" "$amend" || exit
+	git rev-parse --verify HEAD >"$last_head" || exit
 	read -r command args <"$todo"
 
 	case "$command" in
@@ -1031,13 +1038,13 @@ In both case, once you're done, continue with:
   git rebase --continue
 "
 		fi
-		if test -f "$amend"
-		then
-			current_head=$(git rev-parse --verify HEAD)
-			test "$current_head" = $(cat "$amend") ||
+		current_head=$(git rev-parse --verify HEAD)
+		test "$current_head" = $(cat "$last_head") ||
 			die "\
 You have uncommitted changes in your working tree. Please, commit them
 first and then run 'git rebase --continue' again."
+		if test -f "$amend"
+		then
 			git commit --amend --no-verify -F "$msg" -e \
 				${gpg_sign_opt:+"$gpg_sign_opt"} ||
 				die "Could not commit staged changes."
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index c037a07..5955bd8 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -444,6 +444,18 @@ test_expect_success '--continue tries to commit' '
 	git show HEAD | grep chouette
 '
 
+test_expect_success '--continue does not commit after head is moved' '
+	git reset --hard to-be-rebased@{1} &&
+	test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
+	echo resolved >file1 &&
+	git add file1 &&
+	git commit &&
+	echo dirty >file1 &&
+	test_must_fail git rebase --continue &&
+	git checkout file1 &&
+	git rebase --continue
+'
+
 test_expect_success 'verbose flag is heeded, even after --continue' '
 	git reset --hard master@{1} &&
 	test_tick &&
-- 
2.0.1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]