git-rebase.shon commit gitweb: Support for snapshot (cb9c6e5)
   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"
  37unset newbase
  38strategy=recursive
  39do_merge=
  40dotest=$GIT_DIR/.dotest-merge
  41prec=4
  42
  43continue_merge () {
  44        test -n "$prev_head" || die "prev_head must be defined"
  45        test -d "$dotest" || die "$dotest directory does not exist"
  46
  47        unmerged=$(git-ls-files -u)
  48        if test -n "$unmerged"
  49        then
  50                echo "You still have unmerged paths in your index"
  51                echo "did you forget update-index?"
  52                die "$RESOLVEMSG"
  53        fi
  54
  55        if test -n "`git-diff-index HEAD`"
  56        then
  57                if ! git-commit -C "`cat $dotest/current`"
  58                then
  59                        echo "Commit failed, please do not call \"git commit\""
  60                        echo "directly, but instead do one of the following: "
  61                        die "$RESOLVEMSG"
  62                fi
  63                printf "Committed: %0${prec}d" $msgnum
  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 "$RESOLVEMSG"
  91                ;;
  92        2)
  93                echo "Strategy: $rv $strategy failed, try another" 1>&2
  94                die "$RESOLVEMSG"
  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                        --reflog-action=rebase
 136                exit
 137                ;;
 138        --skip)
 139                if test -d "$dotest"
 140                then
 141                        prev_head="`cat $dotest/prev_head`"
 142                        end="`cat $dotest/end`"
 143                        msgnum="`cat $dotest/msgnum`"
 144                        msgnum=$(($msgnum + 1))
 145                        onto="`cat $dotest/onto`"
 146                        while test "$msgnum" -le "$end"
 147                        do
 148                                call_merge "$msgnum"
 149                                continue_merge
 150                        done
 151                        finish_rb_merge
 152                        exit
 153                fi
 154                git am -3 --skip --resolvemsg="$RESOLVEMSG" \
 155                        --reflog-action=rebase
 156                exit
 157                ;;
 158        --abort)
 159                if test -d "$dotest"
 160                then
 161                        rm -r "$dotest"
 162                elif test -d .dotest
 163                then
 164                        rm -r .dotest
 165                else
 166                        die "No rebase in progress?"
 167                fi
 168                git reset --hard ORIG_HEAD
 169                exit
 170                ;;
 171        --onto)
 172                test 2 -le "$#" || usage
 173                newbase="$2"
 174                shift
 175                ;;
 176        -M|-m|--m|--me|--mer|--merg|--merge)
 177                do_merge=t
 178                ;;
 179        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 180                --strateg=*|--strategy=*|\
 181        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
 182                case "$#,$1" in
 183                *,*=*)
 184                        strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
 185                1,*)
 186                        usage ;;
 187                *)
 188                        strategy="$2"
 189                        shift ;;
 190                esac
 191                do_merge=t
 192                ;;
 193        -*)
 194                usage
 195                ;;
 196        *)
 197                break
 198                ;;
 199        esac
 200        shift
 201done
 202
 203# Make sure we do not have .dotest
 204if test -z "$do_merge"
 205then
 206        if mkdir .dotest
 207        then
 208                rmdir .dotest
 209        else
 210                echo >&2 '
 211It seems that I cannot create a .dotest directory, and I wonder if you
 212are in the middle of patch application or another rebase.  If that is not
 213the case, please rm -fr .dotest and run me again.  I am stopping in case
 214you still have something valuable there.'
 215                exit 1
 216        fi
 217else
 218        if test -d "$dotest"
 219        then
 220                die "previous dotest directory $dotest still exists." \
 221                        'try git-rebase < --continue | --abort >'
 222        fi
 223fi
 224
 225# The tree must be really really clean.
 226git-update-index --refresh || exit
 227diff=$(git-diff-index --cached --name-status -r HEAD)
 228case "$diff" in
 229?*)     echo "$diff"
 230        exit 1
 231        ;;
 232esac
 233
 234# The upstream head must be given.  Make sure it is valid.
 235upstream_name="$1"
 236upstream=`git rev-parse --verify "${upstream_name}^0"` ||
 237    die "invalid upstream $upstream_name"
 238
 239# If a hook exists, give it a chance to interrupt
 240if test -x "$GIT_DIR/hooks/pre-rebase"
 241then
 242        "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
 243                echo >&2 "The pre-rebase hook refused to rebase."
 244                exit 1
 245        }
 246fi
 247
 248# If the branch to rebase is given, first switch to it.
 249case "$#" in
 2502)
 251        branch_name="$2"
 252        git-checkout "$2" || usage
 253        ;;
 254*)
 255        branch_name=`git symbolic-ref HEAD` || die "No current branch"
 256        branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
 257        ;;
 258esac
 259branch=$(git-rev-parse --verify "${branch_name}^0") || exit
 260
 261# Make sure the branch to rebase onto is valid.
 262onto_name=${newbase-"$upstream_name"}
 263onto=$(git-rev-parse --verify "${onto_name}^0") || exit
 264
 265# Now we are rebasing commits $upstream..$branch on top of $onto
 266
 267# Check if we are already based on $onto, but this should be
 268# done only when upstream and onto are the same.
 269mb=$(git-merge-base "$onto" "$branch")
 270if test "$upstream" = "$onto" && test "$mb" = "$onto"
 271then
 272        echo >&2 "Current branch $branch_name is up to date."
 273        exit 0
 274fi
 275
 276# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
 277git-reset --hard "$onto"
 278
 279# If the $onto is a proper descendant of the tip of the branch, then
 280# we just fast forwarded.
 281if test "$mb" = "$branch"
 282then
 283        echo >&2 "Fast-forwarded $branch_name to $onto_name."
 284        exit 0
 285fi
 286
 287if test -z "$do_merge"
 288then
 289        git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
 290        git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
 291                --reflog-action=rebase
 292        exit $?
 293fi
 294
 295if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
 296then
 297        die 'The recursive merge strategy currently relies on Python,
 298which this installation of git was not configured with.  Please consider
 299a different merge strategy (e.g. octopus, resolve, stupid, ours)
 300or install Python and git with Python support.'
 301
 302fi
 303
 304# start doing a rebase with git-merge
 305# this is rename-aware if the recursive (default) strategy is used
 306
 307mkdir -p "$dotest"
 308echo "$onto" > "$dotest/onto"
 309prev_head=`git-rev-parse HEAD^0`
 310echo "$prev_head" > "$dotest/prev_head"
 311
 312msgnum=0
 313for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
 314                        | @@PERL@@ -e 'print reverse <>'`
 315do
 316        msgnum=$(($msgnum + 1))
 317        echo "$cmt" > "$dotest/cmt.$msgnum"
 318done
 319
 320echo 1 >"$dotest/msgnum"
 321echo $msgnum >"$dotest/end"
 322
 323end=$msgnum
 324msgnum=1
 325
 326while test "$msgnum" -le "$end"
 327do
 328        call_merge "$msgnum"
 329        continue_merge
 330done
 331
 332finish_rb_merge