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