1
+#!/bin/sh
2
+
3
+test_description='git repack --write-midx=incremental'
4
+
5
+. ./test-lib.sh
6
+
7
+GIT_TEST_MULTI_PACK_INDEX=0
8
+GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
9
+GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0
10
+
11
+objdir=.git/objects
12
+packdir=$objdir/pack
13
+midxdir=$packdir/multi-pack-index.d
14
+midx_chain=$midxdir/multi-pack-index-chain
15
+
16
+# incrementally_repack N
17
+#
18
+# Make "N" new commits, each stored in their own pack, and then repacked
19
+# with the --write-midx=incremental strategy.
20
+incrementally_repack () {
21
+ for i in $(test_seq 1 "$1")
22
+ do
23
+ test_commit "$i" &&
24
+
25
+ git repack --geometric=2 -d --write-midx=incremental \
26
+ --write-bitmap-index &&
27
+ git multi-pack-index verify || return 1
28
+ done
29
+}
30
+
31
+# Create packs with geometrically increasing sizes so that they
32
+# satisfy the geometric progression and survive a --geometric=2
33
+# repack without being rolled up. Creates 3 packs containing 1,
34
+# 2, and 6 commits (3, 6, and 18 objects) respectively.
35
+create_geometric_packs () {
36
+ test_commit "small" &&
37
+ git repack -d &&
38
+
39
+ test_commit_bulk --message="medium" 2 &&
40
+ test_commit_bulk --message="large" 6 &&
41
+
42
+ git repack --geometric=2 -d --write-midx=incremental \
43
+ --write-bitmap-index
44
+}
45
+
46
+# create_layer <test_commit_bulk args>
47
+#
48
+# Creates a new MIDX layer with the contents of "test_commit_bulk $@".
49
+create_layer () {
50
+ test_commit_bulk "$@" &&
51
+
52
+ git multi-pack-index write --incremental --bitmap
53
+}
54
+
55
+# create_layers
56
+#
57
+# Reads lines of "<message> <nr>" from stdin and creates a new MIDX
58
+# layer for each line. See create_layer above for more.
59
+create_layers () {
60
+ while read msg nr
61
+ do
62
+ create_layer --message="$msg" "$nr" || return 1
63
+ done
64
+}
65
+
66
+test_expect_success '--write-midx=incremental requires --geometric' '
67
+ test_must_fail git repack --write-midx=incremental 2>err &&
68
+
69
+ test_grep -- "--write-midx=incremental requires --geometric" err
70
+'
71
+
72
+test_expect_success 'below layer threshold, tip packs excluded' '
73
+ git init below-layer-threshold-tip-packs-excluded &&
74
+ (
75
+ cd below-layer-threshold-tip-packs-excluded &&
76
+
77
+ git config maintenance.auto false &&
78
+ git config repack.midxnewlayerthreshold 4 &&
79
+ git config repack.midxsplitfactor 2 &&
80
+
81
+ # Create 3 packs forming a geometric progression by
82
+ # object count such that they are unmodified by the
83
+ # initial repack. The MIDX chain thusly contains a
84
+ # single layer with three packs.
85
+ create_geometric_packs &&
86
+ ls $packdir/pack-*.idx | sort >packs.before &&
87
+ test_line_count = 1 $midx_chain &&
88
+ cp $midx_chain $midx_chain.before &&
89
+
90
+ # Repack a new commit. Since the layer threshold is
91
+ # unmet, a new MIDX layer is added on top of the
92
+ # existing one.
93
+ test_commit extra &&
94
+ git repack --geometric=2 -d --write-midx=incremental \
95
+ --write-bitmap-index &&
96
+ git multi-pack-index verify &&
97
+
98
+ ls $packdir/pack-*.idx | sort >packs.after &&
99
+ comm -13 packs.before packs.after >packs.new &&
100
+ test_line_count = 1 packs.new &&
101
+
102
+ test_line_count = 2 "$midx_chain" &&
103
+ head -n 1 "$midx_chain.before" >expect &&
104
+ head -n 1 "$midx_chain" >actual &&
105
+ test_cmp expect actual
106
+ )
107
+'
108
+
109
+test_expect_success 'above layer threshold, tip packs repacked' '
110
+ git init above-layer-threshold-tip-packs-repacked &&
111
+ (
112
+ cd above-layer-threshold-tip-packs-repacked &&
113
+
114
+ git config maintenance.auto false &&
115
+ git config repack.midxnewlayerthreshold 2 &&
116
+ git config repack.midxsplitfactor 2 &&
117
+
118
+ # Same setup, but with the layer threshold set to 2.
119
+ # Since the tip MIDX layer meets that threshold, its
120
+ # packs are considered repack candidates.
121
+ create_geometric_packs &&
122
+ cp $midx_chain $midx_chain.before &&
123
+
124
+ # Perturb the existing progression such that it is
125
+ # rolled up into a single new pack, invalidating the
126
+ # existing MIDX layer and replacing it with a new one.
127
+ test_commit extra &&
128
+ git repack -d &&
129
+ git repack --geometric=2 -d --write-midx=incremental \
130
+ --write-bitmap-index &&
131
+
132
+ ! test_cmp $midx_chain.before $midx_chain &&
133
+ test_line_count = 1 $midx_chain &&
134
+
135
+ git multi-pack-index verify
136
+ )
137
+'
138
+
139
+test_expect_success 'above layer threshold, tip layer preserved' '
140
+ git init above-layer-threshold-tip-layer-preserved &&
141
+ (
142
+ cd above-layer-threshold-tip-layer-preserved &&
143
+
144
+ git config maintenance.auto false &&
145
+ git config repack.midxnewlayerthreshold 2 &&
146
+ git config repack.midxsplitfactor 2 &&
147
+
148
+ test_commit_bulk --message="medium" 2 &&
149
+ test_commit_bulk --message="large" 6 &&
150
+
151
+ git repack --geometric=2 -d --write-midx=incremental \
152
+ --write-bitmap-index &&
153
+
154
+ test_line_count = 1 "$midx_chain" &&
155
+ ls $packdir/pack-*.idx | sort >packs.before &&
156
+ cp $midx_chain $midx_chain.before &&
157
+
158
+ # Create objects to form a pack satisfying the geometric
159
+ # progression (thus preserving the tip layer), but not
160
+ # so large that it meets the layer merging condition.
161
+ test_commit_bulk --message="small" 1 &&
162
+ git repack --geometric=2 -d --write-midx=incremental \
163
+ --write-bitmap-index &&
164
+
165
+ ls $packdir/pack-*.idx | sort >packs.after &&
166
+ comm -13 packs.before packs.after >packs.new &&
167
+
168
+ test_line_count = 1 packs.new &&
169
+ test_line_count = 3 packs.after &&
170
+ test_line_count = 2 "$midx_chain" &&
171
+ head -n 1 "$midx_chain.before" >expect &&
172
+ head -n 1 "$midx_chain" >actual &&
173
+ test_cmp expect actual &&
174
+
175
+ git multi-pack-index verify
176
+ )
177
+'
178
+
179
+test_expect_success 'above layer threshold, tip packs preserved' '
180
+ git init above-layer-threshold-tip-packs-preserved &&
181
+ (
182
+ cd above-layer-threshold-tip-packs-preserved &&
183
+
184
+ git config maintenance.auto false &&
185
+ git config repack.midxnewlayerthreshold 2 &&
186
+ git config repack.midxsplitfactor 2 &&
187
+
188
+ create_geometric_packs &&
189
+ ls $packdir/pack-*.idx | sort >packs.before &&
190
+ cp $midx_chain $midx_chain.before &&
191
+
192
+ # Same setup as above, but this time the new objects do
193
+ # not satisfy the new layer merging condition, resulting
194
+ # in a new tip layer.
195
+ test_commit_bulk --message="huge" 18 &&
196
+ git repack --geometric=2 -d --write-midx=incremental \
197
+ --write-bitmap-index &&
198
+
199
+ ls $packdir/pack-*.idx | sort >packs.after &&
200
+ comm -13 packs.before packs.after >packs.new &&
201
+
202
+ ! test_cmp $midx_chain.before $midx_chain &&
203
+ test_line_count = 1 $midx_chain &&
204
+ test_line_count = 1 packs.new &&
205
+
206
+ git multi-pack-index verify
207
+ )
208
+'
209
+
210
+test_expect_success 'new tip absorbs multiple layers' '
211
+ git init new-tip-absorbs-multiple-layers &&
212
+ (
213
+ cd new-tip-absorbs-multiple-layers &&
214
+
215
+ git config maintenance.auto false &&
216
+ git config repack.midxnewlayerthreshold 1 &&
217
+ git config repack.midxsplitfactor 2 &&
218
+
219
+ # Build a 4-layer chain where each layer is too small to
220
+ # absorb the one below it. The sizes must satisfy L(n) <
221
+ # L(n-1)/2 for each adjacent pair:
222
+ #
223
+ # L0 (oldest): 75 obj (25 commits)
224
+ # L1: 21 obj (7 commits, 21 < 75/2)
225
+ # L2: 9 obj (3 commits, 9 < 21/2)
226
+ # L3 (tip): 3 obj (1 commit, 3 < 9/2)
227
+ create_layers <<-\EOF &&
228
+ L0 25
229
+ L1 7
230
+ L2 3
231
+ L3 1
232
+ EOF
233
+
234
+ test_line_count = 4 "$midx_chain" &&
235
+ cp $midx_chain $midx_chain.before &&
236
+
237
+ # Now add a new commit. The merging condition is
238
+ # satisfied between L3-L1, but violated at L0, which is
239
+ # too large relative to the accumulated size.
240
+ #
241
+ # As a result, the chain shrinks from 4 to 2 layers.
242
+ test_commit new &&
243
+ git repack --geometric=2 -d --write-midx=incremental \
244
+ --write-bitmap-index &&
245
+
246
+ ! test_cmp $midx_chain.before $midx_chain &&
247
+ test_line_count = 2 "$midx_chain" &&
248
+ git multi-pack-index verify
249
+ )
250
+'
251
+
252
+test_expect_success 'compaction of older layers' '
253
+ git init compaction-of-older-layers &&
254
+ (
255
+ cd compaction-of-older-layers &&
256
+
257
+ git config maintenance.auto false &&
258
+ git config repack.midxnewlayerthreshold 1 &&
259
+ git config repack.midxsplitfactor 2 &&
260
+
261
+ # Build a chain with two small layers at the bottom
262
+ # and a larger barrier layer on top, producing a
263
+ # chain that violates the compaction invariant, since
264
+ # the two small layers would normally have been merged.
265
+ create_layers <<-\EOF &&
266
+ one 2
267
+ two 4
268
+ barrier 54
269
+ EOF
270
+
271
+ cp $midx_chain $midx_chain.before &&
272
+
273
+ # Running an incremental repack compacts the two
274
+ # small layers at the bottom of the chain as a
275
+ # separate step in the compaction plan.
276
+ test_commit another &&
277
+ git repack --geometric=2 -d --write-midx=incremental \
278
+ --write-bitmap-index &&
279
+
280
+ test_line_count = 2 "$midx_chain" &&
281
+ git multi-pack-index verify
282
+ )
283
+'
284
+
285
+test_expect_success 'geometric rollup with surviving tip packs' '
286
+ git init geometric-rollup-with-surviving-tip-packs &&
287
+ (
288
+ cd geometric-rollup-with-surviving-tip-packs &&
289
+
290
+ git config maintenance.auto false &&
291
+ git config repack.midxnewlayerthreshold 1 &&
292
+ git config repack.midxsplitfactor 2 &&
293
+
294
+ # Create a pack large enough to anchor the geometric
295
+ # progression when small packs are added alongside it.
296
+ create_layer --message="big" 5 &&
297
+
298
+ test_line_count = 1 "$midx_chain" &&
299
+ cp $midx_chain $midx_chain.before &&
300
+
301
+ # Repack a small number of objects such that the
302
+ # progression is unbothered. Note that the existing pack
303
+ # is considered a repack candidate as the new layer
304
+ # threshold is set to 1.
305
+ test_commit small-1 &&
306
+ git repack -d &&
307
+ git repack --geometric=2 -d --write-midx=incremental \
308
+ --write-bitmap-index &&
309
+
310
+ ! test_cmp $midx_chain.before $midx_chain &&
311
+ cp $midx_chain $midx_chain.before
312
+ )
313
+'
314
+
315
+test_expect_success 'kept packs are excluded from repack' '
316
+ git init kept-packs-excluded-from-repack &&
317
+ (
318
+ cd kept-packs-excluded-from-repack &&
319
+
320
+ git config maintenance.auto false &&
321
+ git config repack.midxnewlayerthreshold 1 &&
322
+ git config repack.midxsplitfactor 2 &&
323
+
324
+ # Create two equal-sized packs, marking one as kept.
325
+ for i in A B
326
+ do
327
+ test_commit "$i" && git repack -d || return 1
328
+ done &&
329
+
330
+ keep=$(ls $packdir/pack-*.idx | head -n 1) &&
331
+ touch "${keep%.idx}.keep" &&
332
+
333
+ # The kept pack is excluded as a repacking candidate
334
+ # entirely, so no rollup occurs as there is only one
335
+ # non-kept pack. A new MIDX layer is written containing
336
+ # that pack.
337
+ git repack --geometric=2 -d --write-midx=incremental \
338
+ --write-bitmap-index &&
339
+
340
+ test-tool read-midx $objdir >actual &&
341
+ grep "^pack-.*\.idx$" actual >actual.packs &&
342
+ test_line_count = 1 actual.packs &&
343
+ test_grep ! "$keep" actual.packs &&
344
+
345
+ git multi-pack-index verify &&
346
+
347
+ # All objects (from both kept and non-kept packs)
348
+ # must still be accessible.
349
+ git fsck
350
+ )
351
+'
352
+
353
+test_expect_success 'incremental MIDX with --max-pack-size' '
354
+ git init incremental-midx-with--max-pack-size &&
355
+ (
356
+ cd incremental-midx-with--max-pack-size &&
357
+
358
+ git config maintenance.auto false &&
359
+ git config repack.midxnewlayerthreshold 1 &&
360
+ git config repack.midxsplitfactor 2 &&
361
+
362
+ create_layer --message="base" 1 &&
363
+
364
+ # Now add enough data that a small --max-pack-size will
365
+ # cause pack-objects to split its output. Create objects
366
+ # large enough to fill multiple packs.
367
+ test-tool genrandom foo 1M >big1 &&
368
+ test-tool genrandom bar 1M >big2 &&
369
+ git add big1 big2 &&
370
+ test_tick &&
371
+ git commit -a -m "big blobs" &&
372
+ git repack -d &&
373
+
374
+ git repack --geometric=2 -d --write-midx=incremental \
375
+ --write-bitmap-index --max-pack-size=1M &&
376
+
377
+ test_line_count = 1 "$midx_chain" &&
378
+ test-tool read-midx $objdir >actual &&
379
+ grep "^pack-.*\.idx$" actual >actual.packs &&
380
+ test_line_count -gt 1 actual.packs &&
381
+
382
+ git multi-pack-index verify
383
+ )
384
+'
385
+
386
+test_expect_success 'noop repack preserves valid MIDX chain' '
387
+ git init noop-repack-preserves-valid-midx-chain &&
388
+ (
389
+ cd noop-repack-preserves-valid-midx-chain &&
390
+
391
+ git config maintenance.auto false &&
392
+ git config repack.midxnewlayerthreshold 1 &&
393
+ git config repack.midxsplitfactor 2 &&
394
+
395
+ create_layer --message="base" 1 &&
396
+
397
+ git multi-pack-index verify &&
398
+ cp $midx_chain $midx_chain.before &&
399
+
400
+ # Running again with no new objects should not break
401
+ # the MIDX chain. It produces "Nothing new to pack."
402
+ git repack --geometric=2 -d --write-midx=incremental \
403
+ --write-bitmap-index &&
404
+
405
+ test_cmp $midx_chain.before $midx_chain &&
406
+
407
+ git multi-pack-index verify &&
408
+ git fsck
409
+ )
410
+'
411
+
412
+test_expect_success 'repack -ad removes stale incremental chain' '
413
+ git init repack--ad-removes-stale-incremental-chain &&
414
+ (
415
+ cd repack--ad-removes-stale-incremental-chain &&
416
+
417
+ git config maintenance.auto false &&
418
+ git config repack.midxnewlayerthreshold 1 &&
419
+ git config repack.midxsplitfactor 2 &&
420
+
421
+ create_layers <<-\EOF &&
422
+ one 1
423
+ two 1
424
+ EOF
425
+
426
+ test_path_is_file $midx_chain &&
427
+ test_line_count = 2 $midx_chain &&
428
+
429
+ git repack -ad &&
430
+
431
+ test_path_is_missing $packdir/multi-pack-index &&
432
+ test_dir_is_empty $midxdir
433
+ )
434
+'
435
+
436
+test_expect_success 'repack rejects invalid midxSplitFactor' '
437
+ test_when_finished "rm -fr bad-split-factor" &&
438
+ git init bad-split-factor &&
439
+ (
440
+ cd bad-split-factor &&
441
+ test_commit base &&
442
+
443
+ for v in 0 1 -1
444
+ do
445
+ test_must_fail git -c repack.midxSplitFactor=$v \
446
+ repack -d --geometric=2 --write-midx=incremental 2>err &&
447
+ test_grep "invalid value for --midx-split-factor" err ||
448
+ return 1
449
+ done
450
+ )
451
+'
452
+
453
+test_expect_success 'repack rejects invalid midxNewLayerThreshold' '
454
+ test_when_finished "rm -fr bad-layer-threshold" &&
455
+ git init bad-layer-threshold &&
456
+ (
457
+ cd bad-layer-threshold &&
458
+ test_commit base &&
459
+
460
+ for v in 0 -1
461
+ do
462
+ test_must_fail git -c repack.midxNewLayerThreshold=$v \
463
+ repack -d --geometric=2 --write-midx=incremental 2>err &&
464
+ test_grep "invalid value for --midx-new-layer-threshold" err ||
465
+ return 1
466
+ done
467
+ )
468
+'
469
+
470
+test_done