diff --git a/src/main/ipc/worktree-head-identity-cached-paths.test.ts b/src/main/ipc/worktree-head-identity-cached-paths.test.ts new file mode 100644 index 00000000000..a45130dcecf --- /dev/null +++ b/src/main/ipc/worktree-head-identity-cached-paths.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest' +import { cachedWorktreePathsForScope } from './worktree-head-identity-cached-paths' +import { createWorktreeHeadIdentityCache } from './worktree-head-identity-reader' +import { headIdentityScopeForEntry } from './worktree-head-identity-scope' + +const HEAD = 'a'.repeat(40) + +describe('cachedWorktreePathsForScope', () => { + it('names every checkout whose admin dir folds to the same key', () => { + // Case-sensitive filesystems let Git create both `Foo` and `foo` admin dirs. + const cache = createWorktreeHeadIdentityCache() + cache.entries.set('Foo', { worktreePath: '/a/Foo', head: HEAD, branch: 'refs/heads/x' }) + cache.entries.set('foo', { worktreePath: '/b/foo', head: HEAD, branch: 'refs/heads/y' }) + + const paths = cachedWorktreePathsForScope(cache, headIdentityScopeForEntry('Foo')) + + expect(paths).toEqual(expect.arrayContaining(['/a/Foo', '/b/foo'])) + }) + + it('stays unattributed when an entry is not in the memo', () => { + const cache = createWorktreeHeadIdentityCache() + expect(cachedWorktreePathsForScope(cache, headIdentityScopeForEntry('wt'))).toBeNull() + }) +}) diff --git a/src/main/ipc/worktree-head-identity-cached-paths.ts b/src/main/ipc/worktree-head-identity-cached-paths.ts index 4796e3b8400..c0375509ed5 100644 --- a/src/main/ipc/worktree-head-identity-cached-paths.ts +++ b/src/main/ipc/worktree-head-identity-cached-paths.ts @@ -20,16 +20,19 @@ export function cachedWorktreePathsForScope( } paths.push(cache.primary.worktreePath) } - const pathsByEntryKey = new Map() + // Why: keys fold case, so on case-sensitive filesystems `Foo` and `foo` are + // distinct admin dirs sharing one key; name both rather than drop the writer. + const pathsByEntryKey = new Map() for (const [name, identity] of cache.entries) { - pathsByEntryKey.set(headIdentityEntryKey(name), identity.worktreePath) + const key = headIdentityEntryKey(name) + pathsByEntryKey.set(key, [...(pathsByEntryKey.get(key) ?? []), identity.worktreePath]) } for (const key of scope.entryNames) { - const path = pathsByEntryKey.get(key) - if (path === undefined) { + const entryPaths = pathsByEntryKey.get(key) + if (entryPaths === undefined) { return null } - paths.push(path) + paths.push(...entryPaths) } return paths }