git/t/t3402-rebase-merge.sh
Jonathan Tan 0fcb4f6b62 rebase --merge: optionally skip upstreamed commits
When rebasing against an upstream that has had many commits since the
original branch was created:

 O -- O -- ... -- O -- O (upstream)
  \
   -- O (my-dev-branch)

it must read the contents of every novel upstream commit, in addition to
the tip of the upstream and the merge base, because "git rebase"
attempts to exclude commits that are duplicates of upstream ones. This
can be a significant performance hit, especially in a partial clone,
wherein a read of an object may end up being a fetch.

Add a flag to "git rebase" to allow suppression of this feature. This
flag only works when using the "merge" backend.

This flag changes the behavior of sequencer_make_script(), called from
do_interactive_rebase() <- run_rebase_interactive() <-
run_specific_rebase() <- cmd_rebase(). With this flag, limit_list()
(indirectly called from sequencer_make_script() through
prepare_revision_walk()) will no longer call cherry_pick_list(), and
thus PATCHSAME is no longer set. Refraining from setting PATCHSAME both
means that the intermediate commits in upstream are no longer read (as
shown by the test) and means that no PATCHSAME-caused skipping of
commits is done by sequencer_make_script(), either directly or through
make_script_with_merges().

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 14:15:57 -07:00

242 lines
6.3 KiB
Bash
Executable file

