rebase: operate on a detached HEAD

The interactive version of rebase does all the operations on a detached
HEAD, so that after a successful rebase, <branch>@{1} is the pre-rebase
state.  The reflogs of "HEAD" still show all the actions in detail.

This teaches the non-interactive version to do the same.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johannes Schindelin 2007-11-08 18:19:08 +00:00 committed by Junio C Hamano
parent c238dad407
commit 6fd2f5e60d
2 changed files with 57 additions and 6 deletions

View file

@ -87,7 +87,7 @@ call_merge () {
cmt="$(cat "$dotest/cmt.$1")" cmt="$(cat "$dotest/cmt.$1")"
echo "$cmt" > "$dotest/current" echo "$cmt" > "$dotest/current"
hd=$(git rev-parse --verify HEAD) hd=$(git rev-parse --verify HEAD)
cmt_name=$(git symbolic-ref HEAD) cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
msgnum=$(cat "$dotest/msgnum") msgnum=$(cat "$dotest/msgnum")
end=$(cat "$dotest/end") end=$(cat "$dotest/end")
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
@ -115,7 +115,24 @@ call_merge () {
esac esac
} }
move_to_original_branch () {
test -z "$head_name" &&
head_name="$(cat "$dotest"/head-name)" &&
onto="$(cat "$dotest"/onto)" &&
orig_head="$(cat "$dotest"/orig-head)"
case "$head_name" in
refs/*)
message="rebase finished: $head_name onto $onto"
git update-ref -m "$message" \
$head_name $(git rev-parse HEAD) $orig_head &&
git symbolic-ref HEAD $head_name ||
die "Could not move back to $head_name"
;;
esac
}
finish_rb_merge () { finish_rb_merge () {
move_to_original_branch
rm -r "$dotest" rm -r "$dotest"
echo "All done." echo "All done."
} }
@ -173,16 +190,23 @@ do
finish_rb_merge finish_rb_merge
exit exit
fi fi
git am -3 --skip --resolvemsg="$RESOLVEMSG" head_name=$(cat .dotest/head-name) &&
onto=$(cat .dotest/onto) &&
orig_head=$(cat .dotest/orig-head) &&
git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
exit exit
;; ;;
--abort) --abort)
git rerere clear git rerere clear
if test -d "$dotest" if test -d "$dotest"
then then
move_to_original_branch
rm -r "$dotest" rm -r "$dotest"
elif test -d .dotest elif test -d .dotest
then then
dotest=.dotest
move_to_original_branch
rm -r .dotest rm -r .dotest
else else
die "No rebase in progress?" die "No rebase in progress?"
@ -318,6 +342,19 @@ then
GIT_PAGER='' git diff --stat --summary "$mb" "$onto" GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
fi fi
# move to a detached HEAD
orig_head=$(git rev-parse HEAD^0)
head_name=$(git symbolic-ref HEAD 2> /dev/null)
case "$head_name" in
'')
head_name="detached HEAD"
;;
*)
git checkout "$orig_head" > /dev/null 2>&1 ||
die "could not detach HEAD"
;;
esac
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
echo "First, rewinding head to replay your work on top of it..." echo "First, rewinding head to replay your work on top of it..."
git-reset --hard "$onto" git-reset --hard "$onto"
@ -327,14 +364,21 @@ git-reset --hard "$onto"
if test "$mb" = "$branch" if test "$mb" = "$branch"
then then
echo >&2 "Fast-forwarded $branch_name to $onto_name." echo >&2 "Fast-forwarded $branch_name to $onto_name."
move_to_original_branch
exit 0 exit 0
fi fi
if test -z "$do_merge" if test -z "$do_merge"
then then
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
exit $? move_to_original_branch
ret=$?
test 0 != $ret -a -d .dotest &&
echo $head_name > .dotest/head-name &&
echo $onto > .dotest/onto &&
echo $orig_head > .dotest/orig-head
exit $ret
fi fi
# start doing a rebase with git-merge # start doing a rebase with git-merge
@ -343,8 +387,10 @@ fi
mkdir -p "$dotest" mkdir -p "$dotest"
echo "$onto" > "$dotest/onto" echo "$onto" > "$dotest/onto"
echo "$onto_name" > "$dotest/onto_name" echo "$onto_name" > "$dotest/onto_name"
prev_head=`git rev-parse HEAD^0` prev_head=$orig_head
echo "$prev_head" > "$dotest/prev_head" echo "$prev_head" > "$dotest/prev_head"
echo "$orig_head" > "$dotest/orig-head"
echo "$head_name" > "$dotest/head-name"
msgnum=0 msgnum=0
for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`

View file

@ -48,9 +48,14 @@ test_expect_success 'reference merge' '
git merge -s recursive "reference merge" HEAD master git merge -s recursive "reference merge" HEAD master
' '
PRE_REBASE=$(git rev-parse test-rebase)
test_expect_success rebase ' test_expect_success rebase '
git checkout test-rebase && git checkout test-rebase &&
git rebase --merge master GIT_TRACE=1 git rebase --merge master
'
test_expect_success 'test-rebase@{1} is pre rebase' '
test $PRE_REBASE = $(git rev-parse test-rebase@{1})
' '
test_expect_success 'merge and rebase should match' ' test_expect_success 'merge and rebase should match' '