rebase: Allow merge strategies to be used when rebasing

This solves the problem of rebasing local commits against an
upstream that has renamed files.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Eric Wong 2006-06-21 03:04:41 -07:00 committed by Junio C Hamano
parent 86f660b1f1
commit 58634dbff8
2 changed files with 202 additions and 10 deletions

View File

@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
SYNOPSIS SYNOPSIS
-------- --------
'git-rebase' [--onto <newbase>] <upstream> [<branch>] 'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort 'git-rebase' --continue | --skip | --abort
@ -106,6 +106,24 @@ OPTIONS
--abort:: --abort::
Restore the original branch and abort the rebase operation. Restore the original branch and abort the rebase operation.
--skip::
Restart the rebasing process by skipping the current patch.
This does not work with the --merge option.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
upstream side.
-s <strategy>, \--strategy=<strategy>::
Use the given merge strategy; can be supplied more than
once to specify them in the order they should be tried.
If there is no `-s` option, a built-in list of strategies
is used instead (`git-merge-recursive` when merging a single
head, `git-merge-octopus` otherwise). This implies --merge.
include::merge-strategies.txt[]
NOTES NOTES
----- -----
When you rebase a branch, you are changing its history in a way that When you rebase a branch, you are changing its history in a way that

View File

@ -34,7 +34,96 @@ When you have resolved this problem run \"git rebase --continue\".
If you would prefer to skip this patch, instead run \"git rebase --skip\". If you would prefer to skip this patch, instead run \"git rebase --skip\".
To restore the original branch and stop rebasing run \"git rebase --abort\". To restore the original branch and stop rebasing run \"git rebase --abort\".
" "
MRESOLVEMSG="
When you have resolved this problem run \"git rebase --continue\".
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
unset newbase unset newbase
strategy=recursive
do_merge=
dotest=$GIT_DIR/.dotest-merge
prec=4
continue_merge () {
test -n "$prev_head" || die "prev_head must be defined"
test -d "$dotest" || die "$dotest directory does not exist"
unmerged=$(git-ls-files -u)
if test -n "$unmerged"
then
echo "You still have unmerged paths in your index"
echo "did you forget update-index?"
die "$MRESOLVEMSG"
fi
if test -n "`git-diff-index HEAD`"
then
git-commit -C "`cat $dotest/current`"
else
echo "Previous merge succeeded automatically"
fi
prev_head=`git-rev-parse HEAD^0`
# save the resulting commit so we can read-tree on it later
echo "$prev_head" > "$dotest/`printf %0${prec}d $msgnum`.result"
echo "$prev_head" > "$dotest/prev_head"
# onto the next patch:
msgnum=$(($msgnum + 1))
printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
}
call_merge () {
cmt="$(cat $dotest/`printf %0${prec}d $1`)"
echo "$cmt" > "$dotest/current"
git-merge-$strategy "$cmt^" -- HEAD "$cmt"
rv=$?
case "$rv" in
0)
git-commit -C "$cmt" || die "commit failed: $MRESOLVEMSG"
;;
1)
test -d "$GIT_DIR/rr-cache" && git-rerere
die "$MRESOLVEMSG"
;;
2)
echo "Strategy: $rv $strategy failed, try another" 1>&2
die "$MRESOLVEMSG"
;;
*)
die "Unknown exit code ($rv) from command:" \
"git-merge-$strategy $cmt^ -- HEAD $cmt"
;;
esac
}
finish_rb_merge () {
set -e
msgnum=1
echo "Finalizing rebased commits..."
git-reset --hard "`cat $dotest/onto`"
end="`cat $dotest/end`"
while test "$msgnum" -le "$end"
do
msgnum=`printf "%0${prec}d" "$msgnum"`
printf "%0${prec}d" "$msgnum" > "$dotest/msgnum"
git-read-tree `cat "$dotest/$msgnum.result"`
git-checkout-index -q -f -u -a
git-commit -C "`cat $dotest/$msgnum`"
echo "Committed $msgnum"
echo ' '`git-rev-list --pretty=oneline -1 HEAD | \
sed 's/^[a-f0-9]\+ //'`
msgnum=$(($msgnum + 1))
done
rm -r "$dotest"
echo "All done."
}
while case "$#" in 0) break ;; esac while case "$#" in 0) break ;; esac
do do
case "$1" in case "$1" in
@ -46,17 +135,43 @@ do
exit 1 exit 1
;; ;;
esac esac
if test -d "$dotest"
then
prev_head="`cat $dotest/prev_head`"
end="`cat $dotest/end`"
msgnum="`cat $dotest/msgnum`"
onto="`cat $dotest/onto`"
continue_merge
while test "$msgnum" -le "$end"
do
call_merge "$msgnum"
continue_merge
done
finish_rb_merge
exit
fi
git am --resolved --3way --resolvemsg="$RESOLVEMSG" git am --resolved --3way --resolvemsg="$RESOLVEMSG"
exit exit
;; ;;
--skip) --skip)
if test -d "$dotest"
then
die "--skip is not supported when using --merge"
fi
git am -3 --skip --resolvemsg="$RESOLVEMSG" git am -3 --skip --resolvemsg="$RESOLVEMSG"
exit exit
;; ;;
--abort) --abort)
[ -d .dotest ] || die "No rebase in progress?" if test -d "$dotest"
then
rm -r "$dotest"
elif test -d .dotest
then
rm -r .dotest
else
die "No rebase in progress?"
fi
git reset --hard ORIG_HEAD git reset --hard ORIG_HEAD
rm -r .dotest
exit exit
;; ;;
--onto) --onto)
@ -64,6 +179,23 @@ do
newbase="$2" newbase="$2"
shift shift
;; ;;
-M|-m|--m|--me|--mer|--merg|--merge)
do_merge=t
;;
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
--strateg=*|--strategy=*|\
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
case "$#,$1" in
*,*=*)
strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
1,*)
usage ;;
*)
strategy="$2"
shift ;;
esac
do_merge=t
;;
-*) -*)
usage usage
;; ;;
@ -75,16 +207,25 @@ do
done done
# Make sure we do not have .dotest # Make sure we do not have .dotest
if mkdir .dotest if test -z "$do_merge"
then then
rmdir .dotest if mkdir .dotest
else then
echo >&2 ' rmdir .dotest
else
echo >&2 '
It seems that I cannot create a .dotest directory, and I wonder if you It seems that I cannot create a .dotest directory, and I wonder if you
are in the middle of patch application or another rebase. If that is not are in the middle of patch application or another rebase. If that is not
the case, please rm -fr .dotest and run me again. I am stopping in case the case, please rm -fr .dotest and run me again. I am stopping in case
you still have something valuable there.' you still have something valuable there.'
exit 1 exit 1
fi
else
if test -d "$dotest"
then
die "previous dotest directory $dotest still exists." \
'try git-rebase < --continue | --abort >'
fi
fi fi
# The tree must be really really clean. # The tree must be really really clean.
@ -152,6 +293,39 @@ then
exit 0 exit 0
fi fi
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD | if test -z "$do_merge"
git am --binary -3 -k --resolvemsg="$RESOLVEMSG" then
git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
exit $?
fi
# start doing a rebase with git-merge
# this is rename-aware if the recursive (default) strategy is used
mkdir -p "$dotest"
echo "$onto" > "$dotest/onto"
prev_head=`git-rev-parse HEAD^0`
echo "$prev_head" > "$dotest/prev_head"
msgnum=0
for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
| perl -e 'print reverse <>'`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/`printf "%0${prec}d" $msgnum`"
done
printf "%0${prec}d" 1 > "$dotest/msgnum"
printf "%0${prec}d" "$msgnum" > "$dotest/end"
end=$msgnum
msgnum=1
while test "$msgnum" -le "$end"
do
call_merge "$msgnum"
continue_merge
done
finish_rb_merge