threads / rfc / 46472

RFC patch, 5 partsrebase --continue: add --autostage to stage unstaged changes

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

## tl;dr

19 messages between Jul 26, 2017 and Aug 25, 2017. Diffs are folded; open one to read it.

replies: 18people: 2as markdown or json

Phillip Wood· Jul 26, 2017, 10:27 UTC · lore

[RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

From: Phillip Wood <phillip.wood@dunelm.org.uk>

These patches add an '--autostage' option (and corresponding config variable) to 'rebase --continue' that will stage any unstaged changes before continuing. This saves the user having to type 'git add' before running 'git rebase --continue'.

Phillip Wood (5):
  rebase --continue: add --autostage to stage unstaged changes
  rebase -i: improve --continue --autostage
  Unify rebase amend message when HEAD has changed
  Add tests for rebase --continue --autostage
  Add rebase.continue.autostage config setting
 git-rebase--am.sh             |   1 +
 git-rebase--interactive.sh    | 111 ++++++++++++++++++++++++++++++++----------
 git-rebase--merge.sh          |   1 +
 git-rebase.sh                 |  76 ++++++++++++++++++++++++++---
 sequencer.c                   |  22 +++++++--
 t/t3404-rebase-interactive.sh |   2 +-
 t/t3418-rebase-continue.sh    |  50 ++++++++++++++++++-
 7 files changed, 222 insertions(+), 41 deletions(-)
-- 
2.13.3
Phillip Wood· Jul 26, 2017, 10:27 UTC · re: Phillip Wood · lore
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(-)
Show changes to 4 files +71 −8

git-rebase--am.sh, git-rebase--interactive.sh, git-rebase--merge.sh, git-rebase.sh

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
Phillip Wood· Jul 26, 2017, 10:27 UTC · re: Phillip Wood · lore

[RFC PATCH 3/5] Unify rebase amend message when HEAD has changed

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If rebase --interactive is unable to commit staged changes because HEAD has changed since rebase stopped the user gets different messages depending on whether they specified --autostage or not. Update the messages in the other code paths to match the --autostage one.

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

The change from error() to fprintf() is to keep the messages consistent, maybe the messages in the shell script should be prefixed with 'error:' instead.

 git-rebase--interactive.sh | 10 ++++++----
 sequencer.c                | 22 +++++++++++++++++-----
 2 files changed, 23 insertions(+), 9 deletions(-)
Show changes to 2 files +23 −9

git-rebase--interactive.sh, sequencer.c

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8140c88839b4f3a86f53faaaa2ba4433ecc7f58b..e1845e940b8de05b10b011d8167917a60a7c00b9 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -1164,10 +1164,12 @@ $(unstaged_advice)"
 			die "$(gettext "Error trying to find the author identity to amend commit")"
 		if test -n "$amend_head"
 		then
-			test -n "$amend_ok" ||
-			die "$(gettext "\
-You have uncommitted changes in your working tree. Please commit them
-first and then run 'git rebase --continue' again.")"
+			test -n "$amend_ok" || {
+				gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
+				die "$(gettext "\
+Unable to commit changes as HEAD has changed since git rebase stopped.")
+$(staged_advice)"
+			}
 			do_with_author git commit --amend --no-verify -F "$msg" -e \
 				${gpg_sign_opt:+"$gpg_sign_opt"} ||
 				die "$(gettext "Could not commit staged changes.")"
diff --git a/sequencer.c b/sequencer.c
index 3010faf86398697469e903318a35421d911acb23..2722d36781e5c47ee81eb3359aa6178042430e68 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2214,12 +2214,24 @@ static int commit_staged_changes(struct replay_opts *opts)
 		if (get_sha1_hex(rev.buf, to_amend))
 			return error(_("invalid contents: '%s'"),
 				rebase_path_amend());
-		if (hashcmp(head, to_amend))
-			return error(_("\nYou have uncommitted changes in your "
-				       "working tree. Please, commit them\n"
-				       "first and then run 'git rebase "
-				       "--continue' again."));
+		if (hashcmp(head, to_amend)) {
+			const char *gpg_opt = gpg_sign_opt_quoted(opts);
 
+			fprintf(stderr, _(
+"Unable to commit changes as HEAD has changed since git rebase stopped.\n"
+"If you wish to squash the changes into the last commit, run:\n"
+"\n"
+"  git commit --amend %s\n"
+"\n"
+"If they are meant to go into a new commit, run:\n"
+"\n"
+"  git commit %s\n"
+"\n"
+"In both cases, once you're done, continue with:\n"
+"\n"
+"  git rebase --continue\n"), gpg_opt, gpg_opt);
+			return -1;
+		}
 		strbuf_release(&rev);
 		flags |= AMEND_MSG;
 	}
-- 
2.13.3
Phillip Wood· Jul 26, 2017, 10:27 UTC · re: Phillip Wood · lore

[RFC PATCH 4/5] Add tests for rebase --continue --autostage

From: Phillip Wood <phillip.wood@dunelm.org.uk>

Make sure that --autostage stages any changes and fails if there are merge markers in the file to be staged.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 t/t3418-rebase-continue.sh | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)
Show changes to t/t3418-rebase-continue.sh +34 −1
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 4428b9086e8bcb383df801834d0de323f316f4fa..4e71e5d5c2f26866cd5d32bfea445f899398d6c9 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -14,7 +14,6 @@ test_expect_success 'setup' '
 
 	git checkout -b topic HEAD^ &&
 	test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
-
 	git checkout master
 '
 
@@ -40,6 +39,40 @@ test_expect_success 'non-interactive rebase --continue works with touched file'
 	git rebase --continue
 '
 
