Raw
1 #!/bin/sh
2
3 test_description='commit graph'
4
5 . ./test-lib.sh
6 . "$TEST_DIRECTORY"/lib-chunk.sh
7
8 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
9
10 test_expect_success 'usage' '
11 test_expect_code 129 git commit-graph write blah 2>err &&
12 test_expect_code 129 git commit-graph write verify
13 '
14
15 test_expect_success 'usage shown without sub-command' '
16 test_expect_code 129 git commit-graph 2>err &&
17 grep usage: err
18 '
19
20 test_expect_success 'usage shown with an error on unknown sub-command' '
21 cat >expect <<-\EOF &&
22 error: unknown subcommand: `unknown'\''
23 EOF
24 test_expect_code 129 git commit-graph unknown 2>stderr &&
25 grep error stderr >actual &&
26 test_cmp expect actual
27 '
28
29 objdir=".git/objects"
30
31 test_expect_success 'setup full repo' '
32 git init full
33 '
34
35 test_expect_success POSIXPERM 'tweak umask for modebit tests' '
36 umask 022
37 '
38
39 test_expect_success 'verify graph with no graph file' '
40 git -C full commit-graph verify
41 '
42
43 test_expect_success 'write graph with no packs' '
44 git -C full commit-graph write --object-dir $objdir &&
45 test_path_is_missing full/$objdir/info/commit-graph
46 '
47
48 test_expect_success 'exit with correct error on bad input to --stdin-packs' '
49 echo doesnotexist >in &&
50 test_expect_code 1 git -C full commit-graph write --stdin-packs \
51 <in 2>stderr &&
52 test_grep "error adding pack" stderr
53 '
54
55 test_expect_success 'create commits and repack' '
56 for i in $(test_seq 3)
57 do
58 test_commit -C full $i &&
59 git -C full branch commits/$i || return 1
60 done &&
61 git -C full repack
62 '
63
64 . "$TEST_DIRECTORY"/lib-commit-graph.sh
65
66 graph_git_behavior 'no graph' full commits/3 commits/1
67
68 test_expect_success 'exit with correct error on bad input to --stdin-commits' '
69 # invalid, non-hex OID
70 echo HEAD | test_expect_code 1 git -C full commit-graph write \
71 --stdin-commits 2>stderr &&
72 test_grep "unexpected non-hex object ID: HEAD" stderr &&
73 # non-existent OID
74 echo $ZERO_OID | test_expect_code 1 git -C full commit-graph write \
75 --stdin-commits 2>stderr &&
76 test_grep "invalid object" stderr &&
77 # valid commit and tree OID
78 git -C full rev-parse HEAD HEAD^{tree} >in &&
79 git -C full commit-graph write --stdin-commits <in &&
80 graph_read_expect -C full 3 generation_data
81 '
82
83 test_expect_success 'write graph' '
84 git -C full commit-graph write &&
85 test_path_is_file full/$objdir/info/commit-graph &&
86 graph_read_expect -C full 3 generation_data
87 '
88
89 test_expect_success POSIXPERM 'write graph has correct permissions' '
90 test_path_is_file full/$objdir/info/commit-graph &&
91 echo "-r--r--r--" >expect &&
92 test_modebits full/$objdir/info/commit-graph >actual &&
93 test_cmp expect actual
94 '
95
96 graph_git_behavior 'graph exists' full commits/3 commits/1
97
98 test_expect_success 'Add more commits' '
99 git -C full reset --hard commits/1 &&
100 for i in $(test_seq 4 5)
101 do
102 test_commit -C full $i &&
103 git -C full branch commits/$i || return 1
104 done &&
105 git -C full reset --hard commits/2 &&
106 for i in $(test_seq 6 7)
107 do
108 test_commit -C full $i &&
109 git -C full branch commits/$i || return 1
110 done &&
111 git -C full reset --hard commits/2 &&
112 git -C full merge commits/4 &&
113 git -C full branch merge/1 &&
114 git -C full reset --hard commits/4 &&
115 git -C full merge commits/6 &&
116 git -C full branch merge/2 &&
117 git -C full reset --hard commits/3 &&
118 git -C full merge commits/5 commits/7 &&
119 git -C full branch merge/3 &&
120 git -C full repack
121 '
122
123 test_expect_success 'commit-graph write progress off for redirected stderr' '
124 git -C full commit-graph write 2>err &&
125 test_must_be_empty err
126 '
127
128 test_expect_success 'commit-graph write force progress on for stderr' '
129 GIT_PROGRESS_DELAY=0 git -C full commit-graph write --progress 2>err &&
130 test_file_not_empty err
131 '
132
133 test_expect_success 'commit-graph write with the --no-progress option' '
134 git -C full commit-graph write --no-progress 2>err &&
135 test_must_be_empty err
136 '
137
138 test_expect_success 'commit-graph write --stdin-commits progress off for redirected stderr' '
139 git -C full rev-parse commits/5 >in &&
140 git -C full commit-graph write --stdin-commits <in 2>err &&
141 test_must_be_empty err
142 '
143
144 test_expect_success 'commit-graph write --stdin-commits force progress on for stderr' '
145 git -C full rev-parse commits/5 >in &&
146 GIT_PROGRESS_DELAY=0 git -C full commit-graph write --stdin-commits \
147 --progress <in 2>err &&
148 test_grep "Collecting commits from input" err
149 '
150
151 test_expect_success 'commit-graph write --stdin-commits with the --no-progress option' '
152 git -C full rev-parse commits/5 >in &&
153 git -C full commit-graph write --stdin-commits --no-progress <in 2>err &&
154 test_must_be_empty err
155 '
156
157 test_expect_success 'commit-graph verify progress off for redirected stderr' '
158 git -C full commit-graph verify 2>err &&
159 test_must_be_empty err
160 '
161
162 test_expect_success 'commit-graph verify force progress on for stderr' '
163 GIT_PROGRESS_DELAY=0 git -C full commit-graph verify --progress 2>err &&
164 test_file_not_empty err
165 '
166
167 test_expect_success 'commit-graph verify with the --no-progress option' '
168 git -C full commit-graph verify --no-progress 2>err &&
169 test_must_be_empty err
170 '
171
172 # Current graph structure:
173 #
174 # __M3___
175 # / | \
176 # 3 M1 5 M2 7
177 # |/ \|/ \|
178 # 2 4 6
179 # |___/____/
180 # 1
181
182 test_expect_success 'write graph with merges' '
183 git -C full commit-graph write &&
184 test_path_is_file full/$objdir/info/commit-graph &&
185 graph_read_expect -C full 10 "generation_data extra_edges"
186 '
187
188 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
189 graph_git_behavior 'merge 1 vs 3' full merge/1 merge/3
190 graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
191
192 test_expect_success 'Add one more commit' '
193 test_commit -C full 8 &&
194 git -C full branch commits/8 &&
195 ls full/$objdir/pack | grep idx >existing-idx &&
196 git -C full repack &&
197 ls full/$objdir/pack| grep idx | grep -v -f existing-idx >new-idx
198 '
199
200 # Current graph structure:
201 #
202 # 8
203 # |
204 # __M3___
205 # / | \
206 # 3 M1 5 M2 7
207 # |/ \|/ \|
208 # 2 4 6
209 # |___/____/
210 # 1
211
212 graph_git_behavior 'mixed mode, commit 8 vs merge 1' full commits/8 merge/1
213 graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
214
215 test_expect_success 'write graph with new commit' '
216 git -C full commit-graph write &&
217 test_path_is_file full/$objdir/info/commit-graph &&
218 graph_read_expect -C full 11 "generation_data extra_edges"
219 '
220
221 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
222 graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
223
224 test_expect_success 'write graph with nothing new' '
225 git -C full commit-graph write &&
226 test_path_is_file full/$objdir/info/commit-graph &&
227 graph_read_expect -C full 11 "generation_data extra_edges"
228 '
229
230 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
231 graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
232
233 test_expect_success 'build graph from latest pack with closure' '
234 git -C full commit-graph write --stdin-packs <new-idx &&
235 test_path_is_file full/$objdir/info/commit-graph &&
236 graph_read_expect -C full 9 "generation_data extra_edges"
237 '
238
239 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
240 graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
241
242 test_expect_success 'build graph from commits with closure' '
243 git -C full tag -a -m "merge" tag/merge merge/2 &&
244 git -C full rev-parse tag/merge >commits-in &&
245 git -C full rev-parse merge/1 >>commits-in &&
246 git -C full commit-graph write --stdin-commits <commits-in &&
247 test_path_is_file full/$objdir/info/commit-graph &&
248 graph_read_expect -C full 6 "generation_data"
249 '
250
251 graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
252 graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
253
254 test_expect_success 'build graph from commits with append' '
255 git -C full rev-parse merge/3 >in &&
256 git -C full commit-graph write --stdin-commits --append <in &&
257 test_path_is_file full/$objdir/info/commit-graph &&
258 graph_read_expect -C full 10 "generation_data extra_edges"
259 '
260
261 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
262 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
263
264 test_expect_success 'build graph using --reachable' '
265 git -C full commit-graph write --reachable &&
266 test_path_is_file full/$objdir/info/commit-graph &&
267 graph_read_expect -C full 11 "generation_data extra_edges"
268 '
269
270 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
271 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
272
273 test_expect_success 'setup bare repo' '
274 git clone --bare --no-local full bare
275 '
276
277 graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
278 graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
279
280 test_expect_success 'write graph in bare repo' '
281 git -C bare commit-graph write &&
282 test_path_is_file bare/objects/info/commit-graph &&
283 graph_read_expect -C bare 11 "generation_data extra_edges"
284 '
285
286 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
287 graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
288
289 test_expect_success 'perform fast-forward merge in full repo' '
290 git -C full checkout -b merge-5-to-8 commits/5 &&
291 git -C full merge commits/8 &&
292 git -C full show-ref -s merge-5-to-8 >output &&
293 git -C full show-ref -s commits/8 >expect &&
294 test_cmp expect output
295 '
296
297 test_expect_success 'check that gc computes commit-graph' '
298 test_commit -C full --no-tag blank &&
299 git -C full commit-graph write --reachable &&
300 cp full/$objdir/info/commit-graph commit-graph-before-gc &&
301 git -C full reset --hard HEAD~1 &&
302 test_config -C full gc.writeCommitGraph true &&
303 git -C full gc &&
304 cp full/$objdir/info/commit-graph commit-graph-after-gc &&
305 ! test_cmp_bin commit-graph-before-gc commit-graph-after-gc &&
306 git -C full commit-graph write --reachable &&
307 test_cmp_bin commit-graph-after-gc full/$objdir/info/commit-graph
308 '
309
310 test_expect_success 'replace-objects invalidates commit-graph' '
311 test_when_finished rm -rf replace &&
312 git clone full replace &&
313 (
314 cd replace &&
315 git commit-graph write --reachable &&
316 test_path_is_file .git/objects/info/commit-graph &&
317 git replace HEAD~1 HEAD~2 &&
318 graph_git_two_modes "commit-graph verify" &&
319 git -c core.commitGraph=false log >expect &&
320 git -c core.commitGraph=true log >actual &&
321 test_cmp expect actual &&
322 git commit-graph write --reachable &&
323 git -c core.commitGraph=false --no-replace-objects log >expect &&
324 git -c core.commitGraph=true --no-replace-objects log >actual &&
325 test_cmp expect actual &&
326 rm -rf .git/objects/info/commit-graph &&
327 git commit-graph write --reachable &&
328 test_path_is_file .git/objects/info/commit-graph
329 )
330 '
331
332 test_expect_success 'commit grafts invalidate commit-graph' '
333 test_when_finished rm -rf graft &&
334 git clone --template= full graft &&
335 (
336 cd graft &&
337 git commit-graph write --reachable &&
338 test_path_is_file .git/objects/info/commit-graph &&
339 H1=$(git rev-parse --verify HEAD~1) &&
340 H3=$(git rev-parse --verify HEAD~3) &&
341 mkdir .git/info &&
342 echo "$H1 $H3" >.git/info/grafts &&
343 git -c core.commitGraph=false log >expect &&
344 git -c core.commitGraph=true log >actual &&
345 test_cmp expect actual &&
346 git commit-graph write --reachable &&
347 git -c core.commitGraph=false --no-replace-objects log >expect &&
348 git -c core.commitGraph=true --no-replace-objects log >actual &&
349 test_cmp expect actual &&
350 rm -rf .git/objects/info/commit-graph &&
351 git commit-graph write --reachable &&
352 test_path_is_missing .git/objects/info/commit-graph
353 )
354 '
355
356 test_expect_success 'replace-objects invalidates commit-graph' '
357 test_when_finished rm -rf shallow &&
358 git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
359 (
360 cd shallow &&
361 git commit-graph write --reachable &&
362 test_path_is_missing .git/objects/info/commit-graph &&
363 git fetch origin --unshallow &&
364 git commit-graph write --reachable &&
365 test_path_is_file .git/objects/info/commit-graph
366 )
367 '
368
369 test_expect_success 'warn on improper hash version' '
370 git init --object-format=sha1 sha1 &&
371 (
372 cd sha1 &&
373 test_commit 1 &&
374 git commit-graph write --reachable &&
375 mv .git/objects/info/commit-graph ../cg-sha1
376 ) &&
377 git init --object-format=sha256 sha256 &&
378 (
379 cd sha256 &&
380 test_commit 1 &&
381 git commit-graph write --reachable &&
382 mv .git/objects/info/commit-graph ../cg-sha256
383 ) &&
384 (
385 cd sha1 &&
386 mv ../cg-sha256 .git/objects/info/commit-graph &&
387 git log -1 2>err &&
388 test_grep "commit-graph hash version 2 does not match version 1" err
389 ) &&
390 (
391 cd sha256 &&
392 mv ../cg-sha1 .git/objects/info/commit-graph &&
393 git log -1 2>err &&
394 test_grep "commit-graph hash version 1 does not match version 2" err
395 )
396 '
397
398 test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
399 UNIX_EPOCH_ZERO="@0 +0000" &&
400 FUTURE_DATE="@4147483646 +0000" &&
401 rm -f full/.git/objects/info/commit-graph &&
402 test_commit -C full --date "$FUTURE_DATE" future-1 &&
403 test_commit -C full --date "$UNIX_EPOCH_ZERO" old-1 &&
404 git -C full commit-graph write --reachable &&
405 test_commit -C full --date "$FUTURE_DATE" future-2 &&
406 test_commit -C full --date "$UNIX_EPOCH_ZERO" old-2 &&
407 git -C full commit-graph write --reachable --split=no-merge &&
408 test_commit -C full extra &&
409 git -C full commit-graph write --reachable --split=no-merge &&
410 git -C full commit-graph write --reachable &&
411 graph_read_expect -C full 16 \
412 "generation_data generation_data_overflow extra_edges" &&
413 mv full/.git/objects/info/commit-graph commit-graph-upgraded &&
414 git -C full commit-graph write --reachable &&
415 graph_read_expect -C full 16 \
416 "generation_data generation_data_overflow extra_edges" &&
417 test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
418 '
419
420 test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'overflow chunk when replacing commit-graph' '
421 test_when_finished "rm -rf repo" &&
422 git init repo &&
423 (
424 cd repo &&
425 cat >commit <<-EOF &&
426 tree $(test_oid empty_tree)
427 author Example <committer@example.com> 9223372036854775 +0000
428 committer Example <committer@example.com> 9223372036854775 +0000
429
430 Weird commit date
431 EOF
432 commit_id=$(git hash-object -t commit -w commit) &&
433 git reset --hard "$commit_id" &&
434 git commit-graph write --reachable &&
435 git commit-graph write --reachable --split=replace &&
436 git log
437 )
438 '
439
440 # the verify tests below expect the commit-graph to contain
441 # exactly the commits reachable from the commits/8 branch.
442 # If the file changes the set of commits in the list, then the
443 # offsets into the binary file will result in different edits
444 # and the tests will likely break.
445
446 test_expect_success 'git commit-graph verify' '
447 git -C full rev-parse commits/8 >in &&
448 git -C full -c commitGraph.generationVersion=1 commit-graph write \
449 --stdin-commits <in &&
450 git -C full commit-graph verify >output &&
451 graph_read_expect -C full 9 extra_edges 1
452 '
453
454 NUM_COMMITS=9
455 NUM_OCTOPUS_EDGES=2
456 HASH_LEN="$(test_oid rawsz)"
457 GRAPH_BYTE_VERSION=4
458 GRAPH_BYTE_HASH=5
459 GRAPH_BYTE_CHUNK_COUNT=6
460 GRAPH_CHUNK_LOOKUP_OFFSET=8
461 GRAPH_CHUNK_LOOKUP_WIDTH=12
462 GRAPH_CHUNK_LOOKUP_ROWS=5
463 GRAPH_BYTE_OID_FANOUT_ID=$GRAPH_CHUNK_LOOKUP_OFFSET
464 GRAPH_BYTE_OID_LOOKUP_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
465 1 * $GRAPH_CHUNK_LOOKUP_WIDTH))
466 GRAPH_BYTE_COMMIT_DATA_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
467 2 * $GRAPH_CHUNK_LOOKUP_WIDTH))
468 GRAPH_FANOUT_OFFSET=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
469 $GRAPH_CHUNK_LOOKUP_WIDTH * $GRAPH_CHUNK_LOOKUP_ROWS))
470 GRAPH_BYTE_FANOUT1=$(($GRAPH_FANOUT_OFFSET + 4 * 4))
471 GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
472 GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
473 GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
474 GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
475 GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
476 GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
477 GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
478 GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
479 GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
480 GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
481 GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
482 GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH))
483 GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
484 GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
485 $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
486 GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
487 GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
488
489 corrupt_graph_setup() {
490 test_when_finished mv commit-graph-backup full/$objdir/info/commit-graph &&
491 cp full/$objdir/info/commit-graph commit-graph-backup &&
492 chmod u+w full/$objdir/info/commit-graph
493 }
494
495 corrupt_graph_verify() {
496 grepstr=$1
497 test_must_fail git -C full commit-graph verify 2>test_err &&
498 grep -v "^+" test_err >err &&
499 test_grep "$grepstr" err &&
500 if test "$2" != "no-copy"
501 then
502 cp full/$objdir/info/commit-graph commit-graph-pre-write-test
503 fi &&
504 git -C full status --short &&
505 GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git -C full commit-graph write &&
506 chmod u+w full/$objdir/info/commit-graph &&
507 git -C full commit-graph verify
508 }
509
510 # usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
511 # Manipulates the commit-graph file at the position
512 # by inserting the data, optionally zeroing the file
513 # starting at <zero_pos>, then runs 'git commit-graph verify'
514 # and places the output in the file 'err'. Test 'err' for
515 # the given string.
516 corrupt_graph_and_verify() {
517 pos=$1
518 data="${2:-\0}"
519 grepstr=$3
520 corrupt_graph_setup &&
521 orig_size=$(wc -c <full/$objdir/info/commit-graph) &&
522 zero_pos=${4:-${orig_size}} &&
523 printf "$data" | dd of="full/$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
524 dd of="full/$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
525 test-tool genzeros $(($orig_size - $zero_pos)) >>"full/$objdir/info/commit-graph" &&
526 corrupt_graph_verify "$grepstr"
527
528 }
529
530 test_expect_success POSIXPERM,SANITY 'detect permission problem' '
531 corrupt_graph_setup &&
532 chmod 000 full/$objdir/info/commit-graph &&
533 corrupt_graph_verify "Could not open" "no-copy"
534 '
535
536 test_expect_success 'detect too small' '
537 corrupt_graph_setup &&
538 echo "a small graph" >full/$objdir/info/commit-graph &&
539 corrupt_graph_verify "too small"
540 '
541
542 test_expect_success 'detect bad signature' '
543 corrupt_graph_and_verify 0 "\0" \
544 "graph signature"
545 '
546
547 test_expect_success 'detect bad version' '
548 corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
549 "graph version"
550 '
551
552 test_expect_success 'detect bad hash version' '
553 corrupt_graph_and_verify $GRAPH_BYTE_HASH "\03" \
554 "hash version"
555 '
556
557 test_expect_success 'detect low chunk count' '
558 corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\01" \
559 "final chunk has non-zero id"
560 '
561
562 test_expect_success 'detect missing OID fanout chunk' '
563 corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
564 "commit-graph required OID fanout chunk missing or corrupted"
565 '
566
567 test_expect_success 'detect missing OID lookup chunk' '
568 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
569 "commit-graph required OID lookup chunk missing or corrupted"
570 '
571
572 test_expect_success 'detect missing commit data chunk' '
573 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
574 "commit-graph required commit data chunk missing or corrupted"
575 '
576
577 test_expect_success 'detect incorrect fanout' '
578 corrupt_graph_and_verify $GRAPH_BYTE_FANOUT1 "\01" \
579 "fanout value"
580 '
581
582 test_expect_success 'detect incorrect fanout final value' '
583 corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
584 "OID lookup chunk is the wrong size"
585 '
586
587 test_expect_success 'detect incorrect OID order' '
588 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ORDER "\01" \
589 "incorrect OID order"
590 '
591
592 test_expect_success 'detect OID not in object database' '
593 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_MISSING "\01" \
594 "from object database"
595 '
596
597 test_expect_success 'detect incorrect tree OID' '
598 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_TREE "\01" \
599 "root tree OID for commit"
600 '
601
602 test_expect_success 'detect incorrect parent int-id' '
603 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_PARENT "\01" \
604 "invalid parent"
605 '
606
607 test_expect_success 'detect extra parent int-id' '
608 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_EXTRA_PARENT "\00" \
609 "is too long"
610 '
611
612 test_expect_success 'detect wrong parent' '
613 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_WRONG_PARENT "\01" \
614 "commit-graph parent for"
615 '
616
617 test_expect_success 'detect incorrect generation number' '
618 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\070" \
619 "generation for commit"
620 '
621
622 test_expect_success 'detect incorrect commit date' '
623 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
624 "commit date"
625 '
626
627 test_expect_success 'detect incorrect parent for octopus merge' '
628 corrupt_graph_and_verify $GRAPH_BYTE_OCTOPUS "\01" \
629 "invalid parent"
630 '
631
632 test_expect_success 'detect invalid checksum hash' '
633 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
634 "incorrect checksum"
635 '
636
637 test_expect_success 'detect incorrect chunk count' '
638 corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
639 "commit-graph file is too small to hold [0-9]* chunks" \
640 $GRAPH_CHUNK_LOOKUP_OFFSET
641 '
642
643 test_expect_success 'detect mixed generation numbers (non-zero to zero)' '
644 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \
645 "both zero and non-zero generations"
646 '
647
648 test_expect_success 'detect mixed generation numbers (zero to non-zero)' '
649 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \
650 "both zero and non-zero generations"
651 '
652
653 test_expect_success 'git fsck (checks commit-graph when config set to true)' '
654 git -C full fsck &&
655 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
656 "incorrect checksum" &&
657 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
658 test_must_fail git -C full -c core.commitGraph=true fsck
659 '
660
661 test_expect_success 'git fsck (ignores commit-graph when config set to false)' '
662 git -C full fsck &&
663 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
664 "incorrect checksum" &&
665 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
666 git -C full -c core.commitGraph=false fsck
667 '
668
669 test_expect_success 'git fsck (checks commit-graph when config unset)' '
670 test_when_finished "git -C full config core.commitGraph true" &&
671
672 git -C full fsck &&
673 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
674 "incorrect checksum" &&
675 test_unconfig -C full core.commitGraph &&
676 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
677 test_must_fail git -C full fsck
678 '
679
680 test_expect_success 'git fsck shows commit-graph output with --progress' '
681 git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err &&
682 grep "Verifying commits in commit graph" err
683 '
684
685 test_expect_success 'git fsck suppresses commit-graph output with --no-progress' '
686 git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err &&
687 ! grep "Verifying commits in commit graph" err
688 '
689
690 test_expect_success 'setup non-the_repository tests' '
691 rm -rf repo &&
692 git init repo &&
693 test_commit -C repo one &&
694 test_commit -C repo two &&
695 git -C repo config core.commitGraph true &&
696 git -C repo rev-parse two | \
697 git -C repo commit-graph write --stdin-commits
698 '
699
700 test_expect_success 'parse_commit_in_graph works for non-the_repository' '
701 test-tool repository parse_commit_in_graph \
702 repo/.git repo "$(git -C repo rev-parse two)" >actual &&
703 {
704 git -C repo log --pretty=format:"%ct " -1 &&
705 git -C repo rev-parse one
706 } >expect &&
707 test_cmp expect actual &&
708
709 test-tool repository parse_commit_in_graph \
710 repo/.git repo "$(git -C repo rev-parse one)" >actual &&
711 git -C repo log --pretty="%ct" -1 one >expect &&
712 test_cmp expect actual
713 '
714
715 test_expect_success 'get_commit_tree_in_graph works for non-the_repository' '
716 test-tool repository get_commit_tree_in_graph \
717 repo/.git repo "$(git -C repo rev-parse two)" >actual &&
718 git -C repo rev-parse two^{tree} >expect &&
719 test_cmp expect actual &&
720
721 test-tool repository get_commit_tree_in_graph \
722 repo/.git repo "$(git -C repo rev-parse one)" >actual &&
723 git -C repo rev-parse one^{tree} >expect &&
724 test_cmp expect actual
725 '
726
727 test_expect_success 'corrupt commit-graph write (broken parent)' '
728 rm -rf repo &&
729 git init repo &&
730 (
731 cd repo &&
732 empty="$(git mktree </dev/null)" &&
733 cat >broken <<-EOF &&
734 tree $empty
735 parent $ZERO_OID
736 author whatever <whatever@example.com> 1234 -0000
737 committer whatever <whatever@example.com> 1234 -0000
738
739 broken commit
740 EOF
741 broken="$(git hash-object -w -t commit --literally broken)" &&
742 git commit-tree -p "$broken" -m "good commit" "$empty" >good &&
743 test_must_fail git commit-graph write --stdin-commits \
744 <good 2>test_err &&
745 test_grep "unable to parse commit" test_err
746 )
747 '
748
749 test_expect_success 'corrupt commit-graph write (missing tree)' '
750 rm -rf repo &&
751 git init repo &&
752 (
753 cd repo &&
754 tree="$(git mktree </dev/null)" &&
755 cat >broken <<-EOF &&
756 parent $ZERO_OID
757 author whatever <whatever@example.com> 1234 -0000
758 committer whatever <whatever@example.com> 1234 -0000
759
760 broken commit
761 EOF
762 broken="$(git hash-object -w -t commit --literally broken)" &&
763 git commit-tree -p "$broken" -m "good" "$tree" >good &&
764 test_must_fail git commit-graph write --stdin-commits \
765 <good 2>test_err &&
766 test_grep "unable to parse commit" test_err
767 )
768 '
769
770 # We test the overflow-related code with the following repo history:
771 #
772 # 4:F - 5:N - 6:U
773 # / \
774 # 1:U - 2:N - 3:U M:N
775 # \ /
776 # 7:N - 8:F - 9:N
777 #
778 # Here the commits denoted by U have committer date of zero seconds
779 # since Unix epoch, the commits denoted by N have committer date
780 # starting from 1112354055 seconds since Unix epoch (default committer
781 # date for the test suite), and the commits denoted by F have committer
782 # date of (2 ^ 31 - 2) seconds since Unix epoch.
783 #
784 # The largest offset observed is 2 ^ 31, just large enough to overflow.
785 #
786
787 test_expect_success 'set up and verify repo with generation data overflow chunk' '
788 UNIX_EPOCH_ZERO="@0 +0000" &&
789 FUTURE_DATE="@2147483646 +0000" &&
790
791 git init repo &&
792 (
793 cd repo &&
794
795 test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
796 test_commit 2 &&
797 test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
798 git commit-graph write --reachable &&
799 graph_read_expect 3 generation_data &&
800 test_commit --date "$FUTURE_DATE" 4 &&
801 test_commit 5 &&
802 test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
803 git branch left &&
804 git reset --hard 3 &&
805 test_commit 7 &&
806 test_commit --date "$FUTURE_DATE" 8 &&
807 test_commit 9 &&
808 git branch right &&
809 git reset --hard 3 &&
810 test_merge M left right &&
811 git commit-graph write --reachable &&
812 graph_read_expect 10 "generation_data generation_data_overflow" &&
813 git commit-graph verify
814 )
815 '
816
817 graph_git_behavior 'generation data overflow chunk repo' repo left right
818
819 test_expect_success 'overflow during generation version upgrade' '
820 git init overflow-v2-upgrade &&
821 (
822 cd overflow-v2-upgrade &&
823
824 # This commit will have a date at two seconds past the Epoch,
825 # and a (v1) generation number of 1, since it is a root commit.
826 #
827 # The offset will then be computed as 1-2, which will underflow
828 # to 2^31, which is greater than the v2 offset small limit of
829 # 2^31-1.
830 #
831 # This is sufficient to need a large offset table for the v2
832 # generation numbers.
833 test_commit --date "@2 +0000" base &&
834 git repack -d &&
835
836 # Test that upgrading from generation v1 to v2 correctly
837 # produces the overflow table.
838 git -c commitGraph.generationVersion=1 commit-graph write &&
839 git -c commitGraph.generationVersion=2 commit-graph write \
840 --changed-paths &&
841
842 git rev-list --all
843 )
844 '
845
846 corrupt_chunk () {
847 graph=full/.git/objects/info/commit-graph &&
848 test_when_finished "rm -rf $graph" &&
849 git -C full commit-graph write --reachable &&
850 corrupt_chunk_file $graph "$@"
851 }
852
853 check_corrupt_chunk () {
854 corrupt_chunk "$@" &&
855 git -C full -c core.commitGraph=false log >expect.out &&
856 git -C full -c core.commitGraph=true log >out 2>err &&
857 test_cmp expect.out out
858 }
859
860 test_expect_success PERL_TEST_HELPERS 'reader notices too-small oid fanout chunk' '
861 # make it big enough that the graph file is plausible,
862 # otherwise we hit an earlier check
863 check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
864 cat >expect.err <<-\EOF &&
865 error: commit-graph oid fanout chunk is wrong size
866 error: commit-graph required OID fanout chunk missing or corrupted
867 EOF
868 test_cmp expect.err err
869 '
870
871 test_expect_success PERL_TEST_HELPERS 'reader notices fanout/lookup table mismatch' '
872 check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
873 cat >expect.err <<-\EOF &&
874 error: commit-graph OID lookup chunk is the wrong size
875 error: commit-graph required OID lookup chunk missing or corrupted
876 EOF
877 test_cmp expect.err err
878 '
879
880 test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds fanout' '
881 # Rather than try to corrupt a specific hash, we will just
882 # wreck them all. But we cannot just set them all to 0xFFFFFFFF or
883 # similar, as they are used for hi/lo starts in a binary search (so if
884 # they are identical, that indicates that the search should abort
885 # immediately). Instead, we will give them high values that differ by
886 # 2^24, ensuring that any that are used would cause an out-of-bounds
887 # read.
888 check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
889 cat >expect.err <<-\EOF &&
890 error: commit-graph fanout values out of order
891 error: commit-graph required OID fanout chunk missing or corrupted
892 EOF
893 test_cmp expect.err err
894 '
895
896 test_expect_success PERL_TEST_HELPERS 'reader notices too-small commit data chunk' '
897 check_corrupt_chunk CDAT clear 00000000 &&
898 cat >expect.err <<-\EOF &&
899 error: commit-graph commit data chunk is wrong size
900 error: commit-graph required commit data chunk missing or corrupted
901 EOF
902 test_cmp expect.err err
903 '
904
905 test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds extra edge' '
906 check_corrupt_chunk EDGE clear &&
907 cat >expect.err <<-\EOF &&
908 error: commit-graph extra-edges pointer out of bounds
909 EOF
910 test_cmp expect.err err
911 '
912
913 test_expect_success PERL_TEST_HELPERS 'reader notices too-small generations chunk' '
914 check_corrupt_chunk GDA2 clear 00000000 &&
915 cat >expect.err <<-\EOF &&
916 error: commit-graph generations chunk is wrong size
917 EOF
918 test_cmp expect.err err
919 '
920
921 test_expect_success 'stale commit cannot be parsed when given directly' '
922 test_when_finished "rm -rf repo" &&
923 git init repo &&
924 (
925 cd repo &&
926 test_commit A &&
927 test_commit B &&
928 git commit-graph write --reachable &&
929
930 oid=$(git rev-parse B) &&
931 rm .git/objects/"$(test_oid_to_path "$oid")" &&
932
933 # Verify that it is possible to read the commit from the
934 # commit graph when not being paranoid, ...
935 git rev-list B &&
936 # ... but parsing the commit when double checking that
937 # it actually exists in the object database should fail.
938 test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
939 )
940 '
941
942 test_expect_success 'stale commit cannot be parsed when traversing graph' '
943 test_when_finished "rm -rf repo" &&
944 git init repo &&
945 (
946 cd repo &&
947
948 test_commit A &&
949 test_commit B &&
950 test_commit C &&
951 git commit-graph write --reachable &&
952
953 # Corrupt the repository by deleting the intermediate commit
954 # object. Commands should notice that this object is absent and
955 # thus that the repository is corrupt even if the commit graph
956 # exists.
957 oid=$(git rev-parse B) &&
958 rm .git/objects/"$(test_oid_to_path "$oid")" &&
959
960 # Again, we should be able to parse the commit when not
961 # being paranoid about commit graph staleness...
962 git rev-parse HEAD~2 &&
963 # ... but fail when we are paranoid.
964 test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
965 grep "error: commit $oid exists in commit-graph but not in the object database" error
966 )
967 '
968
969 test_expect_success 'config commitGraph.changedPaths acts like --changed-paths' '
970 git init config-changed-paths &&
971 (
972 cd config-changed-paths &&
973
974 # commitGraph.changedPaths is not set and it should not write Bloom filters
975 test_commit first &&
976 GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
977 test_grep ! "Bloom filters" error &&
978
979 # Set commitGraph.changedPaths to true and it should write Bloom filters
980 test_commit second &&
981 git config commitGraph.changedPaths true &&
982 GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
983 test_grep "Bloom filters" error &&
984
985 # Add one more config commitGraph.changedPaths as false to disable the previous true config value
986 # It should still write Bloom filters due to existing filters
987 test_commit third &&
988 git config --add commitGraph.changedPaths false &&
989 GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
990 test_grep "Bloom filters" error &&
991
992 # commitGraph.changedPaths is still false and command line options should take precedence
993 test_commit fourth &&
994 GIT_PROGRESS_DELAY=0 git commit-graph write --no-changed-paths --reachable --progress 2>error &&
995 test_grep ! "Bloom filters" error &&
996 GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
997 test_grep ! "Bloom filters" error &&
998
999 # commitGraph.changedPaths is all cleared and then set to false again, command line options should take precedence
1000 test_commit fifth &&
1001 git config --unset-all commitGraph.changedPaths &&
1002 git config commitGraph.changedPaths false &&
1003 GIT_PROGRESS_DELAY=0 git commit-graph write --changed-paths --reachable --progress 2>error &&
1004 test_grep "Bloom filters" error &&
1005
1006 # commitGraph.changedPaths is still false and it should write Bloom filters due to existing filters
1007 test_commit sixth &&
1008 GIT_PROGRESS_DELAY=0 git commit-graph write --reachable --progress 2>error &&
1009 test_grep "Bloom filters" error
1010 )
1011 '
1012
1013 test_done