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