+reset () {
+	rm -fr .git/rebase-* &&
+	git reset --hard commit-new-file-F2-on-topic-branch &&
+	git checkout master
+}
+
+test_autostage () {
+	action=$1
+
+	test_expect_success "rebase $action --continue --autostage stages changes" '
+		reset &&
+		test_must_fail git rebase $action --onto master master topic &&
+		echo "Resolved" >F2 &&
+		git rebase --continue --autostage
+'
+
+	test_expect_success "rebase $action --continue --autostage fails when there are merge markers (1)" '
+		reset &&
+		test_must_fail git rebase $action --onto master master topic &&
+		test_must_fail git rebase --continue --autostage
+'
+
+	test_expect_success "rebase $action --continue --autostage  fails when there are merge markers (2)" '
+		reset &&
+		test_must_fail git rebase $action --onto master master topic &&
+		git reset -- F2 &&
+		test_must_fail git rebase --continue --autostage
+	'
+}
+
+test_autostage
+test_autostage -i
+test_autostage -m
+
 test_expect_success 'non-interactive rebase --continue with rerere enabled' '
 	test_config rerere.enabled true &&
 	test_when_finished "test_might_fail git rebase --abort" &&
-- 
2.13.3
Phillip Wood· Jul 26, 2017, 10:27 UTC · re: Phillip Wood · lore

[RFC PATCH 5/5] Add rebase.continue.autostage config setting

From: Phillip Wood <phillip.wood@dunelm.org.uk>
This enables the user to always specify --autostage with --continue

The tests check that setting rebase.continue.autostage=true results in 'git rebase --continue' autostaging unstaged changes and that '--no-autostage' overrides it.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
---
 git-rebase.sh              |  2 +-
 t/t3418-rebase-continue.sh | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)
Show changes to 2 files +16 −1

git-rebase.sh, t/t3418-rebase-continue.sh

diff --git a/git-rebase.sh b/git-rebase.sh
index 9ca387349b1cde440c4244de9125446fd35a7b67..632a19079ba1d88092f47121c7a0797af079aaf5 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -70,7 +70,7 @@ merge_dir="$GIT_DIR"/rebase-merge
 apply_dir="$GIT_DIR"/rebase-apply
 verbose=
 diffstat=
-autostage=false
+autostage=$(git config --bool --get rebase.continue.autostage || echo false)
 test "$(git config --bool rebase.stat)" = true && diffstat=t
 autostash="$(git config --bool rebase.autostash || echo false)"
 fork_point=auto
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 4e71e5d5c2f26866cd5d32bfea445f899398d6c9..7c2a30a3cd8ba7e33519bd68a3f89e90409169da 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -67,6 +67,21 @@ test_autostage () {
 		git reset -- F2 &&
 		test_must_fail git rebase --continue --autostage
 	'
+
+	test_expect_success "rebase.continue.autostage rebase $action works" '
+		reset &&
+		test_must_fail git rebase $action --onto master master topic &&
+		echo "Resolved" >F2 &&
+		git -c rebase.continue.autostage=true rebase --continue
+'
+
+	test_expect_success "rebase $action --continue --no-autostage works" '
+		reset &&
+		test_must_fail git rebase $action --onto master master topic &&
+		echo "Resolved" >F2 &&
+		test_must_fail git -c rebase.continue.autostage=true rebase --continue --no-autostage
+'
+
 }
 
 test_autostage
-- 
2.13.3
Phillip Wood· Jul 26, 2017, 10:27 UTC · re: Phillip Wood · lore

[RFC PATCH 2/5] rebase -i: improve --continue --autostage

From: Phillip Wood <phillip.wood@dunelm.org.uk>

If HEAD has changed since the rebase stopped or rebase stopped due to a failed exec then 'git rebase --continue --autostage' will autostage changes that cannot be commited. Fix this by reordering some of the checks so that 'git rebase --continue --autostage' never stages changes unless they can be committed. Also reword some error messages to account of --autostage and try and make them clearer.

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

This could do with some tests the check the correct error message is given in each failure case. However I'm expecting to change the error messages based on the feedback I get to this patch so I'll add the tests once the messages are finalized. I've tried to give a bit more explanation in the error messages as I found some of the previous messages didn't explain why rebase couldn't continue. Splitting the advice out means it is consistent between different code paths and should make it easier to optionally disable it in future if anyone wanted to add that. As for the formatting of the messages I wonder if they would be better without so many empty lines (I find the current messages a bit intimating as they seem so long). e.g.

If you wish to squash the unstaged changes into the last commit, run:
    git add -u
    git commit --amend \$gpg_sign_opt_quoted
If they are meant to go into a new commit, run:
    git add -u
    git commit \$gpg_sign_opt_quoted
In both cases, once you're done, continue with:
    git rebase --continue
Instead of
If you wish to squash the unstaged changes into the last commit, run:
  git add -u
  git commit --amend \$gpg_sign_opt_quoted
If they are meant to go into a new commit, run:
  git add -u
  git commit \$gpg_sign_opt_quoted
In both cases, once you're done, continue with:
  git rebase --continue
 git-rebase--interactive.sh    | 104 ++++++++++++++++++++++++++++++++----------
 t/t3404-rebase-interactive.sh |   2 +-
 2 files changed, 81 insertions(+), 25 deletions(-)
Show changes to 2 files +81 −25

git-rebase--interactive.sh, t/t3404-rebase-interactive.sh

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 4c037a3f7a9e01406c4205bf8786a3da5060381f..8140c88839b4f3a86f53faaaa2ba4433ecc7f58b 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -1056,6 +1056,42 @@ The possible behaviours are: ignore, warn, error.")"
 	fi
 }
 
+unstaged_advice () {
+	gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
+	eval_gettext "\
+If you wish to squash the unstaged changes into the last commit, run:
+
+  git add -u
+  git commit --amend \$gpg_sign_opt_quoted
+
+If they are meant to go into a new commit, run:
+
+  git add -u
+  git commit \$gpg_sign_opt_quoted
+
+In both cases, once you're done, continue with:
+
+  git rebase --continue
+"
+}
+
+staged_advice () {
+	gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
+	eval_gettext "\
+If you wish to squash the staged changes into the last commit, run:
+
+  git commit --amend \$gpg_sign_opt_quoted
+
+If they are meant to go into a new commit, run:
+
+  git commit \$gpg_sign_opt_quoted
+
+In both cases, once you're done, continue with:
+
+  git rebase --continue
+"
+}
+
 # The whole contents of this file is run by dot-sourcing it from
 # inside a shell function.  It used to be that "return"s we see
 # below were not inside any function, and expected to return
@@ -1067,9 +1103,50 @@ The possible behaviours are: ignore, warn, error.")"
 # here, and immediately call it after defining it.
 git_rebase__interactive () {
 
+amend_head='' amend_ok=''
 case "$action" in
 continue)
-	check_unstaged
+	test -f "$amend" &&
+		amend_head=$(cat "$amend") &&
+		test $amend_head = $(git rev-parse HEAD) &&
+		amend_ok=1
+	git update-index --refresh --ignore-submodules >/dev/null
+	git diff-files --quiet --ignore-submodules
+	unstaged=$?
+	if ! test -f "$author_script"
+	then
+		if test $unstaged = 1
+		then
+			die "$(gettext "Not expecting unstaged changes.")
+$(unstaged_advice)"
+		elif ! git diff-index --cached --quiet --ignore-submodules HEAD --
+		then
+			die "$(gettext "Not expecting staged changes.")
+$(staged_advice)"
+		fi
+	fi
+	if test $unstaged = 1 && test $autostage = true
+	then
+		if test -n "$amend_head" && test -z "$amend_ok"
+		then
+			die "$(gettext "\
+Unable to commit changes as HEAD has changed since git rebase stopped.")
+$(unstaged_advice)"
+		else
+			check_autostage
+		fi
+	elif test $unstaged = 1
+	then
+		if test -n "$amend_head" && test -z "$amend_ok"
+		then
+			die "$(gettext "\
+Unable to continue rebasing as there are unstaged changes and
+HEAD has changed since git rebase stopped.")
+$(unstaged_advice)"
+		else
+			die "$(autostage_advice)"
+		fi
+	fi
 	if test ! -d "$rewritten"
 	then
 		exec git rebase--helper ${force_rebase:+--no-ff} --continue
@@ -1083,31 +1160,11 @@ continue)
 		rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
 		die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
 	else
-		if ! test -f "$author_script"
-		then
-			gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
-			die "$(eval_gettext "\
-You have staged changes in your working tree.
-If these changes are meant to be
-squashed into the previous commit, run:
-
-  git commit --amend \$gpg_sign_opt_quoted
-
-If they are meant to go into a new commit, run:
-
-  git commit \$gpg_sign_opt_quoted
-
-In both cases, once you're done, continue with:
-
-  git rebase --continue
-")"
-		fi
 		. "$author_script" ||
 			die "$(gettext "Error trying to find the author identity to amend commit")"
-		if test -f "$amend"
+		if test -n "$amend_head"
 		then
-			current_head=$(git rev-parse --verify HEAD)
-			test "$current_head" = $(cat "$amend") ||
+			test -n "$amend_ok" ||
 			die "$(gettext "\
 You have uncommitted changes in your working tree. Please commit them
 first and then run 'git rebase --continue' again.")"
@@ -1126,7 +1183,6 @@ first and then run 'git rebase --continue' again.")"
 		record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
 	fi
 
-	require_clean_work_tree "rebase"
 	do_rest
 	return 0
 	;;
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 37821d245433f757fa13f0a3e27da0312bebb7db..3eed8a3bc5a8e9c3bae32e181d367d9a9c0aff80 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -563,7 +563,7 @@ test_expect_success 'clean error after failed "exec"' '
 	echo "edited again" > file7 &&
 	git add file7 &&
 	test_must_fail git rebase --continue 2>error &&
-	test_i18ngrep "you have staged changes in your working tree" error
+	test_i18ngrep "Not expecting staged changes" error
 '
 
 test_expect_success 'rebase a detached HEAD' '
-- 
2.13.3
Junio C Hamano· Jul 26, 2017, 18:21 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Phillip Wood <phillip.wood@talktalk.net> writes:
Show 6 quoted lines
> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>
> These patches add an '--autostage' option (and corresponding config
> variable) to 'rebase --continue' that will stage any unstaged changes
> before continuing. This saves the user having to type 'git add' before
> running 'git rebase --continue'.

I wonder if this interacts with existing rerere.autoupdate configuration variable and if so how (i.e. would they conflict and fight with each other? would they work well together? would one of them make the other unnecessary?). In any case, they look closely related and perhaps should be named similarly.

I even have a suspicion that they may be essentially doing the same thing.

For a previous discussion, you start from here:
  https://public-inbox.org/git/7vej6xb4lr.fsf@gitster.siamese.dyndns.org/#t

and for the context, look at the original post by Ingo, to which the above message is a response to.

