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