diff --git a/src/main/runtime/repo-worktree-row-resolution.test.ts b/src/main/runtime/repo-worktree-row-resolution.test.ts index be50f5678a7..0e2ee14e980 100644 --- a/src/main/runtime/repo-worktree-row-resolution.test.ts +++ b/src/main/runtime/repo-worktree-row-resolution.test.ts @@ -61,6 +61,39 @@ function createDeps(repos: Repo[]): RepoWorktreeRowDeps & { return { store, metaById, scanRepo, listFolderWorkspaces } } +describe('path-equal worktree row collapse', () => { + it('keeps one row and prefers the git branch when the same path is listed twice', async () => { + const owner = repo('repo', '/home/me/repo') + const deps = createDeps([owner]) + deps.scanRepo.mockResolvedValueOnce({ + ok: true, + worktrees: [ + { + path: '/home/me/repo', + head: '', + branch: '', + isBare: false, + isMainWorktree: true + }, + { + path: '/home/me/./repo', + head: 'abc123', + branch: 'refs/heads/master', + isBare: false, + isMainWorktree: true + } + ] + }) + + const rows = await resolveRepoWorktreeRows(deps, owner, deps.metaById, new Map()) + expect(rows).toHaveLength(1) + expect(rows[0]).toMatchObject({ + branch: 'refs/heads/master', + path: '/home/me/./repo' + }) + }) +}) + describe('host-qualified scoped worktree resolution', () => { it('scans only the selected local or SSH owner when repo ids and paths collide', async () => { const owners = [ diff --git a/src/main/runtime/repo-worktree-row-resolution.ts b/src/main/runtime/repo-worktree-row-resolution.ts index b65fd9295f6..c64c5aff965 100644 --- a/src/main/runtime/repo-worktree-row-resolution.ts +++ b/src/main/runtime/repo-worktree-row-resolution.ts @@ -10,6 +10,7 @@ import type { WorktreeMeta } from '../../shared/worktree/meta-types' import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime' import type { Store } from '../persistence' import { areWorktreePathsEqual, mergeWorktree } from '../ipc/worktree-logic' +import { dedupeWorktreesByPath } from '../ipc/worktree-path-comparison' import { pruneLineageForMissingRepoWorktrees } from '../worktree-lineage-pruning' import { getRepoOwnedWorktreeMeta } from '../worktree-metadata-ownership' import { resolveLocalProjectRuntimesForRepos } from '../project-runtime-git-options' @@ -121,7 +122,12 @@ export async function resolveRepoWorktreeRows( RESOLVED_WORKTREE_REPO_TIMEOUT_MS, null )) ?? { ok: false, worktrees: listStoredWorktreeRowsForRepo(store, repo, repoOwnerCount) } - const gitWorktrees = scan.worktrees + const gitWorktrees = dedupeWorktreesByPath( + // Why: keep the git-scanned branch over a stored empty-branch clone of the same path (#15526). + [...scan.worktrees].sort( + (left, right) => Number(Boolean(right.branch)) - Number(Boolean(left.branch)) + ) + ) if (scan.ok) { pruneLineageForMissingRepoWorktrees(store, repo, gitWorktrees) }