2013-12-21 15:00:38 +01:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='exercise basic bitmap functionality'
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
add `ignore_missing_links` mode to revwalk
When pack-objects is computing the reachability bitmap to
serve a fetch request, it can erroneously die() if some of
the UNINTERESTING objects are not present. Upload-pack
throws away HAVE lines from the client for objects we do not
have, but we may have a tip object without all of its
ancestors (e.g., if the tip is no longer reachable and was
new enough to survive a `git prune`, but some of its
reachable objects did get pruned).
In the non-bitmap case, we do a revision walk with the HAVE
objects marked as UNINTERESTING. The revision walker
explicitly ignores errors in accessing UNINTERESTING commits
to handle this case (and we do not bother looking at
UNINTERESTING trees or blobs at all).
When we have bitmaps, however, the process is quite
different. The bitmap index for a pack-objects run is
calculated in two separate steps:
First, we perform an extensive walk from all the HAVEs to
find the full set of objects reachable from them. This walk
is usually optimized away because we are expected to hit an
object with a bitmap during the traversal, which allows us
to terminate early.
Secondly, we perform an extensive walk from all the WANTs,
which usually also terminates early because we hit a commit
with an existing bitmap.
Once we have the resulting bitmaps from the two walks, we
AND-NOT them together to obtain the resulting set of objects
we need to pack.
When we are walking the HAVE objects, the revision walker
does not know that we are walking it only to mark the
results as uninteresting. We strip out the UNINTERESTING flag,
because those objects _are_ interesting to us during the
first walk. We want to keep going to get a complete set of
reachable objects if we can.
We need some way to tell the revision walker that it's OK to
silently truncate the HAVE walk, just like it does for the
UNINTERESTING case. This patch introduces a new
`ignore_missing_links` flag to the `rev_info` struct, which
we set only for the HAVE walk.
It also adds tests to cover UNINTERESTING objects missing
from several positions: a missing blob, a missing tree, and
a missing parent commit. The missing blob already worked (as
we do not care about its contents at all), but the other two
cases caused us to die().
Note that there are a few cases we do not need to test:
1. We do not need to test a missing tree, with the blob
still present. Without the tree that refers to it, we
would not know that the blob is relevant to our walk.
2. We do not need to test a tip commit that is missing.
Upload-pack omits these for us (and in fact, we
complain even in the non-bitmap case if it fails to do
so).
Reported-by: Siddharth Agarwal <sid0@fb.com>
Signed-off-by: Vicent Marti <tanoku@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-03-28 11:00:43 +01:00
|
|
|
objpath () {
|
|
|
|
echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
|
|
|
|
}
|
|
|
|
|
2013-12-21 15:00:38 +01:00
|
|
|
test_expect_success 'setup repo with moderate-sized history' '
|
|
|
|
for i in $(test_seq 1 10); do
|
|
|
|
test_commit $i
|
|
|
|
done &&
|
|
|
|
git checkout -b other HEAD~5 &&
|
|
|
|
for i in $(test_seq 1 10); do
|
|
|
|
test_commit side-$i
|
|
|
|
done &&
|
|
|
|
git checkout master &&
|
|
|
|
blob=$(echo tagged-blob | git hash-object -w --stdin) &&
|
|
|
|
git tag tagged-blob $blob &&
|
2014-06-10 22:20:30 +02:00
|
|
|
git config repack.writebitmaps true &&
|
pack-bitmap: implement optional name_hash cache
When we use pack bitmaps rather than walking the object
graph, we end up with the list of objects to include in the
packfile, but we do not know the path at which any tree or
blob objects would be found.
In a recently packed repository, this is fine. A fetch would
use the paths only as a heuristic in the delta compression
phase, and a fully packed repository should not need to do
much delta compression.
As time passes, though, we may acquire more objects on top
of our large bitmapped pack. If clients fetch frequently,
then they never even look at the bitmapped history, and all
works as usual. However, a client who has not fetched since
the last bitmap repack will have "have" tips in the
bitmapped history, but "want" newer objects.
The bitmaps themselves degrade gracefully in this
circumstance. We manually walk the more recent bits of
history, and then use bitmaps when we hit them.
But we would also like to perform delta compression between
the newer objects and the bitmapped objects (both to delta
against what we know the user already has, but also between
"new" and "old" objects that the user is fetching). The lack
of pathnames makes our delta heuristics much less effective.
This patch adds an optional cache of the 32-bit name_hash
values to the end of the bitmap file. If present, a reader
can use it to match bitmapped and non-bitmapped names during
delta compression.
Here are perf results for p5310:
Test origin/master HEAD^ HEAD
-------------------------------------------------------------------------------------------------
5310.2: repack to disk 36.81(37.82+1.43) 47.70(48.74+1.41) +29.6% 47.75(48.70+1.51) +29.7%
5310.3: simulated clone 30.78(29.70+2.14) 1.08(0.97+0.10) -96.5% 1.07(0.94+0.12) -96.5%
5310.4: simulated fetch 3.16(6.10+0.08) 3.54(10.65+0.06) +12.0% 1.70(3.07+0.06) -46.2%
5310.6: partial bitmap 36.76(43.19+1.81) 6.71(11.25+0.76) -81.7% 4.08(6.26+0.46) -88.9%
You can see that the time spent on an incremental fetch goes
down, as our delta heuristics are able to do their work.
And we save time on the partial bitmap clone for the same
reason.
Signed-off-by: Vicent Marti <tanoku@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-12-21 15:00:45 +01:00
|
|
|
git config pack.writebitmaphashcache true
|
2013-12-21 15:00:38 +01:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'full repack creates bitmaps' '
|
|
|
|
git repack -ad &&
|
|
|
|
ls .git/objects/pack/ | grep bitmap >output &&
|
|
|
|
test_line_count = 1 output
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
|
|
|
|
git rev-list --test-bitmap HEAD
|
|
|
|
'
|
|
|
|
|
|
|
|
rev_list_tests() {
|
|
|
|
state=$1
|
|
|
|
|
|
|
|
test_expect_success "counting commits via bitmap ($state)" '
|
|
|
|
git rev-list --count HEAD >expect &&
|
|
|
|
git rev-list --use-bitmap-index --count HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success "counting partial commits via bitmap ($state)" '
|
|
|
|
git rev-list --count HEAD~5..HEAD >expect &&
|
|
|
|
git rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
rev-list: "adjust" results of "--count --use-bitmap-index -n"
If you ask rev-list for:
git rev-list --count --use-bitmap-index HEAD
we optimize out the actual traversal and just give you the
number of bits set in the commit bitmap. This is faster,
which is good.
But if you ask to limit the size of the traversal, like:
git rev-list --count --use-bitmap-index -n 100 HEAD
we'll still output the full bitmapped number we found. On
the surface, that might even seem OK. You explicitly asked
to use the bitmap index, and it was cheap to compute the
real answer, so we gave it to you.
But there's something much more complicated going on under
the hood. If we don't have a bitmap directly for HEAD, then
we have to actually traverse backwards, looking for a
bitmapped commit. And _that_ traversal is bounded by our
`-n` count.
This is a good thing, because it bounds the work we have to
do, which is probably what the user wanted by asking for
`-n`. But now it makes the output quite confusing. You might
get many values:
- your `-n` value, if we walked back and never found a
bitmap (or fewer if there weren't that many commits)
- the actual full count, if we found a bitmap root for
every path of our traversal with in the `-n` limit
- any number in between! We might have walked back and
found _some_ bitmaps, but then cut off the traversal
early with some commits not accounted for in the result.
So you cannot even see a value higher than your `-n` and say
"OK, bitmaps kicked in, this must be the real full count".
The only sane thing is for git to just clamp the value to a
maximum of the `-n` value, which means we should output the
exact same results whether bitmaps are in use or not.
The test in t5310 demonstrates this by using `-n 1`.
Without this patch we fail in the full-bitmap case (where we
do not have to traverse at all) but _not_ in the
partial-bitmap case (where we have to walk down to find an
actual bitmap). With this patch, both cases just work.
I didn't implement the crazy in-between case, just because
it's complicated to set up, and is really a subset of the
full-count case, which we do cover.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-03 09:07:34 +02:00
|
|
|
test_expect_success "counting commits with limit ($state)" '
|
|
|
|
git rev-list --count -n 1 HEAD >expect &&
|
|
|
|
git rev-list --use-bitmap-index --count -n 1 HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-12-21 15:00:38 +01:00
|
|
|
test_expect_success "counting non-linear history ($state)" '
|
|
|
|
git rev-list --count other...master >expect &&
|
|
|
|
git rev-list --use-bitmap-index --count other...master >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2015-07-01 20:42:17 +02:00
|
|
|
test_expect_success "counting commits with limiting ($state)" '
|
|
|
|
git rev-list --count HEAD -- 1.t >expect &&
|
|
|
|
git rev-list --use-bitmap-index --count HEAD -- 1.t >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-12-21 15:00:38 +01:00
|
|
|
test_expect_success "enumerate --objects ($state)" '
|
|
|
|
git rev-list --objects --use-bitmap-index HEAD >tmp &&
|
|
|
|
cut -d" " -f1 <tmp >tmp2 &&
|
|
|
|
sort <tmp2 >actual &&
|
|
|
|
git rev-list --objects HEAD >tmp &&
|
|
|
|
cut -d" " -f1 <tmp >tmp2 &&
|
|
|
|
sort <tmp2 >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success "bitmap --objects handles non-commit objects ($state)" '
|
|
|
|
git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
|
|
|
|
grep $blob actual
|
|
|
|
'
|
|
|
|
}
|
|
|
|
|
|
|
|
rev_list_tests 'full bitmap'
|
|
|
|
|
|
|
|
test_expect_success 'clone from bitmapped repository' '
|
|
|
|
git clone --no-local --bare . clone.git &&
|
|
|
|
git rev-parse HEAD >expect &&
|
|
|
|
git --git-dir=clone.git rev-parse HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'setup further non-bitmapped commits' '
|
|
|
|
for i in $(test_seq 1 10); do
|
|
|
|
test_commit further-$i
|
|
|
|
done
|
|
|
|
'
|
|
|
|
|
|
|
|
rev_list_tests 'partial bitmap'
|
|
|
|
|
|
|
|
test_expect_success 'fetch (partial bitmap)' '
|
|
|
|
git --git-dir=clone.git fetch origin master:master &&
|
|
|
|
git rev-parse HEAD >expect &&
|
|
|
|
git --git-dir=clone.git rev-parse HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'incremental repack cannot create bitmaps' '
|
|
|
|
test_commit more-1 &&
|
pack-objects: turn off bitmaps when skipping objects
The pack bitmap format requires that we have a single bit
for each object in the pack, and that each object's bitmap
represents its complete set of reachable objects. Therefore
we have no way to represent the bitmap of an object which
references objects outside the pack.
We notice this problem while generating the bitmaps, as we
try to find the offset of a particular object and realize
that we do not have it. In this case we die, and neither the
bitmap nor the pack is generated. This is correct, but
perhaps a little unfriendly. If you have bitmaps turned on
in the config, many repacks will fail which would otherwise
succeed. E.g., incremental repacks, repacks with "-l" when
you have alternates, ".keep" files.
Instead, this patch notices early that we are omitting some
objects from the pack and turns off bitmaps (with a
warning). Note that this is not strictly correct, as it's
possible that the object being omitted is not reachable from
any other object in the pack. In practice, this is almost
never the case, and there are two advantages to doing it
this way:
1. The code is much simpler, as we do not have to cleanly
abort the bitmap-generation process midway through.
2. We do not waste time partially generating bitmaps only
to find out that some object deep in the history is not
being packed.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-03-15 03:38:29 +01:00
|
|
|
find .git/objects/pack -name "*.bitmap" >expect &&
|
|
|
|
git repack -d &&
|
|
|
|
find .git/objects/pack -name "*.bitmap" >actual &&
|
|
|
|
test_cmp expect actual
|
2013-12-21 15:00:38 +01:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'incremental repack can disable bitmaps' '
|
|
|
|
test_commit more-2 &&
|
|
|
|
git repack -d --no-write-bitmap-index
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'full repack, reusing previous bitmaps' '
|
|
|
|
git repack -ad &&
|
|
|
|
ls .git/objects/pack/ | grep bitmap >output &&
|
|
|
|
test_line_count = 1 output
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fetch (full bitmap)' '
|
|
|
|
git --git-dir=clone.git fetch origin master:master &&
|
|
|
|
git rev-parse HEAD >expect &&
|
|
|
|
git --git-dir=clone.git rev-parse HEAD >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
add `ignore_missing_links` mode to revwalk
When pack-objects is computing the reachability bitmap to
serve a fetch request, it can erroneously die() if some of
the UNINTERESTING objects are not present. Upload-pack
throws away HAVE lines from the client for objects we do not
have, but we may have a tip object without all of its
ancestors (e.g., if the tip is no longer reachable and was
new enough to survive a `git prune`, but some of its
reachable objects did get pruned).
In the non-bitmap case, we do a revision walk with the HAVE
objects marked as UNINTERESTING. The revision walker
explicitly ignores errors in accessing UNINTERESTING commits
to handle this case (and we do not bother looking at
UNINTERESTING trees or blobs at all).
When we have bitmaps, however, the process is quite
different. The bitmap index for a pack-objects run is
calculated in two separate steps:
First, we perform an extensive walk from all the HAVEs to
find the full set of objects reachable from them. This walk
is usually optimized away because we are expected to hit an
object with a bitmap during the traversal, which allows us
to terminate early.
Secondly, we perform an extensive walk from all the WANTs,
which usually also terminates early because we hit a commit
with an existing bitmap.
Once we have the resulting bitmaps from the two walks, we
AND-NOT them together to obtain the resulting set of objects
we need to pack.
When we are walking the HAVE objects, the revision walker
does not know that we are walking it only to mark the
results as uninteresting. We strip out the UNINTERESTING flag,
because those objects _are_ interesting to us during the
first walk. We want to keep going to get a complete set of
reachable objects if we can.
We need some way to tell the revision walker that it's OK to
silently truncate the HAVE walk, just like it does for the
UNINTERESTING case. This patch introduces a new
`ignore_missing_links` flag to the `rev_info` struct, which
we set only for the HAVE walk.
It also adds tests to cover UNINTERESTING objects missing
from several positions: a missing blob, a missing tree, and
a missing parent commit. The missing blob already worked (as
we do not care about its contents at all), but the other two
cases caused us to die().
Note that there are a few cases we do not need to test:
1. We do not need to test a missing tree, with the blob
still present. Without the tree that refers to it, we
would not know that the blob is relevant to our walk.
2. We do not need to test a tip commit that is missing.
Upload-pack omits these for us (and in fact, we
complain even in the non-bitmap case if it fails to do
so).
Reported-by: Siddharth Agarwal <sid0@fb.com>
Signed-off-by: Vicent Marti <tanoku@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-03-28 11:00:43 +01:00
|
|
|
test_expect_success 'create objects for missing-HAVE tests' '
|
|
|
|
blob=$(echo "missing have" | git hash-object -w --stdin) &&
|
|
|
|
tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
|
|
|
|
parent=$(echo parent | git commit-tree $tree) &&
|
|
|
|
commit=$(echo commit | git commit-tree $tree -p $parent) &&
|
|
|
|
cat >revs <<-EOF
|
|
|
|
HEAD
|
|
|
|
^HEAD^
|
|
|
|
^$commit
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'pack with missing blob' '
|
|
|
|
rm $(objpath $blob) &&
|
|
|
|
git pack-objects --stdout --revs <revs >/dev/null
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'pack with missing tree' '
|
|
|
|
rm $(objpath $tree) &&
|
|
|
|
git pack-objects --stdout --revs <revs >/dev/null
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'pack with missing parent' '
|
|
|
|
rm $(objpath $parent) &&
|
|
|
|
git pack-objects --stdout --revs <revs >/dev/null
|
|
|
|
'
|
|
|
|
|
2013-12-21 15:00:38 +01:00
|
|
|
test_lazy_prereq JGIT '
|
|
|
|
type jgit
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success JGIT 'we can read jgit bitmaps' '
|
|
|
|
git clone . compat-jgit &&
|
|
|
|
(
|
|
|
|
cd compat-jgit &&
|
|
|
|
rm -f .git/objects/pack/*.bitmap &&
|
|
|
|
jgit gc &&
|
|
|
|
git rev-list --test-bitmap HEAD
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success JGIT 'jgit can read our bitmaps' '
|
|
|
|
git clone . compat-us &&
|
|
|
|
(
|
|
|
|
cd compat-us &&
|
|
|
|
git repack -adb &&
|
|
|
|
# jgit gc will barf if it does not like our bitmaps
|
|
|
|
jgit gc
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2014-10-17 03:11:43 +02:00
|
|
|
test_expect_success 'splitting packs does not generate bogus bitmaps' '
|
|
|
|
test-genrandom foo $((1024 * 1024)) >rand &&
|
|
|
|
git add rand &&
|
|
|
|
git commit -m "commit with big file" &&
|
|
|
|
git -c pack.packSizeLimit=500k repack -adb &&
|
|
|
|
git init --bare no-bitmaps.git &&
|
|
|
|
git -C no-bitmaps.git fetch .. HEAD
|
|
|
|
'
|
|
|
|
|
2013-12-21 15:00:38 +01:00
|
|
|
test_done
|