git-imap-send: Support SSL
[gitweb.git] / git-rebase--interactive.sh
index 090c3e51437969459f2b4f4625d5785d79c05084..4e334ba41dad3067394b79c15ebfe610b2d3e178 100755 (executable)
 # The original idea comes from Eric W. Biederman, in
 # http://article.gmane.org/gmane.comp.version-control.git/22407
 
-USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
-       [--onto <branch>] <upstream> [<branch>])'
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git-rebase [-i] [options] [--] <upstream> [<branch>]
+git-rebase [-i] (--continue | --abort | --skip)
+--
+ Available options are
+v,verbose          display a diffstat of what changed upstream
+onto=              rebase onto given branch instead of upstream
+p,preserve-merges  try to recreate merges instead of ignoring them
+s,strategy=        use the given merge strategy
+m,merge            always used (no-op)
+i,interactive      always used (no-op)
+ Actions:
+continue           continue rebasing process
+abort              abort rebasing process and restore original branch
+skip               skip current patch and continue rebasing process
+"
 
-OPTIONS_SPEC=
 . git-sh-setup
 require_work_tree
 
-DOTEST="$GIT_DIR/.dotest-merge"
+DOTEST="$GIT_DIR/rebase-merge"
 TODO="$DOTEST"/git-rebase-todo
 DONE="$DOTEST"/done
 MSG="$DOTEST"/message
@@ -25,10 +39,8 @@ SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
 PRESERVE_MERGES=
 STRATEGY=
+ONTO=
 VERBOSE=
-test -d "$REWRITTEN" && PRESERVE_MERGES=t
-test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
-test -f "$DOTEST"/verbose && VERBOSE=t
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
@@ -56,9 +68,9 @@ output () {
 require_clean_work_tree () {
        # test if working tree is dirty
        git rev-parse --verify HEAD > /dev/null &&
-       git update-index --refresh &&
-       git diff-files --quiet &&
-       git diff-index --cached --quiet HEAD -- ||
+       git update-index --ignore-submodules --refresh &&
+       git diff-files --quiet --ignore-submodules &&
+       git diff-index --cached --quiet HEAD --ignore-submodules -- ||
        die "Working tree is dirty"
 }
 
@@ -73,14 +85,19 @@ comment_for_reflog () {
        esac
 }
 
+last_count=
 mark_action_done () {
        sed -e 1q < "$TODO" >> "$DONE"
        sed -e 1d < "$TODO" >> "$TODO".new
        mv -f "$TODO".new "$TODO"
-       count=$(($(grep -ve '^$' -e '^#' < "$DONE" | wc -l)))
-       total=$(($count+$(grep -ve '^$' -e '^#' < "$TODO" | wc -l)))
-       printf "Rebasing (%d/%d)\r" $count $total
-       test -z "$VERBOSE" || echo
+       count=$(grep -c '^[^#]' < "$DONE")
+       total=$(($count+$(grep -c '^[^#]' < "$TODO")))
+       if test "$last_count" != "$count"
+       then
+               last_count=$count
+               printf "Rebasing (%d/%d)\r" $count $total
+               test -z "$VERBOSE" || echo
+       fi
 }
 
 make_patch () {
@@ -105,7 +122,7 @@ die_abort () {
 }
 
 has_action () {
-       grep -vqe '^$' -e '^#' "$1"
+       grep '^[^#]' "$1" >/dev/null
 }
 
 pick_one () {
@@ -157,6 +174,8 @@ pick_one_preserving_merges () {
                                new_parents="$new_parents $new_p"
                                ;;
                        esac
+               else
+                       new_parents="$new_parents $p"
                fi
        done
        case $fast_forward in
@@ -213,17 +232,19 @@ nth_string () {
 make_squash_message () {
        if test -f "$SQUASH_MSG"; then
                COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \
-                       < "$SQUASH_MSG" | tail -n 1)+1))
+                       < "$SQUASH_MSG" | sed -ne '$p')+1))
                echo "# This is a combination of $COUNT commits."
-               sed -n "2,\$p" < "$SQUASH_MSG"
+               sed -e 1d -e '2,/^./{
+                       /^$/d
+               }' <"$SQUASH_MSG"
        else
                COUNT=2
                echo "# This is a combination of two commits."
                echo "# The first commit's message is:"
                echo
                git cat-file commit HEAD | sed -e '1,/^$/d'
-               echo
        fi
+       echo
        echo "# This is the $(nth_string $COUNT) commit message:"
        echo
        git cat-file commit $1 | sed -e '1,/^$/d'
@@ -256,11 +277,15 @@ do_next () {
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                make_patch $sha1
                : > "$DOTEST"/amend
-               warn
+               warn "Stopped at $sha1... $rest"
                warn "You can amend the commit now, with"
                warn
                warn "  git commit --amend"
                warn
+               warn "Once you are satisfied with your changes, run"
+               warn
+               warn "  git rebase --continue"
+               warn
                exit 0
                ;;
        squash|s)
@@ -355,25 +380,52 @@ do_rest () {
        done
 }
 
+# check if no other options are set
+is_standalone () {
+       test $# -eq 2 -a "$2" = '--' &&
+       test -z "$ONTO" &&
+       test -z "$PRESERVE_MERGES" &&
+       test -z "$STRATEGY" &&
+       test -z "$VERBOSE"
+}
+
+get_saved_options () {
+       test -d "$REWRITTEN" && PRESERVE_MERGES=t
+       test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
+       test -f "$DOTEST"/verbose && VERBOSE=t
+}
+
 while test $# != 0
 do
        case "$1" in
        --continue)
+               is_standalone "$@" || usage
+               get_saved_options
                comment_for_reflog continue
 
                test -d "$DOTEST" || die "No interactive rebase running"
 
-               # commit if necessary
-               git rev-parse --verify HEAD > /dev/null &&
-               git update-index --refresh &&
-               git diff-files --quiet &&
-               ! git diff-index --cached --quiet HEAD -- &&
-               . "$DOTEST"/author-script && {
-                       test ! -f "$DOTEST"/amend || git reset --soft HEAD^
-               } &&
-               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-               if ! git commit --no-verify -F "$DOTEST"/message -e
+               # Sanity check
+               git rev-parse --verify HEAD >/dev/null ||
+                       die "Cannot read HEAD"
+               git update-index --ignore-submodules --refresh &&
+                       git diff-files --quiet --ignore-submodules ||
+                       die "Working tree is dirty"
+
+               # do we have anything to commit?
+               if git diff-index --cached --quiet --ignore-submodules HEAD --
                then
+                       : Nothing to commit -- skip this
+               else
+                       . "$DOTEST"/author-script ||
+                               die "Cannot find the author identity"
+                       if test -f "$DOTEST"/amend
+                       then
+                               git reset --soft HEAD^ ||
+                               die "Cannot rewind the HEAD"
+                       fi
+                       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
+                       git commit --no-verify -F "$DOTEST"/message -e ||
                        die "Could not commit staged changes."
                fi
 
@@ -381,6 +433,8 @@ do
                do_rest
                ;;
        --abort)
+               is_standalone "$@" || usage
+               get_saved_options
                comment_for_reflog abort
 
                git rerere clear
@@ -398,6 +452,8 @@ do
                exit
                ;;
        --skip)
+               is_standalone "$@" || usage
+               get_saved_options
                comment_for_reflog skip
 
                git rerere clear
@@ -405,7 +461,7 @@ do
 
                output git reset --hard && do_rest
                ;;
-       -s|--strategy)
+       -s)
                case "$#,$1" in
                *,*=*)
                        STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
@@ -416,25 +472,26 @@ do
                        shift ;;
                esac
                ;;
-       --merge)
+       -m)
                # we use merge anyway
                ;;
-       -C*)
-               die "Interactive rebase uses merge, so $1 does not make sense"
-               ;;
-       -v|--verbose)
+       -v)
                VERBOSE=t
                ;;
-       -p|--preserve-merges)
+       -p)
                PRESERVE_MERGES=t
                ;;
-       -i|--interactive)
+       -i)
                # yeah, we know
                ;;
-       ''|-h)
-               usage
+       --onto)
+               shift
+               ONTO=$(git rev-parse --verify "$1") ||
+                       die "Does not point to a valid commit: $1"
                ;;
-       *)
+       --)
+               shift
+               test $# -eq 1 -o $# -eq 2 || usage
                test -d "$DOTEST" &&
                        die "Interactive rebase already started"
 
@@ -443,17 +500,11 @@ do
 
                comment_for_reflog start
 
-               ONTO=
-               case "$1" in
-               --onto)
-                       ONTO=$(git rev-parse --verify "$2") ||
-                               die "Does not point to a valid commit: $2"
-                       shift; shift
-                       ;;
-               esac
-
                require_clean_work_tree
 
+               UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
+               test -z "$ONTO" && ONTO=$UPSTREAM
+
                if test ! -z "$2"
                then
                        output git show-ref --verify --quiet "refs/heads/$2" ||
@@ -463,12 +514,8 @@ do
                fi
 
                HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
-               UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
-
                mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
 
-               test -z "$ONTO" && ONTO=$UPSTREAM
-
                : > "$DOTEST"/interactive || die "Could not mark as interactive"
                git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
                        echo "detached HEAD" > "$DOTEST"/head-name
@@ -510,9 +557,9 @@ do
 # Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
 #
 # Commands:
-#  pick = use commit
-#  edit = use commit, but stop for amending
-#  squash = use commit, but meld into previous commit
+#  p, pick = use commit
+#  e, edit = use commit, but stop for amending
+#  s, squash = use commit, but meld into previous commit
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
@@ -529,6 +576,7 @@ EOF
                has_action "$TODO" ||
                        die_abort "Nothing to do"
 
+               git update-ref ORIG_HEAD $HEAD
                output git checkout $ONTO && do_rest
                ;;
        esac