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