镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(vcs): name the branch-already-checked-out collision - #306

Closed
patroza wants to merge 89 commits into
fork/changesfrom
feat/worktrees-never-use-default-branch
Closed

patroza wants to merge 89 commits into
fork/changesfrom
feat/worktrees-never-use-default-branch

Conversation

@patroza

@patroza patroza commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Why

A thread failed to provision with an opaque VCS error. It left thread.created
with worktreePath: null and then nothing at all — no activity, no message,
nothing in the server journal — so there was no way to tell what went wrong from
either the UI or the logs.

The cause is that createWorktree without newRefName checks out an existing
branch by name, and git allows a branch in only one worktree. Three paths
call it that way, passing whatever branch the thread recorded:

path call
GC-recreate (ws.ts:1044) worktree add <path> <existingBranch>
Lifecycle recreate (WorktreeLifecycle.ts:298,356) worktree add <path> <thread.branch>
New thread (ws.ts:1108) worktree add -b <new> <path> <commitSha> — safe

If that branch is the project's default, a primary clone normally has it checked
out, so the collision is reachable in ordinary use. reuseBaseBranch reaches it
deliberately: it checks the selected branch out rather than branching off it, and
for a local ref clears newRefName (ws.ts:1092-94).

What

Detect the collision before running git worktree add and report which branch,
where it is already checked out, and the two ways out.

How

The guard lives in the driver rather than at the three call sites, so it covers
all of them — including any added later — from one place. It reuses
parseWorktreeBranchPaths, which already parses worktree list --porcelain -z
for listRefs, and only runs on the newRefName-less path, so the safe new-thread
path is untouched.

The message follows the shape preparePullRequestThread already uses for exactly
this condition, which until now was the only path that explained it.

Remarks

Deliberately not included: the t3.json policy for default-branch threads.
The agreed design is worktrees.defaultBranch: "branch" | "root" — "branch"
derives a thread branch off the base, "root" runs the thread in the primary
checkout instead of creating a worktree. I drafted the schema and then pulled it
back out, because "root" means two threads can resolve to the same directory
and I did not want to ship a config key whose interesting value is half-wired.
That needs the shared-path guard CheckpointReactor already does
(other.worktreePath === thread.worktreePath) and its own verification, so it is
a separate PR rather than a rushed addition to this one.

This PR is the part that stands alone: it makes the failure legible no matter
which policy lands later.

Flow doc updated: n/a
E2E coverage: none added — see below
Design angles: assumed goal — thread provisioning that fails legibly instead of
silently; pragmatic (guard the primitive, keep every existing behaviour) vs.
greenfield (a worktree never checks out a shared branch at all: always -b from a
resolved SHA, and default-branch work routes to the root checkout). The greenfield
shape is the follow-up above; this PR does not preclude it.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants