( startPath?: string, )
| 40 | * Memoized per startPath. |
| 41 | */ |
| 42 | export async function resolveGitDir( |
| 43 | startPath?: string, |
| 44 | ): Promise<string | null> { |
| 45 | const cwd = resolve(startPath ?? getCwd()) |
| 46 | const cached = resolveGitDirCache.get(cwd) |
| 47 | if (cached !== undefined) { |
| 48 | return cached |
| 49 | } |
| 50 | |
| 51 | const root = findGitRoot(cwd) |
| 52 | if (!root) { |
| 53 | resolveGitDirCache.set(cwd, null) |
| 54 | return null |
| 55 | } |
| 56 | |
| 57 | const gitPath = join(root, '.git') |
| 58 | try { |
| 59 | const st = await stat(gitPath) |
| 60 | if (st.isFile()) { |
| 61 | // Worktree or submodule: .git is a file with `gitdir: <path>` |
| 62 | // Git strips trailing \n and \r (setup.c read_gitfile_gently). |
| 63 | const content = (await readFile(gitPath, 'utf-8')).trim() |
| 64 | if (content.startsWith('gitdir:')) { |
| 65 | const rawDir = content.slice('gitdir:'.length).trim() |
| 66 | const resolved = resolve(root, rawDir) |
| 67 | resolveGitDirCache.set(cwd, resolved) |
| 68 | return resolved |
| 69 | } |
| 70 | } |
| 71 | // Regular repo: .git is a directory |
| 72 | resolveGitDirCache.set(cwd, gitPath) |
| 73 | return gitPath |
| 74 | } catch { |
| 75 | resolveGitDirCache.set(cwd, null) |
| 76 | return null |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | // --------------------------------------------------------------------------- |
| 81 | // isSafeRefName — validate ref/branch names read from .git/ |
no test coverage detected