feat(worktree): reuse an existing worktree for the branch#2652
Conversation
When starting a worktree task on a branch that exists only on the remote (e.g. a contributor's PR branch not yet checked out locally), confirm with the user and then fetch + check it out into the new worktree, instead of failing with a "branch does not exist" error. - Add remoteBranchExists() and WorktreeManager.createWorktreeForRemoteBranch(). - Add a workspace.checkWorktreeBranch query returning trunk/local/remote-only/missing. - Preflight the branch in the task-creation flow; on "remote-only", show a confirmation dialog and pass allowRemoteBranchCheckout through to the server. Generated-By: PostHog Code Task-Id: 1c8ffc23-3673-4b14-b5b7-50637fca8fb2
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
|
React Doctor could not complete this scan.
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
Problem
Sometimes the branch you want to start a task on already has a worktree — e.g. an overnight agent spun one up, or you created it as a side task. Today, starting a worktree task on such a branch doesn't error:
git worktree addfails, the code catches it, and silently creates a second, detached worktree at that branch's commit. You end up with a duplicate worktree that isn't even on the branch.Changes
When the branch already has a (PostHog-managed) worktree checked out, we now ask instead of duplicating:
workspace.checkWorktreeBranchreports anexistingWorktreePathwhen a worktree under the worktree base path is already on the branch.git worktree add); cancelling aborts before any task is created.reuseExistingWorktreeopt-in from the UI through the saga to the workspace server.The existing-worktree prompt takes priority over the remote-only-branch prompt (a branch with a worktree also exists locally). Scoped to worktrees under the worktree base path, since the task↔worktree association re-derives paths from that base.
How did you test this?
typecheckclean across all changed packages (git/workspace-server/host-router/core/ui).existingWorktreeConfirmStore.test.ts, 5 cases) — passing.Automatic notifications
Created with PostHog Code from a Slack thread