#!/bin/sh
#
# Copyright (c) 2006 Junio C Hamano
#
test_description='git rebase --merge test'
. ./test-lib.sh
T="A quick brown fox
jumps over the lazy dog."
for i in 1 2 3 4 5 6 7 8 9 10
do
echo "$i $T"
done >original
test_expect_success setup '
git add original &&
git commit -m"initial" &&
git branch side &&
echo "11 $T" >>original &&
git commit -a -m"master updates a bit." &&
echo "12 $T" >>original &&
git commit -a -m"master updates a bit more." &&
git checkout side &&
(echo "0 $T" && cat original) >renamed &&
git add renamed &&
git update-index --force-remove original &&
git commit -a -m"side renames and edits." &&
tr "[a-z]" "[A-Z]" <original >newfile &&
git add newfile &&
git commit -a -m"side edits further." &&
git branch second-side &&
tr "[a-m]" "[A-M]" <original >newfile &&
rm -f original &&
git commit -a -m"side edits once again." &&
git branch test-rebase side &&
git branch test-rebase-pick side &&
git branch test-reference-pick side &&
git branch test-conflicts side &&
git checkout -b test-merge side
'
test_expect_success 'reference merge' '
git merge -s recursive -m "reference merge" master
'
PRE_REBASE=$(git rev-parse test-rebase)
test_expect_success rebase '
git checkout test-rebase &&
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' '
git diff-tree -r test-rebase test-merge >difference &&
if test -s difference
then
cat difference
(exit 1)
else
echo happy
fi
'
test_expect_success 'rebase the other way' '
git reset --hard master &&
git rebase --merge side
'
test_expect_success 'rebase -Xtheirs' '
git checkout -b conflicting master~2 &&
echo "AB $T" >> original &&
git commit -mconflicting original &&
git rebase -Xtheirs master &&
grep AB original &&
! grep 11 original
'
test_expect_success 'rebase -Xtheirs from orphan' '
git checkout --orphan orphan-conflicting master~2 &&
echo "AB $T" >> original &&
git commit -morphan-conflicting original &&
git rebase -Xtheirs master &&
grep AB original &&
! grep 11 original
'
test_expect_success 'merge and rebase should match' '
git diff-tree -r test-rebase test-merge >difference &&
if test -s difference
then
cat difference
(exit 1)
else
echo happy
fi
'
test_expect_success 'picking rebase' '
git reset --hard side &&
git rebase --merge --onto master side^^ &&
mb=$(git merge-base master HEAD) &&
if test "$mb" = "$(git rev-parse master)"
then
echo happy
else
git show-branch
(exit 1)
fi &&
f=$(git diff-tree --name-only HEAD^ HEAD) &&
g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
case "$f,$g" in
newfile,newfile)
echo happy ;;
*)
echo "$f"
echo "$g"
(exit 1)
esac
'
test_expect_success 'rebase -s funny -Xopt' '
test_when_finished "rm -fr test-bin funny.was.run" &&
mkdir test-bin &&
cat >test-bin/git-merge-funny <<-EOF &&
#!$SHELL_PATH
case "\$1" in --opt) ;; *) exit 2 ;; esac
shift &&
>funny.was.run &&
exec git merge-recursive "\$@"
EOF
chmod +x test-bin/git-merge-funny &&
git reset --hard &&
git checkout -b test-funny master^ &&
test_commit funny &&
(
PATH=./test-bin:$PATH &&
git rebase -s funny -Xopt master
) &&
test -f funny.was.run
'
test_expect_success 'rebase --skip works with two conflicts in a row' '
git checkout second-side &&
tr "[A-Z]" "[a-z]" <newfile >tmp &&
mv tmp newfile &&
git commit -a -m"edit conflicting with side" &&
tr "[d-f]" "[D-F]" <newfile >tmp &&
mv tmp newfile &&
git commit -a -m"another edit conflicting with side" &&
test_must_fail git rebase --merge test-conflicts &&
test_must_fail git rebase --skip &&
git rebase --skip
'
test_expect_success '--reapply-cherry-picks' '
git init repo &&
# O(1-10) -- O(1-11) -- O(0-10) master
# \
# -- O(1-11) -- O(1-12) otherbranch
printf "Line %d\n" $(test_seq 1 10) >repo/file.txt &&
git -C repo add file.txt &&
git -C repo commit -m "base commit" &&
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
git -C repo commit -a -m "add 11" &&
printf "Line %d\n" $(test_seq 0 10) >repo/file.txt &&
git -C repo commit -a -m "add 0 delete 11" &&
git -C repo checkout -b otherbranch HEAD^^ &&
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
git -C repo commit -a -m "add 11 in another branch" &&
printf "Line %d\n" $(test_seq 1 12) >repo/file.txt &&
git -C repo commit -a -m "add 12 in another branch" &&
# Regular rebase fails, because the 1-11 commit is deduplicated
test_must_fail git -C repo rebase --merge master 2> err &&
test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
git -C repo rebase --abort &&
# With --reapply-cherry-picks, it works
git -C repo rebase --merge --reapply-cherry-picks master
'
test_expect_success '--reapply-cherry-picks refrains from reading unneeded blobs' '
git init server &&
# O(1-10) -- O(1-11) -- O(1-12) master
# \
# -- O(0-10) otherbranch
printf "Line %d\n" $(test_seq 1 10) >server/file.txt &&
git -C server add file.txt &&
git -C server commit -m "merge base" &&
printf "Line %d\n" $(test_seq 1 11) >server/file.txt &&
git -C server commit -a -m "add 11" &&
printf "Line %d\n" $(test_seq 1 12) >server/file.txt &&
git -C server commit -a -m "add 12" &&
git -C server checkout -b otherbranch HEAD^^ &&
printf "Line %d\n" $(test_seq 0 10) >server/file.txt &&
git -C server commit -a -m "add 0" &&
test_config -C server uploadpack.allowfilter 1 &&
test_config -C server uploadpack.allowanysha1inwant 1 &&
git clone --filter=blob:none "file://$(pwd)/server" client &&
git -C client checkout origin/master &&
git -C client checkout origin/otherbranch &&
# Sanity check to ensure that the blobs from the merge base and "add
# 11" are missing
git -C client rev-list --objects --all --missing=print >missing_list &&
MERGE_BASE_BLOB=$(git -C server rev-parse master^^:file.txt) &&
ADD_11_BLOB=$(git -C server rev-parse master^:file.txt) &&
grep "[?]$MERGE_BASE_BLOB" missing_list &&
grep "[?]$ADD_11_BLOB" missing_list &&
git -C client rebase --merge --reapply-cherry-picks origin/master &&
# The blob from the merge base had to be fetched, but not "add 11"
git -C client rev-list --objects --all --missing=print >missing_list &&
! grep "[?]$MERGE_BASE_BLOB" missing_list &&
grep "[?]$ADD_11_BLOB" missing_list
'
test_done