2018-07-12 21:39:21 +02:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='multi-pack-indexes'
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
maintenance: add incremental-repack task
The previous change cleaned up loose objects using the
'loose-objects' that can be run safely in the background. Add a
similar job that performs similar cleanups for pack-files.
One issue with running 'git repack' is that it is designed to
repack all pack-files into a single pack-file. While this is the
most space-efficient way to store object data, it is not time or
memory efficient. This becomes extremely important if the repo is
so large that a user struggles to store two copies of the pack on
their disk.
Instead, perform an "incremental" repack by collecting a few small
pack-files into a new pack-file. The multi-pack-index facilitates
this process ever since 'git multi-pack-index expire' was added in
19575c7 (multi-pack-index: implement 'expire' subcommand,
2019-06-10) and 'git multi-pack-index repack' was added in ce1e4a1
(midx: implement midx_repack(), 2019-06-10).
The 'incremental-repack' task runs the following steps:
1. 'git multi-pack-index write' creates a multi-pack-index file if
one did not exist, and otherwise will update the multi-pack-index
with any new pack-files that appeared since the last write. This
is particularly relevant with the background fetch job.
When the multi-pack-index sees two copies of the same object, it
stores the offset data into the newer pack-file. This means that
some old pack-files could become "unreferenced" which I will use
to mean "a pack-file that is in the pack-file list of the
multi-pack-index but none of the objects in the multi-pack-index
reference a location inside that pack-file."
2. 'git multi-pack-index expire' deletes any unreferenced pack-files
and updaes the multi-pack-index to drop those pack-files from the
list. This is safe to do as concurrent Git processes will see the
multi-pack-index and not open those packs when looking for object
contents. (Similar to the 'loose-objects' job, there are some Git
commands that open pack-files regardless of the multi-pack-index,
but they are rarely used. Further, a user that self-selects to
use background operations would likely refrain from using those
commands.)
3. 'git multi-pack-index repack --bacth-size=<size>' collects a set
of pack-files that are listed in the multi-pack-index and creates
a new pack-file containing the objects whose offsets are listed
by the multi-pack-index to be in those objects. The set of pack-
files is selected greedily by sorting the pack-files by modified
time and adding a pack-file to the set if its "expected size" is
smaller than the batch size until the total expected size of the
selected pack-files is at least the batch size. The "expected
size" is calculated by taking the size of the pack-file divided
by the number of objects in the pack-file and multiplied by the
number of objects from the multi-pack-index with offset in that
pack-file. The expected size approximates how much data from that
pack-file will contribute to the resulting pack-file size. The
intention is that the resulting pack-file will be close in size
to the provided batch size.
The next run of the incremental-repack task will delete these
repacked pack-files during the 'expire' step.
In this version, the batch size is set to "0" which ignores the
size restrictions when selecting the pack-files. It instead
selects all pack-files and repacks all packed objects into a
single pack-file. This will be updated in the next change, but
it requires doing some calculations that are better isolated to
a separate change.
These steps are based on a similar background maintenance step in
Scalar (and VFS for Git) [1]. This was incredibly effective for
users of the Windows OS repository. After using the same VFS for Git
repository for over a year, some users had _thousands_ of pack-files
that combined to up to 250 GB of data. We noticed a few users were
running into the open file descriptor limits (due in part to a bug
in the multi-pack-index fixed by af96fe3 (midx: add packs to
packed_git linked list, 2019-04-29).
These pack-files were mostly small since they contained the commits
and trees that were pushed to the origin in a given hour. The GVFS
protocol includes a "prefetch" step that asks for pre-computed pack-
files containing commits and trees by timestamp. These pack-files
were grouped into "daily" pack-files once a day for up to 30 days.
If a user did not request prefetch packs for over 30 days, then they
would get the entire history of commits and trees in a new, large
pack-file. This led to a large number of pack-files that had poor
delta compression.
By running this pack-file maintenance step once per day, these repos
with thousands of packs spanning 200+ GB dropped to dozens of pack-
files spanning 30-50 GB. This was done all without removing objects
from the system and using a constant batch size of two gigabytes.
Once the work was done to reduce the pack-files to small sizes, the
batch size of two gigabytes means that not every run triggers a
repack operation, so the following run will not expire a pack-file.
This has kept these repos in a "clean" state.
[1] https://github.com/microsoft/scalar/blob/master/Scalar.Common/Maintenance/PackfileMaintenanceStep.cs
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-25 14:33:36 +02:00
|
|
|
GIT_TEST_MULTI_PACK_INDEX=0
|
2018-07-12 21:39:33 +02:00
|
|
|
objdir=.git/objects
|
|
|
|
|
2020-08-17 16:04:48 +02:00
|
|
|
HASH_LEN=$(test_oid rawsz)
|
|
|
|
|
2018-07-12 21:39:23 +02:00
|
|
|
midx_read_expect () {
|
2018-07-12 21:39:26 +02:00
|
|
|
NUM_PACKS=$1
|
2018-07-12 21:39:31 +02:00
|
|
|
NUM_OBJECTS=$2
|
2018-07-12 21:39:32 +02:00
|
|
|
NUM_CHUNKS=$3
|
|
|
|
OBJECT_DIR=$4
|
|
|
|
EXTRA_CHUNKS="$5"
|
2018-07-12 21:39:28 +02:00
|
|
|
{
|
|
|
|
cat <<-EOF &&
|
2020-08-17 16:04:48 +02:00
|
|
|
header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS
|
2018-07-12 21:39:32 +02:00
|
|
|
chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS
|
2018-07-12 21:39:31 +02:00
|
|
|
num_objects: $NUM_OBJECTS
|
2018-07-12 21:39:28 +02:00
|
|
|
packs:
|
|
|
|
EOF
|
|
|
|
if test $NUM_PACKS -ge 1
|
|
|
|
then
|
2018-07-12 21:39:32 +02:00
|
|
|
ls $OBJECT_DIR/pack/ | grep idx | sort
|
2018-07-12 21:39:28 +02:00
|
|
|
fi &&
|
2018-07-12 21:39:32 +02:00
|
|
|
printf "object-dir: $OBJECT_DIR\n"
|
2018-07-12 21:39:28 +02:00
|
|
|
} >expect &&
|
2018-07-12 21:39:32 +02:00
|
|
|
test-tool read-midx $OBJECT_DIR >actual &&
|
2018-07-12 21:39:23 +02:00
|
|
|
test_cmp expect actual
|
|
|
|
}
|
|
|
|
|
2019-12-21 20:49:26 +01:00
|
|
|
test_expect_success 'setup' '
|
|
|
|
test_oid_cache <<-EOF
|
|
|
|
idxoff sha1:2999
|
|
|
|
idxoff sha256:3739
|
|
|
|
|
|
|
|
packnameoff sha1:652
|
|
|
|
packnameoff sha256:940
|
|
|
|
|
|
|
|
fanoutoff sha1:1
|
|
|
|
fanoutoff sha256:3
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2020-03-28 23:18:22 +01:00
|
|
|
test_expect_success "don't write midx with no packs" '
|
|
|
|
test_must_fail git multi-pack-index --object-dir=. write &&
|
|
|
|
test_path_is_missing pack/multi-pack-index
|
|
|
|
'
|
|
|
|
|
2020-08-17 16:04:48 +02:00
|
|
|
test_expect_success SHA1 'warn if a midx contains no oid' '
|
2020-03-28 23:18:22 +01:00
|
|
|
cp "$TEST_DIRECTORY"/t5319/no-objects.midx $objdir/pack/multi-pack-index &&
|
|
|
|
test_must_fail git multi-pack-index verify &&
|
|
|
|
rm $objdir/pack/multi-pack-index
|
2018-07-12 21:39:21 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:24 +02:00
|
|
|
generate_objects () {
|
|
|
|
i=$1
|
|
|
|
iii=$(printf '%03i' $i)
|
|
|
|
{
|
|
|
|
test-tool genrandom "bar" 200 &&
|
|
|
|
test-tool genrandom "baz $iii" 50
|
|
|
|
} >wide_delta_$iii &&
|
|
|
|
{
|
|
|
|
test-tool genrandom "foo"$i 100 &&
|
|
|
|
test-tool genrandom "foo"$(( $i + 1 )) 100 &&
|
|
|
|
test-tool genrandom "foo"$(( $i + 2 )) 100
|
|
|
|
} >deep_delta_$iii &&
|
|
|
|
{
|
|
|
|
echo $iii &&
|
|
|
|
test-tool genrandom "$iii" 8192
|
|
|
|
} >file_$iii &&
|
|
|
|
git update-index --add file_$iii deep_delta_$iii wide_delta_$iii
|
|
|
|
}
|
|
|
|
|
|
|
|
commit_and_list_objects () {
|
|
|
|
{
|
|
|
|
echo 101 &&
|
|
|
|
test-tool genrandom 100 8192;
|
|
|
|
} >file_101 &&
|
|
|
|
git update-index --add file_101 &&
|
|
|
|
tree=$(git write-tree) &&
|
|
|
|
commit=$(git commit-tree $tree -p HEAD</dev/null) &&
|
|
|
|
{
|
|
|
|
echo $tree &&
|
|
|
|
git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
|
|
|
|
} >obj-list &&
|
|
|
|
git reset --hard $commit
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'create objects' '
|
|
|
|
test_commit initial &&
|
|
|
|
for i in $(test_seq 1 5)
|
|
|
|
do
|
|
|
|
generate_objects $i
|
|
|
|
done &&
|
|
|
|
commit_and_list_objects
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'write midx with one v1 pack' '
|
2018-07-12 21:39:33 +02:00
|
|
|
pack=$(git pack-objects --index-version=1 $objdir/pack/test <obj-list) &&
|
|
|
|
test_when_finished rm $objdir/pack/test-$pack.pack \
|
|
|
|
$objdir/pack/test-$pack.idx $objdir/pack/multi-pack-index &&
|
|
|
|
git multi-pack-index --object-dir=$objdir write &&
|
|
|
|
midx_read_expect 1 18 4 $objdir
|
2018-07-12 21:39:24 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:33 +02:00
|
|
|
midx_git_two_modes () {
|
2019-04-05 20:04:56 +02:00
|
|
|
git -c core.multiPackIndex=false $1 >expect &&
|
|
|
|
git -c core.multiPackIndex=true $1 >actual &&
|
2018-08-20 18:52:06 +02:00
|
|
|
if [ "$2" = "sorted" ]
|
|
|
|
then
|
2019-04-05 20:04:56 +02:00
|
|
|
sort <expect >expect.sorted &&
|
|
|
|
mv expect.sorted expect &&
|
|
|
|
sort <actual >actual.sorted &&
|
|
|
|
mv actual.sorted actual
|
2018-08-20 18:52:06 +02:00
|
|
|
fi &&
|
2018-07-12 21:39:33 +02:00
|
|
|
test_cmp expect actual
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_results_with_midx () {
|
|
|
|
MSG=$1
|
|
|
|
test_expect_success "check normal git operations: $MSG" '
|
|
|
|
midx_git_two_modes "rev-list --objects --all" &&
|
2018-08-20 18:52:06 +02:00
|
|
|
midx_git_two_modes "log --raw" &&
|
|
|
|
midx_git_two_modes "count-objects --verbose" &&
|
2019-04-05 20:05:03 +02:00
|
|
|
midx_git_two_modes "cat-file --batch-all-objects --batch-check" &&
|
|
|
|
midx_git_two_modes "cat-file --batch-all-objects --batch-check --unordered" sorted
|
2018-07-12 21:39:33 +02:00
|
|
|
'
|
|
|
|
}
|
|
|
|
|
2018-07-12 21:39:24 +02:00
|
|
|
test_expect_success 'write midx with one v2 pack' '
|
2018-07-12 21:39:33 +02:00
|
|
|
git pack-objects --index-version=2,0x40 $objdir/pack/test <obj-list &&
|
|
|
|
git multi-pack-index --object-dir=$objdir write &&
|
|
|
|
midx_read_expect 1 18 4 $objdir
|
2018-07-12 21:39:24 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:33 +02:00
|
|
|
compare_results_with_midx "one v2 pack"
|
|
|
|
|
packfile.c: protect against disappearing indexes
In 17c35c8969 (packfile: skip loading index if in multi-pack-index,
2018-07-12) we stopped loading the .idx file for packs that are
contained within a multi-pack index.
This saves us the effort of loading an .idx and doing some lightweight
validity checks by way of 'packfile.c:load_idx()', but introduces a race
between processes that need to load the index (e.g., to generate a
reverse index) and processes that can delete the index.
For example, running the following in your shell:
$ git init repo && cd repo
$ git commit --allow-empty -m 'base'
$ git repack -ad && git multi-pack-index write
followed by:
$ rm -f .git/objects/pack/pack-*.idx
$ git rev-parse HEAD | git cat-file --batch-check='%(objectsize:disk)'
will result in a segfault prior to this patch. What's happening here is
that we notice that the pack is in the multi-pack index, and so don't
check that it still has a .idx. When we then try and load that index to
generate a reverse index, we don't have it, so the call to
'find_pack_revindex()' in 'packfile.c:packed_object_info()' returns
NULL, and then dereferencing it causes a segfault.
Of course, we don't ever expect someone to remove the index file by
hand, or to be in a state where we never wrote it to begin with (yet
find that pack in the multi-pack-index). But, this can happen in a
timing race with 'git repack -ad', which removes all existing packs
after writing a new pack containing all of their objects.
Avoid this by reverting the hunk of 17c35c8969 which stops loading the
index when the pack is contained in a MIDX. This makes the latter half
of 17c35c8969 useless, since we'll always have a non-NULL
'p->index_data', in which case that if statement isn't guarding
anything.
These two together effectively revert 17c35c8969, and avoid the race
explained above.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:28 +01:00
|
|
|
test_expect_success 'corrupt idx reports errors' '
|
2019-04-05 20:06:22 +02:00
|
|
|
idx=$(test-tool read-midx $objdir | grep "\.idx\$") &&
|
|
|
|
mv $objdir/pack/$idx backup-$idx &&
|
|
|
|
test_when_finished "mv backup-\$idx \$objdir/pack/\$idx" &&
|
|
|
|
|
|
|
|
# This is the minimum size for a sha-1 based .idx; this lets
|
|
|
|
# us pass perfunctory tests, but anything that actually opens and reads
|
|
|
|
# the idx file will complain.
|
|
|
|
test_copy_bytes 1064 <backup-$idx >$objdir/pack/$idx &&
|
|
|
|
|
|
|
|
git -c core.multiPackIndex=true rev-list --objects --all 2>err &&
|
packfile.c: protect against disappearing indexes
In 17c35c8969 (packfile: skip loading index if in multi-pack-index,
2018-07-12) we stopped loading the .idx file for packs that are
contained within a multi-pack index.
This saves us the effort of loading an .idx and doing some lightweight
validity checks by way of 'packfile.c:load_idx()', but introduces a race
between processes that need to load the index (e.g., to generate a
reverse index) and processes that can delete the index.
For example, running the following in your shell:
$ git init repo && cd repo
$ git commit --allow-empty -m 'base'
$ git repack -ad && git multi-pack-index write
followed by:
$ rm -f .git/objects/pack/pack-*.idx
$ git rev-parse HEAD | git cat-file --batch-check='%(objectsize:disk)'
will result in a segfault prior to this patch. What's happening here is
that we notice that the pack is in the multi-pack index, and so don't
check that it still has a .idx. When we then try and load that index to
generate a reverse index, we don't have it, so the call to
'find_pack_revindex()' in 'packfile.c:packed_object_info()' returns
NULL, and then dereferencing it causes a segfault.
Of course, we don't ever expect someone to remove the index file by
hand, or to be in a state where we never wrote it to begin with (yet
find that pack in the multi-pack-index). But, this can happen in a
timing race with 'git repack -ad', which removes all existing packs
after writing a new pack containing all of their objects.
Avoid this by reverting the hunk of 17c35c8969 which stops loading the
index when the pack is contained in a MIDX. This makes the latter half
of 17c35c8969 useless, since we'll always have a non-NULL
'p->index_data', in which case that if statement isn't guarding
anything.
These two together effectively revert 17c35c8969, and avoid the race
explained above.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:28 +01:00
|
|
|
grep "index unavailable" err
|
2019-04-05 20:06:22 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:24 +02:00
|
|
|
test_expect_success 'add more objects' '
|
|
|
|
for i in $(test_seq 6 10)
|
|
|
|
do
|
|
|
|
generate_objects $i
|
|
|
|
done &&
|
|
|
|
commit_and_list_objects
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'write midx with two packs' '
|
2018-07-12 21:39:33 +02:00
|
|
|
git pack-objects --index-version=1 $objdir/pack/test-2 <obj-list &&
|
|
|
|
git multi-pack-index --object-dir=$objdir write &&
|
|
|
|
midx_read_expect 2 34 4 $objdir
|
2018-07-12 21:39:24 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:33 +02:00
|
|
|
compare_results_with_midx "two packs"
|
|
|
|
|
2019-10-21 20:40:03 +02:00
|
|
|
test_expect_success 'write progress off for redirected stderr' '
|
|
|
|
git multi-pack-index --object-dir=$objdir write 2>err &&
|
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'write force progress on for stderr' '
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress write 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_file_not_empty err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'write with the --no-progress option' '
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:24 +02:00
|
|
|
test_expect_success 'add more packs' '
|
|
|
|
for j in $(test_seq 11 20)
|
|
|
|
do
|
|
|
|
generate_objects $j &&
|
|
|
|
commit_and_list_objects &&
|
2018-07-12 21:39:33 +02:00
|
|
|
git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list
|
2018-07-12 21:39:24 +02:00
|
|
|
done
|
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:33 +02:00
|
|
|
compare_results_with_midx "mixed mode (two packs + extra)"
|
|
|
|
|
2018-07-12 21:39:24 +02:00
|
|
|
test_expect_success 'write midx with twelve packs' '
|
2018-07-12 21:39:33 +02:00
|
|
|
git multi-pack-index --object-dir=$objdir write &&
|
|
|
|
midx_read_expect 12 74 4 $objdir
|
2018-07-12 21:39:32 +02:00
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:33 +02:00
|
|
|
compare_results_with_midx "twelve packs"
|
|
|
|
|
2020-08-17 16:04:48 +02:00
|
|
|
test_expect_success 'warn on improper hash version' '
|
|
|
|
git init --object-format=sha1 sha1 &&
|
|
|
|
(
|
|
|
|
cd sha1 &&
|
|
|
|
git config core.multiPackIndex true &&
|
|
|
|
test_commit 1 &&
|
|
|
|
git repack -a &&
|
|
|
|
git multi-pack-index write &&
|
|
|
|
mv .git/objects/pack/multi-pack-index ../mpi-sha1
|
|
|
|
) &&
|
|
|
|
git init --object-format=sha256 sha256 &&
|
|
|
|
(
|
|
|
|
cd sha256 &&
|
|
|
|
git config core.multiPackIndex true &&
|
|
|
|
test_commit 1 &&
|
|
|
|
git repack -a &&
|
|
|
|
git multi-pack-index write &&
|
|
|
|
mv .git/objects/pack/multi-pack-index ../mpi-sha256
|
|
|
|
) &&
|
|
|
|
(
|
|
|
|
cd sha1 &&
|
|
|
|
mv ../mpi-sha256 .git/objects/pack/multi-pack-index &&
|
|
|
|
git log -1 2>err &&
|
|
|
|
test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err
|
|
|
|
) &&
|
|
|
|
(
|
|
|
|
cd sha256 &&
|
|
|
|
mv ../mpi-sha1 .git/objects/pack/multi-pack-index &&
|
|
|
|
git log -1 2>err &&
|
|
|
|
test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2021-03-30 17:04:11 +02:00
|
|
|
test_expect_success 'midx picks objects from preferred pack' '
|
|
|
|
test_when_finished rm -rf preferred.git &&
|
|
|
|
git init --bare preferred.git &&
|
|
|
|
(
|
|
|
|
cd preferred.git &&
|
|
|
|
|
|
|
|
a=$(echo "a" | git hash-object -w --stdin) &&
|
|
|
|
b=$(echo "b" | git hash-object -w --stdin) &&
|
|
|
|
c=$(echo "c" | git hash-object -w --stdin) &&
|
|
|
|
|
|
|
|
# Set up two packs, duplicating the object "B" at different
|
|
|
|
# offsets.
|
|
|
|
#
|
|
|
|
# Note that the "BC" pack (the one we choose as preferred) sorts
|
|
|
|
# lexically after the "AB" pack, meaning that omitting the
|
|
|
|
# --preferred-pack argument would cause this test to fail (since
|
|
|
|
# the MIDX code would select the copy of "b" in the "AB" pack).
|
|
|
|
git pack-objects objects/pack/test-AB <<-EOF &&
|
|
|
|
$a
|
|
|
|
$b
|
|
|
|
EOF
|
|
|
|
bc=$(git pack-objects objects/pack/test-BC <<-EOF
|
|
|
|
$b
|
|
|
|
$c
|
|
|
|
EOF
|
|
|
|
) &&
|
|
|
|
|
|
|
|
git multi-pack-index --object-dir=objects \
|
|
|
|
write --preferred-pack=test-BC-$bc.idx 2>err &&
|
|
|
|
test_must_be_empty err &&
|
|
|
|
|
|
|
|
test-tool read-midx --show-objects objects >out &&
|
|
|
|
|
|
|
|
ofs=$(git show-index <objects/pack/test-BC-$bc.idx | grep $b |
|
|
|
|
cut -d" " -f1) &&
|
|
|
|
printf "%s %s\tobjects/pack/test-BC-%s.pack\n" \
|
|
|
|
"$b" "$ofs" "$bc" >expect &&
|
|
|
|
grep ^$b out >actual &&
|
|
|
|
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
2020-08-17 16:04:48 +02:00
|
|
|
|
2018-09-13 20:02:13 +02:00
|
|
|
test_expect_success 'verify multi-pack-index success' '
|
|
|
|
git multi-pack-index verify --object-dir=$objdir
|
|
|
|
'
|
|
|
|
|
2019-10-21 20:40:03 +02:00
|
|
|
test_expect_success 'verify progress off for redirected stderr' '
|
|
|
|
git multi-pack-index verify --object-dir=$objdir 2>err &&
|
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify force progress on for stderr' '
|
|
|
|
git multi-pack-index verify --object-dir=$objdir --progress 2>err &&
|
|
|
|
test_file_not_empty err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify with the --no-progress option' '
|
|
|
|
git multi-pack-index verify --object-dir=$objdir --no-progress 2>err &&
|
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:15 +02:00
|
|
|
# usage: corrupt_midx_and_verify <pos> <data> <objdir> <string>
|
|
|
|
corrupt_midx_and_verify() {
|
|
|
|
POS=$1 &&
|
|
|
|
DATA="${2:-\0}" &&
|
|
|
|
OBJDIR=$3 &&
|
|
|
|
GREPSTR="$4" &&
|
2018-09-13 20:02:27 +02:00
|
|
|
COMMAND="$5" &&
|
|
|
|
if test -z "$COMMAND"
|
|
|
|
then
|
|
|
|
COMMAND="git multi-pack-index verify --object-dir=$OBJDIR"
|
|
|
|
fi &&
|
2018-09-13 20:02:15 +02:00
|
|
|
FILE=$OBJDIR/pack/multi-pack-index &&
|
|
|
|
chmod a+w $FILE &&
|
|
|
|
test_when_finished mv midx-backup $FILE &&
|
|
|
|
cp $FILE midx-backup &&
|
|
|
|
printf "$DATA" | dd of="$FILE" bs=1 seek="$POS" conv=notrunc &&
|
2018-09-13 20:02:27 +02:00
|
|
|
test_must_fail $COMMAND 2>test_err &&
|
2018-09-13 20:02:15 +02:00
|
|
|
grep -v "^+" test_err >err &&
|
|
|
|
test_i18ngrep "$GREPSTR" err
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'verify bad signature' '
|
|
|
|
corrupt_midx_and_verify 0 "\00" $objdir \
|
|
|
|
"multi-pack-index signature"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:25 +02:00
|
|
|
NUM_OBJECTS=74
|
2018-09-13 20:02:15 +02:00
|
|
|
MIDX_BYTE_VERSION=4
|
|
|
|
MIDX_BYTE_OID_VERSION=5
|
|
|
|
MIDX_BYTE_CHUNK_COUNT=6
|
2018-09-13 20:02:16 +02:00
|
|
|
MIDX_HEADER_SIZE=12
|
|
|
|
MIDX_BYTE_CHUNK_ID=$MIDX_HEADER_SIZE
|
|
|
|
MIDX_BYTE_CHUNK_OFFSET=$(($MIDX_HEADER_SIZE + 4))
|
2018-09-13 20:02:18 +02:00
|
|
|
MIDX_NUM_CHUNKS=5
|
|
|
|
MIDX_CHUNK_LOOKUP_WIDTH=12
|
|
|
|
MIDX_OFFSET_PACKNAMES=$(($MIDX_HEADER_SIZE + \
|
|
|
|
$MIDX_NUM_CHUNKS * $MIDX_CHUNK_LOOKUP_WIDTH))
|
|
|
|
MIDX_BYTE_PACKNAME_ORDER=$(($MIDX_OFFSET_PACKNAMES + 2))
|
2019-12-21 20:49:26 +01:00
|
|
|
MIDX_OFFSET_OID_FANOUT=$(($MIDX_OFFSET_PACKNAMES + $(test_oid packnameoff)))
|
2018-09-13 20:02:20 +02:00
|
|
|
MIDX_OID_FANOUT_WIDTH=4
|
2019-12-21 20:49:26 +01:00
|
|
|
MIDX_BYTE_OID_FANOUT_ORDER=$((MIDX_OFFSET_OID_FANOUT + 250 * $MIDX_OID_FANOUT_WIDTH + $(test_oid fanoutoff)))
|
2018-09-13 20:02:22 +02:00
|
|
|
MIDX_OFFSET_OID_LOOKUP=$(($MIDX_OFFSET_OID_FANOUT + 256 * $MIDX_OID_FANOUT_WIDTH))
|
|
|
|
MIDX_BYTE_OID_LOOKUP=$(($MIDX_OFFSET_OID_LOOKUP + 16 * $HASH_LEN))
|
2018-09-13 20:02:25 +02:00
|
|
|
MIDX_OFFSET_OBJECT_OFFSETS=$(($MIDX_OFFSET_OID_LOOKUP + $NUM_OBJECTS * $HASH_LEN))
|
|
|
|
MIDX_OFFSET_WIDTH=8
|
|
|
|
MIDX_BYTE_PACK_INT_ID=$(($MIDX_OFFSET_OBJECT_OFFSETS + 16 * $MIDX_OFFSET_WIDTH + 2))
|
|
|
|
MIDX_BYTE_OFFSET=$(($MIDX_OFFSET_OBJECT_OFFSETS + 16 * $MIDX_OFFSET_WIDTH + 6))
|
2018-09-13 20:02:15 +02:00
|
|
|
|
|
|
|
test_expect_success 'verify bad version' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_VERSION "\00" $objdir \
|
|
|
|
"multi-pack-index version"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify bad OID version' '
|
2020-08-17 16:04:48 +02:00
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_OID_VERSION "\03" $objdir \
|
2018-09-13 20:02:15 +02:00
|
|
|
"hash version"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify truncated chunk count' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_CHUNK_COUNT "\01" $objdir \
|
2021-02-18 15:07:36 +01:00
|
|
|
"final chunk has non-zero id"
|
2018-09-13 20:02:15 +02:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify extended chunk count' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_CHUNK_COUNT "\07" $objdir \
|
2021-02-18 15:07:36 +01:00
|
|
|
"terminating chunk id appears earlier than expected"
|
2018-09-13 20:02:15 +02:00
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:16 +02:00
|
|
|
test_expect_success 'verify missing required chunk' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_CHUNK_ID "\01" $objdir \
|
|
|
|
"missing required"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify invalid chunk offset' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_CHUNK_OFFSET "\01" $objdir \
|
2021-02-18 15:07:36 +01:00
|
|
|
"improper chunk offset(s)"
|
2018-09-13 20:02:16 +02:00
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:18 +02:00
|
|
|
test_expect_success 'verify packnames out of order' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "z" $objdir \
|
|
|
|
"pack names out of order"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:19 +02:00
|
|
|
test_expect_success 'verify packnames out of order' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "a" $objdir \
|
|
|
|
"failed to load pack"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:20 +02:00
|
|
|
test_expect_success 'verify oid fanout out of order' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_OID_FANOUT_ORDER "\01" $objdir \
|
|
|
|
"oid fanout out of order"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:22 +02:00
|
|
|
test_expect_success 'verify oid lookup out of order' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_OID_LOOKUP "\00" $objdir \
|
|
|
|
"oid lookup out of order"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:25 +02:00
|
|
|
test_expect_success 'verify incorrect pack-int-id' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_PACK_INT_ID "\07" $objdir \
|
|
|
|
"bad pack-int-id"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'verify incorrect offset' '
|
2019-12-21 20:49:25 +01:00
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
|
2018-09-13 20:02:25 +02:00
|
|
|
"incorrect object offset"
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:27 +02:00
|
|
|
test_expect_success 'git-fsck incorrect offset' '
|
2019-12-21 20:49:25 +01:00
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
|
2018-09-13 20:02:27 +02:00
|
|
|
"incorrect object offset" \
|
|
|
|
"git -c core.multipackindex=true fsck"
|
|
|
|
'
|
|
|
|
|
2019-10-21 20:40:03 +02:00
|
|
|
test_expect_success 'repack progress off for redirected stderr' '
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'repack force progress on for stderr' '
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_file_not_empty err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'repack with the --no-progress option' '
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_line_count = 0 err
|
|
|
|
'
|
|
|
|
|
2020-08-25 18:04:36 +02:00
|
|
|
test_expect_success 'repack removes multi-pack-index when deleting packs' '
|
2018-07-12 21:39:40 +02:00
|
|
|
test_path_is_file $objdir/pack/multi-pack-index &&
|
2020-08-25 18:04:36 +02:00
|
|
|
# Set GIT_TEST_MULTI_PACK_INDEX to 0 to avoid writing a new
|
|
|
|
# multi-pack-index after repacking, but set "core.multiPackIndex" to
|
|
|
|
# true so that "git repack" can read the existing MIDX.
|
|
|
|
GIT_TEST_MULTI_PACK_INDEX=0 git -c core.multiPackIndex repack -adf &&
|
2018-07-12 21:39:40 +02:00
|
|
|
test_path_is_missing $objdir/pack/multi-pack-index
|
|
|
|
'
|
|
|
|
|
2020-08-25 18:04:36 +02:00
|
|
|
test_expect_success 'repack preserves multi-pack-index when creating packs' '
|
|
|
|
git init preserve &&
|
|
|
|
test_when_finished "rm -fr preserve" &&
|
|
|
|
(
|
|
|
|
cd preserve &&
|
|
|
|
packdir=.git/objects/pack &&
|
|
|
|
midx=$packdir/multi-pack-index &&
|
|
|
|
|
|
|
|
test_commit 1 &&
|
|
|
|
pack1=$(git pack-objects --all $packdir/pack) &&
|
|
|
|
touch $packdir/pack-$pack1.keep &&
|
|
|
|
test_commit 2 &&
|
|
|
|
pack2=$(git pack-objects --revs $packdir/pack) &&
|
|
|
|
touch $packdir/pack-$pack2.keep &&
|
|
|
|
|
|
|
|
git multi-pack-index write &&
|
|
|
|
cp $midx $midx.bak &&
|
|
|
|
|
|
|
|
cat >pack-input <<-EOF &&
|
|
|
|
HEAD
|
|
|
|
^HEAD~1
|
|
|
|
EOF
|
|
|
|
test_commit 3 &&
|
|
|
|
pack3=$(git pack-objects --revs $packdir/pack <pack-input) &&
|
|
|
|
test_commit 4 &&
|
|
|
|
pack4=$(git pack-objects --revs $packdir/pack <pack-input) &&
|
|
|
|
|
|
|
|
GIT_TEST_MULTI_PACK_INDEX=0 git -c core.multiPackIndex repack -ad &&
|
|
|
|
ls -la $packdir &&
|
|
|
|
test_path_is_file $packdir/pack-$pack1.pack &&
|
|
|
|
test_path_is_file $packdir/pack-$pack2.pack &&
|
|
|
|
test_path_is_missing $packdir/pack-$pack3.pack &&
|
|
|
|
test_path_is_missing $packdir/pack-$pack4.pack &&
|
|
|
|
test_cmp_bin $midx.bak $midx
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:40 +02:00
|
|
|
compare_results_with_midx "after repack"
|
|
|
|
|
2018-08-20 18:52:06 +02:00
|
|
|
test_expect_success 'multi-pack-index and pack-bitmap' '
|
|
|
|
git -c repack.writeBitmaps=true repack -ad &&
|
|
|
|
git multi-pack-index write &&
|
|
|
|
git rev-list --test-bitmap HEAD
|
|
|
|
'
|
|
|
|
|
2018-08-20 18:52:00 +02:00
|
|
|
test_expect_success 'multi-pack-index and alternates' '
|
|
|
|
git init --bare alt.git &&
|
|
|
|
echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
|
|
|
|
echo content1 >file1 &&
|
|
|
|
altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
|
|
|
|
git cat-file blob $altblob &&
|
|
|
|
git rev-list --all
|
|
|
|
'
|
|
|
|
|
|
|
|
compare_results_with_midx "with alternate (local midx)"
|
|
|
|
|
|
|
|
test_expect_success 'multi-pack-index in an alternate' '
|
2018-08-20 18:52:08 +02:00
|
|
|
mv .git/objects/pack/* alt.git/objects/pack &&
|
|
|
|
test_commit add_local_objects &&
|
|
|
|
git repack --local &&
|
|
|
|
git multi-pack-index write &&
|
|
|
|
midx_read_expect 1 3 4 $objdir &&
|
|
|
|
git reset --hard HEAD~1 &&
|
|
|
|
rm -f .git/objects/pack/*
|
2018-08-20 18:52:00 +02:00
|
|
|
'
|
|
|
|
|
|
|
|
compare_results_with_midx "with alternate (remote midx)"
|
|
|
|
|
2018-07-12 21:39:32 +02:00
|
|
|
# usage: corrupt_data <file> <pos> [<data>]
|
|
|
|
corrupt_data () {
|
|
|
|
file=$1
|
|
|
|
pos=$2
|
|
|
|
data="${3:-\0}"
|
|
|
|
printf "$data" | dd of="$file" bs=1 seek="$pos" conv=notrunc
|
|
|
|
}
|
|
|
|
|
|
|
|
# Force 64-bit offsets by manipulating the idx file.
|
|
|
|
# This makes the IDX file _incorrect_ so be careful to clean up after!
|
|
|
|
test_expect_success 'force some 64-bit offsets with pack-objects' '
|
|
|
|
mkdir objects64 &&
|
|
|
|
mkdir objects64/pack &&
|
|
|
|
for i in $(test_seq 1 11)
|
|
|
|
do
|
|
|
|
generate_objects 11
|
|
|
|
done &&
|
|
|
|
commit_and_list_objects &&
|
|
|
|
pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
|
|
|
|
idx64=objects64/pack/test-64-$pack64.idx &&
|
|
|
|
chmod u+w $idx64 &&
|
2019-12-21 20:49:26 +01:00
|
|
|
corrupt_data $idx64 $(test_oid idxoff) "\02" &&
|
2018-07-12 21:39:32 +02:00
|
|
|
midx64=$(git multi-pack-index --object-dir=objects64 write) &&
|
|
|
|
midx_read_expect 1 63 5 objects64 " large-offsets"
|
2018-07-12 21:39:24 +02:00
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:13 +02:00
|
|
|
test_expect_success 'verify multi-pack-index with 64-bit offsets' '
|
|
|
|
git multi-pack-index verify --object-dir=objects64
|
|
|
|
'
|
|
|
|
|
2018-09-13 20:02:25 +02:00
|
|
|
NUM_OBJECTS=63
|
|
|
|
MIDX_OFFSET_OID_FANOUT=$((MIDX_OFFSET_PACKNAMES + 54))
|
|
|
|
MIDX_OFFSET_OID_LOOKUP=$((MIDX_OFFSET_OID_FANOUT + 256 * $MIDX_OID_FANOUT_WIDTH))
|
|
|
|
MIDX_OFFSET_OBJECT_OFFSETS=$(($MIDX_OFFSET_OID_LOOKUP + $NUM_OBJECTS * $HASH_LEN))
|
|
|
|
MIDX_OFFSET_LARGE_OFFSETS=$(($MIDX_OFFSET_OBJECT_OFFSETS + $NUM_OBJECTS * $MIDX_OFFSET_WIDTH))
|
|
|
|
MIDX_BYTE_LARGE_OFFSET=$(($MIDX_OFFSET_LARGE_OFFSETS + 3))
|
|
|
|
|
|
|
|
test_expect_success 'verify incorrect 64-bit offset' '
|
|
|
|
corrupt_midx_and_verify $MIDX_BYTE_LARGE_OFFSET "\07" objects64 \
|
|
|
|
"incorrect object offset"
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:23 +02:00
|
|
|
test_expect_success 'setup expire tests' '
|
|
|
|
mkdir dup &&
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
git init &&
|
|
|
|
test-tool genrandom "data" 4096 >large_file.txt &&
|
|
|
|
git update-index --add large_file.txt &&
|
|
|
|
for i in $(test_seq 1 20)
|
|
|
|
do
|
|
|
|
test_commit $i
|
|
|
|
done &&
|
|
|
|
git branch A HEAD &&
|
|
|
|
git branch B HEAD~8 &&
|
|
|
|
git branch C HEAD~13 &&
|
|
|
|
git branch D HEAD~16 &&
|
|
|
|
git branch E HEAD~18 &&
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-A <<-EOF &&
|
|
|
|
refs/heads/A
|
|
|
|
^refs/heads/B
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-B <<-EOF &&
|
|
|
|
refs/heads/B
|
|
|
|
^refs/heads/C
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-C <<-EOF &&
|
|
|
|
refs/heads/C
|
|
|
|
^refs/heads/D
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-D <<-EOF &&
|
|
|
|
refs/heads/D
|
|
|
|
^refs/heads/E
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-E <<-EOF &&
|
|
|
|
refs/heads/E
|
|
|
|
EOF
|
2019-06-11 01:35:26 +02:00
|
|
|
git multi-pack-index write &&
|
|
|
|
cp -r .git/objects/pack .git/objects/pack-backup
|
2019-06-11 01:35:23 +02:00
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'expire does not remove any packs' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
ls .git/objects/pack >expect &&
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls .git/objects/pack >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-10-21 20:40:03 +02:00
|
|
|
test_expect_success 'expire progress off for redirected stderr' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
git multi-pack-index expire 2>err &&
|
|
|
|
test_line_count = 0 err
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'expire force progress on for stderr' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --progress expire 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_file_not_empty err
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'expire with the --no-progress option' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
2020-09-25 14:33:35 +02:00
|
|
|
GIT_PROGRESS_DELAY=0 git multi-pack-index --no-progress expire 2>err &&
|
2019-10-21 20:40:03 +02:00
|
|
|
test_line_count = 0 err
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:25 +02:00
|
|
|
test_expect_success 'expire removes unreferenced packs' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-combined <<-EOF &&
|
|
|
|
refs/heads/A
|
|
|
|
^refs/heads/C
|
|
|
|
EOF
|
|
|
|
git multi-pack-index write &&
|
|
|
|
ls .git/objects/pack | grep -v -e pack-[AB] >expect &&
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls .git/objects/pack >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
ls .git/objects/pack/ | grep idx >expect-idx &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >actual-midx &&
|
|
|
|
test_cmp expect-idx actual-midx &&
|
|
|
|
git multi-pack-index verify &&
|
|
|
|
git fsck
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:26 +02:00
|
|
|
test_expect_success 'repack with minimum size does not alter existing packs' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
rm -rf .git/objects/pack &&
|
|
|
|
mv .git/objects/pack-backup .git/objects/pack &&
|
2020-04-01 23:00:43 +02:00
|
|
|
test-tool chmtime =-5 .git/objects/pack/pack-D* &&
|
|
|
|
test-tool chmtime =-4 .git/objects/pack/pack-C* &&
|
|
|
|
test-tool chmtime =-3 .git/objects/pack/pack-B* &&
|
|
|
|
test-tool chmtime =-2 .git/objects/pack/pack-A* &&
|
2019-06-11 01:35:26 +02:00
|
|
|
ls .git/objects/pack >expect &&
|
2019-07-01 15:16:19 +02:00
|
|
|
MINSIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | head -n 1) &&
|
2019-06-11 01:35:26 +02:00
|
|
|
git multi-pack-index repack --batch-size=$MINSIZE &&
|
|
|
|
ls .git/objects/pack >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2020-05-10 18:07:34 +02:00
|
|
|
test_expect_success 'repack respects repack.packKeptObjects=false' '
|
|
|
|
test_when_finished rm -f dup/.git/objects/pack/*keep &&
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 5 idx-list &&
|
|
|
|
ls .git/objects/pack/*.pack | sed "s/\.pack/.keep/" >keep-list &&
|
|
|
|
test_line_count = 5 keep-list &&
|
|
|
|
for keep in $(cat keep-list)
|
|
|
|
do
|
|
|
|
touch $keep || return 1
|
|
|
|
done &&
|
|
|
|
git multi-pack-index repack --batch-size=0 &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 5 idx-list &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 5 midx-list &&
|
|
|
|
THIRD_SMALLEST_SIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | sed -n 3p) &&
|
|
|
|
BATCH_SIZE=$((THIRD_SMALLEST_SIZE + 1)) &&
|
|
|
|
git multi-pack-index repack --batch-size=$BATCH_SIZE &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 5 idx-list &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 5 midx-list
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
midx: implement midx_repack()
To repack with a non-zero batch-size, first sort all pack-files by
their modified time. Second, walk those pack-files from oldest
to newest, compute their expected size, and add the packs to a list
if they are smaller than the given batch-size. Stop when the total
expected size is at least the batch size.
If the batch size is zero, select all packs in the multi-pack-index.
Finally, collect the objects from the multi-pack-index that are in
the selected packs and send them to 'git pack-objects'. Write a new
multi-pack-index that includes the new pack.
Using a batch size of zero is very similar to a standard 'git repack'
command, except that we do not delete the old packs and instead rely
on the new multi-pack-index to prevent new processes from reading the
old packs. This does not disrupt other Git processes that are currently
reading the old packs based on the old multi-pack-index.
While first designing a 'git multi-pack-index repack' operation, I
started by collecting the batches based on the actual size of the
objects instead of the size of the pack-files. This allows repacking
a large pack-file that has very few referencd objects. However, this
came at a significant cost of parsing pack-files instead of simply
reading the multi-pack-index and getting the file information for
the pack-files. The "expected size" version provides similar
behavior, but could skip a pack-file if the average object size is
much larger than the actual size of the referenced objects, or
can create a large pack if the actual size of the referenced objects
is larger than the expected size.
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-11 01:35:27 +02:00
|
|
|
test_expect_success 'repack creates a new pack' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 5 idx-list &&
|
2019-07-01 15:16:19 +02:00
|
|
|
THIRD_SMALLEST_SIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | head -n 3 | tail -n 1) &&
|
midx: implement midx_repack()
To repack with a non-zero batch-size, first sort all pack-files by
their modified time. Second, walk those pack-files from oldest
to newest, compute their expected size, and add the packs to a list
if they are smaller than the given batch-size. Stop when the total
expected size is at least the batch size.
If the batch size is zero, select all packs in the multi-pack-index.
Finally, collect the objects from the multi-pack-index that are in
the selected packs and send them to 'git pack-objects'. Write a new
multi-pack-index that includes the new pack.
Using a batch size of zero is very similar to a standard 'git repack'
command, except that we do not delete the old packs and instead rely
on the new multi-pack-index to prevent new processes from reading the
old packs. This does not disrupt other Git processes that are currently
reading the old packs based on the old multi-pack-index.
While first designing a 'git multi-pack-index repack' operation, I
started by collecting the batches based on the actual size of the
objects instead of the size of the pack-files. This allows repacking
a large pack-file that has very few referencd objects. However, this
came at a significant cost of parsing pack-files instead of simply
reading the multi-pack-index and getting the file information for
the pack-files. The "expected size" version provides similar
behavior, but could skip a pack-file if the average object size is
much larger than the actual size of the referenced objects, or
can create a large pack if the actual size of the referenced objects
is larger than the expected size.
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-11 01:35:27 +02:00
|
|
|
BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) &&
|
|
|
|
git multi-pack-index repack --batch-size=$BATCH_SIZE &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 6 idx-list &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 6 midx-list
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'expire removes repacked packs' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
ls -al .git/objects/pack/*pack &&
|
|
|
|
ls -S .git/objects/pack/*pack | head -n 4 >expect &&
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls -S .git/objects/pack/*pack >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 4 midx-list
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:27 +02:00
|
|
|
test_expect_success 'expire works when adding new packs' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-combined <<-EOF &&
|
|
|
|
refs/heads/A
|
|
|
|
^refs/heads/B
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-combined <<-EOF &&
|
|
|
|
refs/heads/B
|
|
|
|
^refs/heads/C
|
|
|
|
EOF
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-combined <<-EOF &&
|
|
|
|
refs/heads/C
|
|
|
|
^refs/heads/D
|
|
|
|
EOF
|
|
|
|
git multi-pack-index write &&
|
|
|
|
git pack-objects --revs .git/objects/pack/a-pack <<-EOF &&
|
|
|
|
refs/heads/D
|
|
|
|
^refs/heads/E
|
|
|
|
EOF
|
|
|
|
git multi-pack-index write &&
|
|
|
|
git pack-objects --revs .git/objects/pack/z-pack <<-EOF &&
|
|
|
|
refs/heads/E
|
|
|
|
EOF
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls .git/objects/pack/ | grep idx >expect &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git multi-pack-index verify
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:28 +02:00
|
|
|
test_expect_success 'expire respects .keep files' '
|
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
git pack-objects --revs .git/objects/pack/pack-all <<-EOF &&
|
|
|
|
refs/heads/A
|
|
|
|
EOF
|
|
|
|
git multi-pack-index write &&
|
|
|
|
PACKA=$(ls .git/objects/pack/a-pack*\.pack | sed s/\.pack\$//) &&
|
|
|
|
touch $PACKA.keep &&
|
|
|
|
git multi-pack-index expire &&
|
2021-01-26 00:37:38 +01:00
|
|
|
test_path_is_file $PACKA.idx &&
|
|
|
|
test_path_is_file $PACKA.keep &&
|
|
|
|
test_path_is_file $PACKA.pack &&
|
2019-06-11 01:35:28 +02:00
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 2 midx-list
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2019-06-11 01:35:29 +02:00
|
|
|
test_expect_success 'repack --batch-size=0 repacks everything' '
|
2020-08-11 17:30:18 +02:00
|
|
|
cp -r dup dup2 &&
|
2019-06-11 01:35:29 +02:00
|
|
|
(
|
|
|
|
cd dup &&
|
|
|
|
rm .git/objects/pack/*.keep &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 2 idx-list &&
|
|
|
|
git multi-pack-index repack --batch-size=0 &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 3 idx-list &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 3 midx-list &&
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls -al .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 1 idx-list &&
|
|
|
|
git multi-pack-index repack --batch-size=0 &&
|
|
|
|
ls -al .git/objects/pack/*idx >new-idx-list &&
|
|
|
|
test_cmp idx-list new-idx-list
|
|
|
|
)
|
|
|
|
'
|
2019-06-11 01:35:28 +02:00
|
|
|
|
2020-08-11 17:30:18 +02:00
|
|
|
test_expect_success 'repack --batch-size=<large> repacks everything' '
|
|
|
|
(
|
|
|
|
cd dup2 &&
|
|
|
|
rm .git/objects/pack/*.keep &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 2 idx-list &&
|
|
|
|
git multi-pack-index repack --batch-size=2000000 &&
|
|
|
|
ls .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 3 idx-list &&
|
|
|
|
test-tool read-midx .git/objects | grep idx >midx-list &&
|
|
|
|
test_line_count = 3 midx-list &&
|
|
|
|
git multi-pack-index expire &&
|
|
|
|
ls -al .git/objects/pack/*idx >idx-list &&
|
|
|
|
test_line_count = 1 idx-list
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
midx.c: protect against disappearing packs
When a packed object is stored in a multi-pack index, but that pack has
racily gone away, the MIDX code simply calls die(), when it could be
returning an error to the caller, which would in turn lead to
re-scanning the pack directory.
A pack can racily disappear, for example, due to a simultaneous 'git
repack -ad',
You can also reproduce this with two terminals, where one is running:
git init
while true; do
git commit -q --allow-empty -m foo
git repack -ad
git multi-pack-index write
done
(in effect, constantly writing new MIDXs), and the other is running:
obj=$(git rev-parse HEAD)
while true; do
echo $obj | git cat-file --batch-check='%(objectsize:disk)' || break
done
That will sometimes hit the error preparing packfile from
multi-pack-index message, which this patch fixes.
Right now, that path to discovering a missing pack looks something like
'find_pack_entry()' calling 'fill_midx_entry()' and eventually making
its way to call 'nth_midxed_pack_entry()'.
'nth_midxed_pack_entry()' already checks 'is_pack_valid()' and
propagates an error if the pack is invalid. So, this works if the pack
has gone away between calling 'prepare_midx_pack()' and before calling
'is_pack_valid()', but not if it disappears before then.
Catch the case where the pack has already disappeared before
'prepare_midx_pack()' by returning an error in that case, too.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:33 +01:00
|
|
|
test_expect_success 'load reverse index when missing .idx, .pack' '
|
packfile.c: protect against disappearing indexes
In 17c35c8969 (packfile: skip loading index if in multi-pack-index,
2018-07-12) we stopped loading the .idx file for packs that are
contained within a multi-pack index.
This saves us the effort of loading an .idx and doing some lightweight
validity checks by way of 'packfile.c:load_idx()', but introduces a race
between processes that need to load the index (e.g., to generate a
reverse index) and processes that can delete the index.
For example, running the following in your shell:
$ git init repo && cd repo
$ git commit --allow-empty -m 'base'
$ git repack -ad && git multi-pack-index write
followed by:
$ rm -f .git/objects/pack/pack-*.idx
$ git rev-parse HEAD | git cat-file --batch-check='%(objectsize:disk)'
will result in a segfault prior to this patch. What's happening here is
that we notice that the pack is in the multi-pack index, and so don't
check that it still has a .idx. When we then try and load that index to
generate a reverse index, we don't have it, so the call to
'find_pack_revindex()' in 'packfile.c:packed_object_info()' returns
NULL, and then dereferencing it causes a segfault.
Of course, we don't ever expect someone to remove the index file by
hand, or to be in a state where we never wrote it to begin with (yet
find that pack in the multi-pack-index). But, this can happen in a
timing race with 'git repack -ad', which removes all existing packs
after writing a new pack containing all of their objects.
Avoid this by reverting the hunk of 17c35c8969 which stops loading the
index when the pack is contained in a MIDX. This makes the latter half
of 17c35c8969 useless, since we'll always have a non-NULL
'p->index_data', in which case that if statement isn't guarding
anything.
These two together effectively revert 17c35c8969, and avoid the race
explained above.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:28 +01:00
|
|
|
git init repo &&
|
|
|
|
test_when_finished "rm -fr repo" &&
|
|
|
|
(
|
|
|
|
cd repo &&
|
|
|
|
|
|
|
|
git config core.multiPackIndex true &&
|
|
|
|
|
|
|
|
test_commit base &&
|
|
|
|
git repack -ad &&
|
|
|
|
git multi-pack-index write &&
|
|
|
|
|
|
|
|
git rev-parse HEAD >tip &&
|
midx.c: protect against disappearing packs
When a packed object is stored in a multi-pack index, but that pack has
racily gone away, the MIDX code simply calls die(), when it could be
returning an error to the caller, which would in turn lead to
re-scanning the pack directory.
A pack can racily disappear, for example, due to a simultaneous 'git
repack -ad',
You can also reproduce this with two terminals, where one is running:
git init
while true; do
git commit -q --allow-empty -m foo
git repack -ad
git multi-pack-index write
done
(in effect, constantly writing new MIDXs), and the other is running:
obj=$(git rev-parse HEAD)
while true; do
echo $obj | git cat-file --batch-check='%(objectsize:disk)' || break
done
That will sometimes hit the error preparing packfile from
multi-pack-index message, which this patch fixes.
Right now, that path to discovering a missing pack looks something like
'find_pack_entry()' calling 'fill_midx_entry()' and eventually making
its way to call 'nth_midxed_pack_entry()'.
'nth_midxed_pack_entry()' already checks 'is_pack_valid()' and
propagates an error if the pack is invalid. So, this works if the pack
has gone away between calling 'prepare_midx_pack()' and before calling
'is_pack_valid()', but not if it disappears before then.
Catch the case where the pack has already disappeared before
'prepare_midx_pack()' by returning an error in that case, too.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:33 +01:00
|
|
|
pack=$(ls .git/objects/pack/pack-*.pack) &&
|
packfile.c: protect against disappearing indexes
In 17c35c8969 (packfile: skip loading index if in multi-pack-index,
2018-07-12) we stopped loading the .idx file for packs that are
contained within a multi-pack index.
This saves us the effort of loading an .idx and doing some lightweight
validity checks by way of 'packfile.c:load_idx()', but introduces a race
between processes that need to load the index (e.g., to generate a
reverse index) and processes that can delete the index.
For example, running the following in your shell:
$ git init repo && cd repo
$ git commit --allow-empty -m 'base'
$ git repack -ad && git multi-pack-index write
followed by:
$ rm -f .git/objects/pack/pack-*.idx
$ git rev-parse HEAD | git cat-file --batch-check='%(objectsize:disk)'
will result in a segfault prior to this patch. What's happening here is
that we notice that the pack is in the multi-pack index, and so don't
check that it still has a .idx. When we then try and load that index to
generate a reverse index, we don't have it, so the call to
'find_pack_revindex()' in 'packfile.c:packed_object_info()' returns
NULL, and then dereferencing it causes a segfault.
Of course, we don't ever expect someone to remove the index file by
hand, or to be in a state where we never wrote it to begin with (yet
find that pack in the multi-pack-index). But, this can happen in a
timing race with 'git repack -ad', which removes all existing packs
after writing a new pack containing all of their objects.
Avoid this by reverting the hunk of 17c35c8969 which stops loading the
index when the pack is contained in a MIDX. This makes the latter half
of 17c35c8969 useless, since we'll always have a non-NULL
'p->index_data', in which case that if statement isn't guarding
anything.
These two together effectively revert 17c35c8969, and avoid the race
explained above.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:28 +01:00
|
|
|
idx=$(ls .git/objects/pack/pack-*.idx) &&
|
|
|
|
|
|
|
|
mv $idx $idx.bak &&
|
midx.c: protect against disappearing packs
When a packed object is stored in a multi-pack index, but that pack has
racily gone away, the MIDX code simply calls die(), when it could be
returning an error to the caller, which would in turn lead to
re-scanning the pack directory.
A pack can racily disappear, for example, due to a simultaneous 'git
repack -ad',
You can also reproduce this with two terminals, where one is running:
git init
while true; do
git commit -q --allow-empty -m foo
git repack -ad
git multi-pack-index write
done
(in effect, constantly writing new MIDXs), and the other is running:
obj=$(git rev-parse HEAD)
while true; do
echo $obj | git cat-file --batch-check='%(objectsize:disk)' || break
done
That will sometimes hit the error preparing packfile from
multi-pack-index message, which this patch fixes.
Right now, that path to discovering a missing pack looks something like
'find_pack_entry()' calling 'fill_midx_entry()' and eventually making
its way to call 'nth_midxed_pack_entry()'.
'nth_midxed_pack_entry()' already checks 'is_pack_valid()' and
propagates an error if the pack is invalid. So, this works if the pack
has gone away between calling 'prepare_midx_pack()' and before calling
'is_pack_valid()', but not if it disappears before then.
Catch the case where the pack has already disappeared before
'prepare_midx_pack()' by returning an error in that case, too.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:33 +01:00
|
|
|
git cat-file --batch-check="%(objectsize:disk)" <tip &&
|
|
|
|
|
|
|
|
mv $idx.bak $idx &&
|
|
|
|
|
|
|
|
mv $pack $pack.bak &&
|
packfile.c: protect against disappearing indexes
In 17c35c8969 (packfile: skip loading index if in multi-pack-index,
2018-07-12) we stopped loading the .idx file for packs that are
contained within a multi-pack index.
This saves us the effort of loading an .idx and doing some lightweight
validity checks by way of 'packfile.c:load_idx()', but introduces a race
between processes that need to load the index (e.g., to generate a
reverse index) and processes that can delete the index.
For example, running the following in your shell:
$ git init repo && cd repo
$ git commit --allow-empty -m 'base'
$ git repack -ad && git multi-pack-index write
followed by:
$ rm -f .git/objects/pack/pack-*.idx
$ git rev-parse HEAD | git cat-file --batch-check='%(objectsize:disk)'
will result in a segfault prior to this patch. What's happening here is
that we notice that the pack is in the multi-pack index, and so don't
check that it still has a .idx. When we then try and load that index to
generate a reverse index, we don't have it, so the call to
'find_pack_revindex()' in 'packfile.c:packed_object_info()' returns
NULL, and then dereferencing it causes a segfault.
Of course, we don't ever expect someone to remove the index file by
hand, or to be in a state where we never wrote it to begin with (yet
find that pack in the multi-pack-index). But, this can happen in a
timing race with 'git repack -ad', which removes all existing packs
after writing a new pack containing all of their objects.
Avoid this by reverting the hunk of 17c35c8969 which stops loading the
index when the pack is contained in a MIDX. This makes the latter half
of 17c35c8969 useless, since we'll always have a non-NULL
'p->index_data', in which case that if statement isn't guarding
anything.
These two together effectively revert 17c35c8969, and avoid the race
explained above.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-25 18:17:28 +01:00
|
|
|
git cat-file --batch-check="%(objectsize:disk)" <tip
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2018-07-12 21:39:21 +02:00
|
|
|
test_done
|