( repoPath: string, args: string[], options?: ExecGitOptions )
| 139 | * All git operations go through this single entry point for uniform error handling. |
| 140 | */ |
| 141 | export async function execGit( |
| 142 | repoPath: string, |
| 143 | args: string[], |
| 144 | options?: ExecGitOptions |
| 145 | ): Promise<string> { |
| 146 | try { |
| 147 | const { stdout } = await execFileAsync('git', args, { |
| 148 | cwd: repoPath, |
| 149 | maxBuffer: 10 * 1024 * 1024, // 10 MB |
| 150 | timeout: options?.timeout ?? 30_000, |
| 151 | encoding: 'utf-8', |
| 152 | env: options?.optionalLocks === false |
| 153 | ? { ...process.env, GIT_OPTIONAL_LOCKS: '0' } |
| 154 | : undefined, |
| 155 | }); |
| 156 | return stdout; |
| 157 | } catch (err: unknown) { |
| 158 | const error = err as { stderr?: string; message?: string; code?: string | number }; |
| 159 | const stderr = error.stderr ?? error.message ?? 'Unknown git error'; |
| 160 | throw new GitError( |
| 161 | `git ${args.join(' ')} failed: ${stderr.trim()}`, |
| 162 | typeof error.code === 'string' ? error.code : 'GIT_EXEC_ERROR' |
| 163 | ); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Check that the `git` binary is available on PATH. |
no test coverage detected