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. 22 23Example: git-rebase master~1 topic 24 25 A---B---C topic A'\''--B'\''--C'\'' topic 26 / --> / 27 D---E---F---G master D---E---F---G master 28' 29 30SUBDIRECTORY_OK=Yes 31OPTIONS_SPEC= 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 --no-verify -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 2> /dev/null || echo 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 118move_to_original_branch () { 119test -z"$head_name"&& 120 head_name="$(cat "$dotest"/head-name)"&& 121 onto="$(cat "$dotest"/onto)"&& 122 orig_head="$(cat "$dotest"/orig-head)" 123case"$head_name"in 124 refs/*) 125 message="rebase finished:$head_nameonto$onto" 126 git update-ref -m"$message" \ 127$head_name $(git rev-parse HEAD) $orig_head&& 128 git symbolic-ref HEAD $head_name|| 129 die "Could not move back to$head_name" 130;; 131esac 132} 133 134finish_rb_merge () { 135 move_to_original_branch 136rm-r"$dotest" 137echo"All done." 138} 139 140is_interactive () { 141test -f"$dotest"/interactive || 142while:;do case$#,"$1"in0,|*,-i|*,--interactive)break;;esac 143shift 144done&&test -n"$1" 145} 146 147is_interactive "$@"&&exec git-rebase--interactive"$@" 148 149whiletest$#!=0 150do 151case"$1"in 152--continue) 153 git diff-files --quiet|| { 154echo"You must edit all merge conflicts and then" 155echo"mark them as resolved using git add" 156exit1 157} 158iftest -d"$dotest" 159then 160 prev_head=$(cat "$dotest/prev_head") 161 end=$(cat "$dotest/end") 162 msgnum=$(cat "$dotest/msgnum") 163 onto=$(cat "$dotest/onto") 164 continue_merge 165whiletest"$msgnum"-le"$end" 166do 167 call_merge "$msgnum" 168 continue_merge 169done 170 finish_rb_merge 171exit 172fi 173 head_name=$(cat .dotest/head-name)&& 174 onto=$(cat .dotest/onto)&& 175 orig_head=$(cat .dotest/orig-head)&& 176 git am --resolved --3way --resolvemsg="$RESOLVEMSG"&& 177 move_to_original_branch 178exit 179;; 180--skip) 181 git reset--hard HEAD ||exit $? 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 head_name=$(cat .dotest/head-name)&& 199 onto=$(cat .dotest/onto)&& 200 orig_head=$(cat .dotest/orig-head)&& 201 git am -3 --skip --resolvemsg="$RESOLVEMSG"&& 202 move_to_original_branch 203exit 204;; 205--abort) 206 git rerere clear 207iftest -d"$dotest" 208then 209 move_to_original_branch 210eliftest -d .dotest 211then 212 dotest=.dotest 213 move_to_original_branch 214else 215 die "No rebase in progress?" 216fi 217 git reset--hard$(cat $dotest/orig-head) 218rm-r"$dotest" 219exit 220;; 221--onto) 222test2-le"$#"|| usage 223 newbase="$2" 224shift 225;; 226-M|-m|--m|--me|--mer|--merg|--merge) 227 do_merge=t 228;; 229-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ 230--strateg=*|--strategy=*|\ 231-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) 232case"$#,$1"in 233*,*=*) 234 strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'`;; 2351,*) 236 usage ;; 237*) 238 strategy="$2" 239shift;; 240esac 241 do_merge=t 242;; 243-v|--verbose) 244 verbose=t 245;; 246--whitespace=*) 247 git_am_opt="$git_am_opt$1" 248;; 249-C*) 250 git_am_opt="$git_am_opt$1" 251;; 252-*) 253 usage 254;; 255*) 256break 257;; 258esac 259shift 260done 261 262# Make sure we do not have .dotest 263iftest -z"$do_merge" 264then 265ifmkdir .dotest 266then 267rmdir .dotest 268else 269echo>&2' 270It seems that I cannot create a .dotest directory, and I wonder if you 271are in the middle of patch application or another rebase. If that is not 272the case, please rm -fr .dotest and run me again. I am stopping in case 273you still have something valuable there.' 274exit1 275fi 276else 277iftest -d"$dotest" 278then 279 die "previous dotest directory$doteststill exists." \ 280'try git-rebase < --continue | --abort >' 281fi 282fi 283 284# The tree must be really really clean. 285git update-index --refresh||exit 286diff=$(git diff-index --cached --name-status -r HEAD --) 287case"$diff"in 288?*)echo"cannot rebase: your index is not up-to-date" 289echo"$diff" 290exit1 291;; 292esac 293 294# The upstream head must be given. Make sure it is valid. 295upstream_name="$1" 296upstream=`git rev-parse --verify "${upstream_name}^0"`|| 297 die "invalid upstream$upstream_name" 298 299# Make sure the branch to rebase onto is valid. 300onto_name=${newbase-"$upstream_name"} 301onto=$(git rev-parse --verify "${onto_name}^0")||exit 302 303# If a hook exists, give it a chance to interrupt 304iftest -x"$GIT_DIR/hooks/pre-rebase" 305then 306"$GIT_DIR/hooks/pre-rebase"${1+"$@"}|| { 307echo>&2"The pre-rebase hook refused to rebase." 308exit1 309} 310fi 311 312# If the branch to rebase is given, first switch to it. 313case"$#"in 3142) 315 branch_name="$2" 316 git-checkout"$2"|| usage 317;; 318*) 319if branch_name=`git symbolic-ref -q HEAD` 320then 321 branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` 322else 323 branch_name=HEAD ;# detached 324fi 325;; 326esac 327branch=$(git rev-parse --verify "${branch_name}^0")||exit 328 329# Now we are rebasing commits $upstream..$branch on top of $onto 330 331# Check if we are already based on $onto with linear history, 332# but this should be done only when upstream and onto are the same. 333mb=$(git merge-base "$onto" "$branch") 334iftest"$upstream"="$onto"&&test"$mb"="$onto"&& 335# linear history? 336! git rev-list --parents"$onto".."$branch"|grep" .* "> /dev/null 337then 338echo>&2"Current branch$branch_nameis up to date." 339exit0 340fi 341 342iftest -n"$verbose" 343then 344echo"Changes from$mbto$onto:" 345# We want color (if set), but no pager 346 GIT_PAGER='' git diff--stat --summary"$mb""$onto" 347fi 348 349# move to a detached HEAD 350orig_head=$(git rev-parse HEAD^0) 351head_name=$(git symbolic-ref HEAD 2> /dev/null) 352case"$head_name"in 353'') 354 head_name="detached HEAD" 355;; 356*) 357 git checkout "$orig_head"> /dev/null 2>&1|| 358 die "could not detach HEAD" 359;; 360esac 361 362# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. 363echo"First, rewinding head to replay your work on top of it..." 364git-reset --hard"$onto" 365 366# If the $onto is a proper descendant of the tip of the branch, then 367# we just fast forwarded. 368iftest"$mb"="$branch" 369then 370echo>&2"Fast-forwarded$branch_nameto$onto_name." 371 move_to_original_branch 372exit0 373fi 374 375iftest -z"$do_merge" 376then 377 git format-patch -k --stdout --full-index --ignore-if-in-upstream"$upstream"..ORIG_HEAD | 378 git am $git_am_opt--rebasing --resolvemsg="$RESOLVEMSG"&& 379 move_to_original_branch 380 ret=$? 381test0!=$ret-a -d .dotest && 382echo$head_name> .dotest/head-name&& 383echo$onto> .dotest/onto && 384echo$orig_head> .dotest/orig-head 385exit$ret 386fi 387 388# start doing a rebase with git-merge 389# this is rename-aware if the recursive (default) strategy is used 390 391mkdir-p"$dotest" 392echo"$onto">"$dotest/onto" 393echo"$onto_name">"$dotest/onto_name" 394prev_head=$orig_head 395echo"$prev_head">"$dotest/prev_head" 396echo"$orig_head">"$dotest/orig-head" 397echo"$head_name">"$dotest/head-name" 398 399msgnum=0 400for cmt in`git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` 401do 402 msgnum=$(($msgnum + 1)) 403echo"$cmt">"$dotest/cmt.$msgnum" 404done 405 406echo1>"$dotest/msgnum" 407echo$msgnum>"$dotest/end" 408 409end=$msgnum 410msgnum=1 411 412whiletest"$msgnum"-le"$end" 413do 414 call_merge "$msgnum" 415 continue_merge 416done 417 418finish_rb_merge