Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
f9acaea
Documentation
block-sha1
compat
contrib
git-gui
gitk-git
gitweb
perl
ppc
t
templates
xdiff
.gitattributes
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
abspath.c
advice.c
advice.h
alias.c
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
base85.c
bisect.c
bisect.h
blob.c
blob.h
branch.c
branch.h
builtin-add.c
builtin-annotate.c
builtin-apply.c
builtin-archive.c
builtin-bisect--helper.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-help.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-ours.c
builtin-merge-recursive.c
builtin-merge.c
builtin-mktree.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-receive-pack.c
builtin-reflog.c
builtin-remote.c
builtin-replace.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-update-ref.c
builtin-update-server-info.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
check_bindir
color.c
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
config.c
config.mak.in
configure.ac
connect.c
convert.c
copy.c
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
editor.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.h
fixup-builtins
fsck.c
fsck.h
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool--helper.sh
git-difftool.perl
git-filter-branch.sh
git-instaweb.sh
git-lost-found.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-mergetool--lib.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.spec.in
graph.c
graph.h
grep.c
grep.h
hash-object.c
hash.c
hash.h
help.c
help.h
http-fetch.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
levenshtein.c
levenshtein.h
list-objects.c
list-objects.h
ll-merge.c
ll-merge.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-file.c
merge-index.c
merge-recursive.c
merge-recursive.h
merge-tree.c
mktag.c
name-hash.c
object.c
object.h
pack-check.c
pack-redundant.c
pack-refs.c
pack-refs.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
patch-delta.c
patch-id.c
patch-ids.c
patch-ids.h
path.c
pkt-line.c
pkt-line.h
preload-index.c
pretty.c
progress.c
progress.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote-curl.c
remote.c
remote.h
replace_object.c
rerere.c
rerere.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1-lookup.c
sha1-lookup.h
sha1_file.c
sha1_name.c
shallow.c
shell.c
shortlog.h
show-index.c
sideband.c
sideband.h
sigchain.c
sigchain.h
strbuf.c
strbuf.h
string-list.c
string-list.h
symlinks.c
tag.c
tag.h
tar.h
test-chmtime.c
test-ctype.c
test-date.c
test-delta.c
test-dump-cache-tree.c
test-genrandom.c
test-match-trees.c
test-parse-options.c
test-path-utils.c
test-sha1.c
test-sha1.sh
test-sigchain.c
thread-utils.c
thread-utils.h
trace.c
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unimplemented.sh
unpack-file.c
unpack-trees.c
unpack-trees.h
upload-pack.c
usage.c
userdiff.c
userdiff.h
utf8.c
utf8.h
var.c
walker.c
walker.h
wrapper.c
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
Breadcrumbs
git
/
git-rebase--interactive.sh
Blame
Blame
Latest commit
History
History
executable file
·
785 lines (718 loc) · 18.5 KB
Breadcrumbs
git
/
git-rebase--interactive.sh
Top
File metadata and controls
Code
Blame
executable file
·
785 lines (718 loc) · 18.5 KB
Raw
#!/bin/sh # # Copyright (c) 2006 Johannes E. Schindelin # SHORT DESCRIPTION # # This script makes it easy to fix up commits in the middle of a series, # and rearrange commits. # # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 OPTIONS_KEEPDASHDASH= OPTIONS_SPEC="\ git-rebase [-i] [options] [--] <upstream> [<branch>] git-rebase [-i] (--continue | --abort | --skip) -- Available options are v,verbose display a diffstat of what changed upstream onto= rebase onto given branch instead of upstream p,preserve-merges try to recreate merges instead of ignoring them s,strategy= use the given merge strategy m,merge always used (no-op) i,interactive always used (no-op) Actions: continue continue rebasing process abort abort rebasing process and restore original branch skip skip current patch and continue rebasing process no-verify override pre-rebase hook from stopping the operation root rebase all reachable commmits up to the root(s) " . git-sh-setup require_work_tree DOTEST="$GIT_DIR/rebase-merge" TODO="$DOTEST"/git-rebase-todo DONE="$DOTEST"/done MSG="$DOTEST"/message SQUASH_MSG="$DOTEST"/message-squash REWRITTEN="$DOTEST"/rewritten DROPPED="$DOTEST"/dropped PRESERVE_MERGES= STRATEGY= ONTO= VERBOSE= OK_TO_SKIP_PRE_REBASE= REBASE_ROOT= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add <paths>', and run 'git rebase --continue'" export GIT_CHERRY_PICK_HELP warn () { echo "$*" >&2 } output () { case "$VERBOSE" in '') output=$("$@" 2>&1 ) status=$? test $status != 0 && printf "%s\n" "$output" return $status ;; *) "$@" ;; esac } run_pre_rebase_hook () { if test -z "$OK_TO_SKIP_PRE_REBASE" && test -x "$GIT_DIR/hooks/pre-rebase" then "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { echo >&2 "The pre-rebase hook refused to rebase." exit 1 } fi } require_clean_work_tree () { # test if working tree is dirty git rev-parse --verify HEAD > /dev/null && git update-index --ignore-submodules --refresh && git diff-files --quiet --ignore-submodules && git diff-index --cached --quiet HEAD --ignore-submodules -- || die "Working tree is dirty" } ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION" comment_for_reflog () { case "$ORIG_REFLOG_ACTION" in ''|rebase*) GIT_REFLOG_ACTION="rebase -i ($1)" export GIT_REFLOG_ACTION ;; esac } last_count= mark_action_done () { sed -e 1q < "$TODO" >> "$DONE" sed -e 1d < "$TODO" >> "$TODO".new mv -f "$TODO".new "$TODO" count=$(sane_grep -c '^[^#]' < "$DONE") total=$(($count+$(sane_grep -c '^[^#]' < "$TODO"))) if test "$last_count" != "$count" then last_count=$count printf "Rebasing (%d/%d)\r" $count $total test -z "$VERBOSE" || echo fi } make_patch () { sha1_and_parents="$(git rev-list --parents -1 "$1")" case "$sha1_and_parents" in ?*' '?*' '?*) git diff --cc $sha1_and_parents ;; ?*' '?*) git diff-tree -p "$1^!" ;; *) echo "Root commit" ;; esac > "$DOTEST"/patch test -f "$DOTEST"/message || git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message test -f "$DOTEST"/author-script || get_author_ident_from_commit "$1" > "$DOTEST"/author-script } die_with_patch () { make_patch "$1" git rerere die "$2" } die_abort () { rm -rf "$DOTEST" die "$1" } has_action () { sane_grep '^[^#]' "$1" >/dev/null } pick_one () { no_ff= case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return if test ! -z "$REBASE_ROOT" then output git cherry-pick "$@" return fi parent_sha1=$(git rev-parse --verify $sha1^) || die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) if test "$no_ff$current_sha1" = "$parent_sha1"; then output git reset --hard $sha1 test "a$1" = a-n && output git reset --soft $current_sha1 sha1=$(git rev-parse --short $sha1) output warn Fast forward to $sha1 else output git cherry-pick "$@" fi } pick_one_preserving_merges () { fast_forward=t case "$1" in -n) fast_forward=f sha1=$2 ;; *) sha1=$1 ;; esac sha1=$(git rev-parse $sha1) if test -f "$DOTEST"/current-commit then if test "$fast_forward" = t then cat "$DOTEST"/current-commit | while read current_commit do git rev-parse HEAD > "$REWRITTEN"/$current_commit done rm "$DOTEST"/current-commit || die "Cannot write current commit's replacement sha1" fi fi echo $sha1 >> "$DOTEST"/current-commit # rewrite parents; if none were rewritten, we can fast-forward. new_parents= pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)" if test "$pend" = " " then pend=" root" fi while [ "$pend" != "" ] do p=$(expr "$pend" : ' \([^ ]*\)') pend="${pend# $p}" if test -f "$REWRITTEN"/$p then new_p=$(cat "$REWRITTEN"/$p) # If the todo reordered commits, and our parent is marked for # rewriting, but hasn't been gotten to yet, assume the user meant to # drop it on top of the current HEAD if test -z "$new_p" then new_p=$(git rev-parse HEAD) fi test $p != $new_p && fast_forward=f case "$new_parents" in *$new_p*) ;; # do nothing; that parent is already there *) new_parents="$new_parents $new_p" ;; esac else if test -f "$DROPPED"/$p then fast_forward=f replacement="$(cat "$DROPPED"/$p)" test -z "$replacement" && replacement=root pend=" $replacement$pend" else new_parents="$new_parents $p" fi fi done case $fast_forward in t) output warn "Fast forward to $sha1" output git reset --hard $sha1 || die "Cannot fast forward to $sha1" ;; f) first_parent=$(expr "$new_parents" : ' \([^ ]*\)') if [ "$1" != "-n" ] then # detach HEAD to current parent output git checkout $first_parent 2> /dev/null || die "Cannot move HEAD to $first_parent" fi case "$new_parents" in ' '*' '*) test "a$1" = a-n && die "Refusing to squash a merge: $sha1" # redo merge author_script=$(get_author_ident_from_commit $sha1) eval "$author_script" msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" # No point in merging the first parent, that's HEAD new_parents=${new_parents# $first_parent} if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ output git merge $STRATEGY -m "$msg" \ $new_parents then printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG die_with_patch $sha1 "Error redoing merge $sha1" fi ;; *) output git cherry-pick "$@" || die_with_patch $sha1 "Could not pick $sha1" ;; esac ;; esac } nth_string () { case "$1" in *1[0-9]|*[04-9]) echo "$1"th;; *1) echo "$1"st;; *2) echo "$1"nd;; *3) echo "$1"rd;; esac } make_squash_message () { if test -f "$SQUASH_MSG"; then COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \ < "$SQUASH_MSG" | sed -ne '$p')+1)) echo "# This is a combination of $COUNT commits." sed -e 1d -e '2,/^./{ /^$/d }' <"$SQUASH_MSG" else COUNT=2 echo "# This is a combination of two commits." echo "# The first commit's message is:" echo git cat-file commit HEAD | sed -e '1,/^$/d' fi echo echo "# This is the $(nth_string $COUNT) commit message:" echo git cat-file commit $1 | sed -e '1,/^$/d' } peek_next_command () { sed -n "1s/ .*$//p" < "$TODO" } do_next () { rm -f "$DOTEST"/message "$DOTEST"/author-script \ "$DOTEST"/amend || exit read command sha1 rest < "$TODO" case "$command" in '#'*|''|noop) mark_action_done ;; pick|p) comment_for_reflog pick mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" ;; edit|e) comment_for_reflog edit mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" make_patch $sha1 git rev-parse --verify HEAD > "$DOTEST"/amend warn "Stopped at $sha1... $rest" warn "You can amend the commit now, with" warn warn " git commit --amend" warn warn "Once you are satisfied with your changes, run" warn warn " git rebase --continue" warn exit 0 ;; squash|s) comment_for_reflog squash test -f "$DONE" && has_action "$DONE" || die "Cannot 'squash' without a previous commit" mark_action_done make_squash_message $sha1 > "$MSG" failed=f author_script=$(get_author_ident_from_commit HEAD) output git reset --soft HEAD^ pick_one -n $sha1 || failed=t case "$(peek_next_command)" in squash|s) USE_OUTPUT=output MSG_OPT=-F EDIT_OR_FILE="$MSG" cp "$MSG" "$SQUASH_MSG" ;; *) USE_OUTPUT= MSG_OPT= EDIT_OR_FILE=-e rm -f "$SQUASH_MSG" || exit cp "$MSG" "$GIT_DIR"/SQUASH_MSG rm -f "$GIT_DIR"/MERGE_MSG || exit ;; esac echo "$author_script" > "$DOTEST"/author-script if test $failed = f then # This is like --amend, but with a different message eval "$author_script" GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ $USE_OUTPUT git commit --no-verify \ $MSG_OPT "$EDIT_OR_FILE" || failed=t fi if test $failed = t then cp "$MSG" "$GIT_DIR"/MERGE_MSG warn warn "Could not apply $sha1... $rest" die_with_patch $sha1 "" fi ;; *) warn "Unknown command: $command $sha1 $rest" if git rev-parse --verify -q "$sha1" >/dev/null then die_with_patch $sha1 "Please fix this in the file $TODO." else die "Please fix this in the file $TODO." fi ;; esac test -s "$TODO" && return comment_for_reflog finish && HEADNAME=$(cat "$DOTEST"/head-name) && OLDHEAD=$(cat "$DOTEST"/head) && SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && NEWHEAD=$(git rev-parse HEAD) && case $HEADNAME in refs/*) message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" && git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && git symbolic-ref HEAD $HEADNAME ;; esac && { test ! -f "$DOTEST"/verbose || git diff-tree --stat $(cat "$DOTEST"/head)..HEAD } && rm -rf "$DOTEST" && git gc --auto && warn "Successfully rebased and updated $HEADNAME." exit } do_rest () { while : do do_next done } # skip picking commits whose parents are unchanged skip_unnecessary_picks () { fd=3 while read command sha1 rest do # fd=3 means we skip the command case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in 3,pick,"$ONTO"*|3,p,"$ONTO"*) # pick a commit whose parent is current $ONTO -> skip ONTO=$sha1 ;; 3,#*|3,,*) # copy comments ;; *) fd=1 ;; esac echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd done <"$TODO" >"$TODO.new" 3>>"$DONE" && mv -f "$TODO".new "$TODO" || die "Could not skip unnecessary pick commands" } # check if no other options are set is_standalone () { test $# -eq 2 -a "$2" = '--' && test -z "$ONTO" && test -z "$PRESERVE_MERGES" && test -z "$STRATEGY" && test -z "$VERBOSE" } get_saved_options () { test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t test -f "$DOTEST"/rebase-root && REBASE_ROOT=t } while test $# != 0 do case "$1" in --no-verify) OK_TO_SKIP_PRE_REBASE=yes ;; --verify) ;; --continue) is_standalone "$@" || usage get_saved_options comment_for_reflog continue test -d "$DOTEST" || die "No interactive rebase running" # Sanity check git rev-parse --verify HEAD >/dev/null || die "Cannot read HEAD" git update-index --ignore-submodules --refresh && git diff-files --quiet --ignore-submodules || die "Working tree is dirty" # do we have anything to commit? if git diff-index --cached --quiet --ignore-submodules HEAD -- then : Nothing to commit -- skip this else . "$DOTEST"/author-script || die "Cannot find the author identity" amend= if test -f "$DOTEST"/amend then amend=$(git rev-parse --verify HEAD) test "$amend" = $(cat "$DOTEST"/amend) || die "\ You have uncommitted changes in your working tree. Please, commit them first and then run 'git rebase --continue' again." git reset --soft HEAD^ || die "Cannot rewind the HEAD" fi export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE && git commit --no-verify -F "$DOTEST"/message -e || { test -n "$amend" && git reset --soft $amend die "Could not commit staged changes." } fi require_clean_work_tree do_rest ;; --abort) is_standalone "$@" || usage get_saved_options comment_for_reflog abort git rerere clear test -d "$DOTEST" || die "No interactive rebase running" HEADNAME=$(cat "$DOTEST"/head-name) HEAD=$(cat "$DOTEST"/head) case $HEADNAME in refs/*) git symbolic-ref HEAD $HEADNAME ;; esac && output git reset --hard $HEAD && rm -rf "$DOTEST" exit ;; --skip) is_standalone "$@" || usage get_saved_options comment_for_reflog skip git rerere clear test -d "$DOTEST" || die "No interactive rebase running" output git reset --hard && do_rest ;; -s) case "$#,$1" in *,*=*) STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 1,*) usage ;; *) STRATEGY="-s $2" shift ;; esac ;; -m) # we use merge anyway ;; -v) VERBOSE=t ;; -p) PRESERVE_MERGES=t ;; -i) # yeah, we know ;; --root) REBASE_ROOT=t ;; --onto) shift ONTO=$(git rev-parse --verify "$1") || die "Does not point to a valid commit: $1" ;; --) shift test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 || test ! -z "$REBASE_ROOT" -a $# -le 1 || usage test -d "$DOTEST" && die "Interactive rebase already started" git var GIT_COMMITTER_IDENT >/dev/null || die "You need to set your committer info first" if test -z "$REBASE_ROOT" then UPSTREAM_ARG="$1" UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" test -z "$ONTO" && ONTO=$UPSTREAM shift else UPSTREAM= UPSTREAM_ARG=--root test -z "$ONTO" && die "You must specify --onto when using --root" fi run_pre_rebase_hook "$UPSTREAM_ARG" "$@" comment_for_reflog start require_clean_work_tree if test ! -z "$1" then output git show-ref --verify --quiet "refs/heads/$1" || die "Invalid branchname: $1" output git checkout "$1" || die "Could not checkout $1" fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" mkdir "$DOTEST" || die "Could not create temporary $DOTEST" : > "$DOTEST"/interactive || die "Could not mark as interactive" git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || echo "detached HEAD" > "$DOTEST"/head-name echo $HEAD > "$DOTEST"/head case "$REBASE_ROOT" in '') rm -f "$DOTEST"/rebase-root ;; *) : >"$DOTEST"/rebase-root ;; esac echo $ONTO > "$DOTEST"/onto test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy test t = "$VERBOSE" && : > "$DOTEST"/verbose if test t = "$PRESERVE_MERGES" then # $REWRITTEN contains files for each commit that is # reachable by at least one merge base of $HEAD and # $UPSTREAM. They are not necessarily rewritten, but # their children might be. # This ensures that commits on merged, but otherwise # unrelated side branches are left alone. (Think "X" # in the man page's example.) if test -z "$REBASE_ROOT" then mkdir "$REWRITTEN" && for c in $(git merge-base --all $HEAD $UPSTREAM) do echo $ONTO > "$REWRITTEN"/$c || die "Could not init rewritten commits" done else mkdir "$REWRITTEN" && echo $ONTO > "$REWRITTEN"/root || die "Could not init rewritten commits" fi # No cherry-pick because our first pass is to determine # parents to rewrite and skipping dropped commits would # prematurely end our probe MERGES_OPTION= first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" else MERGES_OPTION="--no-merges --cherry-pick" fi SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) if test -z "$REBASE_ROOT" # this is now equivalent to ! -z "$UPSTREAM" then SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) REVISIONS=$UPSTREAM...$HEAD SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD else REVISIONS=$ONTO...$HEAD SHORTREVISIONS=$SHORTHEAD fi git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --topo-order \ $REVISIONS | \ sed -n "s/^>//p" | while read shortsha1 rest do if test t != "$PRESERVE_MERGES" then echo "pick $shortsha1 $rest" >> "$TODO" else sha1=$(git rev-parse $shortsha1) if test -z "$REBASE_ROOT" then preserve=t for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \) then preserve=f fi done else preserve=f fi if test f = "$preserve" then touch "$REWRITTEN"/$sha1 echo "pick $shortsha1 $rest" >> "$TODO" fi fi done # Watch for commits that been dropped by --cherry-pick if test t = "$PRESERVE_MERGES" then mkdir "$DROPPED" # Save all non-cherry-picked changes git rev-list $REVISIONS --left-right --cherry-pick | \ sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks # Now all commits and note which ones are missing in # not-cherry-picks and hence being dropped git rev-list $REVISIONS | while read rev do if test -f "$REWRITTEN"/$rev -a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = "" then # Use -f2 because if rev-list is telling us this commit is # not worthwhile, we don't want to track its multiple heads, # just the history of its first-parent for others that will # be rebasing on top of it git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) sane_grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO" rm "$REWRITTEN"/$rev fi done fi test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF # Rebase $SHORTREVISIONS onto $SHORTONTO # # Commands: # p, pick = use commit # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # EOF has_action "$TODO" || die_abort "Nothing to do" cp "$TODO" "$TODO".backup git_editor "$TODO" || die_abort "Could not execute editor" has_action "$TODO" || die_abort "Nothing to do" test -d "$REWRITTEN" || skip_unnecessary_picks git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest ;; esac shift done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
You can’t perform that action at this time.