"rebase --onto A...B" replays history on the merge base between A and B
authorJunio C Hamano <gitster@pobox.com>
Fri, 20 Nov 2009 11:02:44 +0000 (03:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 21 Nov 2009 17:22:55 +0000 (09:22 -0800)
This is in spirit similar to "checkout A...B".  To re-queue a new set of
patches for a series that the original author prepared to apply on 'next'
on the same base as before, you would do something like this:

    $ git checkout next^0
    $ git am -s rerolled-series.mbox
    $ git rebase --onto next...jh/notes next

The first two commands recreates commits to be rebased as the original
author intended (i.e. applies directly on top of 'next'), and the rebase
command replays that history on top of the same commit the series being
replaced was built on (which is typically much older than the tip of
'next').

Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-rebase.sh

index 6ec155cf03c98f2e075f298039037d0e099311b1..6503113a84e6e01e15bfcddd427c359c597cd7d1 100755 (executable)
@@ -34,6 +34,8 @@ set_reflog_action rebase
 require_work_tree
 cd_to_toplevel
 
+LF='
+'
 OK_TO_SKIP_PRE_REBASE=
 RESOLVEMSG="
 When you have resolved this problem run \"git rebase --continue\".
@@ -417,7 +419,22 @@ fi
 
 # Make sure the branch to rebase onto is valid.
 onto_name=${newbase-"$upstream_name"}
-onto=$(git rev-parse --verify "${onto_name}^0") || exit
+if     left=$(expr "$onto_name" : '\(.*\)\.\.\.') &&
+       right=$(expr "$onto_name" : '\.\.\.\(.*\)$') &&
+       : ${left:=HEAD} ${right:=HEAD} &&
+       onto=$(git merge-base "$left" "$right")
+then
+       case "$onto" in
+       ?*"$LF"?*)
+               die "$onto_name: there are more than one merge bases"
+               ;;
+       '')
+               die "$onto_name: there is no merge base"
+               ;;
+       esac
+else
+       onto=$(git rev-parse --verify "${onto_name}^0") || exit
+fi
 
 # If a hook exists, give it a chance to interrupt
 run_pre_rebase_hook "$upstream_arg" "$@"