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