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