git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 1/5] rebase --continue: add --autostage to stage unstaged changes

From
PWPhillip Wood <phillip.wood@talktalk.net>
Date
Jul 26, 2017, 10:27 UTC
Message-ID
<20170726102720.15274-2-phillip.wood@talktalk.net>
In-Reply-To
<20170726102720.15274-1-phillip.wood@talktalk.net>
From: Phillip Wood <phillip.wood@dunelm.org.uk>

After resolving conflicts during a rebase it is a pain to have to run 'git add' before 'git rebase --continue'. Passing --autostage to 'git rebase --continue' will stage them automatically so long as 'git diff --check' says they are ok. This is a safety measure to avoid accidentally staging files containing unresolved conflicts.

Continuing an interactive rebase after a failed exec or if HEAD has changed since rebase stopped with --autostage will stage changes that wont be committed as rebase --continue will bail out. This will be fixed in the next commit.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---

Using diff check is not ideal as it will error out on whitespace changes (which should be check in a commit hook if the user is worried about them) as well as merge markers. Looking through diff.c it should be possible to add a --check=merge-markers optional argument as the two checks are enabled independently in the code. As Junio pointed out in a previous message [1] the absence of conflict markers does not indicate that a file is fully resolved but checking for them does at least avoid the case of the user blindly continuing when they have forgotten to look at a conflicted file.

The autostaging behaviour is opt-in so users who like the additional safety of having to do git add before git rebase --continue can continue using their current workflow.

I wonder if check_unstaged() should give different error messages depending on the presence of unmerged paths rather than saying 'unstaged changes' all the time. Also it should probably have some message after the check for merge markers fails rather than just the raw output from diff --check.

[1] https://public-inbox.org/git/xmqqmv7td0a5.fsf@gitster.mtv.corp.google.com/
 git-rebase--am.sh          |  1 +
 git-rebase--interactive.sh |  1 +
 git-rebase--merge.sh       |  1 +
 git-rebase.sh              | 76 +++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 71 insertions(+), 8 deletions(-)
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index 375239341fbfe885e51a25e9e0dc2d4fee791345..30faa8c24cce2149a883c0055e3f6e93dabd2dd0 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -17,6 +17,7 @@ git_rebase__am () {
 
 case "$action" in
 continue)
+	check_unstaged
 	git am --resolved --resolvemsg="$resolvemsg" \
 		${gpg_sign_opt:+"$gpg_sign_opt"} &&
 	move_to_original_branch
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 90b1fbe9cf6e8dfb2f4331916809fa40bf9050d2..4c037a3f7a9e01406c4205bf8786a3da5060381f 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -1069,6 +1069,7 @@ git_rebase__interactive () {
 
 case "$action" in
 continue)
