Git object storage and pack files for Eio
at main 774 lines 20 kB view raw
1git-mono split tests 2===================== 3 4Setup: configure git and disable colors 5 6 $ export NO_COLOR=1 7 $ export GIT_AUTHOR_NAME="Test User" 8 $ export GIT_AUTHOR_EMAIL="test@example.com" 9 $ export GIT_AUTHOR_DATE="2025-01-01T00:00:00+00:00" 10 $ export GIT_COMMITTER_NAME="Test User" 11 $ export GIT_COMMITTER_EMAIL="test@example.com" 12 $ export GIT_COMMITTER_DATE="2025-01-01T00:00:00+00:00" 13 14Help and version 15----------------- 16 17 $ git-mono --version 18 4.0.0 19 20Error: missing PREFIX argument 21------------------------------- 22 23 $ git-mono split 2>&1 24 Usage: git-mono split [--help] [OPTION] PREFIX 25 git-mono: required argument PREFIX is missing 26 [124] 27 28Error: not a git repository 29---------------------------- 30 31 $ mkdir empty && cd empty 32 $ git-mono split lib 2>&1 33 error: Could not resolve revision 'HEAD'. 34 git-mono: could not resolve revision 'HEAD' 35 [124] 36 $ cd .. 37 38Single commit with prefix 39-------------------------- 40 41 $ mkdir repo1 && cd repo1 42 $ git init -q 43 $ mkdir lib 44 $ echo "hello" > lib/foo.ml 45 $ git add . && git commit -q -m "add lib/foo.ml" 46 $ HASH=$(git-mono split lib) 47 $ test -n "$HASH" && echo "got hash" 48 got hash 49 50Verify the split commit has lib/ contents at root 51 52 $ git ls-tree "$HASH" --name-only 53 foo.ml 54 $ git show "$HASH:foo.ml" 55 hello 56 $ cd .. 57 58Linear history 59-------------- 60 61 $ mkdir repo2 && cd repo2 62 $ git init -q 63 $ mkdir lib 64 $ echo "v1" > lib/a.ml 65 $ git add . && git commit -q -m "first" 66 $ echo "v2" > lib/a.ml 67 $ git add . && git commit -q -m "second" 68 $ echo "v3" > lib/a.ml 69 $ git add . && git commit -q -m "third" 70 $ HASH=$(git-mono split lib) 71 72The split commit chain should have 3 commits 73 74 $ git rev-list "$HASH" | wc -l | tr -d ' ' 75 3 76 77Each split commit message matches the original 78 79 $ git log --format="%s" "$HASH" 80 third 81 second 82 first 83 84The latest tree should have a.ml with content "v3" 85 86 $ git show "$HASH:a.ml" 87 v3 88 $ cd .. 89 90No commits touch prefix 91------------------------ 92 93 $ mkdir repo3 && cd repo3 94 $ git init -q 95 $ echo "root" > README.md 96 $ git add . && git commit -q -m "root only" 97 $ git-mono split nonexistent 2>&1 98 git-mono: [WARNING] No commits touch prefix 'nonexistent'. 99 $ cd .. 100 101Cache: second run returns same hash 102------------------------------------- 103 104 $ mkdir repo4 && cd repo4 105 $ git init -q 106 $ mkdir lib 107 $ echo "cached" > lib/x.ml 108 $ git add . && git commit -q -m "initial" 109 $ HASH1=$(git-mono split lib) 110 $ HASH2=$(git-mono split lib) 111 $ test "$HASH1" = "$HASH2" && echo "cache hit: same hash" 112 cache hit: same hash 113 114Verify cache file exists 115 116 $ test -f .git/subtree-cache/lib && echo "cache file exists" 117 cache file exists 118 $ cd .. 119 120Incremental split 121----------------- 122 123 $ mkdir repo5 && cd repo5 124 $ git init -q 125 $ mkdir lib 126 $ echo "v1" > lib/a.ml 127 $ git add . && git commit -q -m "first" 128 $ HASH1=$(git-mono split lib) 129 130Add another commit 131 132 $ echo "v2" > lib/a.ml 133 $ git add . && git commit -q -m "second" 134 $ HASH2=$(git-mono split lib) 135 136Hashes differ 137 138 $ test "$HASH1" != "$HASH2" && echo "hashes differ" 139 hashes differ 140 141New split has 2 commits, not 1 142 143 $ git rev-list "$HASH2" | wc -l | tr -d ' ' 144 2 145 146The parent of the new split head is the old split head 147 148 $ PARENT=$(git rev-parse "$HASH2^") 149 $ test "$PARENT" = "$HASH1" && echo "parent matches" 150 parent matches 151 $ cd .. 152 153Merge commits 154------------- 155 156 $ mkdir repo6 && cd repo6 157 $ git init -q 158 $ mkdir lib 159 $ echo "base" > lib/a.ml 160 $ git add . && git commit -q -m "base" 161 162Create a branch and make changes 163 164 $ git checkout -q -b feature 165 $ echo "feature" > lib/b.ml 166 $ git add . && git commit -q -m "add b.ml on feature" 167 168Go back to main and make a different change 169 170 $ git checkout -q main 2>/dev/null || git checkout -q master 171 $ echo "main" > lib/c.ml 172 $ git add . && git commit -q -m "add c.ml on main" 173 174Merge 175 176 $ git merge -q --no-edit feature 177 178Split should produce commits with merge parents 179 180 $ HASH=$(git-mono split lib) 181 $ git rev-list "$HASH" | wc -l | tr -d ' ' 182 4 183 184The head commit should be a merge (2 parents) 185 186 $ git cat-file -p "$HASH" | grep "^parent" | wc -l | tr -d ' ' 187 2 188 $ cd .. 189 190Nested prefix 191-------------- 192 193 $ mkdir repo7 && cd repo7 194 $ git init -q 195 $ mkdir -p src/lib 196 $ echo "deep" > src/lib/deep.ml 197 $ git add . && git commit -q -m "nested" 198 $ HASH=$(git-mono split src/lib) 199 $ git ls-tree "$HASH" --name-only 200 deep.ml 201 $ cd .. 202 203Commits that don't touch the prefix are skipped 204------------------------------------------------- 205 206 $ mkdir repo8 && cd repo8 207 $ git init -q 208 $ mkdir lib 209 $ echo "v1" > lib/a.ml 210 $ git add . && git commit -q -m "touch lib" 211 $ echo "unrelated" > README.md 212 $ git add . && git commit -q -m "touch root only" 213 $ echo "v2" > lib/a.ml 214 $ git add . && git commit -q -m "touch lib again" 215 $ HASH=$(git-mono split lib) 216 217Only 2 split commits (the one touching root only is squashed since subtree didn't change) 218 219 $ git rev-list "$HASH" | wc -l | tr -d ' ' 220 2 221 222Messages are preserved from the commits that touch lib 223 224 $ git log --format="%s" "$HASH" 225 touch lib again 226 touch lib 227 $ cd .. 228 229Split from a specific revision 230------------------------------- 231 232 $ mkdir repo9 && cd repo9 233 $ git init -q 234 $ mkdir lib 235 $ echo "v1" > lib/a.ml 236 $ git add . && git commit -q -m "first" 237 $ FIRST=$(git rev-parse HEAD) 238 $ echo "v2" > lib/a.ml 239 $ git add . && git commit -q -m "second" 240 $ HASH_HEAD=$(git-mono split lib) 241 $ HASH_FIRST=$(git-mono split --rev "$FIRST" lib) 242 243The rev-specific split should have fewer commits 244 245 $ git rev-list "$HASH_FIRST" | wc -l | tr -d ' ' 246 1 247 $ git rev-list "$HASH_HEAD" | wc -l | tr -d ' ' 248 2 249 $ cd .. 250 251Split from a branch name 252------------------------- 253 254 $ mkdir repo10 && cd repo10 255 $ git init -q 256 $ mkdir lib 257 $ echo "main" > lib/a.ml 258 $ git add . && git commit -q -m "on main" 259 $ git checkout -q -b other 260 $ echo "other" > lib/b.ml 261 $ git add . && git commit -q -m "on other" 262 $ git checkout -q main 2>/dev/null || git checkout -q master 263 $ HASH=$(git-mono split --rev other lib) 264 $ git show "$HASH:b.ml" 265 other 266 $ cd .. 267 268Bad revision 269------------ 270 271 $ mkdir repo11 && cd repo11 272 $ git init -q 273 $ git-mono split --rev nonexistent lib 2>&1 274 error: Could not resolve revision 'nonexistent'. 275 git-mono: could not resolve revision 'nonexistent' 276 [124] 277 $ cd .. 278 279Quiet mode (-q) suppresses warnings 280------------------------------------- 281 282 $ mkdir repo12 && cd repo12 283 $ git init -q 284 $ echo "root" > README.md 285 $ git add . && git commit -q -m "root only" 286 $ git-mono split -q nonexistent 2>&1 287 $ cd .. 288 289Verbose mode shows info messages 290--------------------------------- 291 292 $ mkdir repo13 && cd repo13 293 $ git init -q 294 $ mkdir lib 295 $ echo "hello" > lib/a.ml 296 $ git add . && git commit -q -m "initial" 297 $ git-mono split -v lib 2>&1 | grep -c "Splitting prefix" 298 1 299 $ cd .. 300 301Multiple prefixes use separate caches 302--------------------------------------- 303 304 $ mkdir repo14 && cd repo14 305 $ git init -q 306 $ mkdir lib bin 307 $ echo "lib" > lib/a.ml 308 $ echo "bin" > bin/main.ml 309 $ git add . && git commit -q -m "initial" 310 $ HASH_LIB=$(git-mono split lib) 311 $ HASH_BIN=$(git-mono split bin) 312 313Different hashes (different content) 314 315 $ test "$HASH_LIB" != "$HASH_BIN" && echo "different hashes" 316 different hashes 317 318Separate cache files 319 320 $ test -f .git/subtree-cache/lib && echo "lib cache exists" 321 lib cache exists 322 $ test -f .git/subtree-cache/bin && echo "bin cache exists" 323 bin cache exists 324 $ cd .. 325 326Author and committer are preserved 327------------------------------------ 328 329 $ mkdir repo15 && cd repo15 330 $ git init -q 331 $ mkdir lib 332 $ echo "hello" > lib/a.ml 333 $ git add . 334 $ GIT_AUTHOR_NAME="Alice Author" GIT_AUTHOR_EMAIL="alice@example.com" \ 335 > GIT_COMMITTER_NAME="Bob Committer" GIT_COMMITTER_EMAIL="bob@example.com" \ 336 > git commit -q -m "authored commit" 337 $ HASH=$(git-mono split lib) 338 $ git cat-file -p "$HASH" | grep "^author " | sed 's/ [0-9]* [+-][0-9]*//' 339 author Alice Author <alice@example.com> 340 $ git cat-file -p "$HASH" | grep "^committer " | sed 's/ [0-9]* [+-][0-9]*//' 341 committer Bob Committer <bob@example.com> 342 $ cd .. 343 344Dirty working tree does not affect split 345------------------------------------------ 346 347 $ mkdir repo16 && cd repo16 348 $ git init -q 349 $ mkdir lib 350 $ echo "committed" > lib/a.ml 351 $ git add . && git commit -q -m "initial" 352 $ HASH1=$(git-mono split lib) 353 354Make dirty changes (not committed) 355 356 $ echo "dirty" > lib/a.ml 357 $ echo "new file" > lib/b.ml 358 359Split should still work and return same hash (reads object store, not working tree) 360 361 $ HASH2=$(git-mono split lib) 362 $ test "$HASH1" = "$HASH2" && echo "dirty tree ignored" 363 dirty tree ignored 364 $ cd .. 365 366Using -C to specify repository path 367-------------------------------------- 368 369 $ mkdir repo17 && cd repo17 370 $ git init -q 371 $ mkdir lib 372 $ echo "remote" > lib/a.ml 373 $ git add . && git commit -q -m "initial" 374 $ cd .. 375 $ HASH=$(git-mono split -C repo17 lib) 376 $ test -n "$HASH" && echo "got hash via -C" 377 got hash via -C 378 379Large tree: split only extracts the right subtree 380--------------------------------------------------- 381 382 $ mkdir repo19 && cd repo19 383 $ git init -q 384 $ mkdir -p lib bin doc test 385 $ echo "lib" > lib/a.ml 386 $ echo "bin" > bin/main.ml 387 $ echo "doc" > doc/README.md 388 $ echo "test" > test/test.ml 389 $ git add . && git commit -q -m "initial" 390 $ HASH=$(git-mono split lib) 391 $ git ls-tree "$HASH" --name-only 392 a.ml 393 $ cd .. 394 395Commit message is preserved 396----------------------------- 397 398 $ mkdir repo20 && cd repo20 399 $ git init -q 400 $ mkdir lib 401 $ echo "hello" > lib/a.ml 402 $ git add . 403 $ git commit -q -m "Detailed commit message" 404 $ HASH=$(git-mono split lib) 405 $ git log -1 --format="%s" "$HASH" 406 Detailed commit message 407 $ cd .. 408 409Prefix appearing partway through history 410------------------------------------------ 411 412 $ mkdir repo21 && cd repo21 413 $ git init -q 414 $ echo "root" > README.md 415 $ git add . && git commit -q -m "no lib yet" 416 $ mkdir lib 417 $ echo "v1" > lib/a.ml 418 $ git add . && git commit -q -m "add lib" 419 $ echo "v2" > lib/a.ml 420 $ git add . && git commit -q -m "update lib" 421 $ HASH=$(git-mono split lib) 422 423Only 2 commits in split (the "no lib yet" commit is skipped) 424 425 $ git rev-list "$HASH" | wc -l | tr -d ' ' 426 2 427 $ git log --format="%s" "$HASH" 428 update lib 429 add lib 430 $ cd .. 431 432Multiple files in prefix 433------------------------- 434 435 $ mkdir repo22 && cd repo22 436 $ git init -q 437 $ mkdir lib 438 $ echo "a" > lib/a.ml 439 $ echo "b" > lib/b.ml 440 $ echo "c" > lib/c.ml 441 $ git add . && git commit -q -m "initial" 442 $ HASH=$(git-mono split lib) 443 $ git ls-tree "$HASH" --name-only | sort 444 a.ml 445 b.ml 446 c.ml 447 $ cd .. 448 449Nested directories in prefix 450------------------------------- 451 452 $ mkdir repo23 && cd repo23 453 $ git init -q 454 $ mkdir -p lib/src lib/test 455 $ echo "code" > lib/src/main.ml 456 $ echo "test" > lib/test/test.ml 457 $ git add . && git commit -q -m "initial" 458 $ HASH=$(git-mono split lib) 459 $ git ls-tree "$HASH" --name-only 460 src 461 test 462 $ git ls-tree "$HASH:src" --name-only 463 main.ml 464 $ cd .. 465 466Cache file format: pairs of hex hashes 467---------------------------------------- 468 469 $ mkdir repo24 && cd repo24 470 $ git init -q 471 $ mkdir lib 472 $ echo "hello" > lib/a.ml 473 $ git add . && git commit -q -m "initial" 474 $ git-mono split lib > /dev/null 475 $ wc -l < .git/subtree-cache/lib | tr -d ' ' 476 1 477 $ awk '{ print length($1), length($2) }' .git/subtree-cache/lib 478 40 40 479 $ cd .. 480 481Second split with same tree but different message creates new commit 482--------------------------------------------------------------------- 483 484 $ mkdir repo25 && cd repo25 485 $ git init -q 486 $ mkdir lib 487 $ echo "same" > lib/a.ml 488 $ git add . && git commit -q -m "commit A" 489 $ echo "different" > lib/a.ml 490 $ git add . && git commit -q -m "commit B" 491 $ echo "same" > lib/a.ml 492 $ git add . && git commit -q -m "commit C (same tree as A)" 493 $ HASH=$(git-mono split lib) 494 495All 3 commits are present because the tree changes between adjacent parents 496 497 $ git rev-list "$HASH" | wc -l | tr -d ' ' 498 3 499 $ cd .. 500 501Idempotency: running split multiple times produces identical results 502---------------------------------------------------------------------- 503 504 $ mkdir repo26 && cd repo26 505 $ git init -q 506 $ mkdir lib 507 $ echo "v1" > lib/a.ml 508 $ git add . && git commit -q -m "first" 509 $ echo "v2" > lib/a.ml 510 $ git add . && git commit -q -m "second" 511 $ echo "v3" > lib/a.ml 512 $ git add . && git commit -q -m "third" 513 514First split (cold cache) 515 516 $ HASH1=$(git-mono split lib) 517 518Second split (warm cache) 519 520 $ HASH2=$(git-mono split lib) 521 522Third split (still warm cache) 523 524 $ HASH3=$(git-mono split lib) 525 526All three runs produce the same hash 527 528 $ test "$HASH1" = "$HASH2" && test "$HASH2" = "$HASH3" && echo "idempotent" 529 idempotent 530 531 $ cd .. 532 533Incremental: add commits one at a time 534---------------------------------------- 535 536 $ mkdir repo27 && cd repo27 537 $ git init -q 538 $ mkdir lib 539 540 $ echo "v1" > lib/a.ml 541 $ git add . && git commit -q -m "first" 542 $ HASH_A=$(git-mono split lib) 543 544 $ echo "v2" > lib/a.ml 545 $ git add . && git commit -q -m "second" 546 $ HASH_B=$(git-mono split lib) 547 548 $ echo "v3" > lib/a.ml 549 $ git add . && git commit -q -m "third" 550 $ HASH_C=$(git-mono split lib) 551 552Each incremental split extends the chain 553 554 $ git rev-list "$HASH_A" | wc -l | tr -d ' ' 555 1 556 $ git rev-list "$HASH_B" | wc -l | tr -d ' ' 557 2 558 $ git rev-list "$HASH_C" | wc -l | tr -d ' ' 559 3 560 561The chain is consistent: each split head's parent is the previous split head 562 563 $ test "$(git rev-parse "$HASH_B^")" = "$HASH_A" && echo "B parent = A" 564 B parent = A 565 $ test "$(git rev-parse "$HASH_C^")" = "$HASH_B" && echo "C parent = B" 566 C parent = B 567 568All split commits are reachable from the latest 569 570 $ git merge-base --is-ancestor "$HASH_A" "$HASH_C" && echo "A is ancestor of C" 571 A is ancestor of C 572 $ cd .. 573 574Bidirectional incremental: split two branches independently 575------------------------------------------------------------ 576 577 $ mkdir repo28 && cd repo28 578 $ git init -q 579 $ mkdir lib 580 $ echo "base" > lib/a.ml 581 $ git add . && git commit -q -m "base commit" 582 $ HASH_BASE=$(git-mono split lib) 583 584Create two branches from the same base 585 586 $ git checkout -q -b branch-x 587 $ echo "x" > lib/x.ml 588 $ git add . && git commit -q -m "add x" 589 $ HASH_X=$(git-mono split --rev branch-x lib) 590 591 $ git checkout -q main 2>/dev/null || git checkout -q master 592 $ echo "y" > lib/y.ml 593 $ git add . && git commit -q -m "add y" 594 $ HASH_Y=$(git-mono split lib) 595 596Both branches share the same split base 597 598 $ test "$(git rev-parse "$HASH_X^")" = "$HASH_BASE" && echo "X parent = base" 599 X parent = base 600 $ test "$(git rev-parse "$HASH_Y^")" = "$HASH_BASE" && echo "Y parent = base" 601 Y parent = base 602 603Now merge and split again 604 605 $ git merge -q --no-edit branch-x 606 $ HASH_MERGE=$(git-mono split lib) 607 608The merge split has both branch splits as parents 609 610 $ git cat-file -p "$HASH_MERGE" | grep "^parent" | wc -l | tr -d ' ' 611 2 612 613The merge split is a descendant of both branch splits 614 615 $ git merge-base --is-ancestor "$HASH_X" "$HASH_MERGE" && echo "X ancestor of merge" 616 X ancestor of merge 617 $ git merge-base --is-ancestor "$HASH_Y" "$HASH_MERGE" && echo "Y ancestor of merge" 618 Y ancestor of merge 619 $ cd .. 620 621Incremental after cache: only new commits are processed 622--------------------------------------------------------- 623 624 $ mkdir repo29 && cd repo29 625 $ git init -q 626 $ mkdir lib 627 $ echo "v1" > lib/a.ml 628 $ git add . && git commit -q -m "first" 629 $ echo "v2" > lib/a.ml 630 $ git add . && git commit -q -m "second" 631 $ git-mono split lib > /dev/null 632 633Cache should have 2 entries 634 635 $ wc -l < .git/subtree-cache/lib | tr -d ' ' 636 2 637 638Add a new commit and re-split 639 640 $ echo "v3" > lib/a.ml 641 $ git add . && git commit -q -m "third" 642 $ git-mono split lib > /dev/null 643 644Cache should now have 3 entries (only 1 new entry added) 645 646 $ wc -l < .git/subtree-cache/lib | tr -d ' ' 647 3 648 $ cd .. 649 650Gap commits: split bridges over commits that don't contain the prefix 651----------------------------------------------------------------------- 652 653Some workflows (e.g. irmin auto-commits) can insert commits whose tree 654only contains a single subtree or is even empty. The split must bridge 655over these "gap" commits and maintain parent chain continuity. 656 657 $ mkdir repo30 && cd repo30 658 $ git init -q 659 $ mkdir -p lib bin 660 $ echo "v1" > lib/a.ml 661 $ echo "v1" > bin/main.ml 662 $ git add . && git commit -q -m "initial: lib + bin" 663 $ HASH1=$(git-mono split lib) 664 665Verify initial split has 1 commit 666 667 $ git rev-list "$HASH1" | wc -l | tr -d ' ' 668 1 669 670Now simulate a gap: create a commit with an empty tree (like an irmin 671"Initial commit") followed by a commit with only bin/ content. These 672represent commits that got into the main branch but don't have lib/. 673 674 $ EMPTY_TREE=$(git hash-object -t tree /dev/null) 675 $ GAP1=$(git commit-tree "$EMPTY_TREE" -p HEAD -m "gap: empty tree") 676 $ git reset -q "$GAP1" 677 678Create another gap commit with only bin/ content (no lib/) 679 680 $ mkdir -p /tmp/gap-work 681 $ echo "bin-only" > /tmp/gap-work/main.ml 682 $ BIN_BLOB=$(git hash-object -w /tmp/gap-work/main.ml) 683 $ BIN_TREE=$(printf "100644 blob %s\tmain.ml\n" "$BIN_BLOB" | git mktree) 684 $ TOP_TREE=$(printf "040000 tree %s\tbin\n" "$BIN_TREE" | git mktree) 685 $ GAP2=$(git commit-tree "$TOP_TREE" -p HEAD -m "gap: only bin") 686 $ git reset -q "$GAP2" 687 688Now add a normal commit that has lib/ again 689 690 $ echo "v2" > lib/a.ml 691 $ git add . && git commit -q -m "restore lib with v2" 692 $ HASH2=$(git-mono split lib) 693 694The split should have 2 commits (bridging over the 2 gap commits) 695 696 $ git rev-list "$HASH2" | wc -l | tr -d ' ' 697 2 698 699The parent chain should be connected: HASH1 is an ancestor of HASH2 700 701 $ git merge-base --is-ancestor "$HASH1" "$HASH2" && echo "parent chain preserved" 702 parent chain preserved 703 704 $ git log --format="%s" "$HASH2" 705 restore lib with v2 706 initial: lib + bin 707 708Verify the cache correctly handles the gap commits 709 710 $ HASH3=$(git-mono split lib) 711 $ test "$HASH2" = "$HASH3" && echo "cache hit after gap" 712 cache hit after gap 713 $ cd .. 714 715Multiple gap commits in sequence 716---------------------------------- 717 718 $ mkdir repo31 && cd repo31 719 $ git init -q 720 $ mkdir -p lib bin 721 $ echo "v1" > lib/a.ml 722 $ echo "v1" > bin/main.ml 723 $ git add . && git commit -q -m "first with lib" 724 $ HASH1=$(git-mono split lib) 725 726Create 3 gap commits in a row (none have lib/) 727 728 $ EMPTY_TREE=$(git hash-object -t tree /dev/null) 729 $ GAP1=$(git commit-tree "$EMPTY_TREE" -p HEAD -m "gap 1") 730 $ GAP2=$(git commit-tree "$EMPTY_TREE" -p "$GAP1" -m "gap 2") 731 $ GAP3=$(git commit-tree "$EMPTY_TREE" -p "$GAP2" -m "gap 3") 732 $ git reset -q "$GAP3" 733 734Restore lib/ after 3 gaps 735 736 $ echo "v2" > lib/a.ml 737 $ git add . && git commit -q -m "restore lib after 3 gaps" 738 $ HASH2=$(git-mono split lib) 739 740Split should bridge over all 3 gap commits 741 742 $ git rev-list "$HASH2" | wc -l | tr -d ' ' 743 2 744 $ git merge-base --is-ancestor "$HASH1" "$HASH2" && echo "bridged 3 gaps" 745 bridged 3 gaps 746 $ cd .. 747 748Verify repairs orphaned cache entries 749--------------------------------------- 750 751 $ mkdir repo32 && cd repo32 752 $ git init -q 753 $ mkdir lib 754 $ echo "v1" > lib/a.ml 755 $ git add . && git commit -q -m "first" 756 $ HASH1=$(git-mono split lib) 757 $ echo "v2" > lib/a.ml 758 $ git add . && git commit -q -m "second" 759 $ MONO_HEAD=$(git rev-parse HEAD) 760 761Corrupt the cache: add an orphaned split commit (no parents) for HEAD 762 763 $ ORPHAN_TREE=$(git rev-parse "$HASH1^{tree}") 764 $ ORPHAN=$(git commit-tree "$ORPHAN_TREE" -m "orphaned split") 765 $ printf "%s %s\n" "$MONO_HEAD" "$ORPHAN" >> .git/subtree-cache/lib 766 767Split should detect the orphan, clear cache, and rebuild with proper parents 768 769 $ HASH2=$(git-mono split lib) 770 $ git rev-list "$HASH2" | wc -l | tr -d ' ' 771 2 772 $ git merge-base --is-ancestor "$HASH1" "$HASH2" && echo "ancestry restored" 773 ancestry restored 774 $ cd ..