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 -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 211rm-r"$dotest" 212eliftest -d .dotest 213then 214 dotest=.dotest 215 move_to_original_branch 216rm-r .dotest 217else 218 die "No rebase in progress?" 219fi 220 git reset--hard ORIG_HEAD 221exit 222;; 223--onto) 224test2-le"$#"|| usage 225 newbase="$2" 226shift 227;; 228-M|-m|--m|--me|--mer|--merg|--merge) 229 do_merge=t 230;; 231-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ 232--strateg=*|--strategy=*|\ 233-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) 234case"$#,$1"in 235*,*=*) 236 strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'`;; 2371,*) 238 usage ;; 239*) 240 strategy="$2" 241shift;; 242esac 243 do_merge=t 244;; 245-v|--verbose) 246 verbose=t 247;; 248--whitespace=*) 249 git_am_opt="$git_am_opt$1" 250;; 251-C*) 252 git_am_opt="$git_am_opt$1" 253;; 254-*) 255 usage 256;; 257*) 258break 259;; 260esac 261shift 262done 263 264# Make sure we do not have .dotest 265iftest -z"$do_merge" 266then 267ifmkdir .dotest 268then 269rmdir .dotest 270else 271echo>&2' 272It seems that I cannot create a .dotest directory, and I wonder if you 273are in the middle of patch application or another rebase. If that is not 274the case, please rm -fr .dotest and run me again. I am stopping in case 275you still have something valuable there.' 276exit1 277fi 278else 279iftest -d"$dotest" 280then 281 die "previous dotest directory$doteststill exists." \ 282'try git-rebase < --continue | --abort >' 283fi 284fi 285 286# The tree must be really really clean. 287git update-index --refresh||exit 288diff=$(git diff-index --cached --name-status -r HEAD --) 289case"$diff"in 290?*)echo"cannot rebase: your index is not up-to-date" 291echo"$diff" 292exit1 293;; 294esac 295 296# The upstream head must be given. Make sure it is valid. 297upstream_name="$1" 298upstream=`git rev-parse --verify "${upstream_name}^0"`|| 299 die "invalid upstream$upstream_name" 300 301# Make sure the branch to rebase onto is valid. 302onto_name=${newbase-"$upstream_name"} 303onto=$(git rev-parse --verify "${onto_name}^0")||exit 304 305# If a hook exists, give it a chance to interrupt 306iftest -x"$GIT_DIR/hooks/pre-rebase" 307then 308"$GIT_DIR/hooks/pre-rebase"${1+"$@"}|| { 309echo>&2"The pre-rebase hook refused to rebase." 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"|| usage 319;; 320*) 321if branch_name=`git symbolic-ref -q HEAD` 322then 323 branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` 324else 325 branch_name=HEAD ;# detached 326fi 327;; 328esac 329branch=$(git rev-parse --verify "${branch_name}^0")||exit 330 331# Now we are rebasing commits $upstream..$branch on top of $onto 332 333# Check if we are already based on $onto with linear history, 334# but this should be done only when upstream and onto are the same. 335mb=$(git merge-base "$onto" "$branch") 336iftest"$upstream"="$onto"&&test"$mb"="$onto"&& 337# linear history? 338! git rev-list --parents"$onto".."$branch"|grep" .* "> /dev/null 339then 340echo>&2"Current branch$branch_nameis up to date." 341exit0 342fi 343 344iftest -n"$verbose" 345then 346echo"Changes from$mbto$onto:" 347# We want color (if set), but no pager 348 GIT_PAGER='' git diff--stat --summary"$mb""$onto" 349fi 350 351# move to a detached HEAD 352orig_head=$(git rev-parse HEAD^0) 353head_name=$(git symbolic-ref HEAD 2> /dev/null) 354case"$head_name"in 355'') 356 head_name="detached HEAD" 357;; 358*) 359 git checkout "$orig_head"> /dev/null 2>&1|| 360 die "could not detach HEAD" 361;; 362esac 363 364# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. 365echo"First, rewinding head to replay your work on top of it..." 366git-reset --hard"$onto" 367 368# If the $onto is a proper descendant of the tip of the branch, then 369# we just fast forwarded. 370iftest"$mb"="$branch" 371then 372echo>&2"Fast-forwarded$branch_nameto$onto_name." 373 move_to_original_branch 374exit0 375fi 376 377iftest -z"$do_merge" 378then 379 git format-patch -k --stdout --full-index --ignore-if-in-upstream"$upstream"..ORIG_HEAD | 380 git am $git_am_opt--binary -3 -k --resolvemsg="$RESOLVEMSG"&& 381 move_to_original_branch 382 ret=$? 383test0!=$ret-a -d .dotest && 384echo$head_name> .dotest/head-name&& 385echo$onto> .dotest/onto && 386echo$orig_head> .dotest/orig-head 387exit$ret 388fi 389 390# start doing a rebase with git-merge 391# this is rename-aware if the recursive (default) strategy is used 392 393mkdir-p"$dotest" 394echo"$onto">"$dotest/onto" 395echo"$onto_name">"$dotest/onto_name" 396prev_head=$orig_head 397echo"$prev_head">"$dotest/prev_head" 398echo"$orig_head">"$dotest/orig-head" 399echo"$head_name">"$dotest/head-name" 400 401msgnum=0 402for cmt in`git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` 403do 404 msgnum=$(($msgnum + 1)) 405echo"$cmt">"$dotest/cmt.$msgnum" 406done 407 408echo1>"$dotest/msgnum" 409echo$msgnum>"$dotest/end" 410 411end=$msgnum 412msgnum=1 413 414whiletest"$msgnum"-le"$end" 415do 416 call_merge "$msgnum" 417 continue_merge 418done 419 420finish_rb_merge