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 GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ 227 GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ 228 GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ 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} && 573iftest -x"$GIT_DIR"/hooks/post-rewrite&& 574test -s"$REWRITTEN_LIST";then 575"$GIT_DIR"/hooks/post-rewrite rebase <"$REWRITTEN_LIST" 576 true # we don't care if this hook failed 577fi&& 578rm-rf"$DOTEST"&& 579 git gc --auto&& 580 warn "Successfully rebased and updated$HEADNAME." 581 582exit 583} 584 585do_rest () { 586while: 587do 588 do_next 589done 590} 591 592# skip picking commits whose parents are unchanged 593skip_unnecessary_picks () { 594 fd=3 595whileread command sha1 rest 596do 597# fd=3 means we skip the command 598case"$fd,$command,$(git rev-parse --verify --quiet $sha1^)"in 5993,pick,"$ONTO"*|3,p,"$ONTO"*) 600# pick a commit whose parent is current $ONTO -> skip 601 ONTO=$sha1 602;; 6033,#*|3,,*) 604# copy comments 605;; 606*) 607 fd=1 608;; 609esac 610echo"$command${sha1:+ }$sha1${rest:+ }$rest">&$fd 611done<"$TODO">"$TODO.new"3>>"$DONE"&& 612mv-f"$TODO".new "$TODO"&& 613case"$(peek_next_command)"in 614 squash|s|fixup|f) 615 record_in_rewritten "$ONTO" 616;; 617esac|| 618 die "Could not skip unnecessary pick commands" 619} 620 621# check if no other options are set 622is_standalone () { 623test$#-eq2-a"$2"='--'&& 624test -z"$ONTO"&& 625test -z"$PRESERVE_MERGES"&& 626test -z"$STRATEGY"&& 627test -z"$VERBOSE" 628} 629 630get_saved_options () { 631test -d"$REWRITTEN"&& PRESERVE_MERGES=t 632test -f"$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" 633test -f"$DOTEST"/verbose && VERBOSE=t 634test -f"$DOTEST"/rebase-root&& REBASE_ROOT=t 635} 636 637# Rearrange the todo list that has both "pick sha1 msg" and 638# "pick sha1 fixup!/squash! msg" appears in it so that the latter 639# comes immediately after the former, and change "pick" to 640# "fixup"/"squash". 641rearrange_squash () { 642sed-n -e's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \ 643-e's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \ 644"$1">"$1.sq" 645test -s"$1.sq"||return 646 647 used= 648whileread pick sha1 message 649do 650case"$used"in 651*"$sha1"*)continue;; 652esac 653echo"$pick$sha1$message" 654whileread squash action msg 655do 656case"$message"in 657"$msg"*) 658echo"$action$squash$action!$msg" 659 used="$used$squash" 660;; 661esac 662done<"$1.sq" 663done>"$1.rearranged"<"$1" 664cat"$1.rearranged">"$1" 665rm-f"$1.sq""$1.rearranged" 666} 667 668LF=' 669' 670parse_onto () { 671case"$1"in 672*...*) 673if left=${1%...*} right=${1#*...}&& 674 onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD}) 675then 676case"$onto"in 677 ?*"$LF"?* |'') 678exit1;; 679esac 680echo"$onto" 681exit0 682fi 683esac 684 git rev-parse --verify"$1^0" 685} 686 687whiletest$#!=0 688do 689case"$1"in 690--no-verify) 691 OK_TO_SKIP_PRE_REBASE=yes 692;; 693--verify) 694;; 695--continue) 696 is_standalone "$@"|| usage 697 get_saved_options 698 comment_for_reflog continue 699 700test -d"$DOTEST"|| die "No interactive rebase running" 701 702# Sanity check 703 git rev-parse --verify HEAD >/dev/null || 704 die "Cannot read HEAD" 705 git update-index --ignore-submodules --refresh&& 706 git diff-files --quiet --ignore-submodules|| 707 die "Working tree is dirty" 708 709# do we have anything to commit? 710if git diff-index --cached --quiet --ignore-submodules HEAD -- 711then 712: Nothing to commit -- skip this 713else 714 . "$AUTHOR_SCRIPT"|| 715 die "Cannot find the author identity" 716 amend= 717iftest -f"$AMEND" 718then 719 amend=$(git rev-parse --verify HEAD) 720test"$amend"=$(cat "$AMEND")|| 721 die "\ 722You have uncommitted changes in your working tree. Please, commit them 723first and then run 'git rebase --continue' again." 724 git reset--soft HEAD^ || 725 die "Cannot rewind the HEAD" 726fi 727 do_with_author git commit --no-verify -F"$MSG"-e|| { 728test -n"$amend"&& git reset--soft$amend 729 die "Could not commit staged changes." 730} 731 record_in_rewritten "$(cat "$DOTEST"/stopped-sha)" 732fi 733 734 require_clean_work_tree 735 do_rest 736;; 737--abort) 738 is_standalone "$@"|| usage 739 get_saved_options 740 comment_for_reflog abort 741 742 git rerere clear 743test -d"$DOTEST"|| die "No interactive rebase running" 744 745 HEADNAME=$(cat "$DOTEST"/head-name) 746 HEAD=$(cat "$DOTEST"/head) 747case$HEADNAMEin 748 refs/*) 749 git symbolic-ref HEAD $HEADNAME 750;; 751esac&& 752 output git reset--hard$HEAD&& 753rm-rf"$DOTEST" 754exit 755;; 756--skip) 757 is_standalone "$@"|| usage 758 get_saved_options 759 comment_for_reflog skip 760 761 git rerere clear 762test -d"$DOTEST"|| die "No interactive rebase running" 763 764 output git reset--hard&& do_rest 765;; 766-s) 767case"$#,$1"in 768*,*=*) 769 STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 770 1,*) 771 usage ;; 772 *) 773 STRATEGY="-s$2" 774 shift ;; 775 esac 776 ;; 777 -m) 778 # we use merge anyway 779 ;; 780 -v) 781 VERBOSE=t 782 ;; 783 -p) 784 PRESERVE_MERGES=t 785 ;; 786 -i) 787 # yeah, we know 788 ;; 789 --root) 790 REBASE_ROOT=t 791 ;; 792 --autosquash) 793 AUTOSQUASH=t 794 ;; 795 --onto) 796 shift 797 ONTO=$(parse_onto "$1")|| 798 die "Does not point to a valid commit:$1" 799 ;; 800 --) 801 shift 802 test -z "$REBASE_ROOT" -a$#-ge 1 -a$#-le 2 || 803 test ! -z "$REBASE_ROOT" -a$#-le 1 || usage 804 test -d "$DOTEST" && 805 die "Interactive rebase already started" 806 807 git var GIT_COMMITTER_IDENT >/dev/null || 808 die "You need to set your committer info first" 809 810 if test -z "$REBASE_ROOT" 811 then 812 UPSTREAM_ARG="$1" 813 UPSTREAM=$(git rev-parse --verify "$1")|| die "Invalid base" 814 test -z "$ONTO" && ONTO=$UPSTREAM 815 shift 816 else 817 UPSTREAM= 818 UPSTREAM_ARG=--root 819 test -z "$ONTO" && 820 die "You must specify --onto when using --root" 821 fi 822 run_pre_rebase_hook "$UPSTREAM_ARG" "$@" 823 824 comment_for_reflog start 825 826 require_clean_work_tree 827 828 if test ! -z "$1" 829 then 830 output git show-ref --verify --quiet "refs/heads/$1" || 831 die "Invalid branchname:$1" 832 output git checkout "$1" || 833 die "Could not checkout$1" 834 fi 835 836 HEAD=$(git rev-parse --verify HEAD)|| die "No HEAD?" 837 mkdir "$DOTEST" || die "Could not create temporary$DOTEST" 838 839 : > "$DOTEST"/interactive || die "Could not mark as interactive" 840 git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || 841 echo "detached HEAD" > "$DOTEST"/head-name 842 843 echo$HEAD> "$DOTEST"/head 844 case "$REBASE_ROOT" in 845 '') 846 rm -f "$DOTEST"/rebase-root ;; 847 *) 848 : >"$DOTEST"/rebase-root ;; 849 esac 850 echo$ONTO> "$DOTEST"/onto 851 test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy 852 test t = "$VERBOSE" && : > "$DOTEST"/verbose 853 if test t = "$PRESERVE_MERGES" 854 then 855 if test -z "$REBASE_ROOT" 856 then 857 mkdir "$REWRITTEN" && 858 for c in$(git merge-base --all $HEAD $UPSTREAM) 859 do 860 echo$ONTO> "$REWRITTEN"/$c|| 861 die "Could not init rewritten commits" 862 done 863 else 864 mkdir "$REWRITTEN" && 865 echo$ONTO> "$REWRITTEN"/root || 866 die "Could not init rewritten commits" 867 fi 868 # No cherry-pick because our first pass is to determine 869 # parents to rewrite and skipping dropped commits would 870 # prematurely end our probe 871 MERGES_OPTION= 872 first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" 873 else 874 MERGES_OPTION="--no-merges --cherry-pick" 875 fi 876 877 SHORTHEAD=$(git rev-parse --short $HEAD) 878 SHORTONTO=$(git rev-parse --short $ONTO) 879 if test -z "$REBASE_ROOT" 880 # this is now equivalent to ! -z "$UPSTREAM" 881 then 882 SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) 883 REVISIONS=$UPSTREAM...$HEAD 884 SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD 885 else 886 REVISIONS=$ONTO...$HEAD 887 SHORTREVISIONS=$SHORTHEAD 888 fi 889 git rev-list$MERGES_OPTION--pretty=oneline --abbrev-commit \ 890 --abbrev=7 --reverse --left-right --topo-order \ 891$REVISIONS| \ 892 sed -n "s/^>//p" | while read shortsha1 rest 893 do 894 if test t != "$PRESERVE_MERGES" 895 then 896 echo "pick$shortsha1$rest" >> "$TODO" 897 else 898 sha1=$(git rev-parse $shortsha1) 899 if test -z "$REBASE_ROOT" 900 then 901 preserve=t 902 for p in$(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) 903 do 904 if test -f "$REWRITTEN"/$p-a \($p!=$ONTO-o$sha1=$first_after_upstream\) 905 then 906 preserve=f 907 fi 908 done 909 else 910 preserve=f 911 fi 912 if test f = "$preserve" 913 then 914 touch "$REWRITTEN"/$sha1 915 echo "pick$shortsha1$rest" >> "$TODO" 916 fi 917 fi 918 done 919 920 # Watch for commits that been dropped by --cherry-pick 921 if test t = "$PRESERVE_MERGES" 922 then 923 mkdir "$DROPPED" 924 # Save all non-cherry-picked changes 925 git rev-list$REVISIONS--left-right --cherry-pick | \ 926 sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks 927 # Now all commits and note which ones are missing in 928 # not-cherry-picks and hence being dropped 929 git rev-list$REVISIONS| 930 while read rev 931 do 932 if test -f "$REWRITTEN"/$rev-a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = "" 933 then 934 # Use -f2 because if rev-list is telling us this commit is 935 # not worthwhile, we don't want to track its multiple heads, 936# just the history of its first-parent for others that will 937# be rebasing on top of it 938 git rev-list --parents -1$rev| cut -d' '-s -f2>"$DROPPED"/$rev 939 short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) 940 sane_grep -v"^[a-z][a-z]*$short"<"$TODO">"${TODO}2";mv"${TODO}2""$TODO" 941rm"$REWRITTEN"/$rev 942fi 943done 944fi 945 946test -s"$TODO"||echo noop >>"$TODO" 947test -n"$AUTOSQUASH"&& rearrange_squash "$TODO" 948cat>>"$TODO"<< EOF 949 950# Rebase$SHORTREVISIONSonto$SHORTONTO 951# 952# Commands: 953# p, pick = use commit 954# r, reword = use commit, but edit the commit message 955# e, edit = use commit, but stop for amending 956# s, squash = use commit, but meld into previous commit 957# f, fixup = like "squash", but discard this commit's log message 958# 959# If you remove a line here THAT COMMIT WILL BE LOST. 960# However, if you remove everything, the rebase will be aborted. 961# 962EOF 963 964 has_action "$TODO"|| 965 die_abort "Nothing to do" 966 967cp"$TODO""$TODO".backup 968 git_editor "$TODO"|| 969 die_abort "Could not execute editor" 970 971 has_action "$TODO"|| 972 die_abort "Nothing to do" 973 974test -d"$REWRITTEN"|| skip_unnecessary_picks 975 976 git update-ref ORIG_HEAD $HEAD 977 output git checkout $ONTO&& do_rest 978;; 979esac 980shift 981done