diff --git a/src/relay/fs-handler.ts b/src/relay/fs-handler.ts index c9f358fd325..bb2ca22974c 100644 --- a/src/relay/fs-handler.ts +++ b/src/relay/fs-handler.ts @@ -8,10 +8,10 @@ import { rename, cp, rm, - realpath, - access + realpath } from 'fs/promises' -import { extname, join } from 'path' +import { extname } from 'path' +import { execFile } from 'child_process' import type { RelayDispatcher } from './dispatcher' import type { RelayContext } from './context' import { expandTilde } from './context' @@ -233,13 +233,16 @@ export class FsHandler { if (rgAvailable) { return listFilesWithRg(rootPath) } - // Why: git ls-files only works inside git repos. Check for .git first so - // we don't misclassify a git repo with only ignored files as a non-git - // folder — that would surface .gitignore'd files via the readdir fallback. - const isGitRepo = await access(join(rootPath, '.git')).then( - () => true, - () => false - ) + // Why: git ls-files only works inside git repos. Use rev-parse to detect + // git ancestry — unlike checking for a local .git entry, this works from + // subdirectories of a checkout (e.g. /repo/packages/app added as a folder). + // Without this, a git subdirectory would fall through to readdir and + // surface .gitignore'd build artifacts. + const isGitRepo = await new Promise((resolve) => { + execFile('git', ['rev-parse', '--is-inside-work-tree'], { cwd: rootPath }, (err) => + resolve(!err) + ) + }) if (isGitRepo) { return listFilesWithGit(rootPath) }