Thanks.
Show 16 quoted lines
>
> Phillip Wood (5):
>   rebase --continue: add --autostage to stage unstaged changes
>   rebase -i: improve --continue --autostage
>   Unify rebase amend message when HEAD has changed
>   Add tests for rebase --continue --autostage
>   Add rebase.continue.autostage config setting
>
>  git-rebase--am.sh             |   1 +
>  git-rebase--interactive.sh    | 111 ++++++++++++++++++++++++++++++++----------
>  git-rebase--merge.sh          |   1 +
>  git-rebase.sh                 |  76 ++++++++++++++++++++++++++---
>  sequencer.c                   |  22 +++++++--
>  t/t3404-rebase-interactive.sh |   2 +-
>  t/t3418-rebase-continue.sh    |  50 ++++++++++++++++++-
>  7 files changed, 222 insertions(+), 41 deletions(-)
Junio C Hamano· Jul 26, 2017, 21:56 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Junio C Hamano <gitster@pobox.com> writes:
Show 26 quoted lines
> Phillip Wood <phillip.wood@talktalk.net> writes:
>
>> From: Phillip Wood <phillip.wood@dunelm.org.uk>
>>
>> These patches add an '--autostage' option (and corresponding config
>> variable) to 'rebase --continue' that will stage any unstaged changes
>> before continuing. This saves the user having to type 'git add' before
>> running 'git rebase --continue'.
>
> I wonder if this interacts with existing rerere.autoupdate
> configuration variable and if so how (i.e. would they conflict and
> fight with each other?  would they work well together?  would one of
> them make the other unnecessary?).  In any case, they look closely
> related and perhaps should be named similarly.
>
> I even have a suspicion that they may be essentially doing the same
> thing.
>
> For a previous discussion, you start from here:
>
>   https://public-inbox.org/git/7vej6xb4lr.fsf@gitster.siamese.dyndns.org/#t
>
> and for the context, look at the original post by Ingo, to which the
> above message is a response to.
>
> Thanks.
Hmph, this is interesting.

"git rebase" does take "--rerere-autoupdate" option from the command line, and propagates it to a later invocation of "rebase --continue" by storing the value to $state_dir/allow_rerere_autoupdate file and reading the value from it. $allow_rerere_autoupdate shell variable is used to hold the setting.

I'd expect that this variable to be used in invocations of "git am" in git-rebase--am.sh; but that does not seem to be the case. I wonder if this was once working but over time we broke the feature without anybody noticing it, or if the support was added but not completed and the feature was a no-op from the beginning?

Junio C Hamano· Jul 26, 2017, 22:12 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Junio C Hamano <gitster@pobox.com> writes:
Show 13 quoted lines
> Hmph, this is interesting.
>
> "git rebase" does take "--rerere-autoupdate" option from the command
> line, and propagates it to a later invocation of "rebase --continue"
> by storing the value to $state_dir/allow_rerere_autoupdate file and
> reading the value from it.  $allow_rerere_autoupdate shell variable
> is used to hold the setting.  
>
> I'd expect that this variable to be used in invocations of "git am"
> in git-rebase--am.sh; but that does not seem to be the case.  I
> wonder if this was once working but over time we broke the feature
> without anybody noticing it, or if the support was added but not
> completed and the feature was a no-op from the beginning?

At least in v1.7.0 when doing "rebase -m", the rerere-autoupdate was plumbed correctly through to the invocation of "git merge" that is done inside git-rebase.sh. I do not see the same option passed down to the invocation of "git am", so perhaps nobody cared back then about rerere during "git rebase" that does not use "git am" backend, even though "git am" itself were capable of talking the option.

In any case, if you corrected the existing "git rebase" and its backend so that "--rerere-autoupdate" works as advertised, I think you are already 80% there without adding a yet another option, as I suspect that the most of the need for avoiding "git add" during a "git rebase" session is during a conflict resolution, and allowing "rerere" to automatically update the index with auto-resolution will leave _only_ changes to the paths the end user actually needs to take a look and manually fix still not in the index. And from the workflow point of view, encouraging them to "git add" their manual resolution after they are satisified with their changes by not doing "git add" blindly for all changes, like your --autostage" does, is probably a good thing.

Phillip Wood· Jul 27, 2017, 10:36 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 26/07/17 23:12, Junio C Hamano wrote:
Show 25 quoted lines
> Junio C Hamano <gitster@pobox.com> writes:
> 
>> Hmph, this is interesting.
>>
>> "git rebase" does take "--rerere-autoupdate" option from the command
>> line, and propagates it to a later invocation of "rebase --continue"
>> by storing the value to $state_dir/allow_rerere_autoupdate file and
>> reading the value from it.  $allow_rerere_autoupdate shell variable
>> is used to hold the setting.  
>>
>> I'd expect that this variable to be used in invocations of "git am"
>> in git-rebase--am.sh; but that does not seem to be the case.  I
>> wonder if this was once working but over time we broke the feature
>> without anybody noticing it, or if the support was added but not
>> completed and the feature was a no-op from the beginning?
> 
> At least in v1.7.0 when doing "rebase -m", the rerere-autoupdate was
> plumbed correctly through to the invocation of "git merge" that is
> done inside git-rebase.sh.  I do not see the same option passed down
> to the invocation of "git am", so perhaps nobody cared back then
> about rerere during "git rebase" that does not use "git am" backend,
> even though "git am" itself were capable of talking the option.
> 
> In any case, if you corrected the existing "git rebase" and its
> backend so that "--rerere-autoupdate" works as advertised

