π File detail
utils/git/gitignore.ts
π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes isPathGitignored, getGlobalGitignorePath, and addFileGlobRuleToGitignore β mainly functions, hooks, or classes. Dependencies touch Node filesystem, Node OS/process metadata, and Node path helpers. It composes internal code from cwd, errors, execFileNoThrow, git, and log (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import { appendFile, mkdir, readFile, writeFile } from 'fs/promises' import { homedir } from 'os' import { dirname, join } from 'path' import { getCwd } from '../cwd.js' import { getErrnoCode } from '../errors.js'
π€ Exports (heuristic)
isPathGitignoredgetGlobalGitignorePathaddFileGlobRuleToGitignore
π External import roots
Package roots from from "β¦" (relative paths omitted).
fsospath
π₯οΈ Source preview
import { appendFile, mkdir, readFile, writeFile } from 'fs/promises'
import { homedir } from 'os'
import { dirname, join } from 'path'
import { getCwd } from '../cwd.js'
import { getErrnoCode } from '../errors.js'
import { execFileNoThrowWithCwd } from '../execFileNoThrow.js'
import { dirIsInGitRepo } from '../git.js'
import { logError } from '../log.js'
/**
* Checks if a path is ignored by git (via `git check-ignore`).
*
* This consults all applicable gitignore sources: repo `.gitignore` files
* (nested), `.git/info/exclude`, and the global gitignore β with correct
* precedence, because git itself resolves it.
*
* Exit codes: 0 = ignored, 1 = not ignored, 128 = not in a git repo.
* Returns `false` for 128, so callers outside a git repo fail open.
*
* @param filePath The path to check (absolute or relative to cwd)
* @param cwd The working directory to run git from
*/
export async function isPathGitignored(
filePath: string,
cwd: string,
): Promise<boolean> {
const { code } = await execFileNoThrowWithCwd(
'git',
['check-ignore', filePath],
{
preserveOutputOnError: false,
cwd,
},
)
return code === 0
}
/**
* Gets the path to the global gitignore file (.config/git/ignore)
* @returns The path to the global gitignore file
*/
export function getGlobalGitignorePath(): string {
return join(homedir(), '.config', 'git', 'ignore')
}
/**
* Adds a file pattern to the global gitignore file (.config/git/ignore)
* if it's not already ignored by existing patterns in any gitignore file
* @param filename The filename to add to gitignore
* @param cwd The current working directory (optional)
*/
export async function addFileGlobRuleToGitignore(
filename: string,
cwd: string = getCwd(),
): Promise<void> {
try {
if (!(await dirIsInGitRepo(cwd))) {
return
}
// First check if the pattern is already ignored by any gitignore file (including global)
const gitignoreEntry = `**/${filename}`
// For directory patterns (ending with /), check with a sample file inside
const testPath = filename.endsWith('/')
? `${filename}sample-file.txt`
: filename
if (await isPathGitignored(testPath, cwd)) {
// File is already ignored by existing patterns (local or global)
return
}
// Use the global gitignore file in .config/git/ignore
const globalGitignorePath = getGlobalGitignorePath()
// Create the directory if it doesn't exist
const configGitDir = dirname(globalGitignorePath)
await mkdir(configGitDir, { recursive: true })
// Add the entry to the global gitignore
try {
const content = await readFile(globalGitignorePath, { encoding: 'utf-8' })
if (content.includes(gitignoreEntry)) {
return // Pattern already exists, don't add again
}
await appendFile(globalGitignorePath, `\n${gitignoreEntry}\n`)
} catch (e: unknown) {
const code = getErrnoCode(e)
if (code === 'ENOENT') {
// Create global gitignore with entry
await writeFile(globalGitignorePath, `${gitignoreEntry}\n`, 'utf-8')
} else {
throw e
}
}
} catch (error) {
logError(error)
}
}