git-rebase.shon commit git-rebase: allow rebasing a detached HEAD. (bcf3161)
   1#!/bin/sh
   2#
   3# Copyright (c) 2005 Junio C Hamano.
   4#
   5
   6USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
   7LONG_USAGE='git-rebase replaces <branch> with a new branch of the
   8same name.  When the --onto option is provided the new branch starts
   9out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
  10It then attempts to create a new commit for each commit from the original
  11<branch> that does not exist in the <upstream> branch.
  12
  13It is possible that a merge failure will prevent this process from being
  14completely automatic.  You will have to resolve any such merge failure
  15and run git rebase --continue.  Another option is to bypass the commit
  16that caused the merge failure with git rebase --skip.  To restore the
  17original <branch> and remove the .dotest working files, use the command
  18git rebase --abort instead.
  19
  20Note that if <branch> is not specified on the command line, the
  21currently checked out branch is used.  You must be in the top
  22directory of your project to start (or continue) a rebase.
  23
  24Example:       git-rebase master~1 topic
  25
  26        A---B---C topic                   A'\''--B'\''--C'\'' topic
  27       /                   -->           /
  28  D---E---F---G master          D---E---F---G master
  29'
  30
  31SUBDIRECTORY_OK=Yes
  32. git-sh-setup
  33set_reflog_action rebase
  34require_work_tree
  35cd_to_toplevel
  36
  37RESOLVEMSG="
  38When you have resolved this problem run \"git rebase --continue\".
  39If you would prefer to skip this patch, instead run \"git rebase --skip\".
  40To restore the original branch and stop rebasing run \"git rebase --abort\".
  41"
  42unset newbase
  43strategy=recursive
  44do_merge=
  45dotest=$GIT_DIR/.dotest-merge
  46prec=4
  47verbose=
  48
  49continue_merge () {
  50        test -n "$prev_head" || die "prev_head must be defined"
  51        test -d "$dotest" || die "$dotest directory does not exist"
  52
  53        unmerged=$(git-ls-files -u)
  54        if test -n "$unmerged"
  55        then
  56                echo "You still have unmerged paths in your index"
  57                echo "did you forget update-index?"
  58                die "$RESOLVEMSG"
  59        fi
  60
  61        if test -n "`git-diff-index HEAD`"
  62        then
  63                if ! git-commit -C "`cat $dotest/current`"
  64                then
  65                        echo "Commit failed, please do not call \"git commit\""
  66                        echo "directly, but instead do one of the following: "
  67                        die "$RESOLVEMSG"
  68                fi
  69                printf "Committed: %0${prec}d" $msgnum
  70        else
  71                printf "Already applied: %0${prec}d" $msgnum
  72        fi
  73        echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
  74                                sed 's/^[a-f0-9]\+ //'`
  75
  76        prev_head=`git-rev-parse HEAD^0`
  77        # save the resulting commit so we can read-tree on it later
  78        echo "$prev_head" > "$dotest/prev_head"
  79
  80        # onto the next patch:
  81        msgnum=$(($msgnum + 1))
  82        echo "$msgnum" >"$dotest/msgnum"
  83}
  84
  85call_merge () {
  86        cmt="$(cat $dotest/cmt.$1)"
  87        echo "$cmt" > "$dotest/current"
  88        hd=$(git-rev-parse --verify HEAD)
  89        cmt_name=$(git-symbolic-ref HEAD)
  90        msgnum=$(cat $dotest/msgnum)
  91        end=$(cat $dotest/end)
  92        eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
  93        eval GITHEAD_$hd='"$(cat $dotest/onto_name)"'
  94        export GITHEAD_$cmt GITHEAD_$hd
  95        git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
  96        rv=$?
  97        case "$rv" in
  98        0)
  99                unset GITHEAD_$cmt GITHEAD_$hd
 100                return
 101                ;;
 102        1)
 103                test -d "$GIT_DIR/rr-cache" && git-rerere
 104                die "$RESOLVEMSG"
 105                ;;
 106        2)
 107                echo "Strategy: $rv $strategy failed, try another" 1>&2
 108                die "$RESOLVEMSG"
 109                ;;
 110        *)
 111                die "Unknown exit code ($rv) from command:" \
 112                        "git-merge-$strategy $cmt^ -- HEAD $cmt"
 113                ;;
 114        esac
 115}
 116
 117finish_rb_merge () {
 118        rm -r "$dotest"
 119        echo "All done."
 120}
 121
 122while case "$#" in 0) break ;; esac
 123do
 124        case "$1" in
 125        --continue)
 126                diff=$(git-diff-files)
 127                case "$diff" in
 128                ?*)     echo "You must edit all merge conflicts and then"
 129                        echo "mark them as resolved using git update-index"
 130                        exit 1
 131                        ;;
 132                esac
 133                if test -d "$dotest"
 134                then
 135                        prev_head="`cat $dotest/prev_head`"
 136                        end="`cat $dotest/end`"
 137                        msgnum="`cat $dotest/msgnum`"
 138                        onto="`cat $dotest/onto`"
 139                        continue_merge
 140                        while test "$msgnum" -le "$end"
 141                        do
 142                                call_merge "$msgnum"
 143                                continue_merge
 144                        done
 145                        finish_rb_merge
 146                        exit
 147                fi
 148                git am --resolved --3way --resolvemsg="$RESOLVEMSG"
 149                exit
 150                ;;
 151        --skip)
 152                if test -d "$dotest"
 153                then
 154                        if test -d "$GIT_DIR/rr-cache"
 155                        then
 156                                git-rerere clear
 157                        fi
 158                        prev_head="`cat $dotest/prev_head`"
 159                        end="`cat $dotest/end`"
 160                        msgnum="`cat $dotest/msgnum`"
 161                        msgnum=$(($msgnum + 1))
 162                        onto="`cat $dotest/onto`"
 163                        while test "$msgnum" -le "$end"
 164                        do
 165                                call_merge "$msgnum"
 166                                continue_merge
 167                        done
 168                        finish_rb_merge
 169                        exit
 170                fi
 171                git am -3 --skip --resolvemsg="$RESOLVEMSG"
 172                exit
 173                ;;
 174        --abort)
 175                if test -d "$GIT_DIR/rr-cache"
 176                then
 177                        git-rerere clear
 178                fi
 179                if test -d "$dotest"
 180                then
 181                        rm -r "$dotest"
 182                elif test -d .dotest
 183                then
 184                        rm -r .dotest
 185                else
 186                        die "No rebase in progress?"
 187                fi
 188                git reset --hard ORIG_HEAD
 189                exit
 190                ;;
 191        --onto)
 192                test 2 -le "$#" || usage
 193                newbase="$2"
 194                shift
 195                ;;
 196        -M|-m|--m|--me|--mer|--merg|--merge)
 197                do_merge=t
 198                ;;
 199        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 200                --strateg=*|--strategy=*|\
 201        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
 202                case "$#,$1" in
 203                *,*=*)
 204                        strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
 205                1,*)
 206                        usage ;;
 207                *)
 208                        strategy="$2"
 209                        shift ;;
 210                esac
 211                do_merge=t
 212                ;;
 213        -v|--verbose)
 214                verbose=t
 215                ;;
 216        -*)
 217                usage
 218                ;;
 219        *)
 220                break
 221                ;;
 222        esac
 223        shift
 224done
 225
 226# Make sure we do not have .dotest
 227if test -z "$do_merge"
 228then
 229        if mkdir .dotest
 230        then
 231                rmdir .dotest
 232        else
 233                echo >&2 '
 234It seems that I cannot create a .dotest directory, and I wonder if you
 235are in the middle of patch application or another rebase.  If that is not
 236the case, please rm -fr .dotest and run me again.  I am stopping in case
 237you still have something valuable there.'
 238                exit 1
 239        fi
 240else
 241        if test -d "$dotest"
 242        then
 243                die "previous dotest directory $dotest still exists." \
 244                        'try git-rebase < --continue | --abort >'
 245        fi
 246fi
 247
 248# The tree must be really really clean.
 249git-update-index --refresh || exit
 250diff=$(git-diff-index --cached --name-status -r HEAD)
 251case "$diff" in
 252?*)     echo "$diff"
 253        exit 1
 254        ;;
 255esac
 256
 257# The upstream head must be given.  Make sure it is valid.
 258upstream_name="$1"
 259upstream=`git rev-parse --verify "${upstream_name}^0"` ||
 260    die "invalid upstream $upstream_name"
 261
 262# If a hook exists, give it a chance to interrupt
 263if test -x "$GIT_DIR/hooks/pre-rebase"
 264then
 265        "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
 266                echo >&2 "The pre-rebase hook refused to rebase."
 267                exit 1
 268        }
 269fi
 270
 271# If the branch to rebase is given, first switch to it.
 272case "$#" in
 2732)
 274        branch_name="$2"
 275        git-checkout "$2" || usage
 276        ;;
 277*)
 278        if branch_name=`git symbolic-ref -q HEAD`
 279        then
 280                branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
 281        else
 282                branch_name=HEAD ;# detached
 283        fi
 284        ;;
 285esac
 286branch=$(git-rev-parse --verify "${branch_name}^0") || exit
 287
 288# Make sure the branch to rebase onto is valid.
 289onto_name=${newbase-"$upstream_name"}
 290onto=$(git-rev-parse --verify "${onto_name}^0") || exit
 291
 292# Now we are rebasing commits $upstream..$branch on top of $onto
 293
 294# Check if we are already based on $onto, but this should be
 295# done only when upstream and onto are the same.
 296mb=$(git-merge-base "$onto" "$branch")
 297if test "$upstream" = "$onto" && test "$mb" = "$onto"
 298then
 299        echo >&2 "Current branch $branch_name is up to date."
 300        exit 0
 301fi
 302
 303if test -n "$verbose"
 304then
 305        echo "Changes from $mb to $onto:"
 306        git-diff-tree --stat --summary "$mb" "$onto"
 307fi
 308
 309# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
 310echo "First, rewinding head to replay your work on top of it..."
 311git-reset --hard "$onto"
 312
 313# If the $onto is a proper descendant of the tip of the branch, then
 314# we just fast forwarded.
 315if test "$mb" = "$branch"
 316then
 317        echo >&2 "Fast-forwarded $branch_name to $onto_name."
 318        exit 0
 319fi
 320
 321if test -z "$do_merge"
 322then
 323        git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
 324        git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
 325        exit $?
 326fi
 327
 328# start doing a rebase with git-merge
 329# this is rename-aware if the recursive (default) strategy is used
 330
 331mkdir -p "$dotest"
 332echo "$onto" > "$dotest/onto"
 333echo "$onto_name" > "$dotest/onto_name"
 334prev_head=`git-rev-parse HEAD^0`
 335echo "$prev_head" > "$dotest/prev_head"
 336
 337msgnum=0
 338for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
 339                        | @@PERL@@ -e 'print reverse <>'`
 340do
 341        msgnum=$(($msgnum + 1))
 342        echo "$cmt" > "$dotest/cmt.$msgnum"
 343done
 344
 345echo 1 >"$dotest/msgnum"
 346echo $msgnum >"$dotest/end"
 347
 348end=$msgnum
 349msgnum=1
 350
 351while test "$msgnum" -le "$end"
 352do
 353        call_merge "$msgnum"
 354        continue_merge
 355done
 356
 357finish_rb_merge