I've had a quick look and I think it's just a case of adding '$allow_rerere_autoupdate' to the invocation of 'git am'. 'git am' calls rerere_clear() when skipping so that doesn't need to go into the shell script. I'll come up with a test to check it works as I think it does.

Show 7 quoted lines
> I think
> you are already 80% there without adding a yet another option, as I
> suspect that the most of the need for avoiding "git add" during a
> "git rebase" session is during a conflict resolution, and allowing
> "rerere" to automatically update the index with auto-resolution will
> leave _only_ changes to the paths the end user actually needs to
> take a look and manually fix still not in the index. 

I'm interested in the 20% as it's about 100% of my rebase conflicts. I've got rerere enabled but I cannot recall it helping me with a rebase conflict in the five or six years I've been using git [1]. A lot of my rebase conflicts come from fixup! commits or reordering things with rebase -i and generally they're only going to happen once as I don't apply the same fixup! more than once and if I've rearranged commits the conflicts are likely to be different in the (possibly unlikely) event I rearrange them again in the future.

Once I've resolved a conflict I will look at the diff and maybe compile and run some tests. Then, if I'm happy I'll run 'git rebase --continue', to me the 'git add' stage doesn't really add anything useful, it's just an inconvenience.

The other use I have for --autostage is when editing commits with rebase -i, once I'm happy with the edit I just want to continue and have rebase amend the commit. I don't feel having to run 'git add' is helpful in this case.

Show 5 quoted lines
> And from the
> workflow point of view, encouraging them to "git add" their manual
> resolution after they are satisified with their changes by not doing
> "git add" blindly for all changes, like your --autostage" does, is
> probably a good thing.

In my mind running 'git rebase --continue' is the signal to git that the conflicts are resolved.

Best Wishes
Phillip

[1] Maybe I've not noticed it helping when rebasing onto an updated upstream, although if I'm actively changing the rebased commits between upstream updates the conflicts may well change between updates. Also I don't tend to get many upstream conflicts with the projects I'm working on.

Phillip Wood· Jul 27, 2017, 13:25 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 27/07/17 11:36, Phillip Wood wrote:
Show 64 quoted lines
> On 26/07/17 23:12, Junio C Hamano wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Hmph, this is interesting.
>>>
>>> "git rebase" does take "--rerere-autoupdate" option from the command
>>> line, and propagates it to a later invocation of "rebase --continue"
>>> by storing the value to $state_dir/allow_rerere_autoupdate file and
>>> reading the value from it.  $allow_rerere_autoupdate shell variable
>>> is used to hold the setting.
>>>
>>> I'd expect that this variable to be used in invocations of "git am"
>>> in git-rebase--am.sh; but that does not seem to be the case.  I
>>> wonder if this was once working but over time we broke the feature
>>> without anybody noticing it, or if the support was added but not
>>> completed and the feature was a no-op from the beginning?
>>
>> At least in v1.7.0 when doing "rebase -m", the rerere-autoupdate was
>> plumbed correctly through to the invocation of "git merge" that is
>> done inside git-rebase.sh.  I do not see the same option passed down
>> to the invocation of "git am", so perhaps nobody cared back then
>> about rerere during "git rebase" that does not use "git am" backend,
>> even though "git am" itself were capable of talking the option.
>>
>> In any case, if you corrected the existing "git rebase" and its
>> backend so that "--rerere-autoupdate" works as advertised
> 
> I've had a quick look and I think it's just a case of adding
> '$allow_rerere_autoupdate' to the invocation of 'git am'. 'git am' calls
> rerere_clear() when skipping so that doesn't need to go into the shell
> script. I'll come up with a test to check it works as I think it does.
> 
>> I think
>> you are already 80% there without adding a yet another option, as I
>> suspect that the most of the need for avoiding "git add" during a
>> "git rebase" session is during a conflict resolution, and allowing
>> "rerere" to automatically update the index with auto-resolution will
>> leave _only_ changes to the paths the end user actually needs to
>> take a look and manually fix still not in the index.
> 
> I'm interested in the 20% as it's about 100% of my rebase conflicts.
> I've got rerere enabled but I cannot recall it helping me with a rebase
> conflict in the five or six years I've been using git [1]. A lot of my
> rebase conflicts come from fixup! commits or reordering things with
> rebase -i and generally they're only going to happen once as I don't
> apply the same fixup! more than once and if I've rearranged commits the
> conflicts are likely to be different in the (possibly unlikely) event I
> rearrange them again in the future.
> 
> Once I've resolved a conflict I will look at the diff and maybe compile
> and run some tests. Then, if I'm happy I'll run 'git rebase --continue',
> to me the 'git add' stage doesn't really add anything useful, it's just
> an inconvenience.
> 
> The other use I have for --autostage is when editing commits with rebase
> -i, once I'm happy with the edit I just want to continue and have rebase
> amend the commit. I don't feel having to run 'git add' is helpful in
> this case.
> 
>> And from the
>> workflow point of view, encouraging them to "git add" their manual
>> resolution after they are satisified with their changes by not doing
>> "git add" blindly for all changes, like your --autostage" does, is
>> probably a good thing.

Git allows 'git commit -a' to complete a conflicted merge which I think is much the same thing as I'm proposing. Also there's nothing to stop a user accidentally running 'git add' on a path that isn't resolved or just blindly running 'add -u' before continuing the rebase because they think they've resolved everything. I guess it is a kind of convenience/safety trade-off.

Show 10 quoted lines
> In my mind running 'git rebase --continue' is the signal to git that the
> conflicts are resolved.
> Best Wishes
> 
> Phillip
> 
> [1] Maybe I've not noticed it helping when rebasing onto an updated
> upstream, although if I'm actively changing the rebased commits between
> upstream updates the conflicts may well change between updates. Also I
> don't tend to get many upstream conflicts with the projects I'm working on.

