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