Skip to content

Commit

Permalink
rebase: Allow merge strategies to be used when rebasing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Wong authored and Junio C Hamano committed Jun 21, 2006
1 parent 86f660b commit 58634db
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 10 deletions.
20 changes: 19 additions & 1 deletion Documentation/git-rebase.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head

SYNOPSIS
--------
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]

'git-rebase' --continue | --skip | --abort

Expand Down Expand Up @@ -106,6 +106,24 @@ OPTIONS
--abort::
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
-----
When you rebase a branch, you are changing its history in a way that
Expand Down
192 changes: 183 additions & 9 deletions git-rebase.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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\".
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
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
do
case "$1" in
Expand All @@ -46,24 +135,67 @@ do
exit 1
;;
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"
exit
;;
--skip)
if test -d "$dotest"
then
die "--skip is not supported when using --merge"
fi
git am -3 --skip --resolvemsg="$RESOLVEMSG"
exit
;;
--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
rm -r .dotest
exit
;;
--onto)
test 2 -le "$#" || usage
newbase="$2"
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
;;
Expand All @@ -75,16 +207,25 @@ do
done

# Make sure we do not have .dotest
if mkdir .dotest
if test -z "$do_merge"
then
rmdir .dotest
else
echo >&2 '
if mkdir .dotest
then
rmdir .dotest
else
echo >&2 '
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
the case, please rm -fr .dotest and run me again. I am stopping in case
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

# The tree must be really really clean.
Expand Down Expand Up @@ -152,6 +293,39 @@ then
exit 0
fi

git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
if test -z "$do_merge"
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

0 comments on commit 58634db

Please sign in to comment.