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 no_ff= 234case"$1"in-n) sha1=$2; no_ff=t ;; *) 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 parent_sha1=$(git rev-parse --verify $sha1^)|| 244 die "Could not get the parent of$sha1" 245 current_sha1=$(git rev-parse --verify HEAD) 246iftest -z"$no_ff"&&test"$current_sha1"="$parent_sha1" 247then 248 output git reset--hard$sha1 249 output warn Fast-forward to $(git rev-parse --short $sha1) 250else 251 output git cherry-pick"$@" 252fi 253} 254 255pick_one_preserving_merges () { 256 fast_forward=t 257case"$1"in 258-n) 259 fast_forward=f 260 sha1=$2 261;; 262*) 263 sha1=$1 264;; 265esac 266 sha1=$(git rev-parse $sha1) 267 268iftest -f"$DOTEST"/current-commit 269then 270iftest"$fast_forward"= t 271then 272cat"$DOTEST"/current-commit|whileread current_commit 273do 274 git rev-parse HEAD >"$REWRITTEN"/$current_commit 275done 276rm"$DOTEST"/current-commit|| 277 die "Cannot write current commit's replacement sha1" 278fi 279fi 280 281echo$sha1>>"$DOTEST"/current-commit 282 283# rewrite parents; if none were rewritten, we can fast-forward. 284 new_parents= 285 pend="$(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)" 286iftest"$pend"=" " 287then 288 pend=" root" 289fi 290while["$pend"!=""] 291do 292 p=$(expr "$pend" : ' \([^ ]*\)') 293 pend="${pend# $p}" 294 295 if test -f "$REWRITTEN"/$p 296 then 297 new_p=$(cat "$REWRITTEN"/$p) 298 299 # If the todo reordered commits, and our parent is marked for 300 # rewriting, but hasn't been gotten to yet, assume the user meant to 301# drop it on top of the current HEAD 302iftest -z"$new_p" 303then 304 new_p=$(git rev-parse HEAD) 305fi 306 307test$p!=$new_p&& fast_forward=f 308case"$new_parents"in 309*$new_p*) 310;;# do nothing; that parent is already there 311*) 312 new_parents="$new_parents$new_p" 313;; 314esac 315else 316iftest -f"$DROPPED"/$p 317then 318 fast_forward=f 319 replacement="$(cat "$DROPPED"/$p)" 320test -z"$replacement"&& replacement=root 321 pend="$replacement$pend" 322else 323 new_parents="$new_parents$p" 324fi 325fi 326done 327case$fast_forwardin 328 t) 329 output warn "Fast-forward to$sha1" 330 output git reset--hard$sha1|| 331 die "Cannot fast-forward to$sha1" 332;; 333 f) 334 first_parent=$(expr "$new_parents" : ' \([^ ]*\)') 335 336 if [ "$1" != "-n" ] 337 then 338 # detach HEAD to current parent 339 output git checkout$first_parent2> /dev/null || 340 die "Cannot move HEAD to$first_parent" 341 fi 342 343 case "$new_parents" in 344 ''*''*) 345 test "a$1" = a-n && die "Refusing to squash a merge:$sha1" 346 347 # redo merge 348 author_script=$(get_author_ident_from_commit $sha1) 349 eval "$author_script" 350 msg="$(commit_message $sha1)" 351 # No point in merging the first parent, that's HEAD 352 new_parents=${new_parents# $first_parent} 353if! do_with_author output \ 354 git merge $STRATEGY-m"$msg"$new_parents 355then 356printf"%s\n""$msg">"$GIT_DIR"/MERGE_MSG 357 die_with_patch $sha1"Error redoing merge$sha1" 358fi 359echo"$sha1$(git rev-parse HEAD^0)">>"$REWRITTEN_LIST" 360;; 361*) 362 output git cherry-pick"$@"|| 363 die_with_patch $sha1"Could not pick$sha1" 364;; 365esac 366;; 367esac 368} 369 370nth_string () { 371case"$1"in 372*1[0-9]|*[04-9])echo"$1"th;; 373*1)echo"$1"st;; 374*2)echo"$1"nd;; 375*3)echo"$1"rd;; 376esac 377} 378 379update_squash_messages () { 380iftest -f"$SQUASH_MSG";then 381mv"$SQUASH_MSG""$SQUASH_MSG".bak ||exit 382 COUNT=$(($(sed-n \ 383-e"1s/^# This is a combination of \(.*\) commits\./\1/p" \ 384-e"q"<"$SQUASH_MSG".bak)+1)) 385{ 386echo"# This is a combination of$COUNTcommits." 387sed-e1d -e'2,/^./{ 388 /^$/d 389 }'<"$SQUASH_MSG".bak 390} >"$SQUASH_MSG" 391else 392 commit_message HEAD >"$FIXUP_MSG"|| die "Cannot write$FIXUP_MSG" 393 COUNT=2 394{ 395echo"# This is a combination of 2 commits." 396echo"# The first commit's message is:" 397echo 398cat"$FIXUP_MSG" 399} >"$SQUASH_MSG" 400fi 401case$1in 402 squash) 403rm-f"$FIXUP_MSG" 404echo 405echo"# This is the$(nth_string $COUNT)commit message:" 406echo 407 commit_message $2 408;; 409 fixup) 410echo 411echo"# The$(nth_string $COUNT)commit message will be skipped:" 412echo 413 commit_message $2|sed-e's/^/# /' 414;; 415esac>>"$SQUASH_MSG" 416} 417 418peek_next_command () { 419sed-n -e"/^#/d"-e'/^$/d'-e"s/ .*//p"-e"q"<"$TODO" 420} 421 422# A squash/fixup has failed. Prepare the long version of the squash 423# commit message, then die_with_patch. This code path requires the 424# user to edit the combined commit message for all commits that have 425# been squashed/fixedup so far. So also erase the old squash 426# messages, effectively causing the combined commit to be used as the 427# new basis for any further squash/fixups. Args: sha1 rest 428die_failed_squash() { 429mv"$SQUASH_MSG""$MSG"||exit 430rm-f"$FIXUP_MSG" 431cp"$MSG""$GIT_DIR"/MERGE_MSG ||exit 432 warn 433 warn "Could not apply$1...$2" 434 die_with_patch $1"" 435} 436 437flush_rewritten_pending() { 438test -s"$REWRITTEN_PENDING"||return 439 newsha1="$(git rev-parse HEAD^0)" 440sed"s/$/$newsha1/"<"$REWRITTEN_PENDING">>"$REWRITTEN_LIST" 441rm-f"$REWRITTEN_PENDING" 442} 443 444record_in_rewritten() { 445 oldsha1="$(git rev-parse $1)" 446echo"$oldsha1">>"$REWRITTEN_PENDING" 447 448case"$(peek_next_command)"in 449 squash|s|fixup|f) 450;; 451*) 452 flush_rewritten_pending 453;; 454esac 455} 456 457do_next () { 458rm-f"$MSG""$AUTHOR_SCRIPT""$AMEND"||exit 459read command sha1 rest <"$TODO" 460case"$command"in 461'#'*|''|noop) 462 mark_action_done 463;; 464 pick|p) 465 comment_for_reflog pick 466 467 mark_action_done 468 pick_one $sha1|| 469 die_with_patch $sha1"Could not apply$sha1...$rest" 470 record_in_rewritten $sha1 471;; 472 reword|r) 473 comment_for_reflog reword 474 475 mark_action_done 476 pick_one $sha1|| 477 die_with_patch $sha1"Could not apply$sha1...$rest" 478 git commit --amend --no-post-rewrite 479 record_in_rewritten $sha1 480;; 481 edit|e) 482 comment_for_reflog edit 483 484 mark_action_done 485 pick_one $sha1|| 486 die_with_patch $sha1"Could not apply$sha1...$rest" 487echo"$1">"$DOTEST"/stopped-sha 488 make_patch $sha1 489 git rev-parse --verify HEAD >"$AMEND" 490 warn "Stopped at$sha1...$rest" 491 warn "You can amend the commit now, with" 492 warn 493 warn " git commit --amend" 494 warn 495 warn "Once you are satisfied with your changes, run" 496 warn 497 warn " git rebase --continue" 498 warn 499exit0 500;; 501 squash|s|fixup|f) 502case"$command"in 503 squash|s) 504 squash_style=squash 505;; 506 fixup|f) 507 squash_style=fixup 508;; 509esac 510 comment_for_reflog $squash_style 511 512test -f"$DONE"&& has_action "$DONE"|| 513 die "Cannot '$squash_style' without a previous commit" 514 515 mark_action_done 516 update_squash_messages $squash_style $sha1 517 author_script=$(get_author_ident_from_commit HEAD) 518echo"$author_script">"$AUTHOR_SCRIPT" 519eval"$author_script" 520 output git reset--soft HEAD^ 521 pick_one -n$sha1|| die_failed_squash $sha1"$rest" 522case"$(peek_next_command)"in 523 squash|s|fixup|f) 524# This is an intermediate commit; its message will only be 525# used in case of trouble. So use the long version: 526 do_with_author output git commit --no-verify -F"$SQUASH_MSG"|| 527 die_failed_squash $sha1"$rest" 528;; 529*) 530# This is the final command of this squash/fixup group 531iftest -f"$FIXUP_MSG" 532then 533 do_with_author git commit --no-verify -F"$FIXUP_MSG"|| 534 die_failed_squash $sha1"$rest" 535else 536cp"$SQUASH_MSG""$GIT_DIR"/SQUASH_MSG ||exit 537rm-f"$GIT_DIR"/MERGE_MSG 538 do_with_author git commit --no-verify -e|| 539 die_failed_squash $sha1"$rest" 540fi 541rm-f"$SQUASH_MSG""$FIXUP_MSG" 542;; 543esac 544 record_in_rewritten $sha1 545;; 546*) 547 warn "Unknown command:$command$sha1$rest" 548if git rev-parse --verify -q"$sha1">/dev/null 549then 550 die_with_patch $sha1"Please fix this in the file$TODO." 551else 552 die "Please fix this in the file$TODO." 553fi 554;; 555esac 556test -s"$TODO"&&return 557 558 comment_for_reflog finish && 559 HEADNAME=$(cat "$DOTEST"/head-name)&& 560 OLDHEAD=$(cat "$DOTEST"/head)&& 561 SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && 562 NEWHEAD=$(git rev-parse HEAD)&& 563case$HEADNAMEin 564 refs/*) 565 message="$GIT_REFLOG_ACTION:$HEADNAMEonto$SHORTONTO"&& 566 git update-ref -m"$message"$HEADNAME $NEWHEAD $OLDHEAD&& 567 git symbolic-ref HEAD $HEADNAME 568;; 569esac&& { 570test!-f"$DOTEST"/verbose || 571 git diff-tree --stat$(cat "$DOTEST"/head)..HEAD 572} && 573{ 574 git notes copy --for-rewrite=rebase <"$REWRITTEN_LIST"|| 575 true # we don't care if this copying failed 576} && 577iftest -x"$GIT_DIR"/hooks/post-rewrite&& 578test -s"$REWRITTEN_LIST";then 579"$GIT_DIR"/hooks/post-rewrite rebase <"$REWRITTEN_LIST" 580 true # we don't care if this hook failed 581fi&& 582rm-rf"$DOTEST"&& 583 git gc --auto&& 584 warn "Successfully rebased and updated$HEADNAME." 585 586exit 587} 588 589do_rest () { 590while: 591do 592 do_next 593done 594} 595 596# skip picking commits whose parents are unchanged 597skip_unnecessary_picks () { 598 fd=3 599whileread command sha1 rest 600do 601# fd=3 means we skip the command 602case"$fd,$command,$(git rev-parse --verify --quiet $sha1^)"in 6033,pick,"$ONTO"*|3,p,"$ONTO"*) 604# pick a commit whose parent is current $ONTO -> skip 605 ONTO=$sha1 606;; 6073,#*|3,,*) 608# copy comments 609;; 610*) 611 fd=1 612;; 613esac 614echo"$command${sha1:+ }$sha1${rest:+ }$rest">&$fd 615done<"$TODO">"$TODO.new"3>>"$DONE"&& 616mv-f"$TODO".new "$TODO"&& 617case"$(peek_next_command)"in 618 squash|s|fixup|f) 619 record_in_rewritten "$ONTO" 620;; 621esac|| 622 die "Could not skip unnecessary pick commands" 623} 624 625# check if no other options are set 626is_standalone () { 627test$#-eq2-a"$2"='--'&& 628test -z"$ONTO"&& 629test -z"$PRESERVE_MERGES"&& 630test -z"$STRATEGY"&& 631test -z"$VERBOSE" 632} 633 634get_saved_options () { 635test -d"$REWRITTEN"&& PRESERVE_MERGES=t 636test -f"$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" 637test -f"$DOTEST"/verbose && VERBOSE=t 638test -f"$DOTEST"/rebase-root&& REBASE_ROOT=t 639} 640 641# Rearrange the todo list that has both "pick sha1 msg" and 642# "pick sha1 fixup!/squash! msg" appears in it so that the latter 643# comes immediately after the former, and change "pick" to 644# "fixup"/"squash". 645rearrange_squash () { 646sed-n -e's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \ 647-e's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \ 648"$1">"$1.sq" 649test -s"$1.sq"||return 650 651 used= 652whileread pick sha1 message 653do 654case"$used"in 655*"$sha1"*)continue;; 656esac 657echo"$pick$sha1$message" 658whileread squash action msg 659do 660case"$message"in 661"$msg"*) 662echo"$action$squash$action!$msg" 663 used="$used$squash" 664;; 665esac 666done<"$1.sq" 667done>"$1.rearranged"<"$1" 668cat"$1.rearranged">"$1" 669rm-f"$1.sq""$1.rearranged" 670} 671 672LF=' 673' 674parse_onto () { 675case"$1"in 676*...*) 677if left=${1%...*} right=${1#*...}&& 678 onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD}) 679then 680case"$onto"in 681 ?*"$LF"?* |'') 682exit1;; 683esac 684echo"$onto" 685exit0 686fi 687esac 688 git rev-parse --verify"$1^0" 689} 690 691whiletest$#!=0 692do 693case"$1"in 694--no-verify) 695 OK_TO_SKIP_PRE_REBASE=yes 696;; 697--verify) 698;; 699--continue) 700 is_standalone "$@"|| usage 701 get_saved_options 702 comment_for_reflog continue 703 704test -d"$DOTEST"|| die "No interactive rebase running" 705 706# Sanity check 707 git rev-parse --verify HEAD >/dev/null || 708 die "Cannot read HEAD" 709 git update-index --ignore-submodules --refresh&& 710 git diff-files --quiet --ignore-submodules|| 711 die "Working tree is dirty" 712 713# do we have anything to commit? 714if git diff-index --cached --quiet --ignore-submodules HEAD -- 715then 716: Nothing to commit -- skip this 717else 718 . "$AUTHOR_SCRIPT"|| 719 die "Cannot find the author identity" 720 amend= 721iftest -f"$AMEND" 722then 723 amend=$(git rev-parse --verify HEAD) 724test"$amend"=$(cat "$AMEND")|| 725 die "\ 726You have uncommitted changes in your working tree. Please, commit them 727first and then run 'git rebase --continue' again." 728 git reset--soft HEAD^ || 729 die "Cannot rewind the HEAD" 730fi 731 do_with_author git commit --no-verify -F"$MSG"-e|| { 732test -n"$amend"&& git reset--soft$amend 733 die "Could not commit staged changes." 734} 735 record_in_rewritten "$(cat "$DOTEST"/stopped-sha)" 736fi 737 738 require_clean_work_tree 739 do_rest 740;; 741--abort) 742 is_standalone "$@"|| usage 743 get_saved_options 744 comment_for_reflog abort 745 746 git rerere clear 747test -d"$DOTEST"|| die "No interactive rebase running" 748 749 HEADNAME=$(cat "$DOTEST"/head-name) 750 HEAD=$(cat "$DOTEST"/head) 751case$HEADNAMEin 752 refs/*) 753 git symbolic-ref HEAD $HEADNAME 754;; 755esac&& 756 output git reset--hard$HEAD&& 757rm-rf"$DOTEST" 758exit 759;; 760--skip) 761 is_standalone "$@"|| usage 762 get_saved_options 763 comment_for_reflog skip 764 765 git rerere clear 766test -d"$DOTEST"|| die "No interactive rebase running" 767 768 output git reset--hard&& do_rest 769;; 770-s) 771case"$#,$1"in 772*,*=*) 773 STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 774 1,*) 775 usage ;; 776 *) 777 STRATEGY="-s$2" 778 shift ;; 779 esac 780 ;; 781 -m) 782 # we use merge anyway 783 ;; 784 -v) 785 VERBOSE=t 786 ;; 787 -p) 788 PRESERVE_MERGES=t 789 ;; 790 -i) 791 # yeah, we know 792 ;; 793 --root) 794 REBASE_ROOT=t 795 ;; 796 --autosquash) 797 AUTOSQUASH=t 798 ;; 799 --onto) 800 shift 801 ONTO=$(parse_onto "$1")|| 802 die "Does not point to a valid commit:$1" 803 ;; 804 --) 805 shift 806 test -z "$REBASE_ROOT" -a$#-ge 1 -a$#-le 2 || 807 test ! -z "$REBASE_ROOT" -a$#-le 1 || usage 808 test -d "$DOTEST" && 809 die "Interactive rebase already started" 810 811 git var GIT_COMMITTER_IDENT >/dev/null || 812 die "You need to set your committer info first" 813 814 if test -z "$REBASE_ROOT" 815 then 816 UPSTREAM_ARG="$1" 817 UPSTREAM=$(git rev-parse --verify "$1")|| die "Invalid base" 818 test -z "$ONTO" && ONTO=$UPSTREAM 819 shift 820 else 821 UPSTREAM= 822 UPSTREAM_ARG=--root 823 test -z "$ONTO" && 824 die "You must specify --onto when using --root" 825 fi 826 run_pre_rebase_hook "$UPSTREAM_ARG" "$@" 827 828 comment_for_reflog start 829 830 require_clean_work_tree 831 832 if test ! -z "$1" 833 then 834 output git show-ref --verify --quiet "refs/heads/$1" || 835 die "Invalid branchname:$1" 836 output git checkout "$1" || 837 die "Could not checkout$1" 838 fi 839 840 HEAD=$(git rev-parse --verify HEAD)|| die "No HEAD?" 841 mkdir "$DOTEST" || die "Could not create temporary$DOTEST" 842 843 : > "$DOTEST"/interactive || die "Could not mark as interactive" 844 git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || 845 echo "detached HEAD" > "$DOTEST"/head-name 846 847 echo$HEAD> "$DOTEST"/head 848 case "$REBASE_ROOT" in 849 '') 850 rm -f "$DOTEST"/rebase-root ;; 851 *) 852 : >"$DOTEST"/rebase-root ;; 853 esac 854 echo$ONTO> "$DOTEST"/onto 855 test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy 856 test t = "$VERBOSE" && : > "$DOTEST"/verbose 857 if test t = "$PRESERVE_MERGES" 858 then 859 if test -z "$REBASE_ROOT" 860 then 861 mkdir "$REWRITTEN" && 862 for c in$(git merge-base --all $HEAD $UPSTREAM) 863 do 864 echo$ONTO> "$REWRITTEN"/$c|| 865 die "Could not init rewritten commits" 866 done 867 else 868 mkdir "$REWRITTEN" && 869 echo$ONTO> "$REWRITTEN"/root || 870 die "Could not init rewritten commits" 871 fi 872 # No cherry-pick because our first pass is to determine 873 # parents to rewrite and skipping dropped commits would 874 # prematurely end our probe 875 MERGES_OPTION= 876 first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" 877 else 878 MERGES_OPTION="--no-merges --cherry-pick" 879 fi 880 881 SHORTHEAD=$(git rev-parse --short $HEAD) 882 SHORTONTO=$(git rev-parse --short $ONTO) 883 if test -z "$REBASE_ROOT" 884 # this is now equivalent to ! -z "$UPSTREAM" 885 then 886 SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) 887 REVISIONS=$UPSTREAM...$HEAD 888 SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD 889 else 890 REVISIONS=$ONTO...$HEAD 891 SHORTREVISIONS=$SHORTHEAD 892 fi 893 git rev-list$MERGES_OPTION--pretty=oneline --abbrev-commit \ 894 --abbrev=7 --reverse --left-right --topo-order \ 895$REVISIONS| \ 896 sed -n "s/^>//p" | while read shortsha1 rest 897 do 898 if test t != "$PRESERVE_MERGES" 899 then 900 echo "pick$shortsha1$rest" >> "$TODO" 901 else 902 sha1=$(git rev-parse $shortsha1) 903 if test -z "$REBASE_ROOT" 904 then 905 preserve=t 906 for p in$(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) 907 do 908 if test -f "$REWRITTEN"/$p-a \($p!=$ONTO-o$sha1=$first_after_upstream\) 909 then 910 preserve=f 911 fi 912 done 913 else 914 preserve=f 915 fi 916 if test f = "$preserve" 917 then 918 touch "$REWRITTEN"/$sha1 919 echo "pick$shortsha1$rest" >> "$TODO" 920 fi 921 fi 922 done 923 924 # Watch for commits that been dropped by --cherry-pick 925 if test t = "$PRESERVE_MERGES" 926 then 927 mkdir "$DROPPED" 928 # Save all non-cherry-picked changes 929 git rev-list$REVISIONS--left-right --cherry-pick | \ 930 sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks 931 # Now all commits and note which ones are missing in 932 # not-cherry-picks and hence being dropped 933 git rev-list$REVISIONS| 934 while read rev 935 do 936 if test -f "$REWRITTEN"/$rev-a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = "" 937 then 938 # Use -f2 because if rev-list is telling us this commit is 939 # not worthwhile, we don't want to track its multiple heads, 940# just the history of its first-parent for others that will 941# be rebasing on top of it 942 git rev-list --parents -1$rev| cut -d' '-s -f2>"$DROPPED"/$rev 943 short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) 944 sane_grep -v"^[a-z][a-z]*$short"<"$TODO">"${TODO}2";mv"${TODO}2""$TODO" 945rm"$REWRITTEN"/$rev 946fi 947done 948fi 949 950test -s"$TODO"||echo noop >>"$TODO" 951test -n"$AUTOSQUASH"&& rearrange_squash "$TODO" 952cat>>"$TODO"<< EOF 953 954# Rebase$SHORTREVISIONSonto$SHORTONTO 955# 956# Commands: 957# p, pick = use commit 958# r, reword = use commit, but edit the commit message 959# e, edit = use commit, but stop for amending 960# s, squash = use commit, but meld into previous commit 961# f, fixup = like "squash", but discard this commit's log message 962# 963# If you remove a line here THAT COMMIT WILL BE LOST. 964# However, if you remove everything, the rebase will be aborted. 965# 966EOF 967 968 has_action "$TODO"|| 969 die_abort "Nothing to do" 970 971cp"$TODO""$TODO".backup 972 git_editor "$TODO"|| 973 die_abort "Could not execute editor" 974 975 has_action "$TODO"|| 976 die_abort "Nothing to do" 977 978test -d"$REWRITTEN"|| skip_unnecessary_picks 979 980 git update-ref ORIG_HEAD $HEAD 981 output git checkout $ONTO&& do_rest 982;; 983esac 984shift 985done