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