From: Phillip Wood <phillip.w...@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.w...@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....@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

Reply via email to