1#!/bin/sh 2# 3# Copyright (c) 2005 Junio C Hamano. 4# 5 6USAGE='[--interactive | -i] [-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= 48git_am_opt= 49 50continue_merge () { 51test -n"$prev_head"|| die "prev_head must be defined" 52test -d"$dotest"|| die "$dotestdirectory does not exist" 53 54 unmerged=$(git ls-files -u) 55iftest -n"$unmerged" 56then 57echo"You still have unmerged paths in your index" 58echo"did you forget to use git add?" 59 die "$RESOLVEMSG" 60fi 61 62 cmt=`cat$dotest/current` 63if! git diff-index --quiet HEAD 64then 65if! git-commit -C"$cmt" 66then 67echo"Commit failed, please do not call\"git commit\"" 68echo"directly, but instead do one of the following: " 69 die "$RESOLVEMSG" 70fi 71printf"Committed: %0${prec}d "$msgnum 72else 73printf"Already applied: %0${prec}d "$msgnum 74fi 75 git rev-list --pretty=oneline -1"$cmt"|sed-e's/^[^ ]* //' 76 77 prev_head=`git rev-parse HEAD^0` 78# save the resulting commit so we can read-tree on it later 79echo"$prev_head">"$dotest/prev_head" 80 81# onto the next patch: 82 msgnum=$(($msgnum + 1)) 83echo"$msgnum">"$dotest/msgnum" 84} 85 86call_merge () { 87 cmt="$(cat $dotest/cmt.$1)" 88echo"$cmt">"$dotest/current" 89 hd=$(git rev-parse --verify HEAD) 90 cmt_name=$(git symbolic-ref HEAD) 91 msgnum=$(cat $dotest/msgnum) 92 end=$(cat $dotest/end) 93eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' 94eval GITHEAD_$hd='"$(cat $dotest/onto_name)"' 95export GITHEAD_$cmt GITHEAD_$hd 96 git-merge-$strategy"$cmt^"--"$hd""$cmt" 97 rv=$? 98case"$rv"in 990) 100unset GITHEAD_$cmt GITHEAD_$hd 101return 102;; 1031) 104 git rerere 105 die "$RESOLVEMSG" 106;; 1072) 108echo"Strategy:$rv$strategyfailed, try another"1>&2 109 die "$RESOLVEMSG" 110;; 111*) 112 die "Unknown exit code ($rv) from command:" \ 113"git-merge-$strategy$cmt^ -- HEAD$cmt" 114;; 115esac 116} 117 118finish_rb_merge () { 119iftest -f"$dotest/stash" 120then 121 stash=$(cat "$dotest/stash") 122 git stash apply --index"$stash" 123fi 124rm-r"$dotest" 125echo"All done." 126} 127 128read_stash () { 129iftest -f"$1" 130then 131cat"$1" 132fi 133} 134unstash_and_exit () { 135 err=$? 136iftest -f"$1"&&test$err=0 137then 138 git stash apply --index"$1" 139fi 140exit$err 141} 142 143is_interactive () { 144test -f"$dotest"/interactive || 145while case$#,"$1"in0,|*,-i|*,--interactive)break;;esac 146do 147shift 148done&&test -n"$1" 149} 150 151is_interactive "$@"&&exec git-rebase--interactive"$@" 152 153while case"$#"in0)break;;esac 154do 155case"$1"in 156--continue) 157 git diff-files --quiet|| { 158echo"You must edit all merge conflicts and then" 159echo"mark them as resolved using git add" 160exit1 161} 162iftest -d"$dotest" 163then 164 prev_head="`cat$dotest/prev_head`" 165 end="`cat$dotest/end`" 166 msgnum="`cat$dotest/msgnum`" 167 onto="`cat$dotest/onto`" 168 continue_merge 169whiletest"$msgnum"-le"$end" 170do 171 call_merge "$msgnum" 172 continue_merge 173done 174 finish_rb_merge 175exit 176fi 177 stash=$(read_stash ".dotest/stash") 178 git am --resolved --3way --resolvemsg="$RESOLVEMSG" 179 unstash_and_exit "$stash" 180;; 181--skip) 182iftest -d"$dotest" 183then 184 git rerere clear 185 prev_head="`cat$dotest/prev_head`" 186 end="`cat$dotest/end`" 187 msgnum="`cat$dotest/msgnum`" 188 msgnum=$(($msgnum + 1)) 189 onto="`cat$dotest/onto`" 190whiletest"$msgnum"-le"$end" 191do 192 call_merge "$msgnum" 193 continue_merge 194done 195 finish_rb_merge 196exit 197fi 198 stash=$(read_stash ".dotest/stash") 199 git am -3 --skip --resolvemsg="$RESOLVEMSG" 200 unstash_and_exit "$stash" 201;; 202--abort) 203 git rerere clear 204iftest -d"$dotest" 205then 206iftest -f"$dotest/stash" 207then 208 stash=$(cat "$dotest/stash") 209fi 210rm-r"$dotest" 211eliftest -d .dotest 212then 213iftest -f".dotest/stash" 214then 215 stash=$(cat ".dotest/stash") 216fi 217rm-r .dotest 218else 219 die "No rebase in progress?" 220fi 221 git reset--hard ORIG_HEAD 222test -z"$stash"|| git stash apply --index"$stash" 223exit 224;; 225--onto) 226test2-le"$#"|| usage 227 newbase="$2" 228shift 229;; 230-M|-m|--m|--me|--mer|--merg|--merge) 231 do_merge=t 232;; 233-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ 234--strateg=*|--strategy=*|\ 235-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) 236case"$#,$1"in 237*,*=*) 238 strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'`;; 2391,*) 240 usage ;; 241*) 242 strategy="$2" 243shift;; 244esac 245 do_merge=t 246;; 247-v|--verbose) 248 verbose=t 249;; 250--whitespace=*) 251 git_am_opt="$git_am_opt$1" 252;; 253-C*) 254 git_am_opt="$git_am_opt$1" 255;; 256-*) 257 usage 258;; 259*) 260break 261;; 262esac 263shift 264done 265 266# Make sure we do not have .dotest 267iftest -z"$do_merge" 268then 269ifmkdir .dotest 270then 271rmdir .dotest 272else 273echo>&2' 274It seems that I cannot create a .dotest directory, and I wonder if you 275are in the middle of patch application or another rebase. If that is not 276the case, please rm -fr .dotest and run me again. I am stopping in case 277you still have something valuable there.' 278exit1 279fi 280else 281iftest -d"$dotest" 282then 283 die "previous dotest directory$doteststill exists." \ 284'try git-rebase < --continue | --abort >' 285fi 286fi 287 288# The upstream head must be given. Make sure it is valid. 289upstream_name="$1" 290upstream=`git rev-parse --verify "${upstream_name}^0"`|| 291 die "invalid upstream$upstream_name" 292 293# Make sure the branch to rebase onto is valid. 294onto_name=${newbase-"$upstream_name"} 295onto=$(git rev-parse --verify "${onto_name}^0")||exit 296 297# The tree must be clean enough for us to create a stash 298stash=$(git stash create)||exit 299iftest -n"$stash" 300then 301echo>&2"Stashed away your working tree changes" 302fi 303 304# If a hook exists, give it a chance to interrupt 305iftest -x"$GIT_DIR/hooks/pre-rebase" 306then 307"$GIT_DIR/hooks/pre-rebase"${1+"$@"}|| { 308echo>&2"The pre-rebase hook refused to rebase." 309test -z"$stash"|| git stash apply --index"$stash" 310exit1 311} 312fi 313 314# If the branch to rebase is given, first switch to it. 315case"$#"in 3162) 317 branch_name="$2" 318 git-checkout"$2"|| { 319test -z"$stash"|| git stash apply --index"$stash" 320 usage 321} 322;; 323*) 324if branch_name=`git symbolic-ref -q HEAD` 325then 326 branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` 327else 328 branch_name=HEAD ;# detached 329fi 330;; 331esac 332branch=$(git rev-parse --verify "${branch_name}^0")||exit 333 334# Now we are rebasing commits $upstream..$branch on top of $onto 335 336# Check if we are already based on $onto with linear history, 337# but this should be done only when upstream and onto are the same. 338mb=$(git merge-base "$onto" "$branch") 339iftest"$upstream"="$onto"&&test"$mb"="$onto"&& 340# linear history? 341! git rev-list --parents"$onto".."$branch"|grep" .* "> /dev/null 342then 343echo>&2"Current branch$branch_nameis up to date." 344test -z"$stash"|| git stash apply --index"$stash" 345exit0 346fi 347 348iftest -n"$verbose" 349then 350echo"Changes from$mbto$onto:" 351# We want color (if set), but no pager 352 GIT_PAGER='' git diff--stat --summary"$mb""$onto" 353fi 354 355# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. 356echo"First, rewinding head to replay your work on top of it..." 357git-reset --hard"$onto" 358 359# If the $onto is a proper descendant of the tip of the branch, then 360# we just fast forwarded. 361iftest"$mb"="$branch" 362then 363echo>&2"Fast-forwarded$branch_nameto$onto_name." 364test -z"$stash"|| git stash apply --index"$stash" 365exit0 366fi 367 368iftest -z"$do_merge" 369then 370 git format-patch -k --stdout --full-index --ignore-if-in-upstream"$upstream"..ORIG_HEAD | 371 git am $git_am_opt--binary -3 -k --resolvemsg="$RESOLVEMSG" 372 err=$? 373 374iftest$err=0 375then 376test -z"$stash"|| git stash apply --index"$stash" 377exit 378else 379test -z"$stash"||echo"$stash">.dotest/stash 380exit$err 381fi 382fi 383 384# start doing a rebase with git-merge 385# this is rename-aware if the recursive (default) strategy is used 386 387mkdir-p"$dotest" 388echo"$onto">"$dotest/onto" 389echo"$onto_name">"$dotest/onto_name" 390prev_head=`git rev-parse HEAD^0` 391echo"$prev_head">"$dotest/prev_head" 392test -z"$stash"||echo"$stash">"$dotest/stash" 393 394msgnum=0 395for cmt in`git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` 396do 397 msgnum=$(($msgnum + 1)) 398echo"$cmt">"$dotest/cmt.$msgnum" 399done 400 401echo1>"$dotest/msgnum" 402echo$msgnum>"$dotest/end" 403 404end=$msgnum 405msgnum=1 406 407whiletest"$msgnum"-le"$end" 408do 409 call_merge "$msgnum" 410 continue_merge 411done 412 413finish_rb_merge