Enforce one Space per Git repository with worktree adoption - #4
Merged
Merged
Conversation
"Add Folder…" on a folder that is a linked worktree of a repository already open as a Space now adds it to that Space as a linked workspace instead of creating a second Space for the same repository. Repository identity comes from libgit2's common `.git` directory, which the main working tree and every linked worktree share; when both are open, the main working tree wins, since its folder is what worktree operations run against. Nothing is created on disk — the branch and worktree already exist — so no `setup` hook runs; the workspace takes the worktree's branch as its name and the Space's primary branch as its merge base. Re-adding a folder Casper already tracks now selects it rather than being silently dropped. Deleting such a workspace resolves the worktree's registered admin-entry name by path instead of assuming it matches the branch, so pruning an adopted worktree leaves no dangling `.git/worktrees` entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9NWyCjBz5QJd37EP1n2YJ
The mirror image of worktree adoption: opening a folder that is a Git repository whose worktrees are already open as Spaces of their own folds those Spaces into the one being created, so a repository is a single Space either way round — whichever of its working trees the user adds first. The absorbed workspaces move whole: same ids, ports, layouts and live terminals, nothing torn down or respawned. Only the fields that make a workspace linked are normalized — each absorbed Space's own worktree stops being a primary and takes its branch as its name (a Space is named after its repository, which would merely duplicate the new primary's name), and a workspace with no base branch of its own inherits the new primary's, while one that already recorded a base keeps it. The Space list is rewritten in a single assignment, so a reunified workspace is never momentarily absent from the model. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9NWyCjBz5QJd37EP1n2YJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement the core grouping logic for Git worktrees: a folder that is a linked worktree of a repository already open as a Space is now adopted into that Space as a linked workspace, and opening a repository whose worktrees are already open as Spaces reunifies them into a single Space.
Summary
This change enforces the invariant that Casper maintains one Space per Git repository, identified by the repository's common
.gitdirectory (shared by all working trees). The implementation handles three scenarios:Key changes
Repository identity: Extended
Repositoryto exposecommonDirPath(the shared.gitdirectory) andisLinkedWorktreeflag via libgit2. UpdatedWorkspaceFactory.GitInfoto carry these fields for routing decisions.Worktree adoption:
AppModel.addSpacenow detects when a folder is a linked worktree of an open repository and callsadoptWorktreeto add it as a linked workspace instead of creating a new Space. The adopted workspace is named after its branch, inherits the primary workspace's branch as its base, and runs nosetuphook (the worktree already exists).Reunification: When opening a repository,
addSpaceidentifies any Spaces rooted at that repository's worktrees viaworktreeSpacesand folds them into the new Space viareunify. Absorbed workspaces move whole; an ex-primary becomes a linked workspace named after its branch, and workspaces with no recorded base branch inherit the primary's branch as their base (those with a recorded base keep it).Deduplication:
trackedWorkspaceIDchecks both Space roots and workspace worktree paths (canonicalized to handle symlink variations) and selects the workspace instead of adding a duplicate.Worktree removal:
WorktreeManager.registeredNameresolves a worktree's admin entry by path, so removing an adopted worktree (which may carry a name other than its branch) finds the correct entry to prune.Implementation details
https://claude.ai/code/session_01K9NWyCjBz5QJd37EP1n2YJ