diff --git a/extensions/vscode/src/extension/services/__tests__/gitMap.test.ts b/extensions/vscode/src/extension/services/__tests__/gitMap.test.ts index b8eb991a..20a873a4 100644 --- a/extensions/vscode/src/extension/services/__tests__/gitMap.test.ts +++ b/extensions/vscode/src/extension/services/__tests__/gitMap.test.ts @@ -205,6 +205,21 @@ describe('pickRepoRoot', () => { const roots = ['/a/repo', '/b/repo']; expect(pickRepoRoot(roots, undefined)).toBe('/a/repo'); }); + + it('selects a Windows repository containing the workspace subdirectory', () => { + const roots = ['C:\\other', 'C:\\repo']; + expect(pickRepoRoot(roots, 'C:\\repo\\packages\\app')).toBe('C:\\repo'); + }); + + it('does not treat a Windows path with the same prefix as an ancestor', () => { + const roots = ['C:\\repo', 'C:\\repository']; + expect(pickRepoRoot(roots, 'C:\\repository\\src')).toBe('C:\\repository'); + }); + + it('handles mixed separators between a UNC root and workspace path', () => { + const roots = ['//server/share/other', '//server/share/repo']; + expect(pickRepoRoot(roots, '\\\\server\\share\\repo\\src')).toBe('//server/share/repo'); + }); }); describe('getCommitFiles: git show revision placement', () => { diff --git a/extensions/vscode/src/extension/services/gitMap.ts b/extensions/vscode/src/extension/services/gitMap.ts index 68b3e21b..6fbc2932 100644 --- a/extensions/vscode/src/extension/services/gitMap.ts +++ b/extensions/vscode/src/extension/services/gitMap.ts @@ -1,4 +1,5 @@ import { FileChange } from '../../shared/types'; +import path from 'path'; export function mapStatusCode(code: string): FileChange['status'] { switch (code) { @@ -87,12 +88,20 @@ export function pickRepoRoot(roots: string[], workspacePath?: string): string | if (roots.length === 0) return null; if (!workspacePath) return roots[0]; - const exact = roots.find((r) => r === workspacePath); - if (exact) return exact; + const candidates = roots.map((root) => { + const isWindowsPath = [root, workspacePath].some((p) => /^[a-z]:[\\/]/i.test(p) || p.includes('\\')); + const pathApi = isWindowsPath ? path.win32 : path.posix; + return { root, relative: pathApi.relative(root, workspacePath), pathApi }; + }); - const ancestors = roots.filter((r) => workspacePath.startsWith(r.endsWith('/') ? r : r + '/')); + const exact = candidates.find(({ relative }) => relative === ''); + if (exact) return exact.root; + + const ancestors = candidates.filter(({ relative, pathApi }) => + relative !== '..' && !relative.startsWith(`..${pathApi.sep}`) && !pathApi.isAbsolute(relative)); if (ancestors.length > 0) { - return ancestors.reduce((deepest, r) => (r.length > deepest.length ? r : deepest)); + return ancestors.reduce((deepest, candidate) => + candidate.relative.length < deepest.relative.length ? candidate : deepest).root; } return roots[0];