Git fork
1#!/bin/sh
2
3test_description='commit graph'
4
5. ./test-lib.sh
6. "$TEST_DIRECTORY"/lib-chunk.sh
7
8GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
9
10test_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
15test_expect_success 'usage shown without sub-command' '
16 test_expect_code 129 git commit-graph 2>err &&
17 grep usage: err
18'
19
20test_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
29objdir=".git/objects"
30
31test_expect_success 'setup full repo' '
32 git init full
33'
34
35test_expect_success POSIXPERM 'tweak umask for modebit tests' '
36 umask 022
37'
38
39test_expect_success 'verify graph with no graph file' '
40 git -C full commit-graph verify
41'
42
43test_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
48test_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
55test_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
66graph_git_behavior 'no graph' full commits/3 commits/1
67
68test_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
83test_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
89test_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
96graph_git_behavior 'graph exists' full commits/3 commits/1
97
98test_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
123test_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
128test_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
133test_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
138test_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
144test_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
151test_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
157test_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
162test_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
167test_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
182test_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
188graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
189graph_git_behavior 'merge 1 vs 3' full merge/1 merge/3
190graph_git_behavior 'merge 2 vs 3' full merge/2 merge/3
191
192test_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
212graph_git_behavior 'mixed mode, commit 8 vs merge 1' full commits/8 merge/1
213graph_git_behavior 'mixed mode, commit 8 vs merge 2' full commits/8 merge/2
214
215test_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
221graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
222graph_git_behavior 'full graph, commit 8 vs merge 2' full commits/8 merge/2
223
224test_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
230graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
231graph_git_behavior 'cleared graph, commit 8 vs merge 2' full commits/8 merge/2
232
233test_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
239graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
240graph_git_behavior 'graph from pack, commit 8 vs merge 2' full commits/8 merge/2
241
242test_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
251graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
252graph_git_behavior 'graph from commits, commit 8 vs merge 2' full commits/8 merge/2
253
254test_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
261graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
262graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
263
264test_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
270graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
271graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
272
273test_expect_success 'setup bare repo' '
274 git clone --bare --no-local full bare
275'
276
277graph_git_behavior 'bare repo, commit 8 vs merge 1' bare commits/8 merge/1
278graph_git_behavior 'bare repo, commit 8 vs merge 2' bare commits/8 merge/2
279
280test_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
286graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
287graph_git_behavior 'bare repo with graph, commit 8 vs merge 2' bare commits/8 merge/2
288
289test_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
297test_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
310test_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
332test_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
356test_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
369test_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
398test_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# the verify tests below expect the commit-graph to contain
421# exactly the commits reachable from the commits/8 branch.
422# If the file changes the set of commits in the list, then the
423# offsets into the binary file will result in different edits
424# and the tests will likely break.
425
426test_expect_success 'git commit-graph verify' '
427 git -C full rev-parse commits/8 >in &&
428 git -C full -c commitGraph.generationVersion=1 commit-graph write \
429 --stdin-commits <in &&
430 git -C full commit-graph verify >output &&
431 graph_read_expect -C full 9 extra_edges 1
432'
433
434NUM_COMMITS=9
435NUM_OCTOPUS_EDGES=2
436HASH_LEN="$(test_oid rawsz)"
437GRAPH_BYTE_VERSION=4
438GRAPH_BYTE_HASH=5
439GRAPH_BYTE_CHUNK_COUNT=6
440GRAPH_CHUNK_LOOKUP_OFFSET=8
441GRAPH_CHUNK_LOOKUP_WIDTH=12
442GRAPH_CHUNK_LOOKUP_ROWS=5
443GRAPH_BYTE_OID_FANOUT_ID=$GRAPH_CHUNK_LOOKUP_OFFSET
444GRAPH_BYTE_OID_LOOKUP_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
445 1 * $GRAPH_CHUNK_LOOKUP_WIDTH))
446GRAPH_BYTE_COMMIT_DATA_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
447 2 * $GRAPH_CHUNK_LOOKUP_WIDTH))
448GRAPH_FANOUT_OFFSET=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
449 $GRAPH_CHUNK_LOOKUP_WIDTH * $GRAPH_CHUNK_LOOKUP_ROWS))
450GRAPH_BYTE_FANOUT1=$(($GRAPH_FANOUT_OFFSET + 4 * 4))
451GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
452GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
453GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
454GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
455GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
456GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
457GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
458GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
459GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
460GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
461GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
462GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH))
463GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
464GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
465 $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
466GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
467GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
468
469corrupt_graph_setup() {
470 test_when_finished mv commit-graph-backup full/$objdir/info/commit-graph &&
471 cp full/$objdir/info/commit-graph commit-graph-backup &&
472 chmod u+w full/$objdir/info/commit-graph
473}
474
475corrupt_graph_verify() {
476 grepstr=$1
477 test_must_fail git -C full commit-graph verify 2>test_err &&
478 grep -v "^+" test_err >err &&
479 test_grep "$grepstr" err &&
480 if test "$2" != "no-copy"
481 then
482 cp full/$objdir/info/commit-graph commit-graph-pre-write-test
483 fi &&
484 git -C full status --short &&
485 GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE=true git -C full commit-graph write &&
486 chmod u+w full/$objdir/info/commit-graph &&
487 git -C full commit-graph verify
488}
489
490# usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
491# Manipulates the commit-graph file at the position
492# by inserting the data, optionally zeroing the file
493# starting at <zero_pos>, then runs 'git commit-graph verify'
494# and places the output in the file 'err'. Test 'err' for
495# the given string.
496corrupt_graph_and_verify() {
497 pos=$1
498 data="${2:-\0}"
499 grepstr=$3
500 corrupt_graph_setup &&
501 orig_size=$(wc -c <full/$objdir/info/commit-graph) &&
502 zero_pos=${4:-${orig_size}} &&
503 printf "$data" | dd of="full/$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
504 dd of="full/$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
505 test-tool genzeros $(($orig_size - $zero_pos)) >>"full/$objdir/info/commit-graph" &&
506 corrupt_graph_verify "$grepstr"
507
508}
509
510test_expect_success POSIXPERM,SANITY 'detect permission problem' '
511 corrupt_graph_setup &&
512 chmod 000 full/$objdir/info/commit-graph &&
513 corrupt_graph_verify "Could not open" "no-copy"
514'
515
516test_expect_success 'detect too small' '
517 corrupt_graph_setup &&
518 echo "a small graph" >full/$objdir/info/commit-graph &&
519 corrupt_graph_verify "too small"
520'
521
522test_expect_success 'detect bad signature' '
523 corrupt_graph_and_verify 0 "\0" \
524 "graph signature"
525'
526
527test_expect_success 'detect bad version' '
528 corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
529 "graph version"
530'
531
532test_expect_success 'detect bad hash version' '
533 corrupt_graph_and_verify $GRAPH_BYTE_HASH "\03" \
534 "hash version"
535'
536
537test_expect_success 'detect low chunk count' '
538 corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\01" \
539 "final chunk has non-zero id"
540'
541
542test_expect_success 'detect missing OID fanout chunk' '
543 corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
544 "commit-graph required OID fanout chunk missing or corrupted"
545'
546
547test_expect_success 'detect missing OID lookup chunk' '
548 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
549 "commit-graph required OID lookup chunk missing or corrupted"
550'
551
552test_expect_success 'detect missing commit data chunk' '
553 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
554 "commit-graph required commit data chunk missing or corrupted"
555'
556
557test_expect_success 'detect incorrect fanout' '
558 corrupt_graph_and_verify $GRAPH_BYTE_FANOUT1 "\01" \
559 "fanout value"
560'
561
562test_expect_success 'detect incorrect fanout final value' '
563 corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
564 "OID lookup chunk is the wrong size"
565'
566
567test_expect_success 'detect incorrect OID order' '
568 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ORDER "\01" \
569 "incorrect OID order"
570'
571
572test_expect_success 'detect OID not in object database' '
573 corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_MISSING "\01" \
574 "from object database"
575'
576
577test_expect_success 'detect incorrect tree OID' '
578 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_TREE "\01" \
579 "root tree OID for commit"
580'
581
582test_expect_success 'detect incorrect parent int-id' '
583 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_PARENT "\01" \
584 "invalid parent"
585'
586
587test_expect_success 'detect extra parent int-id' '
588 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_EXTRA_PARENT "\00" \
589 "is too long"
590'
591
592test_expect_success 'detect wrong parent' '
593 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_WRONG_PARENT "\01" \
594 "commit-graph parent for"
595'
596
597test_expect_success 'detect incorrect generation number' '
598 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\070" \
599 "generation for commit"
600'
601
602test_expect_success 'detect incorrect commit date' '
603 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
604 "commit date"
605'
606
607test_expect_success 'detect incorrect parent for octopus merge' '
608 corrupt_graph_and_verify $GRAPH_BYTE_OCTOPUS "\01" \
609 "invalid parent"
610'
611
612test_expect_success 'detect invalid checksum hash' '
613 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
614 "incorrect checksum"
615'
616
617test_expect_success 'detect incorrect chunk count' '
618 corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\377" \
619 "commit-graph file is too small to hold [0-9]* chunks" \
620 $GRAPH_CHUNK_LOOKUP_OFFSET
621'
622
623test_expect_success 'detect mixed generation numbers (non-zero to zero)' '
624 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \
625 "both zero and non-zero generations"
626'
627
628test_expect_success 'detect mixed generation numbers (zero to non-zero)' '
629 corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \
630 "both zero and non-zero generations"
631'
632
633test_expect_success 'git fsck (checks commit-graph when config set to true)' '
634 git -C full fsck &&
635 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
636 "incorrect checksum" &&
637 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
638 test_must_fail git -C full -c core.commitGraph=true fsck
639'
640
641test_expect_success 'git fsck (ignores commit-graph when config set to false)' '
642 git -C full fsck &&
643 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
644 "incorrect checksum" &&
645 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
646 git -C full -c core.commitGraph=false fsck
647'
648
649test_expect_success 'git fsck (checks commit-graph when config unset)' '
650 test_when_finished "git -C full config core.commitGraph true" &&
651
652 git -C full fsck &&
653 corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
654 "incorrect checksum" &&
655 test_unconfig -C full core.commitGraph &&
656 cp commit-graph-pre-write-test full/$objdir/info/commit-graph &&
657 test_must_fail git -C full fsck
658'
659
660test_expect_success 'git fsck shows commit-graph output with --progress' '
661 git -C "$TRASH_DIRECTORY/full" fsck --progress 2>err &&
662 grep "Verifying commits in commit graph" err
663'
664
665test_expect_success 'git fsck suppresses commit-graph output with --no-progress' '
666 git -C "$TRASH_DIRECTORY/full" fsck --no-progress 2>err &&
667 ! grep "Verifying commits in commit graph" err
668'
669
670test_expect_success 'setup non-the_repository tests' '
671 rm -rf repo &&
672 git init repo &&
673 test_commit -C repo one &&
674 test_commit -C repo two &&
675 git -C repo config core.commitGraph true &&
676 git -C repo rev-parse two | \
677 git -C repo commit-graph write --stdin-commits
678'
679
680test_expect_success 'parse_commit_in_graph works for non-the_repository' '
681 test-tool repository parse_commit_in_graph \
682 repo/.git repo "$(git -C repo rev-parse two)" >actual &&
683 {
684 git -C repo log --pretty=format:"%ct " -1 &&
685 git -C repo rev-parse one
686 } >expect &&
687 test_cmp expect actual &&
688
689 test-tool repository parse_commit_in_graph \
690 repo/.git repo "$(git -C repo rev-parse one)" >actual &&
691 git -C repo log --pretty="%ct" -1 one >expect &&
692 test_cmp expect actual
693'
694
695test_expect_success 'get_commit_tree_in_graph works for non-the_repository' '
696 test-tool repository get_commit_tree_in_graph \
697 repo/.git repo "$(git -C repo rev-parse two)" >actual &&
698 git -C repo rev-parse two^{tree} >expect &&
699 test_cmp expect actual &&
700
701 test-tool repository get_commit_tree_in_graph \
702 repo/.git repo "$(git -C repo rev-parse one)" >actual &&
703 git -C repo rev-parse one^{tree} >expect &&
704 test_cmp expect actual
705'
706
707test_expect_success 'corrupt commit-graph write (broken parent)' '
708 rm -rf repo &&
709 git init repo &&
710 (
711 cd repo &&
712 empty="$(git mktree </dev/null)" &&
713 cat >broken <<-EOF &&
714 tree $empty
715 parent $ZERO_OID
716 author whatever <whatever@example.com> 1234 -0000
717 committer whatever <whatever@example.com> 1234 -0000
718
719 broken commit
720 EOF
721 broken="$(git hash-object -w -t commit --literally broken)" &&
722 git commit-tree -p "$broken" -m "good commit" "$empty" >good &&
723 test_must_fail git commit-graph write --stdin-commits \
724 <good 2>test_err &&
725 test_grep "unable to parse commit" test_err
726 )
727'
728
729test_expect_success 'corrupt commit-graph write (missing tree)' '
730 rm -rf repo &&
731 git init repo &&
732 (
733 cd repo &&
734 tree="$(git mktree </dev/null)" &&
735 cat >broken <<-EOF &&
736 parent $ZERO_OID
737 author whatever <whatever@example.com> 1234 -0000
738 committer whatever <whatever@example.com> 1234 -0000
739
740 broken commit
741 EOF
742 broken="$(git hash-object -w -t commit --literally broken)" &&
743 git commit-tree -p "$broken" -m "good" "$tree" >good &&
744 test_must_fail git commit-graph write --stdin-commits \
745 <good 2>test_err &&
746 test_grep "unable to parse commit" test_err
747 )
748'
749
750# We test the overflow-related code with the following repo history:
751#
752# 4:F - 5:N - 6:U
753# / \
754# 1:U - 2:N - 3:U M:N
755# \ /
756# 7:N - 8:F - 9:N
757#
758# Here the commits denoted by U have committer date of zero seconds
759# since Unix epoch, the commits denoted by N have committer date
760# starting from 1112354055 seconds since Unix epoch (default committer
761# date for the test suite), and the commits denoted by F have committer
762# date of (2 ^ 31 - 2) seconds since Unix epoch.
763#
764# The largest offset observed is 2 ^ 31, just large enough to overflow.
765#
766
767test_expect_success 'set up and verify repo with generation data overflow chunk' '
768 UNIX_EPOCH_ZERO="@0 +0000" &&
769 FUTURE_DATE="@2147483646 +0000" &&
770
771 git init repo &&
772 (
773 cd repo &&
774
775 test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
776 test_commit 2 &&
777 test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
778 git commit-graph write --reachable &&
779 graph_read_expect 3 generation_data &&
780 test_commit --date "$FUTURE_DATE" 4 &&
781 test_commit 5 &&
782 test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
783 git branch left &&
784 git reset --hard 3 &&
785 test_commit 7 &&
786 test_commit --date "$FUTURE_DATE" 8 &&
787 test_commit 9 &&
788 git branch right &&
789 git reset --hard 3 &&
790 test_merge M left right &&
791 git commit-graph write --reachable &&
792 graph_read_expect 10 "generation_data generation_data_overflow" &&
793 git commit-graph verify
794 )
795'
796
797graph_git_behavior 'generation data overflow chunk repo' repo left right
798
799test_expect_success 'overflow during generation version upgrade' '
800 git init overflow-v2-upgrade &&
801 (
802 cd overflow-v2-upgrade &&
803
804 # This commit will have a date at two seconds past the Epoch,
805 # and a (v1) generation number of 1, since it is a root commit.
806 #
807 # The offset will then be computed as 1-2, which will underflow
808 # to 2^31, which is greater than the v2 offset small limit of
809 # 2^31-1.
810 #
811 # This is sufficient to need a large offset table for the v2
812 # generation numbers.
813 test_commit --date "@2 +0000" base &&
814 git repack -d &&
815
816 # Test that upgrading from generation v1 to v2 correctly
817 # produces the overflow table.
818 git -c commitGraph.generationVersion=1 commit-graph write &&
819 git -c commitGraph.generationVersion=2 commit-graph write \
820 --changed-paths &&
821
822 git rev-list --all
823 )
824'
825
826corrupt_chunk () {
827 graph=full/.git/objects/info/commit-graph &&
828 test_when_finished "rm -rf $graph" &&
829 git -C full commit-graph write --reachable &&
830 corrupt_chunk_file $graph "$@"
831}
832
833check_corrupt_chunk () {
834 corrupt_chunk "$@" &&
835 git -C full -c core.commitGraph=false log >expect.out &&
836 git -C full -c core.commitGraph=true log >out 2>err &&
837 test_cmp expect.out out
838}
839
840test_expect_success PERL_TEST_HELPERS 'reader notices too-small oid fanout chunk' '
841 # make it big enough that the graph file is plausible,
842 # otherwise we hit an earlier check
843 check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
844 cat >expect.err <<-\EOF &&
845 error: commit-graph oid fanout chunk is wrong size
846 error: commit-graph required OID fanout chunk missing or corrupted
847 EOF
848 test_cmp expect.err err
849'
850
851test_expect_success PERL_TEST_HELPERS 'reader notices fanout/lookup table mismatch' '
852 check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
853 cat >expect.err <<-\EOF &&
854 error: commit-graph OID lookup chunk is the wrong size
855 error: commit-graph required OID lookup chunk missing or corrupted
856 EOF
857 test_cmp expect.err err
858'
859
860test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds fanout' '
861 # Rather than try to corrupt a specific hash, we will just
862 # wreck them all. But we cannot just set them all to 0xFFFFFFFF or
863 # similar, as they are used for hi/lo starts in a binary search (so if
864 # they are identical, that indicates that the search should abort
865 # immediately). Instead, we will give them high values that differ by
866 # 2^24, ensuring that any that are used would cause an out-of-bounds
867 # read.
868 check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
869 cat >expect.err <<-\EOF &&
870 error: commit-graph fanout values out of order
871 error: commit-graph required OID fanout chunk missing or corrupted
872 EOF
873 test_cmp expect.err err
874'
875
876test_expect_success PERL_TEST_HELPERS 'reader notices too-small commit data chunk' '
877 check_corrupt_chunk CDAT clear 00000000 &&
878 cat >expect.err <<-\EOF &&
879 error: commit-graph commit data chunk is wrong size
880 error: commit-graph required commit data chunk missing or corrupted
881 EOF
882 test_cmp expect.err err
883'
884
885test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds extra edge' '
886 check_corrupt_chunk EDGE clear &&
887 cat >expect.err <<-\EOF &&
888 error: commit-graph extra-edges pointer out of bounds
889 EOF
890 test_cmp expect.err err
891'
892
893test_expect_success PERL_TEST_HELPERS 'reader notices too-small generations chunk' '
894 check_corrupt_chunk GDA2 clear 00000000 &&
895 cat >expect.err <<-\EOF &&
896 error: commit-graph generations chunk is wrong size
897 EOF
898 test_cmp expect.err err
899'
900
901test_expect_success 'stale commit cannot be parsed when given directly' '
902 test_when_finished "rm -rf repo" &&
903 git init repo &&
904 (
905 cd repo &&
906 test_commit A &&
907 test_commit B &&
908 git commit-graph write --reachable &&
909
910 oid=$(git rev-parse B) &&
911 rm .git/objects/"$(test_oid_to_path "$oid")" &&
912
913 # Verify that it is possible to read the commit from the
914 # commit graph when not being paranoid, ...
915 git rev-list B &&
916 # ... but parsing the commit when double checking that
917 # it actually exists in the object database should fail.
918 test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
919 )
920'
921
922test_expect_success 'stale commit cannot be parsed when traversing graph' '
923 test_when_finished "rm -rf repo" &&
924 git init repo &&
925 (
926 cd repo &&
927
928 test_commit A &&
929 test_commit B &&
930 test_commit C &&
931 git commit-graph write --reachable &&
932
933 # Corrupt the repository by deleting the intermediate commit
934 # object. Commands should notice that this object is absent and
935 # thus that the repository is corrupt even if the commit graph
936 # exists.
937 oid=$(git rev-parse B) &&
938 rm .git/objects/"$(test_oid_to_path "$oid")" &&
939
940 # Again, we should be able to parse the commit when not
941 # being paranoid about commit graph staleness...
942 git rev-parse HEAD~2 &&
943 # ... but fail when we are paranoid.
944 test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
945 grep "error: commit $oid exists in commit-graph but not in the object database" error
946 )
947'
948
949test_done