feat(tree): add BranchCheckout for viewless, branch-bound checkouts#27217
feat(tree): add BranchCheckout for viewless, branch-bound checkouts#27217jzaffiro wants to merge 2 commits intomicrosoft:mainfrom
Conversation
Introduces `BranchCheckout`, a TreeCheckout subclass that is permanently bound to its underlying SharedTreeBranch, with a module-level WeakMap registry (`getBranchCheckout(branch)`) and a `forkAsBranchCheckout(parent)` helper. Refactors `TreeCheckout.fork` into a generic `forkWith<T>(ctor)` so subclasses can participate in forking without duplicating the fork machinery; the `@throwIfBroken` guard lives on `forkWith` so every entry point — including subclass overrides and `forkAsBranchCheckout` — runs the broken-state check. `BranchCheckout` overrides `[disposeSymbol]` (rather than `dispose`) to clean up the registry on both explicit disposal and the merge auto-dispose path. `switchBranch` is overridden to throw, with the parameter preserved to keep the signature LSP-compatible with the base class. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (422 lines, 4 files), I've queued these reviewers:
How this works
|
|
On it! Starting review with: Correctness, Security, API Compatibility, Testing |
🔭 PR Review Fleet ReportNote This report is generated by an experimental AI review fleet and is provided as a beta feature. Findings are a starting point for discussion, not a gate. Use your own judgement. Verdict: ❌ Request Changes 0 Alert, 1 Stop, 4 Caution Findings
|
Description
Adds
BranchCheckout, aTreeCheckoutsubclass that is permanently bound to theSharedTreeBranchit was created over. Companion helpersforkAsBranchCheckout(parent)(fork any checkout into a viewless branch checkout) andgetBranchCheckout(branch)(look up the canonicalBranchCheckoutfor a given branch via a module-levelWeakMap) round out the surface. All three are package-internal (@internal).To make this work cleanly,
TreeCheckout.forkis refactored into a genericforkWith<T>(ctor), which subclasses (andforkAsBranchCheckout) call directly. The@throwIfBrokenguard now sits onforkWithrather than onfork, so every fork entry point — including subclass overrides — runs the broken-state check.BranchCheckoutoverrides[disposeSymbol](notdispose) so that both explicit disposal and the merge auto-dispose path (TreeCheckout.mergecallscheckout[disposeSymbol]()directly) clear the registry entry.switchBranchis overridden to throw aUsageError, with the base parameter preserved to keep the override signature-compatible withTreeCheckout.Reviewer Guidance
The review process is outlined on this wiki page.
@internal— no public API surface change, no*.api.mddiffs.WeakMapregistry live on the SharedTree instance instead of the module? Module-global is simpler and works because branch identities are unique across trees, but happy to refactor if you prefer per-tree isolation.treeCheckout.ts:322-323(createTreeCheckoutalways setsisSharedBranch=true) that affects howforkAsBranchCheckoutinteracts with views fromindependentView— out of scope here, but worth a follow-up.branchCheckout.spec.tscover construction, viewlessness, permanence, edits/merges, dispose semantics (including the merge auto-dispose registry cleanup), broken-state propagation, chained forking, anddisposeForksAfterTransactionpropagation.