fix(desktop): honor gitignore across Windows path casing

This commit is contained in:
atakan g 2026-07-20 00:12:00 +03:00 committed by Teknium
parent 15317e1fd7
commit 89490ae373
2 changed files with 38 additions and 2 deletions

View file

@ -82,6 +82,33 @@ describe('readProjectDir', () => {
expect(readFileDataUrl).toHaveBeenCalledWith('C:/repo/.gitignore')
})
it('filters gitignored entries when Windows path casing differs across IPC results', async () => {
gitRoot.mockResolvedValue('C:\\Repo')
readDir.mockImplementation(async path => {
if (path === 'c:\\repo\\src') {
return ok([
{ name: 'debug.log', path: 'c:\\repo\\src\\debug.log', isDirectory: false },
{ name: 'keep.ts', path: 'c:\\repo\\src\\keep.ts', isDirectory: false }
])
}
if (path === 'C:/Repo') {
return ok([{ name: '.gitignore', path: 'C:/Repo/.gitignore', isDirectory: false }])
}
if (path === 'C:/Repo/src') {
return ok([])
}
return ok([])
})
readFileDataUrl.mockResolvedValue(dataUrl('src/*.log\n'))
const result = await readProjectDir('c:\\repo\\src', 'c:\\repo')
expect(result.entries.map(entry => entry.name)).toEqual(['keep.ts'])
})
it('does not fetch .gitignore contents when listings do not contain .gitignore', async () => {
gitRoot.mockResolvedValue('/repo')
readDir.mockImplementation(async path => {

View file

@ -32,16 +32,25 @@ function clean(path: string) {
return path.replace(/\\/g, '/').replace(/\/+$/, '') || '/'
}
// Windows path identity is case-insensitive. Fold only comparison keys so the
// relative path returned below keeps the filesystem's original spelling;
// POSIX paths remain case-sensitive.
function comparisonPath(path: string) {
return /^[A-Za-z]:(?:\/|$)/.test(path) || path.startsWith('//') ? path.toLowerCase() : path
}
/** Strict POSIX-style relative path; null if `child` is not inside `root`. */
function relativeTo(root: string, child: string) {
const r = clean(root)
const c = clean(child)
const rKey = comparisonPath(r)
const cKey = comparisonPath(c)
if (c === r) {
if (cKey === rKey) {
return ''
}
return c.startsWith(`${r}/`) ? c.slice(r.length + 1) : null
return cKey.startsWith(`${rKey}/`) ? c.slice(r.length + 1) : null
}
/** Repo-root → repo-root/a → repo-root/a/b → … for every dir between root and `dir`. */