+	check_unstaged
 	if test ! -d "$rewritten"
 	then
 		exec git rebase--helper ${force_rebase:+--no-ff} --continue
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index 06a4723d4db3db74ea17ace60d824e83cdee25e9..81723fc485882750c3ed7214b880d49cf55c6d68 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -16,6 +16,7 @@ read_state () {
 continue_merge () {
 	test -d "$state_dir" || die "$state_dir directory does not exist"
 
+	check_unstaged
 	unmerged=$(git ls-files -u)
 	if test -n "$unmerged"
 	then
diff --git a/git-rebase.sh b/git-rebase.sh
index 2cf73b88e8e83ca34b9eb319dbc2b0a220139b0f..9ca387349b1cde440c4244de9125446fd35a7b67 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -9,7 +9,7 @@ OPTIONS_STUCKLONG=t
 OPTIONS_SPEC="\
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git-rebase --continue | --abort | --skip | --edit-todo
+git-rebase --continue [--autostage] | --abort | --skip | --edit-todo
 --
  Available options are
 v,verbose!         display a diffstat of what changed upstream
@@ -32,6 +32,7 @@ verify             allow pre-rebase hook to run
 rerere-autoupdate  allow rerere to update index with resolved conflicts
 root!              rebase all reachable commits up to the root(s)
 autosquash         move commits that begin with squash!/fixup! under -i
+a,autostage        add unstaged changes to the index when continuing
 committer-date-is-author-date! passed to 'git am'
 ignore-date!       passed to 'git am'
 signoff            passed to 'git am'
@@ -69,6 +70,7 @@ merge_dir="$GIT_DIR"/rebase-merge
 apply_dir="$GIT_DIR"/rebase-apply
 verbose=
 diffstat=
+autostage=false
 test "$(git config --bool rebase.stat)" = true && diffstat=t
 autostash="$(git config --bool rebase.autostash || echo false)"
 fork_point=auto
@@ -213,6 +215,67 @@ run_pre_rebase_hook () {
 	fi
 }
 
+check_autostage () {
+	# If the user has already staged files that contain whitespace
+	# errors or merge markers then we want ignore them so rebase
+	# --continue behaves consistency with and without --autostage
+	git diff-index --diff-filter=U --cached --name-only -z HEAD |
+		xargs -0 git diff-index --check HEAD -- &&
+	git diff-files --diff-filter=MA --check &&
+	git add -u ||
+		exit $?
+}
+
+autostage_advice () {
+	gettext "\
+Unable to continue rebasing as there are unstaged changes.
+Please stage or reset the changes before continuing with:
+
+  git rebase --continue
+
+or run:
+
+  git rebase --continue --autostage
+
+to stage them automatically.
+"
+}
+
+check_unstaged () {
+	git update-index --ignore-submodules --refresh >/dev/null
+	if ! git diff-files --quiet --ignore-submodules
+	then
+		if test $autostage = true
+		then
+			check_autostage
+		else
+			die "$(autostage_advice)"
+		fi
+	fi
+}
+
+parse_continue () {
+	action=continue
+	shift
+	while test $# != 0
+	do
+		case "$1" in
+		--autostage)
+			autostage=true
+			;;
+		--no-autostage)
+			autostage=false
+			;;
+		--)
+			;;
+		*)
+			usage
+			;;
+		esac
+	shift
+	done
+}
+
 test -f "$apply_dir"/applying &&
 	die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
 
@@ -243,7 +306,10 @@ do
 	--verify)
 		ok_to_skip_pre_rebase=
 		;;
-	--continue|--skip|--abort|--quit|--edit-todo)
+	--continue)
+		parse_continue "$@"
+		;;
+	--skip|--abort|--quit|--edit-todo)
 		test $total_argc -eq 2 || usage
 		action=${1##--}
 		;;
@@ -374,12 +440,6 @@ continue)
 	# Sanity check
 	git rev-parse --verify HEAD >/dev/null ||
 		die "$(gettext "Cannot read HEAD")"
-	git update-index --ignore-submodules --refresh &&
-	git diff-files --quiet --ignore-submodules || {
-		echo "$(gettext "You must edit all merge conflicts and then
-mark them as resolved using git add")"
-		exit 1
-	}
 	read_basic_state
 	run_specific_rebase
 	;;
-- 
2.13.3
Previous: Phillip WoodNext: Phillip Wood
Message 2 of 19 in “Add option to autostage changes when continuing a rebase”
  1. 0/5 Add option to autostage changes when continuing a rebasePhillip Wood, Jul 26, 2017
  2. 1/5 rebase --continue: add --autostage to stage unstaged changesPhillip Wood, Jul 26, 2017
  3. 3/5 Unify rebase amend message when HEAD has changedPhillip Wood, Jul 26, 2017
  4. 4/5 Add tests for rebase --continue --autostagePhillip Wood, Jul 26, 2017
  5. 5/5 Add rebase.continue.autostage config settingPhillip Wood, Jul 26, 2017
  6. 2/5 rebase -i: improve --continue --autostagePhillip Wood, Jul 26, 2017
  7. Junio C HamanoJul 26, 2017
  8. Junio C HamanoJul 26, 2017
  9. Junio C HamanoJul 26, 2017
  10. Phillip WoodJul 27, 2017
  11. Phillip WoodJul 27, 2017
  12. Junio C HamanoJul 27, 2017
  13. Phillip WoodAug 21, 2017
  14. Junio C HamanoAug 21, 2017
  15. Phillip WoodAug 22, 2017
  16. Junio C HamanoAug 22, 2017
  17. Phillip WoodAug 24, 2017
  18. Junio C HamanoAug 24, 2017
  19. Phillip WoodAug 25, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.