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