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