git/git-rebase--am.sh
Elijah Newren c91c944a06 rebase: define linearization ordering and enforce it
Ever since commit 3f213981e4 ("add tests for rebasing merged history",
2013-06-06), t3425 has had tests which included the rebasing of merged
history and whose order of applied commits was checked.  Unfortunately,
the tests expected different behavior depending on which backend was in
use.  Implementing these checks was the following four lines (including
the TODO message) which were repeated verbatim three times in t3425:

    #TODO: make order consistent across all flavors of rebase
    test_run_rebase success 'e n o' ''
    test_run_rebase success 'e n o' -m
    test_run_rebase success 'n o e' -i

As part of the effort to reduce differences between the rebase backends
so that users get more uniform behavior, let's define the correct
behavior and modify the different backends so they all get the right
answer.  It turns out that the difference in behavior here is entirely
due to topological sorting; since some backends require topological
sorting (particularly when --rebase-merges is specified), require it for
all modes.  Modify the am and merge backends to implement this.

Performance Considerations:

I was unable to measure any appreciable performance difference with this
change.  Trying to control the run-to-run variation was difficult; I
eventually found a headless beefy box that I could ssh into, which
seemed to help.  Using git.git, I ran the following testcase:
    $ git reset --hard v2.20.0-rc1~2
    $ time git rebase --quiet v2.20.0-rc0~16

I first ran once to warm any disk caches, then ran five subsequent runs
and recorded the times of those five.  I observed the following results
for the average time:

     Before this change:
       "real" timing: 1.340s (standard deviation: 0.040s)
       "user" timing: 1.050s (standard deviation: 0.041s)
       "sys"  timing: 0.270s (standard deviation: 0.011s)
     After  this change:
       "real" timing: 1.327s (standard deviation: 0.065s)
       "user" timing: 1.031s (standard deviation: 0.061s)
       "sys"  timing: 0.280s (standard deviation: 0.014s)

Measurements aside, I would expect the timing for walking revisions to
be dwarfed by the work involved in creating and applying patches, so
this isn't too surprising.  Further, while somewhat counter-intuitive,
it is possible that turning on topological sorting is actually a
performance improvement: by way of comparison, turning on --topo-order
made fast-export faster (see
https://public-inbox.org/git/20090211135640.GA19600@coredump.intra.peff.net/).

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-28 12:49:48 -08:00

85 lines
1.6 KiB
Bash

# This shell script fragment is sourced by git-rebase to implement
# its default, fast, patch-based, non-interactive mode.
#
# Copyright (c) 2010 Junio C Hamano.
#
git_rebase__am () {
case "$action" in
continue)
git am --resolved --resolvemsg="$resolvemsg" \
${gpg_sign_opt:+"$gpg_sign_opt"} &&
move_to_original_branch
return
;;
skip)
git am --skip --resolvemsg="$resolvemsg" &&
move_to_original_branch
return
;;
show-current-patch)
exec git am --show-current-patch
;;
esac
if test -z "$rebase_root"
# this is now equivalent to ! -z "$upstream"
then
revisions=$upstream...$orig_head
else
revisions=$onto...$orig_head
fi
ret=0
rm -f "$GIT_DIR/rebased-patches"
git format-patch -k --stdout --full-index --cherry-pick --right-only \
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
--pretty=mboxrd --topo-order \
$git_format_patch_opt \
"$revisions" ${restrict_revision+^$restrict_revision} \
>"$GIT_DIR/rebased-patches"
ret=$?
if test 0 != $ret
then
rm -f "$GIT_DIR/rebased-patches"
case "$head_name" in
refs/heads/*)
git checkout -q "$head_name"
;;
*)
git checkout -q "$orig_head"
;;
esac
cat >&2 <<-EOF
git encountered an error while preparing the patches to replay
these revisions:
$revisions
As a result, git cannot rebase them.
EOF
return $ret
fi
git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
--patch-format=mboxrd \
$allow_rerere_autoupdate \
${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
ret=$?
rm -f "$GIT_DIR/rebased-patches"
if test 0 != $ret
then
test -d "$state_dir" && write_basic_state
return $ret
fi
move_to_original_branch
}