Extract the code that is executed when $preserve_merges is t from git-rebase--interactive to git-rebase--interactive--preserve-merges.sh. The extracted code uses functions from git-rebase--interactve--lib. Signed-off-by: Wink Saville <wink@xxxxxxxxxxx> --- .gitignore | 1 + Makefile | 1 + git-rebase--interactive--preserve-merges.sh | 134 ++++++++++++++++++++++++++++ git-rebase.sh | 7 +- 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 git-rebase--interactive--preserve-merges.sh diff --git a/.gitignore b/.gitignore index 4ea246780..c57a6b563 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,7 @@ /git-rebase--am /git-rebase--helper /git-rebase--interactive +/git-rebase--interactive--preserve-merges /git-rebase--interactive--lib /git-rebase--merge /git-receive-pack diff --git a/Makefile b/Makefile index f13540da6..543e0a659 100644 --- a/Makefile +++ b/Makefile @@ -567,6 +567,7 @@ SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--am SCRIPT_LIB += git-rebase--interactive +SCRIPT_LIB += git-rebase--interactive--preserve-merges SCRIPT_LIB += git-rebase--interactive--lib SCRIPT_LIB += git-rebase--merge SCRIPT_LIB += git-sh-setup diff --git a/git-rebase--interactive--preserve-merges.sh b/git-rebase--interactive--preserve-merges.sh new file mode 100644 index 000000000..e00b5c990 --- /dev/null +++ b/git-rebase--interactive--preserve-merges.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its interactive mode with --preserve-merges flag. +# "git rebase --interactive" makes it easy to fix up commits in the +# middle of a series and rearrange commits and adding --preserve-merges +# requests it to preserve merges while rebase. +# +# Copyright (c) 2006 Johannes E. Schindelin +# +# The original idea comes from Eric W. Biederman, in +# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@xxxxxxxxxxxxxxxxxxxxxxxxx/ + +. git-rebase--interactive--lib + +# The whole contents of this file is run by dot-sourcing it from +# inside a shell function. It used to be that "return"s we see +# below were not inside any function, and expected to return +# to the function that dot-sourced us. +# +# However, older (9.x) versions of FreeBSD /bin/sh misbehave on such a +# construct and continue to run the statements that follow such a "return". +# As a work-around, we introduce an extra layer of a function +# here, and immediately call it after defining it. +git_rebase__interactive__preserve_merges () { + initiate_action "$action" + ret=$? + if test $ret == 0; then + return 0 + fi + + setup_reflog_action + init_basic_state + + if test -z "$rebase_root" + then + mkdir "$rewritten" && + for c in $(git merge-base --all $orig_head $upstream) + do + echo $onto > "$rewritten"/$c || + die "$(gettext "Could not init rewritten commits")" + done + else + mkdir "$rewritten" && + echo $onto > "$rewritten"/root || + die "$(gettext "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= + + shorthead=$(git rev-parse --short $orig_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...$orig_head + shortrevisions=$shortupstream..$shorthead + else + revisions=$onto...$orig_head + shortrevisions=$shorthead + fi + + # The 'rev-list .. | sed' + # requires %m to parse; where as the the instruction + # requires %H to parse + format=$(git config --get rebase.instructionFormat) + git rev-list $merges_option --format="%m%H ${format:-%s}" \ + --reverse --left-right --topo-order \ + $revisions ${restrict_revision+^$restrict_revision} | \ + sed -n "s/^>//p" | + while read -r sha1 rest + do + if test -z "$keep_empty" \ + && is_empty_commit $sha1 \ + && ! is_merge_commit $sha1 + then + comment_out="$comment_char " + else + comment_out= + fi + + 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 + then + preserve=f + fi + done + else + preserve=f + fi + if test f = "$preserve" + then + touch "$rewritten"/$sha1 + printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo" + fi + done + + mkdir "$dropped" + # Save all non-cherry-picked changes + git rev-list $revisions --left-right --cherry-pick | \ + sed -n "s/^>//p" > "$state_dir"/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 && + ! sane_grep "$rev" "$state_dir"/not-cherry-picks >/dev/null + 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 + sha1=$(git rev-list -1 $rev) + sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" + mv "${todo}2" "$todo" + rm "$rewritten"/$rev + fi + done + + complete_action +} +# ... and then we call the whole thing. +git_rebase__interactive__preserve_merges diff --git a/git-rebase.sh b/git-rebase.sh index a1f6e5de6..3c2fc35f7 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -196,7 +196,12 @@ run_specific_rebase () { export GIT_EDITOR autosquash= fi - . git-rebase--$type + if test "$type" = interactive + then + . git-rebase--${type}${preserve_merges:+--preserve-merges} + else + . git-rebase--${type} + fi ret=$? if test $ret -eq 0 then -- 2.16.2