Merge branch 'rj/prune-packed-excess-args'
[git] / t / t5310-pack-bitmaps.sh
1 #!/bin/sh
2
3 test_description='exercise basic bitmap functionality'
4 . ./test-lib.sh
5
6 objpath () {
7         echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
8 }
9
10 # show objects present in pack ($1 should be associated *.idx)
11 list_packed_objects () {
12         git show-index <"$1" >object-list &&
13         cut -d' ' -f2 object-list
14 }
15
16 # has_any pattern-file content-file
17 # tests whether content-file has any entry from pattern-file with entries being
18 # whole lines.
19 has_any () {
20         grep -Ff "$1" "$2"
21 }
22
23 test_expect_success 'setup repo with moderate-sized history' '
24         for i in $(test_seq 1 10)
25         do
26                 test_commit $i
27         done &&
28         git checkout -b other HEAD~5 &&
29         for i in $(test_seq 1 10)
30         do
31                 test_commit side-$i
32         done &&
33         git checkout master &&
34         bitmaptip=$(git rev-parse master) &&
35         blob=$(echo tagged-blob | git hash-object -w --stdin) &&
36         git tag tagged-blob $blob &&
37         git config repack.writebitmaps true &&
38         git config pack.writebitmaphashcache true
39 '
40
41 test_expect_success 'full repack creates bitmaps' '
42         git repack -ad &&
43         ls .git/objects/pack/ | grep bitmap >output &&
44         test_line_count = 1 output
45 '
46
47 test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
48         git rev-list --test-bitmap HEAD
49 '
50
51 rev_list_tests() {
52         state=$1
53
54         test_expect_success "counting commits via bitmap ($state)" '
55                 git rev-list --count HEAD >expect &&
56                 git rev-list --use-bitmap-index --count HEAD >actual &&
57                 test_cmp expect actual
58         '
59
60         test_expect_success "counting partial commits via bitmap ($state)" '
61                 git rev-list --count HEAD~5..HEAD >expect &&
62                 git rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&
63                 test_cmp expect actual
64         '
65
66         test_expect_success "counting commits with limit ($state)" '
67                 git rev-list --count -n 1 HEAD >expect &&
68                 git rev-list --use-bitmap-index --count -n 1 HEAD >actual &&
69                 test_cmp expect actual
70         '
71
72         test_expect_success "counting non-linear history ($state)" '
73                 git rev-list --count other...master >expect &&
74                 git rev-list --use-bitmap-index --count other...master >actual &&
75                 test_cmp expect actual
76         '
77
78         test_expect_success "counting commits with limiting ($state)" '
79                 git rev-list --count HEAD -- 1.t >expect &&
80                 git rev-list --use-bitmap-index --count HEAD -- 1.t >actual &&
81                 test_cmp expect actual
82         '
83
84         test_expect_success "enumerate --objects ($state)" '
85                 git rev-list --objects --use-bitmap-index HEAD >tmp &&
86                 cut -d" " -f1 <tmp >tmp2 &&
87                 sort <tmp2 >actual &&
88                 git rev-list --objects HEAD >tmp &&
89                 cut -d" " -f1 <tmp >tmp2 &&
90                 sort <tmp2 >expect &&
91                 test_cmp expect actual
92         '
93
94         test_expect_success "bitmap --objects handles non-commit objects ($state)" '
95                 git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
96                 grep $blob actual
97         '
98 }
99
100 rev_list_tests 'full bitmap'
101
102 test_expect_success 'clone from bitmapped repository' '
103         git clone --no-local --bare . clone.git &&
104         git rev-parse HEAD >expect &&
105         git --git-dir=clone.git rev-parse HEAD >actual &&
106         test_cmp expect actual
107 '
108
109 test_expect_success 'setup further non-bitmapped commits' '
110         for i in $(test_seq 1 10)
111         do
112                 test_commit further-$i
113         done
114 '
115
116 rev_list_tests 'partial bitmap'
117
118 test_expect_success 'fetch (partial bitmap)' '
119         git --git-dir=clone.git fetch origin master:master &&
120         git rev-parse HEAD >expect &&
121         git --git-dir=clone.git rev-parse HEAD >actual &&
122         test_cmp expect actual
123 '
124
125 test_expect_success 'incremental repack fails when bitmaps are requested' '
126         test_commit more-1 &&
127         test_must_fail git repack -d 2>err &&
128         test_i18ngrep "Incremental repacks are incompatible with bitmap" err
129 '
130
131 test_expect_success 'incremental repack can disable bitmaps' '
132         test_commit more-2 &&
133         git repack -d --no-write-bitmap-index
134 '
135
136 test_expect_success 'pack-objects respects --local (non-local loose)' '
137         git init --bare alt.git &&
138         echo $(pwd)/alt.git/objects >.git/objects/info/alternates &&
139         echo content1 >file1 &&
140         # non-local loose object which is not present in bitmapped pack
141         altblob=$(GIT_DIR=alt.git git hash-object -w file1) &&
142         # non-local loose object which is also present in bitmapped pack
143         git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin &&
144         git add file1 &&
145         test_tick &&
146         git commit -m commit_file1 &&
147         echo HEAD | git pack-objects --local --stdout --revs >1.pack &&
148         git index-pack 1.pack &&
149         list_packed_objects 1.idx >1.objects &&
150         printf "%s\n" "$altblob" "$blob" >nonlocal-loose &&
151         ! has_any nonlocal-loose 1.objects
152 '
153
154 test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' '
155         echo content2 >file2 &&
156         blob2=$(git hash-object -w file2) &&
157         git add file2 &&
158         test_tick &&
159         git commit -m commit_file2 &&
160         printf "%s\n" "$blob2" "$bitmaptip" >keepobjects &&
161         pack2=$(git pack-objects pack2 <keepobjects) &&
162         mv pack2-$pack2.* .git/objects/pack/ &&
163         >.git/objects/pack/pack2-$pack2.keep &&
164         rm $(objpath $blob2) &&
165         echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack &&
166         git index-pack 2a.pack &&
167         list_packed_objects 2a.idx >2a.objects &&
168         ! has_any keepobjects 2a.objects
169 '
170
171 test_expect_success 'pack-objects respects --local (non-local pack)' '
172         mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ &&
173         echo HEAD | git pack-objects --local --stdout --revs >2b.pack &&
174         git index-pack 2b.pack &&
175         list_packed_objects 2b.idx >2b.objects &&
176         ! has_any keepobjects 2b.objects
177 '
178
179 test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' '
180         ls .git/objects/pack/ | grep bitmap >output &&
181         test_line_count = 1 output &&
182         packbitmap=$(basename $(cat output) .bitmap) &&
183         list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects &&
184         test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" &&
185         >.git/objects/pack/$packbitmap.keep &&
186         echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack &&
187         git index-pack 3a.pack &&
188         list_packed_objects 3a.idx >3a.objects &&
189         ! has_any packbitmap.objects 3a.objects
190 '
191
192 test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' '
193         mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ &&
194         rm -f .git/objects/pack/multi-pack-index &&
195         test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" &&
196         echo HEAD | git pack-objects --local --stdout --revs >3b.pack &&
197         git index-pack 3b.pack &&
198         list_packed_objects 3b.idx >3b.objects &&
199         ! has_any packbitmap.objects 3b.objects
200 '
201
202 test_expect_success 'pack-objects to file can use bitmap' '
203         # make sure we still have 1 bitmap index from previous tests
204         ls .git/objects/pack/ | grep bitmap >output &&
205         test_line_count = 1 output &&
206         # verify equivalent packs are generated with/without using bitmap index
207         packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) &&
208         packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) &&
209         list_packed_objects packa-$packasha1.idx >packa.objects &&
210         list_packed_objects packb-$packbsha1.idx >packb.objects &&
211         test_cmp packa.objects packb.objects
212 '
213
214 test_expect_success 'full repack, reusing previous bitmaps' '
215         git repack -ad &&
216         ls .git/objects/pack/ | grep bitmap >output &&
217         test_line_count = 1 output
218 '
219
220 test_expect_success 'fetch (full bitmap)' '
221         git --git-dir=clone.git fetch origin master:master &&
222         git rev-parse HEAD >expect &&
223         git --git-dir=clone.git rev-parse HEAD >actual &&
224         test_cmp expect actual
225 '
226
227 test_expect_success 'create objects for missing-HAVE tests' '
228         blob=$(echo "missing have" | git hash-object -w --stdin) &&
229         tree=$(printf "100644 blob $blob\tfile\n" | git mktree) &&
230         parent=$(echo parent | git commit-tree $tree) &&
231         commit=$(echo commit | git commit-tree $tree -p $parent) &&
232         cat >revs <<-EOF
233         HEAD
234         ^HEAD^
235         ^$commit
236         EOF
237 '
238
239 test_expect_success 'pack-objects respects --incremental' '
240         cat >revs2 <<-EOF &&
241         HEAD
242         $commit
243         EOF
244         git pack-objects --incremental --stdout --revs <revs2 >4.pack &&
245         git index-pack 4.pack &&
246         list_packed_objects 4.idx >4.objects &&
247         test_line_count = 4 4.objects &&
248         git rev-list --objects $commit >revlist &&
249         cut -d" " -f1 revlist |sort >objects &&
250         test_cmp 4.objects objects
251 '
252
253 test_expect_success 'pack with missing blob' '
254         rm $(objpath $blob) &&
255         git pack-objects --stdout --revs <revs >/dev/null
256 '
257
258 test_expect_success 'pack with missing tree' '
259         rm $(objpath $tree) &&
260         git pack-objects --stdout --revs <revs >/dev/null
261 '
262
263 test_expect_success 'pack with missing parent' '
264         rm $(objpath $parent) &&
265         git pack-objects --stdout --revs <revs >/dev/null
266 '
267
268 test_expect_success JGIT 'we can read jgit bitmaps' '
269         git clone --bare . compat-jgit.git &&
270         (
271                 cd compat-jgit.git &&
272                 rm -f .git/objects/pack/*.bitmap &&
273                 jgit gc &&
274                 git rev-list --test-bitmap HEAD
275         )
276 '
277
278 test_expect_success JGIT 'jgit can read our bitmaps' '
279         git clone --bare . compat-us.git &&
280         (
281                 cd compat-us.git &&
282                 git repack -adb &&
283                 # jgit gc will barf if it does not like our bitmaps
284                 jgit gc
285         )
286 '
287
288 test_expect_success 'splitting packs does not generate bogus bitmaps' '
289         test-tool genrandom foo $((1024 * 1024)) >rand &&
290         git add rand &&
291         git commit -m "commit with big file" &&
292         git -c pack.packSizeLimit=500k repack -adb &&
293         git init --bare no-bitmaps.git &&
294         git -C no-bitmaps.git fetch .. HEAD
295 '
296
297 test_expect_success 'set up reusable pack' '
298         rm -f .git/objects/pack/*.keep &&
299         git repack -adb &&
300         reusable_pack () {
301                 git for-each-ref --format="%(objectname)" |
302                 git pack-objects --delta-base-offset --revs --stdout "$@"
303         }
304 '
305
306 test_expect_success 'pack reuse respects --honor-pack-keep' '
307         test_when_finished "rm -f .git/objects/pack/*.keep" &&
308         for i in .git/objects/pack/*.pack
309         do
310                 >${i%.pack}.keep
311         done &&
312         reusable_pack --honor-pack-keep >empty.pack &&
313         git index-pack empty.pack &&
314         git show-index <empty.idx >actual &&
315         test_must_be_empty actual
316 '
317
318 test_expect_success 'pack reuse respects --local' '
319         mv .git/objects/pack/* alt.git/objects/pack/ &&
320         test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
321         reusable_pack --local >empty.pack &&
322         git index-pack empty.pack &&
323         git show-index <empty.idx >actual &&
324         test_must_be_empty actual
325 '
326
327 test_expect_success 'pack reuse respects --incremental' '
328         reusable_pack --incremental >empty.pack &&
329         git index-pack empty.pack &&
330         git show-index <empty.idx >actual &&
331         test_must_be_empty actual
332 '
333
334 test_expect_success 'truncated bitmap fails gracefully' '
335         git repack -ad &&
336         git rev-list --use-bitmap-index --count --all >expect &&
337         bitmap=$(ls .git/objects/pack/*.bitmap) &&
338         test_when_finished "rm -f $bitmap" &&
339         test_copy_bytes 512 <$bitmap >$bitmap.tmp &&
340         mv -f $bitmap.tmp $bitmap &&
341         git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
342         test_cmp expect actual &&
343         test_i18ngrep corrupt stderr
344 '
345
346 # have_delta <obj> <expected_base>
347 #
348 # Note that because this relies on cat-file, it might find _any_ copy of an
349 # object in the repository. The caller is responsible for making sure
350 # there's only one (e.g., via "repack -ad", or having just fetched a copy).
351 have_delta () {
352         echo $2 >expect &&
353         echo $1 | git cat-file --batch-check="%(deltabase)" >actual &&
354         test_cmp expect actual
355 }
356
357 # Create a state of history with these properties:
358 #
359 #  - refs that allow a client to fetch some new history, while sharing some old
360 #    history with the server; we use branches delta-reuse-old and
361 #    delta-reuse-new here
362 #
363 #  - the new history contains an object that is stored on the server as a delta
364 #    against a base that is in the old history
365 #
366 #  - the base object is not immediately reachable from the tip of the old
367 #    history; finding it would involve digging down through history we know the
368 #    other side has
369 #
370 # This should result in a state where fetching from old->new would not
371 # traditionally reuse the on-disk delta (because we'd have to dig to realize
372 # that the client has it), but we will do so if bitmaps can tell us cheaply
373 # that the other side has it.
374 test_expect_success 'set up thin delta-reuse parent' '
375         # This first commit contains the buried base object.
376         test-tool genrandom delta 16384 >file &&
377         git add file &&
378         git commit -m "delta base" &&
379         base=$(git rev-parse --verify HEAD:file) &&
380
381         # These intermediate commits bury the base back in history.
382         # This becomes the "old" state.
383         for i in 1 2 3 4 5
384         do
385                 echo $i >file &&
386                 git commit -am "intermediate $i" || return 1
387         done &&
388         git branch delta-reuse-old &&
389
390         # And now our new history has a delta against the buried base. Note
391         # that this must be smaller than the original file, since pack-objects
392         # prefers to create deltas from smaller objects to larger.
393         test-tool genrandom delta 16300 >file &&
394         git commit -am "delta result" &&
395         delta=$(git rev-parse --verify HEAD:file) &&
396         git branch delta-reuse-new &&
397
398         # Repack with bitmaps and double check that we have the expected delta
399         # relationship.
400         git repack -adb &&
401         have_delta $delta $base
402 '
403
404 # Now we can sanity-check the non-bitmap behavior (that the server is not able
405 # to reuse the delta). This isn't strictly something we care about, so this
406 # test could be scrapped in the future. But it makes sure that the next test is
407 # actually triggering the feature we want.
408 #
409 # Note that our tools for working with on-the-wire "thin" packs are limited. So
410 # we actually perform the fetch, retain the resulting pack, and inspect the
411 # result.
412 test_expect_success 'fetch without bitmaps ignores delta against old base' '
413         test_config pack.usebitmaps false &&
414         test_when_finished "rm -rf client.git" &&
415         git init --bare client.git &&
416         (
417                 cd client.git &&
418                 git config transfer.unpackLimit 1 &&
419                 git fetch .. delta-reuse-old:delta-reuse-old &&
420                 git fetch .. delta-reuse-new:delta-reuse-new &&
421                 have_delta $delta $ZERO_OID
422         )
423 '
424
425 # And do the same for the bitmap case, where we do expect to find the delta.
426 test_expect_success 'fetch with bitmaps can reuse old base' '
427         test_config pack.usebitmaps true &&
428         test_when_finished "rm -rf client.git" &&
429         git init --bare client.git &&
430         (
431                 cd client.git &&
432                 git config transfer.unpackLimit 1 &&
433                 git fetch .. delta-reuse-old:delta-reuse-old &&
434                 git fetch .. delta-reuse-new:delta-reuse-new &&
435                 have_delta $delta $base
436         )
437 '
438
439 test_done