git/t/t5314-pack-cycle-detection.sh
SZEDER Gábor 1c5e94f459 tests: use 'test_must_be_empty' instead of 'test_cmp <empty> <out>'
Using 'test_must_be_empty' is shorter and more idiomatic than

  >empty &&
  test_cmp empty out

as it saves the creation of an empty file.  Furthermore, sometimes the
expected empty file doesn't have such a descriptive name like 'empty',
and its creation is far away from the place where it's finally used
for comparison (e.g. in 't7600-merge.sh', where two expected empty
files are created in the 'setup' test, but are used only about 500
lines later).

These cases were found by instrumenting 'test_cmp' to error out the
test script when it's used to compare empty files, and then converted
manually.

Note that even after this patch there still remain a lot of cases
where we use 'test_cmp' to check empty files:

  - Sometimes the expected output is not hard-coded in the test, but
    'test_cmp' is used to ensure that two similar git commands produce
    the same output, and that output happens to be empty, e.g. the
    test 'submodule update --merge  - ignores --merge  for new
    submodules' in 't7406-submodule-update.sh'.

  - Repetitive common tasks, including preparing the expected results
    and running 'test_cmp', are often extracted into a helper
    function, and some of this helper's callsites expect no output.

  - For the same reason as above, the whole 'test_expect_success'
    block is within a helper function, e.g. in 't3070-wildmatch.sh'.

  - Or 'test_cmp' is invoked in a loop, e.g. the test 'cvs update
    (-p)' in 't9400-git-cvsserver-server.sh'.

Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-21 11:48:36 -07:00

113 lines
3.8 KiB
Bash
Executable File

#!/bin/sh
test_description='test handling of inter-pack delta cycles during repack
The goal here is to create a situation where we have two blobs, A and B, with A
as a delta against B in one pack, and vice versa in the other. Then if we can
persuade a full repack to find A from one pack and B from the other, that will
give us a cycle when we attempt to reuse those deltas.
The trick is in the "persuade" step, as it depends on the internals of how
pack-objects picks which pack to reuse the deltas from. But we can assume
that it does so in one of two general strategies:
1. Using a static ordering of packs. In this case, no inter-pack cycles can
happen. Any objects with a delta relationship must be present in the same
pack (i.e., no "--thin" packs on disk), so we will find all related objects
from that pack. So assuming there are no cycles within a single pack (and
we avoid generating them via pack-objects or importing them via
index-pack), then our result will have no cycles.
So this case should pass the tests no matter how we arrange things.
2. Picking the next pack to examine based on locality (i.e., where we found
something else recently).
In this case, we want to make sure that we find the delta versions of A and
B and not their base versions. We can do this by putting two blobs in each
pack. The first is a "dummy" blob that can only be found in the pack in
question. And then the second is the actual delta we want to find.
The two blobs must be present in the same tree, not present in other trees,
and the dummy pathname must sort before the delta path.
The setup below focuses on case 2. We have two commits HEAD and HEAD^, each
which has two files: "dummy" and "file". Then we can make two packs which
contain:
[pack one]
HEAD:dummy
HEAD:file (as delta against HEAD^:file)
HEAD^:file (as base)
[pack two]
HEAD^:dummy
HEAD^:file (as delta against HEAD:file)
HEAD:file (as base)
Then no matter which order we start looking at the packs in, we know that we
will always find a delta for "file", because its lookup will always come
immediately after the lookup for "dummy".
'
. ./test-lib.sh
# Create a pack containing the the tree $1 and blob $1:file, with
# the latter stored as a delta against $2:file.
#
# We convince pack-objects to make the delta in the direction of our choosing
# by marking $2 as a preferred-base edge. That results in $1:file as a thin
# delta, and index-pack completes it by adding $2:file as a base.
#
# Note that the two variants of "file" must be similar enough to convince git
# to create the delta.
make_pack () {
{
printf '%s\n' "-$(git rev-parse $2)"
printf '%s dummy\n' "$(git rev-parse $1:dummy)"
printf '%s file\n' "$(git rev-parse $1:file)"
} |
git pack-objects --stdout |
git index-pack --stdin --fix-thin
}
test_expect_success 'setup' '
test-tool genrandom base 4096 >base &&
for i in one two
do
# we want shared content here to encourage deltas...
cp base file &&
echo $i >>file &&
# ...whereas dummy should be short, because we do not want
# deltas that would create duplicates when we --fix-thin
echo $i >dummy &&
git add file dummy &&
test_tick &&
git commit -m $i ||
return 1
done &&
make_pack HEAD^ HEAD &&
make_pack HEAD HEAD^
'
test_expect_success 'repack' '
# We first want to check that we do not have any internal errors,
# and also that we do not hit the last-ditch cycle-breaking code
# in write_object(), which will issue a warning to stderr.
git repack -ad 2>stderr &&
test_must_be_empty stderr &&
# And then double-check that the resulting pack is usable (i.e.,
# we did not fail to notice any cycles). We know we are accessing
# the objects via the new pack here, because "repack -d" will have
# removed the others.
git cat-file blob HEAD:file >/dev/null &&
git cat-file blob HEAD^:file >/dev/null
'
test_done