1#!/bin/sh 2# 3# Copyright (c) 2006 Johannes E. Schindelin 4 5# SHORT DESCRIPTION 6# 7# This script makes it easy to fix up commits in the middle of a series, 8# and rearrange commits. 9# 10# The original idea comes from Eric W. Biederman, in 11# http://article.gmane.org/gmane.comp.version-control.git/22407 12 13OPTIONS_KEEPDASHDASH= 14OPTIONS_SPEC="\ 15git-rebase [-i] [options] [--] <upstream> [<branch>] 16git-rebase [-i] (--continue | --abort | --skip) 17-- 18 Available options are 19v,verbose display a diffstat of what changed upstream 20onto= rebase onto given branch instead of upstream 21p,preserve-merges try to recreate merges instead of ignoring them 22s,strategy= use the given merge strategy 23m,merge always used (no-op) 24i,interactive always used (no-op) 25 Actions: 26continue continue rebasing process 27abort abort rebasing process and restore original branch 28skip skip current patch and continue rebasing process 29no-verify override pre-rebase hook from stopping the operation 30root rebase all reachable commmits up to the root(s) 31" 32 33. git-sh-setup 34require_work_tree 35 36DOTEST="$GIT_DIR/rebase-merge" 37 38# The file containing rebase commands, comments, and empty lines. 39# This file is created by "git rebase -i" then edited by the user. As 40# the lines are processed, they are removed from the front of this 41# file and written to the tail of $DONE. 42TODO="$DOTEST"/git-rebase-todo 43 44# The rebase command lines that have already been processed. A line 45# is moved here when it is first handled, before any associated user 46# actions. 47DONE="$DOTEST"/done 48 49# The commit message that is planned to be used for any changes that 50# need to be committed following a user interaction. 51MSG="$DOTEST"/message 52 53# The file into which is accumulated the suggested commit message for 54# squash/fixup commands. When the first of a series of squash/fixups 55# is seen, the file is created and the commit message from the 56# previous commit and from the first squash/fixup commit are written 57# to it. The commit message for each subsequent squash/fixup commit 58# is appended to the file as it is processed. 59# 60# The first line of the file is of the form 61# # This is a combination of $COUNT commits. 62# where $COUNT is the number of commits whose messages have been 63# written to the file so far (including the initial "pick" commit). 64# Each time that a commit message is processed, this line is read and 65# updated. It is deleted just before the combined commit is made. 66SQUASH_MSG="$DOTEST"/message-squash 67 68# $REWRITTEN is the name of a directory containing files for each 69# commit that is reachable by at least one merge base of $HEAD and 70# $UPSTREAM. They are not necessarily rewritten, but their children 71# might be. This ensures that commits on merged, but otherwise 72# unrelated side branches are left alone. (Think "X" in the man page's 73# example.) 74REWRITTEN="$DOTEST"/rewritten 75 76DROPPED="$DOTEST"/dropped 77 78# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and 79# GIT_AUTHOR_DATE that will be used for the commit that is currently 80# being rebased. 81AUTHOR_SCRIPT="$DOTEST"/author-script 82 83# When an "edit" rebase command is being processed, the SHA1 of the 84# commit to be edited is recorded in this file. When "git rebase 85# --continue" is executed, if there are any staged changes then they 86# will be amended to the HEAD commit, but only provided the HEAD 87# commit is still the commit to be edited. When any other rebase 88# command is processed, this file is deleted. 89AMEND="$DOTEST"/amend 90 91PRESERVE_MERGES= 92STRATEGY= 93ONTO= 94VERBOSE= 95OK_TO_SKIP_PRE_REBASE= 96REBASE_ROOT= 97 98GIT_CHERRY_PICK_HELP=" After resolving the conflicts, 99mark the corrected paths with 'git add <paths>', and 100run 'git rebase --continue'" 101export GIT_CHERRY_PICK_HELP 102 103warn () { 104echo"$*">&2 105} 106 107output () { 108case"$VERBOSE"in 109'') 110 output=$("$@" 2>&1 ) 111 status=$? 112test$status!=0&&printf"%s\n""$output" 113return$status 114;; 115*) 116"$@" 117;; 118esac 119} 120 121run_pre_rebase_hook () { 122iftest -z"$OK_TO_SKIP_PRE_REBASE"&& 123test -x"$GIT_DIR/hooks/pre-rebase" 124then 125"$GIT_DIR/hooks/pre-rebase"${1+"$@"}|| { 126echo>&2"The pre-rebase hook refused to rebase." 127exit1 128} 129fi 130} 131 132require_clean_work_tree () { 133# test if working tree is dirty 134 git rev-parse --verify HEAD > /dev/null && 135 git update-index --ignore-submodules --refresh&& 136 git diff-files --quiet --ignore-submodules&& 137 git diff-index --cached --quiet HEAD --ignore-submodules --|| 138 die "Working tree is dirty" 139} 140 141ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION" 142 143comment_for_reflog () { 144case"$ORIG_REFLOG_ACTION"in 145''|rebase*) 146 GIT_REFLOG_ACTION="rebase -i ($1)" 147export GIT_REFLOG_ACTION 148;; 149esac 150} 151 152last_count= 153mark_action_done () { 154sed-e1q <"$TODO">>"$DONE" 155sed-e1d <"$TODO">>"$TODO".new 156mv-f"$TODO".new "$TODO" 157 count=$(sane_grep -c '^[^#]' < "$DONE") 158 total=$(($count+$(sane_grep -c '^[^#]' < "$TODO"))) 159iftest"$last_count"!="$count" 160then 161 last_count=$count 162printf"Rebasing (%d/%d)\r"$count $total 163test -z"$VERBOSE"||echo 164fi 165} 166 167make_patch () { 168 sha1_and_parents="$(git rev-list --parents -1 "$1")" 169case"$sha1_and_parents"in 170 ?*' '?*' '?*) 171 git diff--cc$sha1_and_parents 172;; 173 ?*' '?*) 174 git diff-tree -p"$1^!" 175;; 176*) 177echo"Root commit" 178;; 179esac>"$DOTEST"/patch 180test -f"$MSG"|| 181 git cat-file commit "$1"|sed"1,/^$/d">"$MSG" 182test -f"$AUTHOR_SCRIPT"|| 183 get_author_ident_from_commit "$1">"$AUTHOR_SCRIPT" 184} 185 186die_with_patch () { 187 make_patch "$1" 188 git rerere 189 die "$2" 190} 191 192die_abort () { 193rm-rf"$DOTEST" 194 die "$1" 195} 196 197has_action () { 198 sane_grep '^[^#]'"$1">/dev/null 199} 200 201pick_one () { 202 no_ff= 203case"$1"in-n) sha1=$2; no_ff=t ;; *) sha1=$1;;esac 204 output git rev-parse --verify$sha1|| die "Invalid commit name:$sha1" 205test -d"$REWRITTEN"&& 206 pick_one_preserving_merges "$@"&&return 207iftest -n"$REBASE_ROOT" 208then 209 output git cherry-pick"$@" 210return 211fi 212 parent_sha1=$(git rev-parse --verify $sha1^)|| 213 die "Could not get the parent of$sha1" 214 current_sha1=$(git rev-parse --verify HEAD) 215iftest -z"$no_ff"-a"$current_sha1"="$parent_sha1" 216then 217 output git reset--hard$sha1 218 output warn Fast-forward to $(git rev-parse --short $sha1) 219else 220 output git cherry-pick"$@" 221fi 222} 223 224pick_one_preserving_merges () { 225 fast_forward=t 226case"$1"in 227-n) 228 fast_forward=f 229 sha1=$2 230;; 231*) 232 sha1=$1 233;; 234esac 235 sha1=$(git rev-parse $sha1) 236 237iftest -f"$DOTEST"/current-commit 238then 239iftest"$fast_forward"= t 240then 241cat"$DOTEST"/current-commit|whileread current_commit 242do 243 git rev-parse HEAD >"$REWRITTEN"/$current_commit 244done 245rm"$DOTEST"/current-commit|| 246 die "Cannot write current commit's replacement sha1" 247fi 248fi 249 250echo$sha1>>"$DOTEST"/current-commit 251 252# rewrite parents; if none were rewritten, we can fast-forward. 253 new_parents= 254 pend="$(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)" 255iftest"$pend"=" " 256then 257 pend=" root" 258fi 259while["$pend"!=""] 260do 261 p=$(expr "$pend" : ' \([^ ]*\)') 262 pend="${pend# $p}" 263 264 if test -f "$REWRITTEN"/$p 265 then 266 new_p=$(cat "$REWRITTEN"/$p) 267 268 # If the todo reordered commits, and our parent is marked for 269 # rewriting, but hasn't been gotten to yet, assume the user meant to 270# drop it on top of the current HEAD 271iftest -z"$new_p" 272then 273 new_p=$(git rev-parse HEAD) 274fi 275 276test$p!=$new_p&& fast_forward=f 277case"$new_parents"in 278*$new_p*) 279;;# do nothing; that parent is already there 280*) 281 new_parents="$new_parents$new_p" 282;; 283esac 284else 285iftest -f"$DROPPED"/$p 286then 287 fast_forward=f 288 replacement="$(cat "$DROPPED"/$p)" 289test -z"$replacement"&& replacement=root 290 pend="$replacement$pend" 291else 292 new_parents="$new_parents$p" 293fi 294fi 295done 296case$fast_forwardin 297 t) 298 output warn "Fast-forward to$sha1" 299 output git reset--hard$sha1|| 300 die "Cannot fast-forward to$sha1" 301;; 302 f) 303 first_parent=$(expr "$new_parents" : ' \([^ ]*\)') 304 305 if [ "$1" != "-n" ] 306 then 307 # detach HEAD to current parent 308 output git checkout$first_parent2> /dev/null || 309 die "Cannot move HEAD to$first_parent" 310 fi 311 312 case "$new_parents" in 313 ''*''*) 314 test "a$1" = a-n && die "Refusing to squash a merge:$sha1" 315 316 # redo merge 317 author_script=$(get_author_ident_from_commit $sha1) 318 eval "$author_script" 319 msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" 320 # No point in merging the first parent, that's HEAD 321 new_parents=${new_parents# $first_parent} 322if! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ 323 GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ 324 GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ 325 output git merge $STRATEGY-m"$msg" \ 326$new_parents 327then 328printf"%s\n""$msg">"$GIT_DIR"/MERGE_MSG 329 die_with_patch $sha1"Error redoing merge$sha1" 330fi 331;; 332*) 333 output git cherry-pick"$@"|| 334 die_with_patch $sha1"Could not pick$sha1" 335;; 336esac 337;; 338esac 339} 340 341nth_string () { 342case"$1"in 343*1[0-9]|*[04-9])echo"$1"th;; 344*1)echo"$1"st;; 345*2)echo"$1"nd;; 346*3)echo"$1"rd;; 347esac 348} 349 350make_squash_message () { 351iftest -f"$SQUASH_MSG";then 352# We want to be careful about matching only the commit 353# message comment lines generated by this function. 354# "[snrt][tdh]" matches the nth_string endings. 355 COUNT=$(($(sed -n "s/^# Th[^0-9]*\([1-9][0-9]*\)[snrt][tdh] commit message.*:/\1/p" \ 356 < "$SQUASH_MSG" | sed -ne '$p')+1)) 357 echo "# This is a combination of $COUNT commits." 358sed-e1d -e'2,/^./{ 359 /^$/d 360 }'<"$SQUASH_MSG" 361else 362 COUNT=2 363echo"# This is a combination of two commits." 364echo"# The first commit's message is:" 365echo 366 git cat-file commit HEAD |sed-e'1,/^$/d' 367fi 368case$1in 369 squash) 370echo 371echo"# This is the$(nth_string $COUNT)commit message:" 372echo 373 git cat-file commit $2|sed-e'1,/^$/d' 374;; 375 fixup) 376echo 377echo"# The$(nth_string $COUNT)commit message will be skipped:" 378echo 379# Comment the lines of the commit message out using 380# "# " rather than "# " to make them less likely to 381# confuse the sed regexp above. 382 git cat-file commit $2|sed-e'1,/^$/d'-e's/^/# /' 383;; 384esac 385} 386 387peek_next_command () { 388sed-n -e"/^#/d"-e"/^$/d"-e"s/ .*//p"-e"q"<"$TODO" 389} 390 391do_next () { 392rm-f"$MSG""$AUTHOR_SCRIPT""$AMEND"||exit 393read command sha1 rest <"$TODO" 394case"$command"in 395'#'*|''|noop) 396 mark_action_done 397;; 398 pick|p) 399 comment_for_reflog pick 400 401 mark_action_done 402 pick_one $sha1|| 403 die_with_patch $sha1"Could not apply$sha1...$rest" 404;; 405 reword|r) 406 comment_for_reflog reword 407 408 mark_action_done 409 pick_one $sha1|| 410 die_with_patch $sha1"Could not apply$sha1...$rest" 411 git commit --amend 412;; 413 edit|e) 414 comment_for_reflog edit 415 416 mark_action_done 417 pick_one $sha1|| 418 die_with_patch $sha1"Could not apply$sha1...$rest" 419 make_patch $sha1 420 git rev-parse --verify HEAD >"$AMEND" 421 warn "Stopped at$sha1...$rest" 422 warn "You can amend the commit now, with" 423 warn 424 warn " git commit --amend" 425 warn 426 warn "Once you are satisfied with your changes, run" 427 warn 428 warn " git rebase --continue" 429 warn 430exit0 431;; 432 squash|s|fixup|f) 433case"$command"in 434 squash|s) 435 squash_style=squash 436;; 437 fixup|f) 438 squash_style=fixup 439;; 440esac 441 comment_for_reflog $squash_style 442 443test -f"$DONE"&& has_action "$DONE"|| 444 die "Cannot '$squash_style' without a previous commit" 445 446 mark_action_done 447 make_squash_message $squash_style $sha1>"$MSG" 448 failed=f 449 author_script=$(get_author_ident_from_commit HEAD) 450 output git reset--soft HEAD^ 451 pick_one -n$sha1|| failed=t 452case"$(peek_next_command)"in 453 squash|s|fixup|f) 454 USE_OUTPUT=output 455 MSG_OPT=-F 456 EDIT_OR_FILE="$MSG" 457cp"$MSG""$SQUASH_MSG" 458;; 459*) 460 USE_OUTPUT= 461 MSG_OPT= 462 EDIT_OR_FILE=-e 463rm-f"$SQUASH_MSG"||exit 464cp"$MSG""$GIT_DIR"/SQUASH_MSG 465rm-f"$GIT_DIR"/MERGE_MSG ||exit 466;; 467esac 468echo"$author_script">"$AUTHOR_SCRIPT" 469iftest$failed= f 470then 471# This is like --amend, but with a different message 472eval"$author_script" 473 GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ 474 GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ 475 GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ 476$USE_OUTPUT git commit --no-verify \ 477$MSG_OPT"$EDIT_OR_FILE"|| failed=t 478fi 479iftest$failed= t 480then 481cp"$MSG""$GIT_DIR"/MERGE_MSG 482 warn 483 warn "Could not apply$sha1...$rest" 484 die_with_patch $sha1"" 485fi 486;; 487*) 488 warn "Unknown command:$command$sha1$rest" 489if git rev-parse --verify -q"$sha1">/dev/null 490then 491 die_with_patch $sha1"Please fix this in the file$TODO." 492else 493 die "Please fix this in the file$TODO." 494fi 495;; 496esac 497test -s"$TODO"&&return 498 499 comment_for_reflog finish && 500 HEADNAME=$(cat "$DOTEST"/head-name)&& 501 OLDHEAD=$(cat "$DOTEST"/head)&& 502 SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && 503 NEWHEAD=$(git rev-parse HEAD)&& 504case$HEADNAMEin 505 refs/*) 506 message="$GIT_REFLOG_ACTION:$HEADNAMEonto$SHORTONTO"&& 507 git update-ref -m"$message"$HEADNAME $NEWHEAD $OLDHEAD&& 508 git symbolic-ref HEAD $HEADNAME 509;; 510esac&& { 511test!-f"$DOTEST"/verbose || 512 git diff-tree --stat$(cat "$DOTEST"/head)..HEAD 513} && 514rm-rf"$DOTEST"&& 515 git gc --auto&& 516 warn "Successfully rebased and updated$HEADNAME." 517 518exit 519} 520 521do_rest () { 522while: 523do 524 do_next 525done 526} 527 528# skip picking commits whose parents are unchanged 529skip_unnecessary_picks () { 530 fd=3 531whileread command sha1 rest 532do 533# fd=3 means we skip the command 534case"$fd,$command,$(git rev-parse --verify --quiet $sha1^)"in 5353,pick,"$ONTO"*|3,p,"$ONTO"*) 536# pick a commit whose parent is current $ONTO -> skip 537 ONTO=$sha1 538;; 5393,#*|3,,*) 540# copy comments 541;; 542*) 543 fd=1 544;; 545esac 546echo"$command${sha1:+ }$sha1${rest:+ }$rest">&$fd 547done<"$TODO">"$TODO.new"3>>"$DONE"&& 548mv-f"$TODO".new "$TODO"|| 549 die "Could not skip unnecessary pick commands" 550} 551 552# check if no other options are set 553is_standalone () { 554test$#-eq2-a"$2"='--'&& 555test -z"$ONTO"&& 556test -z"$PRESERVE_MERGES"&& 557test -z"$STRATEGY"&& 558test -z"$VERBOSE" 559} 560 561get_saved_options () { 562test -d"$REWRITTEN"&& PRESERVE_MERGES=t 563test -f"$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" 564test -f"$DOTEST"/verbose && VERBOSE=t 565test -f"$DOTEST"/rebase-root&& REBASE_ROOT=t 566} 567 568whiletest$#!=0 569do 570case"$1"in 571--no-verify) 572 OK_TO_SKIP_PRE_REBASE=yes 573;; 574--verify) 575;; 576--continue) 577 is_standalone "$@"|| usage 578 get_saved_options 579 comment_for_reflog continue 580 581test -d"$DOTEST"|| die "No interactive rebase running" 582 583# Sanity check 584 git rev-parse --verify HEAD >/dev/null || 585 die "Cannot read HEAD" 586 git update-index --ignore-submodules --refresh&& 587 git diff-files --quiet --ignore-submodules|| 588 die "Working tree is dirty" 589 590# do we have anything to commit? 591if git diff-index --cached --quiet --ignore-submodules HEAD -- 592then 593: Nothing to commit -- skip this 594else 595 . "$AUTHOR_SCRIPT"|| 596 die "Cannot find the author identity" 597 amend= 598iftest -f"$AMEND" 599then 600 amend=$(git rev-parse --verify HEAD) 601test"$amend"=$(cat "$AMEND")|| 602 die "\ 603You have uncommitted changes in your working tree. Please, commit them 604first and then run 'git rebase --continue' again." 605 git reset--soft HEAD^ || 606 die "Cannot rewind the HEAD" 607fi 608export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE && 609 git commit --no-verify -F"$MSG"-e|| { 610test -n"$amend"&& git reset--soft$amend 611 die "Could not commit staged changes." 612} 613fi 614 615 require_clean_work_tree 616 do_rest 617;; 618--abort) 619 is_standalone "$@"|| usage 620 get_saved_options 621 comment_for_reflog abort 622 623 git rerere clear 624test -d"$DOTEST"|| die "No interactive rebase running" 625 626 HEADNAME=$(cat "$DOTEST"/head-name) 627 HEAD=$(cat "$DOTEST"/head) 628case$HEADNAMEin 629 refs/*) 630 git symbolic-ref HEAD $HEADNAME 631;; 632esac&& 633 output git reset--hard$HEAD&& 634rm-rf"$DOTEST" 635exit 636;; 637--skip) 638 is_standalone "$@"|| usage 639 get_saved_options 640 comment_for_reflog skip 641 642 git rerere clear 643test -d"$DOTEST"|| die "No interactive rebase running" 644 645 output git reset--hard&& do_rest 646;; 647-s) 648case"$#,$1"in 649*,*=*) 650 STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 651 1,*) 652 usage ;; 653 *) 654 STRATEGY="-s$2" 655 shift ;; 656 esac 657 ;; 658 -m) 659 # we use merge anyway 660 ;; 661 -v) 662 VERBOSE=t 663 ;; 664 -p) 665 PRESERVE_MERGES=t 666 ;; 667 -i) 668 # yeah, we know 669 ;; 670 --root) 671 REBASE_ROOT=t 672 ;; 673 --onto) 674 shift 675 ONTO=$(git rev-parse --verify "$1")|| 676 die "Does not point to a valid commit:$1" 677 ;; 678 --) 679 shift 680 test -z "$REBASE_ROOT" -a$#-ge 1 -a$#-le 2 || 681 test ! -z "$REBASE_ROOT" -a$#-le 1 || usage 682 test -d "$DOTEST" && 683 die "Interactive rebase already started" 684 685 git var GIT_COMMITTER_IDENT >/dev/null || 686 die "You need to set your committer info first" 687 688 if test -z "$REBASE_ROOT" 689 then 690 UPSTREAM_ARG="$1" 691 UPSTREAM=$(git rev-parse --verify "$1")|| die "Invalid base" 692 test -z "$ONTO" && ONTO=$UPSTREAM 693 shift 694 else 695 UPSTREAM= 696 UPSTREAM_ARG=--root 697 test -z "$ONTO" && 698 die "You must specify --onto when using --root" 699 fi 700 run_pre_rebase_hook "$UPSTREAM_ARG" "$@" 701 702 comment_for_reflog start 703 704 require_clean_work_tree 705 706 if test ! -z "$1" 707 then 708 output git show-ref --verify --quiet "refs/heads/$1" || 709 die "Invalid branchname:$1" 710 output git checkout "$1" || 711 die "Could not checkout$1" 712 fi 713 714 HEAD=$(git rev-parse --verify HEAD)|| die "No HEAD?" 715 mkdir "$DOTEST" || die "Could not create temporary$DOTEST" 716 717 : > "$DOTEST"/interactive || die "Could not mark as interactive" 718 git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || 719 echo "detached HEAD" > "$DOTEST"/head-name 720 721 echo$HEAD> "$DOTEST"/head 722 case "$REBASE_ROOT" in 723 '') 724 rm -f "$DOTEST"/rebase-root ;; 725 *) 726 : >"$DOTEST"/rebase-root ;; 727 esac 728 echo$ONTO> "$DOTEST"/onto 729 test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy 730 test t = "$VERBOSE" && : > "$DOTEST"/verbose 731 if test t = "$PRESERVE_MERGES" 732 then 733 if test -z "$REBASE_ROOT" 734 then 735 mkdir "$REWRITTEN" && 736 for c in$(git merge-base --all $HEAD $UPSTREAM) 737 do 738 echo$ONTO> "$REWRITTEN"/$c|| 739 die "Could not init rewritten commits" 740 done 741 else 742 mkdir "$REWRITTEN" && 743 echo$ONTO> "$REWRITTEN"/root || 744 die "Could not init rewritten commits" 745 fi 746 # No cherry-pick because our first pass is to determine 747 # parents to rewrite and skipping dropped commits would 748 # prematurely end our probe 749 MERGES_OPTION= 750 first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" 751 else 752 MERGES_OPTION="--no-merges --cherry-pick" 753 fi 754 755 SHORTHEAD=$(git rev-parse --short $HEAD) 756 SHORTONTO=$(git rev-parse --short $ONTO) 757 if test -z "$REBASE_ROOT" 758 # this is now equivalent to ! -z "$UPSTREAM" 759 then 760 SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) 761 REVISIONS=$UPSTREAM...$HEAD 762 SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD 763 else 764 REVISIONS=$ONTO...$HEAD 765 SHORTREVISIONS=$SHORTHEAD 766 fi 767 git rev-list$MERGES_OPTION--pretty=oneline --abbrev-commit \ 768 --abbrev=7 --reverse --left-right --topo-order \ 769$REVISIONS| \ 770 sed -n "s/^>//p" | while read shortsha1 rest 771 do 772 if test t != "$PRESERVE_MERGES" 773 then 774 echo "pick$shortsha1$rest" >> "$TODO" 775 else 776 sha1=$(git rev-parse $shortsha1) 777 if test -z "$REBASE_ROOT" 778 then 779 preserve=t 780 for p in$(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) 781 do 782 if test -f "$REWRITTEN"/$p-a \($p!=$ONTO-o$sha1=$first_after_upstream\) 783 then 784 preserve=f 785 fi 786 done 787 else 788 preserve=f 789 fi 790 if test f = "$preserve" 791 then 792 touch "$REWRITTEN"/$sha1 793 echo "pick$shortsha1$rest" >> "$TODO" 794 fi 795 fi 796 done 797 798 # Watch for commits that been dropped by --cherry-pick 799 if test t = "$PRESERVE_MERGES" 800 then 801 mkdir "$DROPPED" 802 # Save all non-cherry-picked changes 803 git rev-list$REVISIONS--left-right --cherry-pick | \ 804 sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks 805 # Now all commits and note which ones are missing in 806 # not-cherry-picks and hence being dropped 807 git rev-list$REVISIONS| 808 while read rev 809 do 810 if test -f "$REWRITTEN"/$rev-a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = "" 811 then 812 # Use -f2 because if rev-list is telling us this commit is 813 # not worthwhile, we don't want to track its multiple heads, 814# just the history of its first-parent for others that will 815# be rebasing on top of it 816 git rev-list --parents -1$rev| cut -d' '-s -f2>"$DROPPED"/$rev 817 short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) 818 sane_grep -v"^[a-z][a-z]*$short"<"$TODO">"${TODO}2";mv"${TODO}2""$TODO" 819rm"$REWRITTEN"/$rev 820fi 821done 822fi 823 824test -s"$TODO"||echo noop >>"$TODO" 825cat>>"$TODO"<< EOF 826 827# Rebase$SHORTREVISIONSonto$SHORTONTO 828# 829# Commands: 830# p, pick = use commit 831# r, reword = use commit, but edit the commit message 832# e, edit = use commit, but stop for amending 833# s, squash = use commit, but meld into previous commit 834# f, fixup = like "squash", but discard this commit's log message 835# 836# If you remove a line here THAT COMMIT WILL BE LOST. 837# However, if you remove everything, the rebase will be aborted. 838# 839EOF 840 841 has_action "$TODO"|| 842 die_abort "Nothing to do" 843 844cp"$TODO""$TODO".backup 845 git_editor "$TODO"|| 846 die "Could not execute editor" 847 848 has_action "$TODO"|| 849 die_abort "Nothing to do" 850 851test -d"$REWRITTEN"|| skip_unnecessary_picks 852 853 git update-ref ORIG_HEAD $HEAD 854 output git checkout $ONTO&& do_rest 855;; 856esac 857shift 858done