fix(worktree): reuse an existing worktree instead of duplicating it#2652
fix(worktree): reuse an existing worktree instead of duplicating it#2652haacked wants to merge 6 commits into
Conversation
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
0f66f43 to
7bfb8e9
Compare
4086085 to
a70b01e
Compare
be1a58a to
f73ce30
Compare
…ved from name Make the persisted `worktrees.path` column the source of truth for a task's worktree path. The `TaskAssociation` worktree variant now carries the stored `path` alongside the cosmetic `name`, and every read/delete/occupancy path in WorkspaceService uses it instead of recomputing `<base>/<name>/<repo>` via the private `deriveWorktreePath` helper (now removed here). Guard `cleanupRepoWorktreeFolder` so it only reclaims the managed `<base>/<repo>` parent folder when the deleted worktree actually lived under the managed base path. Externally located worktrees never created it, so their parent dirs are left untouched. Behavioral no-op for app-created worktrees: their stored `path` already equals the previously derived path, so no data migration is needed. The legacy-layout name-recovery hack the original scope referenced (findExistingWorktreeForBranch / finalSegment) no longer exists in the tree, so that item produced no diff.
Fold the now-redundant folderPath guard in getWorktreeInfo's worktree branch into the branch condition, matching the sibling local branch. Parameterise the two delete-guard tests into a single it.each table since they share setup and differ only by whether the stored path is under the base path.
Adds test case verifying that verifyWorkspaceExists correctly uses the stored external path rather than deriving it from the worktree name. Also improves comments in cleanupRepoWorktreeFolder for clarity.
When starting a worktree task on a branch that already has a worktree checked out, confirm with the user and reuse that worktree for the task instead of silently creating a second, detached worktree at the same commit. - checkWorktreeBranch now reports an existingWorktreePath when a PostHog-managed worktree (under the worktree base path) is already on the branch. - The task-creation preflight prompts on that and passes reuseExistingWorktree through to the server, which registers the task against the existing worktree rather than running `git worktree add`. Stacked on the remote-only-branch checkout change; base that PR first. Generated-By: PostHog Code Task-Id: 1c8ffc23-3673-4b14-b5b7-50637fca8fb2
Discovery for worktree reuse was limited to worktrees under the managed base path, so starting a task on a branch already checked out in a hand-created worktree elsewhere fell through to a detached duplicate. Add listLinkedWorktrees (every linked worktree, any location) and use it in findExistingWorktreeForBranch. The stored path column is authoritative since the path refactor, so an external worktree round-trips like a managed one. listTwigWorktrees stays for managed-only listing.
c92c7c2 to
5362c14
Compare
Problem
Starting a worktree task on a branch that already has a worktree didn't error. It silently went wrong.
git worktree addfails, the code catches it, and creates a second, detached worktree at the branch's commit. You end up with a junk duplicate that isn't even on the branch.Fix
Handle the already-has-a-worktree case correctly instead of duplicating, for a worktree in any location — including one you created by hand outside PostHog's managed base path. When the branch already has a worktree, we look at whether a task is already using it:
git worktree add); cancelling aborts before any task is created. The dialog warns that deleting the task later removes the worktree and any uncommitted work in it, even though it existed beforehand.Either way, every worktree stays associated with at most one task — the same as before this change.
How it works:
workspace.checkWorktreeBranchreportsexistingWorktreePathonly for an unused worktree on the branch, andexistingWorktreeTaskIdwhen a task already holds it (mutually exclusive).reuseExistingWorktreeopt-in threads from the UI through the saga to the workspace server;doCreateWorkspacere-checks occupancy before reusing and fails the step if the worktree was claimed between preflight and create.listLinkedWorktrees— every linked worktree for the repo, in any location (only the main repo is excluded). It is not limited to the managed base path. The storedpathcolumn is the source of truth for a task's worktree (since the path refactor in refactor(worktree): resolve worktree path from stored value, not derived from name #2709), so an externally-created worktree round-trips just like a managed one.listTwigWorktreesstays in place for the managed-only worktree listing (listGitWorktrees).nameis a cosmetic label only; it is recovered layout-aware for managed worktrees and degrades to the path's final segment for an external one.The unused-worktree prompt takes priority over the remote-only-branch prompt (a branch with a worktree also exists locally).
Note
Stacked on #2709 (resolve worktree path from the stored value, not derived from name) — that refactor makes the stored
pathauthoritative, which is what lets a worktree outside the base path attach to a task. Base/merge that PR first; this PR targets its branch.How did you test this?
pnpm --filter @posthog/workspace-server testfor the touched suites:workspace.testandworktree-query.test— 32 passing, including a newcheckWorktreeBranchcase that offers an out-of-base-path worktree for reuse and newlistLinkedWorktreescoverage (keeps worktrees in any location, excludes the main repo).typecheckclean across all changed packages (workspace-server / host-router / ui / core / shared).existingWorktreeConfirmStore.test.ts, 5 cases).Automatic notifications
Created with PostHog Code from a Slack thread