blob: 305d6771082d557ce6584f636804f41f2886d8c1 [file]
#!/bin/sh
test_description='pseudo-merge bitmaps'
GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
. ./test-lib.sh
test_pseudo_merges () {
test-tool bitmap dump-pseudo-merges
}
test_pseudo_merge_commits () {
test-tool bitmap dump-pseudo-merge-commits "$1"
}
test_pseudo_merges_satisfied () {
test_trace2_data bitmap pseudo_merges_satisfied "$1"
}
test_pseudo_merges_cascades () {
test_trace2_data bitmap pseudo_merges_cascades "$1"
}
test_pseudo_merges_reused () {
test_trace2_data pack-bitmap-write building_bitmaps_pseudo_merge_reused "$1"
}
tag_everything () {
git rev-list --all --no-object-names >in &&
sed 's|\(.*\)|create refs/tags/\1 \1|' in |
git update-ref --stdin
}
test_expect_success 'setup' '
test_commit_bulk 512 &&
tag_everything
'
test_expect_success 'bitmap traversal without pseudo-merges' '
git repack -adb &&
git rev-list --count --all --objects >expect &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --all --objects --use-bitmap-index >actual &&
test_pseudo_merges_satisfied 0 <trace2.txt &&
test_pseudo_merges_cascades 0 <trace2.txt &&
test_pseudo_merges >merges &&
test_must_be_empty merges &&
test_cmp expect actual
'
test_expect_success 'pseudo-merges accurately represent their objects' '
test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
test_config bitmapPseudoMerge.test.maxMerges 8 &&
test_config bitmapPseudoMerge.test.stableThreshold never &&
git repack -adb &&
test_pseudo_merges >merges &&
test_line_count = 8 merges &&
for i in $(test_seq 0 $(($(wc -l <merges)-1)))
do
test-tool bitmap dump-pseudo-merge-commits $i >commits &&
git rev-list --objects --no-object-names --stdin <commits >expect.raw &&
test-tool bitmap dump-pseudo-merge-objects $i >actual.raw &&
sort -u <expect.raw >expect &&
sort -u <actual.raw >actual &&
test_cmp expect actual || return 1
done
'
test_expect_success 'bitmap traversal with pseudo-merges' '
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --all --objects --use-bitmap-index >actual &&
git rev-list --count --all --objects >expect &&
test_pseudo_merges_satisfied 8 <trace2.txt &&
test_pseudo_merges_cascades 1 <trace2.txt &&
test_cmp expect actual
'
test_expect_success 'stale bitmap traversal with pseudo-merges' '
test_commit other &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --all --objects --use-bitmap-index >actual &&
git rev-list --count --all --objects >expect &&
test_pseudo_merges_satisfied 8 <trace2.txt &&
test_pseudo_merges_cascades 1 <trace2.txt &&
test_cmp expect actual
'
test_expect_success PERL_TEST_HELPERS 'bitmapPseudoMerge.sampleRate adjusts commit selection rate' '
test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
test_config bitmapPseudoMerge.test.maxMerges 1 &&
test_config bitmapPseudoMerge.test.stableThreshold never &&
commits_nr=$(git rev-list --all --count) &&
for rate in 1.0 0.5 0.25
do
git -c bitmapPseudoMerge.test.sampleRate=$rate repack -adb &&
test_pseudo_merges >merges &&
test_line_count = 1 merges &&
test_pseudo_merge_commits 0 >commits &&
test-tool bitmap list-commits >bitmaps &&
bitmaps_nr="$(wc -l <bitmaps)" &&
perl -MPOSIX -e "print ceil(\$ARGV[0]*(\$ARGV[1]-\$ARGV[2]))" \
"$rate" "$commits_nr" "$bitmaps_nr" >expect &&
test $(cat expect) -eq $(wc -l <commits) || return 1
done
'
test_expect_success 'bitmapPseudoMerge.threshold excludes newer commits' '
git init pseudo-merge-threshold &&
(
cd pseudo-merge-threshold &&
new="1672549200" && # 2023-01-01
old="1641013200" && # 2022-01-01
GIT_COMMITTER_DATE="$new +0000" &&
export GIT_COMMITTER_DATE &&
test_commit_bulk --message="new" --notick 128 &&
GIT_COMMITTER_DATE="$old +0000" &&
export GIT_COMMITTER_DATE &&
test_commit_bulk --message="old" --notick 128 &&
tag_everything &&
git \
-c bitmapPseudoMerge.test.pattern="refs/tags/" \
-c bitmapPseudoMerge.test.maxMerges=1 \
-c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
-c bitmapPseudoMerge.test.stableThreshold=never \
repack -adb &&
test_pseudo_merges >merges &&
test_line_count = 1 merges &&
test_pseudo_merge_commits 0 >oids &&
git cat-file --batch <oids >commits &&
test $(wc -l <oids) = $(grep -c "^committer.*$old +0000$" commits)
)
'
test_expect_success 'bitmapPseudoMerge.stableThreshold creates stable groups' '
(
cd pseudo-merge-threshold &&
new="1672549200" && # 2023-01-01
mid="1654059600" && # 2022-06-01
old="1641013200" && # 2022-01-01
GIT_COMMITTER_DATE="$mid +0000" &&
export GIT_COMMITTER_DATE &&
test_commit_bulk --message="mid" --notick 128 &&
git for-each-ref --format="delete %(refname)" refs/tags >in &&
git update-ref --stdin <in &&
tag_everything &&
git \
-c bitmapPseudoMerge.test.pattern="refs/tags/" \
-c bitmapPseudoMerge.test.maxMerges=1 \
-c bitmapPseudoMerge.test.threshold=$(($new - 1)) \
-c bitmapPseudoMerge.test.stableThreshold=$(($mid - 1)) \
-c bitmapPseudoMerge.test.stableSize=10 \
repack -adb &&
test_pseudo_merges >merges &&
merges_nr="$(wc -l <merges)" &&
for i in $(test_seq $(($merges_nr - 1)))
do
test_pseudo_merge_commits 0 >oids &&
git cat-file --batch <oids >commits &&
expect="$(grep -c "^committer.*$old +0000$" commits)" &&
actual="$(wc -l <oids)" &&
test $expect = $actual || return 1
done &&
test_pseudo_merge_commits $(($merges_nr - 1)) >oids &&
git cat-file --batch <oids >commits &&
test $(wc -l <oids) = $(grep -c "^committer.*$mid +0000$" commits)
)
'
test_expect_success 'out of order thresholds are rejected' '
# Disable the test var to remove a stderr message.
test_must_fail env GIT_TEST_NAME_HASH_VERSION=1 git \
-c bitmapPseudoMerge.test.pattern="refs/*" \
-c bitmapPseudoMerge.test.threshold=1.month.ago \
-c bitmapPseudoMerge.test.stableThreshold=1.week.ago \
repack -adb 2>err &&
cat >expect <<-EOF &&
fatal: pseudo-merge group ${SQ}test${SQ} has unstable threshold before stable one
EOF
test_cmp expect err
'
test_expect_success 'pseudo-merge pattern with capture groups' '
git init pseudo-merge-captures &&
(
cd pseudo-merge-captures &&
test_commit_bulk 128 &&
tag_everything &&
for r in $(test_seq 8)
do
test_commit_bulk 16 &&
git rev-list HEAD~16.. >in &&
sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1|" in >refs &&
git update-ref --stdin <refs || return 1
done &&
git \
-c bitmapPseudoMerge.tags.pattern="refs/remotes/([0-9]+)/tags/" \
-c bitmapPseudoMerge.tags.maxMerges=1 \
repack -adb &&
git for-each-ref --format="%(objectname) %(refname)" >refs &&
test_pseudo_merges >merges &&
for m in $(test_seq 0 $(($(wc -l <merges) - 1)))
do
test_pseudo_merge_commits $m >oids &&
grep -f oids refs |
sed -n "s|refs/remotes/\([0-9][0-9]*\)/|\1|p" &&
sort -u || return 1
done >remotes &&
test $(wc -l <remotes) -eq $(sort -u <remotes | wc -l)
)
'
test_expect_success 'pseudo-merge overlap setup' '
git init pseudo-merge-overlap &&
(
cd pseudo-merge-overlap &&
test_commit_bulk 256 &&
tag_everything &&
git \
-c bitmapPseudoMerge.all.pattern="refs/" \
-c bitmapPseudoMerge.all.maxMerges=1 \
-c bitmapPseudoMerge.all.stableThreshold=never \
-c bitmapPseudoMerge.tags.pattern="refs/tags/" \
-c bitmapPseudoMerge.tags.maxMerges=1 \
-c bitmapPseudoMerge.tags.stableThreshold=never \
repack -adb
)
'
test_expect_success 'pseudo-merge overlap generates overlapping groups' '
(
cd pseudo-merge-overlap &&
test_pseudo_merges >merges &&
test_line_count = 2 merges &&
test_pseudo_merge_commits 0 >commits-0.raw &&
test_pseudo_merge_commits 1 >commits-1.raw &&
sort commits-0.raw >commits-0 &&
sort commits-1.raw >commits-1 &&
comm -12 commits-0 commits-1 >overlap &&
test_line_count -gt 0 overlap
)
'
test_expect_success 'pseudo-merge overlap traversal' '
(
cd pseudo-merge-overlap &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --all --objects --use-bitmap-index >actual &&
git rev-list --count --all --objects >expect &&
test_pseudo_merges_satisfied 2 <trace2.txt &&
test_pseudo_merges_cascades 1 <trace2.txt &&
test_cmp expect actual
)
'
test_expect_success 'pseudo-merge overlap stale traversal' '
(
cd pseudo-merge-overlap &&
test_commit other &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --all --objects --use-bitmap-index >actual &&
git rev-list --count --all --objects >expect &&
test_pseudo_merges_satisfied 2 <trace2.txt &&
test_pseudo_merges_cascades 1 <trace2.txt &&
test_cmp expect actual
)
'
test_expect_success 'pseudo-merge reuse' '
git init pseudo-merge-reuse &&
(
cd pseudo-merge-reuse &&
stable="1641013200" && # 2022-01-01
unstable="1672549200" && # 2023-01-01
GIT_COMMITTER_DATE="$stable +0000" &&
export GIT_COMMITTER_DATE &&
test_commit_bulk --notick 128 &&
GIT_COMMITTER_DATE="$unstable +0000" &&
export GIT_COMMITTER_DATE &&
test_commit_bulk --notick 128 &&
tag_everything &&
git \
-c bitmapPseudoMerge.test.pattern="refs/tags/" \
-c bitmapPseudoMerge.test.maxMerges=1 \
-c bitmapPseudoMerge.test.threshold=now \
-c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
-c bitmapPseudoMerge.test.stableSize=512 \
repack -adb &&
test_pseudo_merges >merges &&
test_line_count = 2 merges &&
test_pseudo_merge_commits 0 >stable-oids.before &&
test_pseudo_merge_commits 1 >unstable-oids.before &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt git \
-c bitmapPseudoMerge.test.pattern="refs/tags/" \
-c bitmapPseudoMerge.test.maxMerges=2 \
-c bitmapPseudoMerge.test.threshold=now \
-c bitmapPseudoMerge.test.stableThreshold=$(($unstable - 1)) \
-c bitmapPseudoMerge.test.stableSize=512 \
repack -adb &&
test_pseudo_merges_reused 1 <trace2.txt &&
test_pseudo_merges >merges &&
test_line_count = 3 merges &&
test_pseudo_merge_commits 0 >stable-oids.after &&
for i in 1 2
do
test_pseudo_merge_commits $i || return 1
done >unstable-oids.after &&
sort -u <stable-oids.before >expect &&
sort -u <stable-oids.after >actual &&
test_cmp expect actual &&
sort -u <unstable-oids.before >expect &&
sort -u <unstable-oids.after >actual &&
test_cmp expect actual
)
'
test_expect_success 'empty pseudo-merge group' '
git init pseudo-merge-empty-group &&
(
cd pseudo-merge-empty-group &&
# Ensure that a pseudo-merge group with no unstable
# commits does not generate an empty pseudo-merge
# bitmap.
git config bitmapPseudoMerge.empty.pattern refs/ &&
test_commit base &&
git repack -adb &&
test-tool bitmap dump-pseudo-merges >merges &&
test_line_count = 1 merges &&
test 0 -eq "$(grep -c commits=0 <merges)"
)
'
test_expect_success 'pseudo-merge closure' '
git init pseudo-merge-closure &&
(
cd pseudo-merge-closure &&
test_commit A &&
git repack -d &&
test_commit B &&
# Note that the contents of A is packed, but B is not. A
# (and the objects reachable from it) are thus visible
# to the MIDX, but the same is not true for B and its
# objects.
#
# Ensure that we do not attempt to create a pseudo-merge
# for B, depsite it matching the below pseudo-merge
# group pattern, as doing so would result in a failure
# to write a non-closed bitmap.
git config bitmapPseudoMerge.test.pattern refs/ &&
git config bitmapPseudoMerge.test.threshold now &&
git multi-pack-index write --bitmap &&
test-tool bitmap dump-pseudo-merges >pseudo-merges &&
test_line_count = 1 pseudo-merges &&
git rev-parse A >expect &&
test-tool bitmap list-commits >actual &&
test_cmp expect actual &&
test-tool bitmap dump-pseudo-merge-commits 0 >actual &&
test_cmp expect actual
)
'
test_expect_success 'use pseudo-merge in boundary traversal' '
git init pseudo-merge-boundary-traversal &&
(
cd pseudo-merge-boundary-traversal &&
git config bitmapPseudoMerge.test.pattern refs/ &&
git config pack.useBitmapBoundaryTraversal true &&
test_commit A &&
git repack -adb &&
test_commit B &&
nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) &&
test 1 -eq "$nr"
)
'
test_expect_success 'apply pseudo-merges during fill-in traversal' '
test_when_finished "rm -fr pseudo-merge-fill-in-traversal" &&
git init pseudo-merge-fill-in-traversal &&
(
cd pseudo-merge-fill-in-traversal &&
git config bitmapPseudoMerge.test.pattern refs/tags/ &&
git config bitmapPseudoMerge.test.maxMerges 1 &&
git config bitmapPseudoMerge.test.stableThreshold never &&
test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&
pack=$(ls .git/objects/pack/pack-*.pack) &&
git rev-parse HEAD~63 >in &&
test-tool bitmap write "$(basename $pack)" <in &&
test_pseudo_merges >merges &&
test_line_count = 1 merges &&
test_commit stale &&
git rev-list --count --objects HEAD >expect &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --objects --use-bitmap-index HEAD >actual &&
test_pseudo_merges_satisfied 1 <trace2.txt &&
test_cmp expect actual
)
'
test_expect_success 'apply pseudo-merges from multiple groups during fill-in' '
test_when_finished "rm -fr pseudo-merge-fill-in-multi" &&
git init pseudo-merge-fill-in-multi &&
(
cd pseudo-merge-fill-in-multi &&
test_commit base &&
base=$(git rev-parse HEAD) &&
for side in left right
do
git checkout -B $side base &&
test_commit_bulk --id=$side 64 &&
git rev-list --no-object-names HEAD --not $base >in &&
while read oid
do
echo "create refs/group-$side/$oid $oid" || return 1
done <in | git update-ref --stdin || return 1
done &&
git checkout left &&
git merge right &&
git repack -ad &&
git config bitmapPseudoMerge.left.pattern "refs/group-left/" &&
git config bitmapPseudoMerge.left.maxMerges 1 &&
git config bitmapPseudoMerge.left.stableThreshold never &&
git config bitmapPseudoMerge.right.pattern "refs/group-right/" &&
git config bitmapPseudoMerge.right.maxMerges 1 &&
git config bitmapPseudoMerge.right.stableThreshold never &&
pack="$(ls .git/objects/pack/pack-*.pack)" &&
git rev-parse "$base" >in &&
test-tool bitmap write "$(basename $pack)" <in &&
test_pseudo_merges >merges &&
test_line_count = 2 merges &&
test_commit stale &&
git rev-list --count --objects HEAD >expect &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --objects --use-bitmap-index HEAD >actual &&
test_pseudo_merges_satisfied 2 <trace2.txt &&
test_cmp expect actual
)
'
test_expect_success 'apply pseudo-merges with overlapping groups during fill-in' '
test_when_finished "rm -fr pseudo-merge-fill-in-overlap" &&
git init pseudo-merge-fill-in-overlap &&
(
cd pseudo-merge-fill-in-overlap &&
test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&
pack="$(ls .git/objects/pack/pack-*.pack)" &&
# Use two pseudo-merge group patterns that both match
# refs/tags/, so every tagged commit belongs to both
# groups. This exercises the extended lookup table
# path in apply_pseudo_merges_for_commit().
git config bitmapPseudoMerge.all.pattern "refs/tags/" &&
git config bitmapPseudoMerge.all.maxMerges 1 &&
git config bitmapPseudoMerge.all.stableThreshold never &&
git config bitmapPseudoMerge.tags.pattern "refs/tags/" &&
git config bitmapPseudoMerge.tags.maxMerges 1 &&
git config bitmapPseudoMerge.tags.stableThreshold never &&
git rev-parse HEAD~63 >in &&
test-tool bitmap write "$(basename $pack)" <in &&
test_pseudo_merges >merges &&
test_line_count = 2 merges &&
test_commit stale &&
git rev-list --count --objects HEAD >expect &&
: >trace2.txt &&
GIT_TRACE2_EVENT=$PWD/trace2.txt \
git rev-list --count --objects --use-bitmap-index HEAD >actual &&
test_pseudo_merges_satisfied 2 <trace2.txt &&
test_cmp expect actual
)
'
test_expect_success 'pseudo-merge commits are correctly classified by date' '
test_when_finished "rm -fr pseudo-merge-date-classification" &&
git init pseudo-merge-date-classification &&
(
cd pseudo-merge-date-classification &&
test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&
pack="$(ls .git/objects/pack/pack-*.pack)" &&
# Configure two pseudo-merge groups: one that only
# matches "stable" refs (older than one month), and
# one that matches all refs. With 64 tags whose
# commits are all younger than one month, the
# "stable" group should have zero pseudo-merges and
# the "all" group should have one.
#
# Use GIT_TEST_DATE_NOW to align "now" (and therefore
# "1.month.ago") with the test_tick timestamps so that
# the commits are within the last month.
#
# Without parsing the commit, its date field would
# be zero, causing it to satisfy date <= threshold
# for the "stable" group as well, and both groups
# would produce pseudo-merges.
git config bitmapPseudoMerge.stable.pattern "refs/tags/" &&
git config bitmapPseudoMerge.stable.maxMerges 64 &&
git config bitmapPseudoMerge.stable.stableThreshold never &&
git config bitmapPseudoMerge.stable.threshold 1.month.ago &&
git config bitmapPseudoMerge.all.pattern "refs/tags/" &&
git config bitmapPseudoMerge.all.maxMerges 1 &&
git config bitmapPseudoMerge.all.stableThreshold never &&
git config bitmapPseudoMerge.all.threshold now &&
git rev-parse HEAD~63 >in &&
GIT_TEST_DATE_NOW=$test_tick \
test-tool bitmap write "$(basename $pack)" <in &&
test_pseudo_merges >merges &&
test_line_count = 1 merges
)
'
test_expect_success 'sampleRate=0 does not cause division by zero' '
test_when_finished "rm -fr pseudo-merge-sample-rate-zero" &&
git init pseudo-merge-sample-rate-zero &&
(
cd pseudo-merge-sample-rate-zero &&
test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&
pack="$(ls .git/objects/pack/pack-*.pack)" &&
git config bitmapPseudoMerge.test.pattern "refs/tags/" &&
git config bitmapPseudoMerge.test.maxMerges 1 &&
git config bitmapPseudoMerge.test.sampleRate 0 &&
git config bitmapPseudoMerge.test.threshold now &&
git config bitmapPseudoMerge.test.stableThreshold never &&
git rev-parse HEAD~63 >in &&
test-tool bitmap write "$(basename $pack)" <in
)
'
test_expect_success 'duplicate pseudo-merge pattern does not leak' '
test_when_finished "rm -fr pseudo-merge-dup-pattern" &&
git init pseudo-merge-dup-pattern &&
(
cd pseudo-merge-dup-pattern &&
test_commit_bulk 64 &&
tag_everything &&
git repack -ad &&
pack=$(ls .git/objects/pack/pack-*.pack) &&
# Set the same group'\''s pattern twice. The second
# assignment should cleanly release the compiled regex
# from the first without leaking.
git config bitmapPseudoMerge.test.pattern "refs/tags/" &&
git config --add bitmapPseudoMerge.test.pattern "refs/tags/" &&
git config bitmapPseudoMerge.test.maxMerges 1 &&
git config bitmapPseudoMerge.test.threshold now &&
git config bitmapPseudoMerge.test.stableThreshold never &&
git rev-parse HEAD~63 >in &&
test-tool bitmap write "$(basename $pack)" <in &&
test_pseudo_merges >merges &&
test_line_count = 1 merges
)
'
test_done