My aggregated monorepo of OCaml code, automaintained
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix local sync detection to compare tree content instead of commits

The previous implementation compared commit hashes from subtree merge
commits, but this didn't account for changes pushed via `subtree push`
which don't create merge commits in the monorepo.

Now compares actual tree hashes:
- Monorepo: git rev-parse HEAD:<prefix>
- Checkout: git rev-parse HEAD^{tree}

If trees match, content is in sync regardless of push/pull direction.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+33 -32
+33 -32
monopam/lib/status.ml
··· 50 50 if Git.Subtree.exists ~fs:fs_t ~repo:monorepo ~prefix then Present 51 51 else Not_added 52 52 in 53 - (* Compute subtree sync state: compare subtree's last upstream commit with checkout HEAD *) 53 + (* Compute subtree sync state: compare tree content between monorepo subtree and checkout. 54 + This is more accurate than commit ancestry because it handles both push and pull directions. 55 + If the trees match, the content is in sync regardless of how it got there. *) 54 56 let subtree_sync = 55 57 match (checkout, subtree) with 56 58 | (Missing | Not_a_repo | Dirty), _ -> Unknown 57 59 | _, Not_added -> Unknown 58 60 | Clean _, Present -> 59 - (* Get the last upstream commit the subtree was synced from *) 60 - let subtree_commit = 61 - Git.subtree_last_upstream_commit ~proc ~fs:fs_t ~repo:monorepo ~prefix () 62 - in 63 - (* Get the checkout's current HEAD *) 64 - let checkout_head = Git.head_commit ~proc ~fs:fs_t checkout_dir in 65 - match (subtree_commit, checkout_head) with 66 - | None, _ -> Unknown (* Can't determine subtree's upstream commit *) 67 - | _, Error _ -> Unknown (* Can't get checkout HEAD *) 68 - | Some subtree_sha, Ok checkout_sha -> 69 - (* Normalize to short hashes for comparison *) 70 - let subtree_short = String.sub subtree_sha 0 (min 7 (String.length subtree_sha)) in 71 - let checkout_short = String.sub checkout_sha 0 (min 7 (String.length checkout_sha)) in 72 - if subtree_short = checkout_short then In_sync 73 - else 74 - (* Check if subtree commit is ancestor of checkout (subtree behind) *) 75 - if Git.is_ancestor ~proc ~fs:fs_t ~repo:checkout_dir 76 - ~commit1:subtree_sha ~commit2:checkout_sha () then 77 - (* Subtree is behind checkout - checkout has newer commits *) 78 - let count = Git.count_commits_between ~proc ~fs:fs_t ~repo:checkout_dir 79 - ~base:subtree_sha ~head:checkout_sha () in 80 - Subtree_behind count 81 - else if Git.is_ancestor ~proc ~fs:fs_t ~repo:checkout_dir 82 - ~commit1:checkout_sha ~commit2:subtree_sha () then 83 - (* Checkout is behind subtree - subtree has commits not pushed *) 84 - let count = Git.count_commits_between ~proc ~fs:fs_t ~repo:checkout_dir 85 - ~base:checkout_sha ~head:subtree_sha () in 86 - Subtree_ahead count 87 - else 88 - (* Diverged - treat as unknown for now *) 89 - Unknown 61 + (* Get tree hash of subtree directory in monorepo *) 62 + let subtree_tree = Git.rev_parse ~proc ~fs:fs_t ~rev:("HEAD:" ^ prefix) monorepo in 63 + (* Get tree hash of checkout root *) 64 + let checkout_tree = Git.rev_parse ~proc ~fs:fs_t ~rev:"HEAD^{tree}" checkout_dir in 65 + match (subtree_tree, checkout_tree) with 66 + | Ok st, Ok ct when st = ct -> In_sync 67 + | Ok _, Ok _ -> 68 + (* Trees differ - check commit ancestry to determine direction *) 69 + let subtree_commit = 70 + Git.subtree_last_upstream_commit ~proc ~fs:fs_t ~repo:monorepo ~prefix () 71 + in 72 + let checkout_head = Git.head_commit ~proc ~fs:fs_t checkout_dir in 73 + (match (subtree_commit, checkout_head) with 74 + | Some subtree_sha, Ok checkout_sha -> 75 + if Git.is_ancestor ~proc ~fs:fs_t ~repo:checkout_dir 76 + ~commit1:subtree_sha ~commit2:checkout_sha () then 77 + (* Checkout has commits not in subtree - need subtree pull *) 78 + let count = Git.count_commits_between ~proc ~fs:fs_t ~repo:checkout_dir 79 + ~base:subtree_sha ~head:checkout_sha () in 80 + Subtree_behind count 81 + else if Git.is_ancestor ~proc ~fs:fs_t ~repo:checkout_dir 82 + ~commit1:checkout_sha ~commit2:subtree_sha () then 83 + (* Subtree has content not in checkout - need push *) 84 + let count = Git.count_commits_between ~proc ~fs:fs_t ~repo:checkout_dir 85 + ~base:checkout_sha ~head:subtree_sha () in 86 + Subtree_ahead count 87 + else 88 + Unknown 89 + | _ -> Unknown) 90 + | _ -> Unknown 90 91 in 91 92 { package = pkg; checkout; subtree; subtree_sync } 92 93