Thinking about it some more, once a conflict is resolved, the next time that rewritten commit is rebased onto an updated upstream the same conflict will not occur as the rewritten commit that's being replayed does not conflict with the previous upstream - the resolution is baked into the commit that's being replayed.

Junio C Hamano· Jul 27, 2017, 15:24 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Phillip Wood <phillip.wood@talktalk.net> writes:
Show 5 quoted lines
>> On 26/07/17 23:12, Junio C Hamano wrote:
>>> I think
>>> you are already 80% there without adding a yet another option,...
>> ...
>> I'm interested in the 20% as it's about 100% of my rebase conflicts.

OK, then at least a fixed --rerere-autoupdate would hopefully limit the scope of the additional 20%; I'd suspect that a new option would also internally turn on --rerere-autoupdate, so that the remaining changes you would see upon --continue would be limited to what the user had to manually resolve (and edit without having textual conflict, aka evil merge to resolve semantic conflicts).

I am *not* opposed to an option to tell the command to blindly take such remaining changes, as long as it stays optional---the use of the option can then be taken as a strong signal that the user is OK with the local changes in the working tree, even if the user may not have marked them as resolved with "git add".

Show 8 quoted lines
>>> And from the
>>> workflow point of view, encouraging them to "git add" their manual
>>> resolution after they are satisified with their changes by not doing
>>> "git add" blindly for all changes, like your --autostage" does, is
>>> probably a good thing.
>
> Git allows 'git commit -a' to complete a conflicted merge which I
> think is much the same thing as I'm proposing....

"-a" is a strong enough sign that the user is OK with all the paths; "git commit" without an option does not. So it is OK for a new option (perhaps "--all-autoupdate", which does more than the existing "--rerere-autoupdate") to become the signal that the user is OK with all the local changes.

This is a tangent, but concluding a merge with "commit -a" (or "add -u && commit -a") has always been discouraged among Git expert users, and it will stay to be so. If you search the list archive, you would find a good explanation by Linus on this, but a short version is that this is because it is normal to start a merge in a dirty working tree where the user has local changes that the user knows will not interfere with the merge.

Because "rebase" refuses to work in a dirty working tree, the analogy with "merge" does not quite hold. Doing "add -u" before telling it to "--continue" would be much safer.

Thanks.
Phillip Wood· Aug 21, 2017, 10:32 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 27/07/17 16:24, Junio C Hamano wrote:
Show 14 quoted lines
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>>> On 26/07/17 23:12, Junio C Hamano wrote:
>>>> I think
>>>> you are already 80% there without adding a yet another option,...
>>> ...
>>> I'm interested in the 20% as it's about 100% of my rebase conflicts.
> 
> OK, then at least a fixed --rerere-autoupdate would hopefully limit
> the scope of the additional 20%; I'd suspect that a new option would
> also internally turn on --rerere-autoupdate, so that the remaining
> changes you would see upon --continue would be limited to what the
> user had to manually resolve (and edit without having textual conflict,
> aka evil merge to resolve semantic conflicts).

I can see the logic in that but I was imagining (and the patches implement) that the --autostage option would be passed to 'rebase --continue' not when the rebase was started by which time it is too late to turn on --rerere-autoupdate for the current conflicts. I prefer having to pass --autostage with --continue so that it is a concious decision by the user to stage unstaged changes when they continue rather than rebase just doing it each time it continues.

Show 5 quoted lines
> I am *not* opposed to an option to tell the command to blindly take
> such remaining changes, as long as it stays optional---the use of
> the option can then be taken as a strong signal that the user is OK
> with the local changes in the working tree, even if the user may not
> have marked them as resolved with "git add".

