3 test_description
='split commit graph'
6 GIT_TEST_COMMIT_GRAPH
=0
7 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS
=0
9 test_expect_success
'setup repo' '
11 git config core.commitGraph true &&
12 git config gc.writeCommitGraph false &&
13 infodir=".git/objects/info" &&
14 graphdir="$infodir/commit-graphs" &&
34 header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
36 chunks: oid_fanout oid_lookup commit_metadata generation_data
38 test-tool read-graph
>output
&&
39 test_cmp expect output
42 test_expect_success POSIXPERM
'tweak umask for modebit tests' '
46 test_expect_success
'create commits and write commit-graph' '
47 for i in $(test_seq 3)
50 git branch commits/$i || return 1
52 git commit-graph write --reachable &&
53 test_path_is_file $infodir/commit-graph &&
57 graph_git_two_modes
() {
58 git
${2:+ -C "$2"} -c core.commitGraph
=true
$1 >output
&&
59 git
${2:+ -C "$2"} -c core.commitGraph
=false
$1 >expect
&&
60 test_cmp expect output
63 graph_git_behavior
() {
68 test_expect_success
"check normal git operations: $MSG" '
69 graph_git_two_modes "log --oneline $BRANCH" "$DIR" &&
70 graph_git_two_modes "log --topo-order $BRANCH" "$DIR" &&
71 graph_git_two_modes "log --graph $COMPARE..$BRANCH" "$DIR" &&
72 graph_git_two_modes "branch -vv" "$DIR" &&
73 graph_git_two_modes "merge-base -a $BRANCH $COMPARE" "$DIR"
77 graph_git_behavior
'graph exists' commits
/3 commits
/1
79 verify_chain_files_exist
() {
80 for hash in $
(cat $1/commit-graph-chain
)
82 test_path_is_file
$1/graph-
$hash.graph ||
return 1
86 test_expect_success
'add more commits, and write a new base graph' '
87 git reset --hard commits/1 &&
88 for i in $(test_seq 4 5)
91 git branch commits/$i || return 1
93 git reset --hard commits/2 &&
94 for i in $(test_seq 6 10)
97 git branch commits/$i || return 1
99 git reset --hard commits/2 &&
100 git merge commits/4 &&
101 git branch merge/1 &&
102 git reset --hard commits/4 &&
103 git merge commits/6 &&
104 git branch merge/2 &&
105 git commit-graph write --reachable &&
109 test_expect_success
'fork and fail to base a chain on a commit-graph file' '
110 test_when_finished rm -rf fork &&
114 rm .git/objects/info/commit-graph &&
115 echo "$(pwd)/../.git/objects" >.git/objects/info/alternates &&
116 test_commit new-commit &&
117 git commit-graph write --reachable --split &&
118 test_path_is_file $graphdir/commit-graph-chain &&
119 test_line_count = 1 $graphdir/commit-graph-chain &&
120 verify_chain_files_exist $graphdir
124 test_expect_success
'add three more commits, write a tip graph' '
125 git reset --hard commits/3 &&
127 git merge commits/5 &&
129 git branch merge/3 &&
130 git commit-graph write --reachable --split &&
131 test_path_is_missing $infodir/commit-graph &&
132 test_path_is_file $graphdir/commit-graph-chain &&
133 ls $graphdir/graph-*.graph >graph-files &&
134 test_line_count = 2 graph-files &&
135 verify_chain_files_exist $graphdir
138 graph_git_behavior
'split commit-graph: merge 3 vs 2' merge
/3 merge
/2
140 test_expect_success
'add one commit, write a tip graph' '
142 git branch commits/11 &&
143 git commit-graph write --reachable --split &&
144 test_path_is_missing $infodir/commit-graph &&
145 test_path_is_file $graphdir/commit-graph-chain &&
146 ls $graphdir/graph-*.graph >graph-files &&
147 test_line_count = 3 graph-files &&
148 verify_chain_files_exist $graphdir
151 graph_git_behavior
'three-layer commit-graph: commit 11 vs 6' commits
/11 commits
/6
153 test_expect_success
'add one commit, write a merged graph' '
155 git branch commits/12 &&
156 git commit-graph write --reachable --split &&
157 test_path_is_file $graphdir/commit-graph-chain &&
158 test_line_count = 2 $graphdir/commit-graph-chain &&
159 ls $graphdir/graph-*.graph >graph-files &&
160 test_line_count = 2 graph-files &&
161 verify_chain_files_exist $graphdir
164 graph_git_behavior
'merged commit-graph: commit 12 vs 6' commits
/12 commits
/6
166 test_expect_success
'create fork and chain across alternate' '
170 git config core.commitGraph true &&
172 echo "$(pwd)/../.git/objects" >.git/objects/info/alternates &&
174 git branch commits/13 &&
175 git commit-graph write --reachable --split &&
176 test_path_is_file $graphdir/commit-graph-chain &&
177 test_line_count = 3 $graphdir/commit-graph-chain &&
178 ls $graphdir/graph-*.graph >graph-files &&
179 test_line_count = 1 graph-files &&
180 git -c core.commitGraph=true rev-list HEAD >expect &&
181 git -c core.commitGraph=false rev-list HEAD >actual &&
182 test_cmp expect actual &&
184 git commit-graph write --reachable --split --object-dir=.git/objects/ &&
185 test_line_count = 3 $graphdir/commit-graph-chain &&
186 ls $graphdir/graph-*.graph >graph-files &&
187 test_line_count = 1 graph-files
193 graph_git_behavior
'alternate: commit 13 vs 6' commits
/13 origin
/commits
/6 "fork"
196 test_expect_success
'test merge stragety constants' '
197 git clone . merge-2 &&
200 git config core.commitGraph true &&
201 test_line_count = 2 $graphdir/commit-graph-chain &&
203 git commit-graph write --reachable --split --size-multiple=2 &&
204 test_line_count = 3 $graphdir/commit-graph-chain
207 git clone . merge-10 &&
210 git config core.commitGraph true &&
211 test_line_count = 2 $graphdir/commit-graph-chain &&
213 git commit-graph write --reachable --split --size-multiple=10 &&
214 test_line_count = 1 $graphdir/commit-graph-chain &&
215 ls $graphdir/graph-*.graph >graph-files &&
216 test_line_count = 1 graph-files
218 git clone . merge-10-expire &&
220 cd merge-10-expire &&
221 git config core.commitGraph true &&
222 test_line_count = 2 $graphdir/commit-graph-chain &&
224 touch $graphdir/to-delete.graph $graphdir/to-keep.graph &&
225 test-tool chmtime =1546362000 $graphdir/to-delete.graph &&
226 test-tool chmtime =1546362001 $graphdir/to-keep.graph &&
227 git commit-graph write --reachable --split --size-multiple=10 \
228 --expire-time="2019-01-01 12:00 -05:00" &&
229 test_line_count = 1 $graphdir/commit-graph-chain &&
230 test_path_is_missing $graphdir/to-delete.graph &&
231 test_path_is_file $graphdir/to-keep.graph &&
232 ls $graphdir/graph-*.graph >graph-files &&
233 test_line_count = 3 graph-files
235 git clone --no-hardlinks . max-commits &&
238 git config core.commitGraph true &&
239 test_line_count = 2 $graphdir/commit-graph-chain &&
242 git commit-graph write --reachable --split --max-commits=1 &&
243 test_line_count = 1 $graphdir/commit-graph-chain &&
244 ls $graphdir/graph-*.graph >graph-files &&
245 test_line_count = 1 graph-files
249 test_expect_success
'remove commit-graph-chain file after flattening' '
250 git clone . flatten &&
253 test_line_count = 2 $graphdir/commit-graph-chain &&
254 git commit-graph write --reachable &&
255 test_path_is_missing $graphdir/commit-graph-chain &&
256 ls $graphdir >graph-files &&
257 test_line_count = 0 graph-files
266 printf "$data" |
dd of
="$file" bs
=1 seek
="$pos" conv
=notrunc
269 test_expect_success
'verify hashes along chain, even in shallow' '
270 git clone --no-hardlinks . verify &&
273 git commit-graph verify &&
274 base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
275 corrupt_file "$base_file" $(test_oid shallow) "\01" &&
276 test_must_fail git commit-graph verify --shallow 2>test_err &&
277 grep -v "^+" test_err >err &&
278 test_i18ngrep "incorrect checksum" err
282 test_expect_success
'verify --shallow does not check base contents' '
283 git clone --no-hardlinks . verify-shallow &&
286 git commit-graph verify &&
287 base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
288 corrupt_file "$base_file" 1000 "\01" &&
289 git commit-graph verify --shallow &&
290 test_must_fail git commit-graph verify 2>test_err &&
291 grep -v "^+" test_err >err &&
292 test_i18ngrep "incorrect checksum" err
296 test_expect_success
'warn on base graph chunk incorrect' '
297 git clone --no-hardlinks . base-chunk &&
300 git commit-graph verify &&
301 base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
302 corrupt_file "$base_file" $(test_oid base) "\01" &&
303 git commit-graph verify --shallow 2>test_err &&
304 grep -v "^+" test_err >err &&
305 test_i18ngrep "commit-graph chain does not match" err
309 test_expect_success
'verify after commit-graph-chain corruption' '
310 git clone --no-hardlinks . verify-chain &&
313 corrupt_file "$graphdir/commit-graph-chain" 60 "G" &&
314 git commit-graph verify 2>test_err &&
315 grep -v "^+" test_err >err &&
316 test_i18ngrep "invalid commit-graph chain" err &&
317 corrupt_file "$graphdir/commit-graph-chain" 60 "A" &&
318 git commit-graph verify 2>test_err &&
319 grep -v "^+" test_err >err &&
320 test_i18ngrep "unable to find all commit-graph files" err
324 test_expect_success
'verify across alternates' '
325 git clone --no-hardlinks . verify-alt &&
329 altdir="$(pwd)/../.git/objects" &&
330 echo "$altdir" >.git/objects/info/alternates &&
331 git commit-graph verify --object-dir="$altdir/" &&
333 git commit-graph write --reachable --split &&
334 tip_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
335 corrupt_file "$tip_file" 100 "\01" &&
336 test_must_fail git commit-graph verify --shallow 2>test_err &&
337 grep -v "^+" test_err >err &&
338 test_i18ngrep "commit-graph has incorrect fanout value" err
342 test_expect_success
'add octopus merge' '
343 git reset --hard commits/10 &&
344 git merge commits/3 commits/4 &&
345 git branch merge/octopus &&
346 git commit-graph write --reachable --split &&
347 git commit-graph verify --progress 2>err &&
348 test_line_count = 3 err &&
349 test_i18ngrep ! warning err &&
350 test_line_count = 3 $graphdir/commit-graph-chain
353 graph_git_behavior
'graph exists' merge
/octopus commits
/12
355 test_expect_success
'split across alternate where alternate is not split' '
356 git commit-graph write --reachable &&
357 test_path_is_file .git/objects/info/commit-graph &&
358 cp .git/objects/info/commit-graph . &&
359 git clone --no-hardlinks . alt-split &&
362 rm -f .git/objects/info/commit-graph &&
363 echo "$(pwd)"/../.git/objects >.git/objects/info/alternates &&
365 git commit-graph write --reachable --split &&
366 test_line_count = 1 $graphdir/commit-graph-chain
368 test_cmp commit-graph .git/objects/info/commit-graph
371 test_expect_success
'--split=no-merge always writes an incremental' '
372 test_when_finished rm -rf a b &&
373 rm -rf $graphdir $infodir/commit-graph &&
374 git reset --hard commits/2 &&
375 git rev-list HEAD~1 >a &&
376 git rev-list HEAD >b &&
377 git commit-graph write --split --stdin-commits <a &&
378 git commit-graph write --split=no-merge --stdin-commits <b &&
379 test_line_count = 2 $graphdir/commit-graph-chain
382 test_expect_success
'--split=replace replaces the chain' '
383 rm -rf $graphdir $infodir/commit-graph &&
384 git reset --hard commits/3 &&
385 git rev-list -1 HEAD~2 >a &&
386 git rev-list -1 HEAD~1 >b &&
387 git rev-list -1 HEAD >c &&
388 git commit-graph write --split=no-merge --stdin-commits <a &&
389 git commit-graph write --split=no-merge --stdin-commits <b &&
390 git commit-graph write --split=no-merge --stdin-commits <c &&
391 test_line_count = 3 $graphdir/commit-graph-chain &&
392 git commit-graph write --stdin-commits --split=replace <b &&
393 test_path_is_missing $infodir/commit-graph &&
394 test_path_is_file $graphdir/commit-graph-chain &&
395 ls $graphdir/graph-*.graph >graph-files &&
396 test_line_count = 1 graph-files &&
397 verify_chain_files_exist $graphdir &&
401 test_expect_success ULIMIT_FILE_DESCRIPTORS
'handles file descriptor exhaustion' '
405 for i in $(test_seq 64)
408 run_with_limited_open_files test_might_fail git commit-graph write \
409 --split=no-merge --reachable || return 1
414 while read mode modebits
416 test_expect_success POSIXPERM
"split commit-graph respects core.sharedrepository $mode" '
417 rm -rf $graphdir $infodir/commit-graph &&
418 git reset --hard commits/1 &&
419 test_config core.sharedrepository "$mode" &&
420 git commit-graph write --split --reachable &&
421 ls $graphdir/graph-*.graph >graph-files &&
422 test_line_count = 1 graph-files &&
423 echo "$modebits" >expect &&
424 test_modebits $graphdir/graph-*.graph >actual &&
425 test_cmp expect actual &&
426 test_modebits $graphdir/commit-graph-chain >actual &&
427 test_cmp expect actual
434 test_expect_success
'--split=replace with partial Bloom data' '
435 rm -rf $graphdir $infodir/commit-graph &&
436 git reset --hard commits/3 &&
437 git rev-list -1 HEAD~2 >a &&
438 git rev-list -1 HEAD~1 >b &&
439 git commit-graph write --split=no-merge --stdin-commits --changed-paths <a &&
440 git commit-graph write --split=no-merge --stdin-commits <b &&
441 git commit-graph write --split=replace --stdin-commits --changed-paths <c &&
442 ls $graphdir/graph-*.graph >graph-files &&
443 test_line_count = 1 graph-files &&
444 verify_chain_files_exist $graphdir
447 test_expect_success
'prevent regression for duplicate commits across layers' '
449 git -C dup commit --allow-empty -m one &&
450 git -C dup -c core.commitGraph=false commit-graph write --split=no-merge --reachable 2>err &&
451 test_i18ngrep "attempting to write a commit-graph" err &&
452 git -C dup commit-graph write --split=no-merge --reachable &&
453 git -C dup commit --allow-empty -m two &&
454 git -C dup commit-graph write --split=no-merge --reachable &&
455 git -C dup commit --allow-empty -m three &&
456 git -C dup commit-graph write --split --reachable &&
457 git -C dup commit-graph verify
460 NUM_FIRST_LAYER_COMMITS
=64
461 NUM_SECOND_LAYER_COMMITS
=16
462 NUM_THIRD_LAYER_COMMITS
=7
463 NUM_FOURTH_LAYER_COMMITS
=8
464 NUM_FIFTH_LAYER_COMMITS
=16
465 SECOND_LAYER_SEQUENCE_START
=$
(($NUM_FIRST_LAYER_COMMITS + 1))
466 SECOND_LAYER_SEQUENCE_END
=$
(($SECOND_LAYER_SEQUENCE_START + $NUM_SECOND_LAYER_COMMITS - 1))
467 THIRD_LAYER_SEQUENCE_START
=$
(($SECOND_LAYER_SEQUENCE_END + 1))
468 THIRD_LAYER_SEQUENCE_END
=$
(($THIRD_LAYER_SEQUENCE_START + $NUM_THIRD_LAYER_COMMITS - 1))
469 FOURTH_LAYER_SEQUENCE_START
=$
(($THIRD_LAYER_SEQUENCE_END + 1))
470 FOURTH_LAYER_SEQUENCE_END
=$
(($FOURTH_LAYER_SEQUENCE_START + $NUM_FOURTH_LAYER_COMMITS - 1))
471 FIFTH_LAYER_SEQUENCE_START
=$
(($FOURTH_LAYER_SEQUENCE_END + 1))
472 FIFTH_LAYER_SEQUENCE_END
=$
(($FIFTH_LAYER_SEQUENCE_START + $NUM_FIFTH_LAYER_COMMITS - 1))
474 # Current split graph chain:
476 # 16 commits (No GDAT)
477 # ------------------------
480 test_expect_success
'setup repo for mixed generation commit-graph-chain' '
481 graphdir=".git/objects/info/commit-graphs" &&
482 test_oid_cache <<-EOF &&
489 git config core.commitGraph true &&
490 git config gc.writeCommitGraph false &&
491 for i in $(test_seq $NUM_FIRST_LAYER_COMMITS)
494 git branch commits/$i || return 1
496 git -c commitGraph.generationVersion=2 commit-graph write --reachable --split &&
497 graph_read_expect $NUM_FIRST_LAYER_COMMITS &&
498 test_line_count = 1 $graphdir/commit-graph-chain &&
499 for i in $(test_seq $SECOND_LAYER_SEQUENCE_START $SECOND_LAYER_SEQUENCE_END)
502 git branch commits/$i || return 1
504 git -c commitGraph.generationVersion=1 commit-graph write --reachable --split=no-merge &&
505 test_line_count = 2 $graphdir/commit-graph-chain &&
506 test-tool read-graph >output &&
507 cat >expect <<-EOF &&
508 header: 43475048 1 $(test_oid oid_version) 4 1
509 num_commits: $NUM_SECOND_LAYER_COMMITS
510 chunks: oid_fanout oid_lookup commit_metadata
512 test_cmp expect output &&
513 git commit-graph verify &&
514 cat $graphdir/commit-graph-chain
518 # The new layer will be added without generation data chunk as it was not
519 # present on the layer underneath it.
521 # 7 commits (No GDAT)
522 # ------------------------
523 # 16 commits (No GDAT)
524 # ------------------------
527 test_expect_success
'do not write generation data chunk if not present on existing tip' '
528 git clone mixed mixed-no-gdat &&
531 for i in $(test_seq $THIRD_LAYER_SEQUENCE_START $THIRD_LAYER_SEQUENCE_END)
534 git branch commits/$i || return 1
536 git commit-graph write --reachable --split=no-merge &&
537 test_line_count = 3 $graphdir/commit-graph-chain &&
538 test-tool read-graph >output &&
539 cat >expect <<-EOF &&
540 header: 43475048 1 $(test_oid oid_version) 4 2
541 num_commits: $NUM_THIRD_LAYER_COMMITS
542 chunks: oid_fanout oid_lookup commit_metadata
544 test_cmp expect output &&
545 git commit-graph verify
549 # Number of commits in each layer of the split-commit graph before merge:
551 # 8 commits (No GDAT)
552 # ------------------------
553 # 7 commits (No GDAT)
554 # ------------------------
555 # 16 commits (No GDAT)
556 # ------------------------
559 # The top two layers are merged and do not have generation data chunk as layer below them does
560 # not have generation data chunk.
562 # 15 commits (No GDAT)
563 # ------------------------
564 # 16 commits (No GDAT)
565 # ------------------------
568 test_expect_success
'do not write generation data chunk if the topmost remaining layer does not have generation data chunk' '
569 git clone mixed-no-gdat mixed-merge-no-gdat &&
571 cd mixed-merge-no-gdat &&
572 for i in $(test_seq $FOURTH_LAYER_SEQUENCE_START $FOURTH_LAYER_SEQUENCE_END)
575 git branch commits/$i || return 1
577 git commit-graph write --reachable --split --size-multiple 1 &&
578 test_line_count = 3 $graphdir/commit-graph-chain &&
579 test-tool read-graph >output &&
580 cat >expect <<-EOF &&
581 header: 43475048 1 $(test_oid oid_version) 4 2
582 num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
583 chunks: oid_fanout oid_lookup commit_metadata
585 test_cmp expect output &&
586 git commit-graph verify
590 # Number of commits in each layer of the split-commit graph before merge:
592 # 16 commits (No GDAT)
593 # ------------------------
594 # 15 commits (No GDAT)
595 # ------------------------
596 # 16 commits (No GDAT)
597 # ------------------------
600 # The top three layers are merged and has generation data chunk as the topmost remaining layer
601 # has generation data chunk.
604 # ------------------------
607 test_expect_success
'write generation data chunk if topmost remaining layer has generation data chunk' '
608 git clone mixed-merge-no-gdat mixed-merge-gdat &&
610 cd mixed-merge-gdat &&
611 for i in $(test_seq $FIFTH_LAYER_SEQUENCE_START $FIFTH_LAYER_SEQUENCE_END)
614 git branch commits/$i || return 1
616 git commit-graph write --reachable --split --size-multiple 1 &&
617 test_line_count = 2 $graphdir/commit-graph-chain &&
618 test-tool read-graph >output &&
619 cat >expect <<-EOF &&
620 header: 43475048 1 $(test_oid oid_version) 5 1
621 num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
622 chunks: oid_fanout oid_lookup commit_metadata generation_data
624 test_cmp expect output
628 test_expect_success
'write generation data chunk when commit-graph chain is replaced' '
629 git clone mixed mixed-replace &&
632 git commit-graph write --reachable --split=replace &&
633 test_path_is_file $graphdir/commit-graph-chain &&
634 test_line_count = 1 $graphdir/commit-graph-chain &&
635 verify_chain_files_exist $graphdir &&
636 graph_read_expect $(($NUM_FIRST_LAYER_COMMITS + $NUM_SECOND_LAYER_COMMITS)) &&
637 git commit-graph verify