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 .git/rebase-apply working files, use the 18command git 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 37OK_TO_SKIP_PRE_REBASE= 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"/rebase-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 --ignore-submodules 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 148run_pre_rebase_hook () { 149iftest -z"$OK_TO_SKIP_PRE_REBASE"&& 150test -x"$GIT_DIR/hooks/pre-rebase" 151then 152"$GIT_DIR/hooks/pre-rebase"${1+"$@"}|| { 153echo>&2"The pre-rebase hook refused to rebase." 154exit1 155} 156fi 157} 158 159test -f"$GIT_DIR"/rebase-apply/applying && 160 die 'It looks like git-am is in progress. Cannot rebase.' 161 162is_interactive "$@"&&exec git-rebase--interactive"$@" 163 164iftest$#-eq0 165then 166test -d"$dotest"-o -d"$GIT_DIR"/rebase-apply|| usage 167test -d"$dotest"-o -f"$GIT_DIR"/rebase-apply/rebasing && 168 die 'A rebase is in progress, try --continue, --skip or --abort.' 169 die "No arguments given and$GIT_DIR/rebase-apply already exists." 170fi 171 172whiletest$#!=0 173do 174case"$1"in 175--no-verify) 176 OK_TO_SKIP_PRE_REBASE=yes 177;; 178--continue) 179test -d"$dotest"-o -d"$GIT_DIR"/rebase-apply|| 180 die "No rebase in progress?" 181 182 git diff-files --quiet --ignore-submodules|| { 183echo"You must edit all merge conflicts and then" 184echo"mark them as resolved using git add" 185exit1 186} 187iftest -d"$dotest" 188then 189 prev_head=$(cat "$dotest/prev_head") 190 end=$(cat "$dotest/end") 191 msgnum=$(cat "$dotest/msgnum") 192 onto=$(cat "$dotest/onto") 193 continue_merge 194whiletest"$msgnum"-le"$end" 195do 196 call_merge "$msgnum" 197 continue_merge 198done 199 finish_rb_merge 200exit 201fi 202 head_name=$(cat "$GIT_DIR"/rebase-apply/head-name)&& 203 onto=$(cat "$GIT_DIR"/rebase-apply/onto)&& 204 orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head)&& 205 git am --resolved --3way --resolvemsg="$RESOLVEMSG"&& 206 move_to_original_branch 207exit 208;; 209--skip) 210test -d"$dotest"-o -d"$GIT_DIR"/rebase-apply|| 211 die "No rebase in progress?" 212 213 git reset--hard HEAD ||exit $? 214iftest -d"$dotest" 215then 216 git rerere clear 217 prev_head=$(cat "$dotest/prev_head") 218 end=$(cat "$dotest/end") 219 msgnum=$(cat "$dotest/msgnum") 220 msgnum=$(($msgnum + 1)) 221 onto=$(cat "$dotest/onto") 222whiletest"$msgnum"-le"$end" 223do 224 call_merge "$msgnum" 225 continue_merge 226done 227 finish_rb_merge 228exit 229fi 230 head_name=$(cat "$GIT_DIR"/rebase-apply/head-name)&& 231 onto=$(cat "$GIT_DIR"/rebase-apply/onto)&& 232 orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head)&& 233 git am -3 --skip --resolvemsg="$RESOLVEMSG"&& 234 move_to_original_branch 235exit 236;; 237--abort) 238test -d"$dotest"-o -d"$GIT_DIR"/rebase-apply|| 239 die "No rebase in progress?" 240 241 git rerere clear 242iftest -d"$dotest" 243then 244 move_to_original_branch 245else 246 dotest="$GIT_DIR"/rebase-apply 247 move_to_original_branch 248fi 249 git reset--hard$(cat "$dotest/orig-head") 250rm-r"$dotest" 251exit 252;; 253--onto) 254test2-le"$#"|| usage 255 newbase="$2" 256shift 257;; 258-M|-m|--m|--me|--mer|--merg|--merge) 259 do_merge=t 260;; 261-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ 262--strateg=*|--strategy=*|\ 263-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) 264case"$#,$1"in 265*,*=*) 266 strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'`;; 2671,*) 268 usage ;; 269*) 270 strategy="$2" 271shift;; 272esac 273 do_merge=t 274;; 275-v|--verbose) 276 verbose=t 277;; 278--whitespace=*) 279 git_am_opt="$git_am_opt$1" 280;; 281-C*) 282 git_am_opt="$git_am_opt$1" 283;; 284-*) 285 usage 286;; 287*) 288break 289;; 290esac 291shift 292done 293 294# Make sure we do not have $GIT_DIR/rebase-apply 295iftest -z"$do_merge" 296then 297ifmkdir"$GIT_DIR"/rebase-apply2>/dev/null 298then 299rmdir"$GIT_DIR"/rebase-apply 300else 301echo>&2' 302It seems that I cannot create a rebase-apply directory, and 303I wonder if you are in the middle of patch application or another 304rebase. If that is not the case, please 305 rm -fr '"$GIT_DIR"'/rebase-apply 306and run me again. I am stopping in case you still have something 307valuable there.' 308exit1 309fi 310else 311iftest -d"$dotest" 312then 313 die "previous rebase directory$doteststill exists." \ 314'Try git rebase (--continue | --abort | --skip)' 315fi 316fi 317 318# The tree must be really really clean. 319git update-index --ignore-submodules --refresh||exit 320diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) 321case"$diff"in 322?*)echo"cannot rebase: your index is not up-to-date" 323echo"$diff" 324exit1 325;; 326esac 327 328# The upstream head must be given. Make sure it is valid. 329upstream_name="$1" 330upstream=`git rev-parse --verify "${upstream_name}^0"`|| 331 die "invalid upstream$upstream_name" 332 333# Make sure the branch to rebase onto is valid. 334onto_name=${newbase-"$upstream_name"} 335onto=$(git rev-parse --verify "${onto_name}^0")||exit 336 337# If a hook exists, give it a chance to interrupt 338run_pre_rebase_hook ${1+"$@"} 339 340# If the branch to rebase is given, that is the branch we will rebase 341# $branch_name -- branch being rebased, or HEAD (already detached) 342# $orig_head -- commit object name of tip of the branch before rebasing 343# $head_name -- refs/heads/<that-branch> or "detached HEAD" 344switch_to= 345case"$#"in 3462) 347# Is it "rebase other $branchname" or "rebase other $commit"? 348 branch_name="$2" 349 switch_to="$2" 350 351if git show-ref --verify --quiet --"refs/heads/$2"&& 352 branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null) 353then 354 head_name="refs/heads/$2" 355elif branch=$(git rev-parse --verify "$2" 2>/dev/null) 356then 357 head_name="detached HEAD" 358else 359 usage 360fi 361;; 362*) 363# Do not need to switch branches, we are already on it. 364if branch_name=`git symbolic-ref -q HEAD` 365then 366 head_name=$branch_name 367 branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` 368else 369 head_name="detached HEAD" 370 branch_name=HEAD ;# detached 371fi 372 branch=$(git rev-parse --verify "${branch_name}^0")||exit 373;; 374esac 375orig_head=$branch 376 377# Now we are rebasing commits $upstream..$branch on top of $onto 378 379# Check if we are already based on $onto with linear history, 380# but this should be done only when upstream and onto are the same. 381mb=$(git merge-base "$onto" "$branch") 382iftest"$upstream"="$onto"&&test"$mb"="$onto"&& 383# linear history? 384! (git rev-list --parents"$onto".."$branch"|grep" .* ") > /dev/null 385then 386# Lazily switch to the target branch if needed... 387test -z"$switch_to"|| git checkout "$switch_to" 388echo>&2"Current branch$branch_nameis up to date." 389exit0 390fi 391 392iftest -n"$verbose" 393then 394echo"Changes from$mbto$onto:" 395# We want color (if set), but no pager 396 GIT_PAGER='' git diff--stat --summary"$mb""$onto" 397fi 398 399# Detach HEAD and reset the tree 400echo"First, rewinding head to replay your work on top of it..." 401git checkout -q"$onto^0"|| die "could not detach HEAD" 402git update-ref ORIG_HEAD $branch 403 404# If the $onto is a proper descendant of the tip of the branch, then 405# we just fast forwarded. 406iftest"$mb"="$branch" 407then 408echo>&2"Fast-forwarded$branch_nameto$onto_name." 409 move_to_original_branch 410exit0 411fi 412 413iftest -z"$do_merge" 414then 415 git format-patch -k --stdout --full-index --ignore-if-in-upstream \ 416"$upstream..$orig_head"| 417 git am $git_am_opt--rebasing --resolvemsg="$RESOLVEMSG"&& 418 move_to_original_branch 419 ret=$? 420test0!=$ret-a -d"$GIT_DIR"/rebase-apply&& 421echo$head_name>"$GIT_DIR"/rebase-apply/head-name&& 422echo$onto>"$GIT_DIR"/rebase-apply/onto && 423echo$orig_head>"$GIT_DIR"/rebase-apply/orig-head 424exit$ret 425fi 426 427# start doing a rebase with git-merge 428# this is rename-aware if the recursive (default) strategy is used 429 430mkdir-p"$dotest" 431echo"$onto">"$dotest/onto" 432echo"$onto_name">"$dotest/onto_name" 433prev_head=$orig_head 434echo"$prev_head">"$dotest/prev_head" 435echo"$orig_head">"$dotest/orig-head" 436echo"$head_name">"$dotest/head-name" 437 438msgnum=0 439for cmt in`git rev-list --reverse --no-merges "$upstream..$orig_head"` 440do 441 msgnum=$(($msgnum + 1)) 442echo"$cmt">"$dotest/cmt.$msgnum" 443done 444 445echo1>"$dotest/msgnum" 446echo$msgnum>"$dotest/end" 447 448end=$msgnum 449msgnum=1 450 451whiletest"$msgnum"-le"$end" 452do 453 call_merge "$msgnum" 454 continue_merge 455done 456 457finish_rb_merge