I agree it definitely needs to be optional (possibly with a config option to have it on by default but I'm not wedded to that as it is easy to set up a git or shell alias)

What is the best way forward on this? The patches I posted add a '--autostage' option to be passed with '--continue' which means that staging all the unstaged changes remains optional but does not allow --rerere-autoupdate to be automatically enabled. I'm not sure about the check for merge markers, as it is implemented it also checks for whitespace errors which is really the domain of the pre-commit hook. If we go for an explicit --autostage without the config key to make it on by default then maybe it is OK to drop the check, but it keeping it could be a useful safety measure. I don't think it would be too much work the change diff to allow '--check=merge-markers' as internally the whitespace and marker checks are implemented separately.

Best Wishes
Phillip
Show 31 quoted lines
>>>> And from the
>>>> workflow point of view, encouraging them to "git add" their manual
>>>> resolution after they are satisified with their changes by not doing
>>>> "git add" blindly for all changes, like your --autostage" does, is
>>>> probably a good thing.
>>
>> Git allows 'git commit -a' to complete a conflicted merge which I
>> think is much the same thing as I'm proposing....
> 
> "-a" is a strong enough sign that the user is OK with all the paths;
> "git commit" without an option does not.  So it is OK for a new
> option (perhaps "--all-autoupdate", which does more than the
> existing "--rerere-autoupdate") to become the signal that the user
> is OK with all the local changes.
> 
> This is a tangent, but concluding a merge with "commit -a" (or "add
> -u && commit -a") has always been discouraged among Git expert
> users, and it will stay to be so.  If you search the list archive,
> you would find a good explanation by Linus on this, but a short
> version is that this is because it is normal to start a merge in a
> dirty working tree where the user has local changes that the user
> knows will not interfere with the merge.  
> 
> Because "rebase" refuses to work in a dirty working tree, the
> analogy with "merge" does not quite hold.  Doing "add -u" before
> telling it to "--continue" would be much safer.
> 
> Thanks.
> 
> 
> 
Junio C Hamano· Aug 21, 2017, 22:41 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Phillip Wood <phillip.wood@talktalk.net> writes:
> ... I prefer
> having to pass --autostage with --continue so that it is a concious
> decision by the user to stage unstaged changes when they continue rather
> than rebase just doing it each time it continues.
In other words, instead of
	git add -u && git rebase --continue
you would want a quicker way to say
	git rebase --continue $something_here 

If that is the case, that is understandable to me. Is the "-u" (I think "git add -u" is short for "--update" but I didn't check) taken as a valid option to "git rebase"? If not, that $something_here could be "-u".

Thanks for pinging the thread; otherwise I would have forgotten, especially because not many other people were involved in the discussion to begin with.

Phillip Wood· Aug 22, 2017, 10:54 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 21/08/17 23:41, Junio C Hamano wrote:
Show 14 quoted lines
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> ... I prefer
>> having to pass --autostage with --continue so that it is a concious
>> decision by the user to stage unstaged changes when they continue rather
>> than rebase just doing it each time it continues.
> 
> In other words, instead of
> 
> 	git add -u && git rebase --continue
> 
> you would want a quicker way to say
> 
> 	git rebase --continue $something_here 
Exactly
> If that is the case, that is understandable to me.  Is the "-u" (I
> think "git add -u" is short for "--update" but I didn't check) taken
> as a valid option to "git rebase"?  If not, that $something_here could
> be "-u".

At the moment $something_else is -a/--autostage but -u/--update (I've checked the add man page and you're right) could be good as well.

rebase --continue -a

behaves like commit -a in that it commits all updated tracked files and does not take pathspecs, if we go for -u then there is a difference with 'git add -u' as that can take an optional pathspec.

Did you have any further thoughts on what checks if any this new option should make to avoid staging obviously unresolved files?

> Thanks for pinging the thread; otherwise I would have forgotten,
> especially because not many other people were involved in the
> discussion to begin with.
Yes it would be interesting to hear if this would be useful for others too.
Best Wishes
Phillip
Junio C Hamano· Aug 22, 2017, 15:54 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Phillip Wood <phillip.wood@talktalk.net> writes:
Show 14 quoted lines
>> In other words, instead of
>> 
>> 	git add -u && git rebase --continue
>> 
>> you would want a quicker way to say
>> 
>> 	git rebase --continue $something_here 
>
> Exactly
> ...
> rebase --continue -a
>
> behaves like commit -a in that it commits all updated tracked files and
> does not take pathspecs.

Hmph, but what 'a' in "commit -a" wants to convey is "all", and in the context of "rebase" we want to avoid saying "all". A user can be confused into thinking "all" refers to "all subsequent commits" not "all paths conflicted".

Besides, with the $something_here option, the user is explicitly telling us that the user finisished the resolution of conflicts left in the working tree. There is nothing "auto" about it.

> Did you have any further thoughts on what checks if any this new option
> should make to avoid staging obviously unresolved files?

The more I think about this, the more I am convinced that it is a very bad idea to give such a $something_here option only to "rebase".

The standard flow for any operation that could stop in the middle because the command needs help from the user with conflicts that cannot be mechanically resolved is

 (1) write out conflicted state in the working tree, and mark these
     paths in the index by leaving them in higher stages
     (i.e. "ls-files -u" would list them);
 (2) the user edits them and marks the ones that are now resolved;
 (3) the user tells us to "continue".

and this is not limited to "rebase". "cherry-pick", "am", and "revert" all share the above, and even "merge" (which is a single commit operation to which "continue" in its original meaning---to tell us the user is done with this step and wants us to go on processing further commits or patches---does not quite apply) does.

And "rebase" is an oddball in that it is the only command that we could deduce which files in the working tree should be "add"ed, i.e. "all the files that are different from HEAD". All others allow users to begin in a dirty working tree (they only require the index to be clean), so your

	git [cherry-pick|am|revert] --continue $something_here

cannot be "git add -u && git [that command] --continue". Blindly taking any and all files that have modifications from HEAD will produce a wrong result.

You cannot even sanely solve that by first recording the paths that are conflicted before giving control back to the user and limit the "add" to them, i.e. doing "git add" only for paths that appear in "git ls-files -u" output would not catch additional changes the was needed in files that did not initially conflict (i.e. "evil merge" will have to modify a file that was not involved in the mechanical part of the conflict), because out of the files that are dirty in the working tree, some are evil merge resolutions and others are ones that were dirty from the beginning.

The only reason "git rebase" can get away without having to worry about all of the above is because it insists that the working tree is clean before it begins. In the ideal world, however, we would want to lift that and allow it to start in a working tree that has an unfinished local modification that does not interfere with the rebase, just like all other operations that could stop upon a conflict. And when that happens, your $something_here option to "git rebase --continue" will have to worry about it, too.

So...
Phillip Wood· Aug 24, 2017, 13:25 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 22/08/17 16:54, Junio C Hamano wrote:
Show 55 quoted lines
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>>> In other words, instead of
>>>
>>> 	git add -u && git rebase --continue
>>>
>>> you would want a quicker way to say
>>>
>>> 	git rebase --continue $something_here 
>>
>> Exactly
>> ...
>> rebase --continue -a
>>
>> behaves like commit -a in that it commits all updated tracked files and
>> does not take pathspecs.
> 
> Hmph, but what 'a' in "commit -a" wants to convey is "all", and in
> the context of "rebase" we want to avoid saying "all".  A user can
> be confused into thinking "all" refers to "all subsequent commits" 
> not "all paths conflicted".
> 
> Besides, with the $something_here option, the user is explicitly
> telling us that the user finisished the resolution of conflicts left
> in the working tree.  There is nothing "auto" about it.
> 
>> Did you have any further thoughts on what checks if any this new option
>> should make to avoid staging obviously unresolved files?
> 
> The more I think about this, the more I am convinced that it is a
> very bad idea to give such a $something_here option only to "rebase".
> 
> The standard flow for any operation that could stop in the middle
> because the command needs help from the user with conflicts that
> cannot be mechanically resolved is
> 
>  (1) write out conflicted state in the working tree, and mark these
>      paths in the index by leaving them in higher stages
>      (i.e. "ls-files -u" would list them);
> 
>  (2) the user edits them and marks the ones that are now resolved;
> 
>  (3) the user tells us to "continue".
> 
> and this is not limited to "rebase".  "cherry-pick", "am", and
> "revert" all share the above, and even "merge" (which is a single
> commit operation to which "continue" in its original meaning---to
> tell us the user is done with this step and wants us to go on
> processing further commits or patches---does not quite apply) does.
> 
> And "rebase" is an oddball in that it is the only command that we
> could deduce which files in the working tree should be "add"ed, i.e.
> "all the files that are different from HEAD".  All others allow
> users to begin in a dirty working tree (they only require the index
> to be clean), 

Are you sure about that, the second sentence of the cherry-pick man page is "This requires your working tree to be clean (no modifications from the HEAD commit).". If you pass '--no-commit' then this is relaxed so that the index can differ from HEAD but it is not clear from the man page if the working tree can differ from the index (potentially that could give different conflicts in the index and working tree). I'm not sure what a rebase that does not commit changes would look like.

Show 17 quoted lines
> so your 
> 
> 	git [cherry-pick|am|revert] --continue $something_here
> 
> cannot be "git add -u && git [that command] --continue".  Blindly
> taking any and all files that have modifications from HEAD will
> produce a wrong result.
> 
> You cannot even sanely solve that by first recording the paths that
> are conflicted before giving control back to the user and limit the
> "add" to them, i.e. doing "git add" only for paths that appear in
> "git ls-files -u" output would not catch additional changes the was
> needed in files that did not initially conflict (i.e. "evil merge"
> will have to modify a file that was not involved in the mechanical
> part of the conflict), because out of the files that are dirty in
> the working tree, some are evil merge resolutions and others are
> ones that were dirty from the beginning.

I wonder if git could record the state of the working tree in a temporary index just before it exits with conflicts. Then when the user has resolved the conflicts

git [cherry-pick|am|revert] --continue $something_here
could do something like
{ GIT_INDEX_FILE=$TMP_INDEX git diff-files --name-only &&
    git ls-files -u } | git update-index --remove --stdin

to stage the files that the user has edited. This would not catch the case where a new untracked file has been created that should be added to the conflict resolution, but I think it is reasonable to expect the user to manually add new files in this case. It would also not add an unchanged file whose changes should be part of the conflict resolution but I can't think of a sensible case where the user might want that at the moment.

Show 8 quoted lines
> 
> The only reason "git rebase" can get away without having to worry
> about all of the above is because it insists that the working tree
> is clean before it begins.  In the ideal world, however, we would
> want to lift that and allow it to start in a working tree that has
> an unfinished local modification that does not interfere with the
> rebase, just like all other operations that could stop upon a
> conflict. 

It could be expensive to check that the local modifications will not interfere with the rebase - wouldn't it have to look at all the files touched by each commit before starting? What do cherry-pick and am do here when picking several commits?

 And when that happens, your $something_here option to
> "git rebase --continue" will have to worry about it, too.
> So...
> 
Junio C Hamano· Aug 24, 2017, 16:46 UTC · re: Phillip Wood · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

Phillip Wood <phillip.wood@talktalk.net> writes:
> It could be expensive to check that the local modifications will not
> interfere with the rebase - wouldn't it have to look at all the files
> touched by each commit before starting? What do cherry-pick and am do
> here when picking several commits?
Yes, so?

"I do not think of a way to implement it cheaply, so we forbid anybody from finding a clever optimization to implement it cheaply by adding a feature that will not work well with it when it happens?"

Phillip Wood· Aug 25, 2017, 11:54 UTC · re: Junio C Hamano · lore

Re: [RFC PATCH 0/5] Add option to autostage changes when continuing a rebase

On 24/08/17 17:46, Junio C Hamano wrote:
Show 14 quoted lines
> Phillip Wood <phillip.wood@talktalk.net> writes:
> 
>> It could be expensive to check that the local modifications will not
>> interfere with the rebase - wouldn't it have to look at all the files
>> touched by each commit before starting? What do cherry-pick and am do
>> here when picking several commits?
> 
> Yes, so?
> 
> "I do not think of a way to implement it cheaply, so we forbid
> anybody from finding a clever optimization to implement it cheaply
> by adding a feature that will not work well with it when it
> happens?"
> 

Ouch, I think that is a rather unfair characterization of what I said and certainly does not reflect what I was trying to say. I was asking since cherry-pick and am can pick/apply multiple commits/patches and apparently allow the user to start with a dirty tree what do they do about checking that the applied commits/patches do not interfere with any local changes. As for "adding a feature that will not work well with it when it happens?" I suggested a possible solution in the message.

Anyway you're not convinced this feature is a good idea and no one else has commented so I'll drop it.

Phil

← back to recent threads