sha1-lookup: make selection of 'middle' less aggressive
[gitweb.git] / git-rebase.sh
index 1583402a060793c25e49c3446c2a35fe27101883..9b13b833cb5762542848ee3e85e23d3ca0f76fa6 100755 (executable)
@@ -18,8 +18,7 @@ original <branch> and remove the .dotest working files, use the command
 git rebase --abort instead.
 
 Note that if <branch> is not specified on the command line, the
-currently checked out branch is used.  You must be in the top
-directory of your project to start (or continue) a rebase.
+currently checked out branch is used.
 
 Example:       git-rebase master~1 topic
 
@@ -29,6 +28,7 @@ Example:       git-rebase master~1 topic
 '
 
 SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
 . git-sh-setup
 set_reflog_action rebase
 require_work_tree
@@ -59,10 +59,10 @@ continue_merge () {
                die "$RESOLVEMSG"
        fi
 
-       cmt=`cat $dotest/current`
-       if ! git diff-index --quiet HEAD
+       cmt=`cat "$dotest/current"`
+       if ! git diff-index --quiet HEAD --
        then
-               if ! git-commit -C "$cmt"
+               if ! git commit --no-verify -C "$cmt"
                then
                        echo "Commit failed, please do not call \"git commit\""
                        echo "directly, but instead do one of the following: "
@@ -84,14 +84,14 @@ continue_merge () {
 }
 
 call_merge () {
-       cmt="$(cat $dotest/cmt.$1)"
+       cmt="$(cat "$dotest/cmt.$1")"
        echo "$cmt" > "$dotest/current"
        hd=$(git rev-parse --verify HEAD)
-       cmt_name=$(git symbolic-ref HEAD)
-       msgnum=$(cat $dotest/msgnum)
-       end=$(cat $dotest/end)
+       cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
+       msgnum=$(cat "$dotest/msgnum")
+       end=$(cat "$dotest/end")
        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
-       eval GITHEAD_$hd='"$(cat $dotest/onto_name)"'
+       eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
        export GITHEAD_$cmt GITHEAD_$hd
        git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
        rv=$?
@@ -115,7 +115,24 @@ call_merge () {
        esac
 }
 
+move_to_original_branch () {
+       test -z "$head_name" &&
+               head_name="$(cat "$dotest"/head-name)" &&
+               onto="$(cat "$dotest"/onto)" &&
+               orig_head="$(cat "$dotest"/orig-head)"
+       case "$head_name" in
+       refs/*)
+               message="rebase finished: $head_name onto $onto"
+               git update-ref -m "$message" \
+                       $head_name $(git rev-parse HEAD) $orig_head &&
+               git symbolic-ref HEAD $head_name ||
+               die "Could not move back to $head_name"
+               ;;
+       esac
+}
+
 finish_rb_merge () {
+       move_to_original_branch
        rm -r "$dotest"
        echo "All done."
 }
@@ -140,10 +157,10 @@ do
                }
                if test -d "$dotest"
                then
-                       prev_head="`cat $dotest/prev_head`"
-                       end="`cat $dotest/end`"
-                       msgnum="`cat $dotest/msgnum`"
-                       onto="`cat $dotest/onto`"
+                       prev_head=$(cat "$dotest/prev_head")
+                       end=$(cat "$dotest/end")
+                       msgnum=$(cat "$dotest/msgnum")
+                       onto=$(cat "$dotest/onto")
                        continue_merge
                        while test "$msgnum" -le "$end"
                        do
@@ -153,18 +170,23 @@ do
                        finish_rb_merge
                        exit
                fi
-               git am --resolved --3way --resolvemsg="$RESOLVEMSG"
+               head_name=$(cat .dotest/head-name) &&
+               onto=$(cat .dotest/onto) &&
+               orig_head=$(cat .dotest/orig-head) &&
+               git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
+               move_to_original_branch
                exit
                ;;
        --skip)
+               git reset --hard HEAD || exit $?
                if test -d "$dotest"
                then
                        git rerere clear
-                       prev_head="`cat $dotest/prev_head`"
-                       end="`cat $dotest/end`"
-                       msgnum="`cat $dotest/msgnum`"
+                       prev_head=$(cat "$dotest/prev_head")
+                       end=$(cat "$dotest/end")
+                       msgnum=$(cat "$dotest/msgnum")
                        msgnum=$(($msgnum + 1))
-                       onto="`cat $dotest/onto`"
+                       onto=$(cat "$dotest/onto")
                        while test "$msgnum" -le "$end"
                        do
                                call_merge "$msgnum"
@@ -173,21 +195,27 @@ do
                        finish_rb_merge
                        exit
                fi
-               git am -3 --skip --resolvemsg="$RESOLVEMSG"
+               head_name=$(cat .dotest/head-name) &&
+               onto=$(cat .dotest/onto) &&
+               orig_head=$(cat .dotest/orig-head) &&
+               git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
+               move_to_original_branch
                exit
                ;;
        --abort)
                git rerere clear
                if test -d "$dotest"
                then
-                       rm -r "$dotest"
+                       move_to_original_branch
                elif test -d .dotest
                then
-                       rm -r .dotest
+                       dotest=.dotest
+                       move_to_original_branch
                else
                        die "No rebase in progress?"
                fi
-               git reset --hard ORIG_HEAD
+               git reset --hard $(cat $dotest/orig-head)
+               rm -r "$dotest"
                exit
                ;;
        --onto)
@@ -255,7 +283,7 @@ fi
 
 # The tree must be really really clean.
 git update-index --refresh || exit
-diff=$(git diff-index --cached --name-status -r HEAD)
+diff=$(git diff-index --cached --name-status -r HEAD --)
 case "$diff" in
 ?*)    echo "cannot rebase: your index is not up-to-date"
        echo "$diff"
@@ -281,22 +309,42 @@ then
        }
 fi
 
-# If the branch to rebase is given, first switch to it.
+# If the branch to rebase is given, that is the branch we will rebase
+# $branch_name -- branch being rebased, or HEAD (already detached)
+# $orig_head -- commit object name of tip of the branch before rebasing
+# $head_name -- refs/heads/<that-branch> or "detached HEAD"
+switch_to=
 case "$#" in
 2)
+       # Is it "rebase other $branchname" or "rebase other $commit"?
        branch_name="$2"
-       git-checkout "$2" || usage
+       switch_to="$2"
+
+       if git show-ref --verify --quiet -- "refs/heads/$2" &&
+          branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
+       then
+               head_name="refs/heads/$2"
+       elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
+       then
+               head_name="detached HEAD"
+       else
+               usage
+       fi
        ;;
 *)
+       # Do not need to switch branches, we are already on it.
        if branch_name=`git symbolic-ref -q HEAD`
        then
+               head_name=$branch_name
                branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
        else
+               head_name="detached HEAD"
                branch_name=HEAD ;# detached
        fi
+       branch=$(git rev-parse --verify "${branch_name}^0") || exit
        ;;
 esac
-branch=$(git rev-parse --verify "${branch_name}^0") || exit
+orig_head=$branch
 
 # Now we are rebasing commits $upstream..$branch on top of $onto
 
@@ -307,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
        # linear history?
        ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
 then
+       # Lazily switch to the target branch if needed...
+       test -z "$switch_to" || git checkout "$switch_to"
        echo >&2 "Current branch $branch_name is up to date."
        exit 0
 fi
@@ -318,23 +368,33 @@ then
        GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
 fi
 
-# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
+# Detach HEAD and reset the tree
 echo "First, rewinding head to replay your work on top of it..."
-git-reset --hard "$onto"
+git checkout "$onto^0" >/dev/null 2>&1 ||
+       die "could not detach HEAD"
+# git reset --hard "$onto^0"
 
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast forwarded.
 if test "$mb" = "$branch"
 then
        echo >&2 "Fast-forwarded $branch_name to $onto_name."
+       move_to_original_branch
        exit 0
 fi
 
 if test -z "$do_merge"
 then
-       git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
-       git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG"
-       exit $?
+       git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+               "$upstream..$orig_head" |
+       git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
+       move_to_original_branch
+       ret=$?
+       test 0 != $ret -a -d .dotest &&
+               echo $head_name > .dotest/head-name &&
+               echo $onto > .dotest/onto &&
+               echo $orig_head > .dotest/orig-head
+       exit $ret
 fi
 
 # start doing a rebase with git-merge
@@ -343,11 +403,13 @@ fi
 mkdir -p "$dotest"
 echo "$onto" > "$dotest/onto"
 echo "$onto_name" > "$dotest/onto_name"
-prev_head=`git rev-parse HEAD^0`
+prev_head=$orig_head
 echo "$prev_head" > "$dotest/prev_head"
+echo "$orig_head" > "$dotest/orig-head"
+echo "$head_name" > "$dotest/head-name"
 
 msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
+for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
 do
        msgnum=$(($msgnum + 1))
        echo "$cmt" > "$dotest/cmt.$msgnum"