Git object storage and pack files for Eio
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 ..