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 3 commits into
Conversation
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
| return { | ||
| worktreePath: match.worktreePath, | ||
| worktreeName: path.basename(path.dirname(match.worktreePath)), | ||
| branchName: branch, | ||
| baseBranch: branch, | ||
| createdAt: new Date().toISOString(), | ||
| }; |
There was a problem hiding this comment.
Wrong
worktreeName for legacy-layout worktrees
path.basename(path.dirname(match.worktreePath)) extracts the correct name only for the new layout <base>/<name>/<repo>. For legacy layout <base>/<repo>/<name> (still handled by deriveWorktreePath) this expression yields the repository name, not the worktree name.
That incorrect name is stored via worktreeRepo.create({ name: worktree.worktreeName }) and is later fed back into deriveWorktreePath(folderPath, assoc.worktree), which would reconstruct the path as <base>/<repoName>/<repoName> — a path that does not exist — causing every subsequent verifyWorkspaceExists, getWorkspace, and getWorkspaceEnv call to fail for that task.
A reliable extraction: if the final component of match.worktreePath equals path.basename(mainRepoPath) it's the new layout and the name is path.basename(path.dirname(…)); otherwise it's the legacy layout and the name is path.basename(match.worktreePath).
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/workspace-server/src/services/workspace/workspace.ts
Line: 479-485
Comment:
**Wrong `worktreeName` for legacy-layout worktrees**
`path.basename(path.dirname(match.worktreePath))` extracts the correct name only for the *new* layout `<base>/<name>/<repo>`. For *legacy* layout `<base>/<repo>/<name>` (still handled by `deriveWorktreePath`) this expression yields the repository name, not the worktree name.
That incorrect name is stored via `worktreeRepo.create({ name: worktree.worktreeName })` and is later fed back into `deriveWorktreePath(folderPath, assoc.worktree)`, which would reconstruct the path as `<base>/<repoName>/<repoName>` — a path that does not exist — causing every subsequent `verifyWorkspaceExists`, `getWorkspace`, and `getWorkspaceEnv` call to fail for that task.
A reliable extraction: if the final component of `match.worktreePath` equals `path.basename(mainRepoPath)` it's the new layout and the name is `path.basename(path.dirname(…))`; otherwise it's the legacy layout and the name is `path.basename(match.worktreePath)`.
How can I resolve this? If you propose a fix, please make it concise.0f66f43 to
7bfb8e9
Compare
4086085 to
a70b01e
Compare
25c8d6a to
be1a58a
Compare
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
be1a58a to
f73ce30
Compare
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.
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