Skip to content

Commit 0cb0664

Browse files
committed
rebase [--onto O] A B: omit needless checkout
This teaches "git rebase [--onto O] A B" to omit an unnecessary checkout of branch B before it goes on. "git-rebase" originally was about rebasing the current branch to somewhere else, and when the extra parameter to name which branch to rebase was added, it defined the semantics to the safest but stupid "first switch to the named branch and then operate exactly the same way as if we were already on that branch". But the first thing the real part of "rebase" does is to reset the work tree and the index to the "onto" commit. Which means the "rebase that branch" form switched the work tree to the tip of the branch only to immediately switch again to another commit. This was wasteful. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7092882 commit 0cb0664

File tree

1 file changed

+32
-20
lines changed

1 file changed

+32
-20
lines changed

git-rebase.sh

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -309,22 +309,42 @@ then
309309
}
310310
fi
311311

312-
# If the branch to rebase is given, first switch to it.
312+
# If the branch to rebase is given, that is the branch we will rebase
313+
# $branch_name -- branch being rebased, or HEAD (already detached)
314+
# $orig_head -- commit object name of tip of the branch before rebasing
315+
# $head_name -- refs/heads/<that-branch> or "detached HEAD"
316+
switch_to=
313317
case "$#" in
314318
2)
319+
# Is it "rebase other $branchname" or "rebase other $commit"?
315320
branch_name="$2"
316-
git-checkout "$2" || usage
321+
switch_to="$2"
322+
323+
if git show-ref --verify --quiet -- "refs/heads/$2" &&
324+
branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
325+
then
326+
head_name="refs/heads/$2"
327+
elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
328+
then
329+
head_name="detached HEAD"
330+
else
331+
usage
332+
fi
317333
;;
318334
*)
335+
# Do not need to switch branches, we are already on it.
319336
if branch_name=`git symbolic-ref -q HEAD`
320337
then
338+
head_name=$branch_name
321339
branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
322340
else
341+
head_name="detached HEAD"
323342
branch_name=HEAD ;# detached
324343
fi
344+
branch=$(git rev-parse --verify "${branch_name}^0") || exit
325345
;;
326346
esac
327-
branch=$(git rev-parse --verify "${branch_name}^0") || exit
347+
orig_head=$branch
328348

329349
# Now we are rebasing commits $upstream..$branch on top of $onto
330350

@@ -335,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
335355
# linear history?
336356
! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
337357
then
358+
# Lazily switch to the target branch if needed...
359+
test -z "$switch_to" || git checkout "$switch_to"
338360
echo >&2 "Current branch $branch_name is up to date."
339361
exit 0
340362
fi
@@ -346,22 +368,11 @@ then
346368
GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
347369
fi
348370

349-
# move to a detached HEAD
350-
orig_head=$(git rev-parse HEAD^0)
351-
head_name=$(git symbolic-ref HEAD 2> /dev/null)
352-
case "$head_name" in
353-
'')
354-
head_name="detached HEAD"
355-
;;
356-
*)
357-
git checkout "$orig_head" > /dev/null 2>&1 ||
358-
die "could not detach HEAD"
359-
;;
360-
esac
361-
362-
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
371+
# Detach HEAD and reset the tree
363372
echo "First, rewinding head to replay your work on top of it..."
364-
git-reset --hard "$onto"
373+
git checkout "$onto^0" >/dev/null 2>&1 ||
374+
die "could not detach HEAD"
375+
# git reset --hard "$onto^0"
365376

366377
# If the $onto is a proper descendant of the tip of the branch, then
367378
# we just fast forwarded.
@@ -374,7 +385,8 @@ fi
374385

375386
if test -z "$do_merge"
376387
then
377-
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
388+
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
389+
"$upstream..$orig_head" |
378390
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
379391
move_to_original_branch
380392
ret=$?
@@ -397,7 +409,7 @@ echo "$orig_head" > "$dotest/orig-head"
397409
echo "$head_name" > "$dotest/head-name"
398410

399411
msgnum=0
400-
for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
412+
for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
401413
do
402414
msgnum=$(($msgnum + 1))
403415
echo "$cmt" > "$dotest/cmt.$msgnum"

0 commit comments

Comments
 (0)