| 185 | } |
| 186 | |
| 187 | async function generateDiffFromCommit( |
| 188 | repoPath: string, |
| 189 | commitSha: string, |
| 190 | ): Promise<CommitDiff[]> { |
| 191 | // Get list of files changed in this commit |
| 192 | const changedFiles = execFileSync( |
| 193 | 'git', |
| 194 | ['show', '--name-only', '--pretty=format:', commitSha], |
| 195 | { |
| 196 | cwd: repoPath, |
| 197 | }, |
| 198 | ) |
| 199 | .toString() |
| 200 | .trim() |
| 201 | .split('\n') |
| 202 | .filter(Boolean) |
| 203 | |
| 204 | // Limit number of files to avoid overwhelming the LLM |
| 205 | const MAX_FILES_PER_COMMIT = 30 |
| 206 | const filesToProcess = changedFiles.slice(0, MAX_FILES_PER_COMMIT) |
| 207 | |
| 208 | if (changedFiles.length > MAX_FILES_PER_COMMIT) { |
| 209 | console.log( |
| 210 | `Commit ${commitSha}: Processing ${MAX_FILES_PER_COMMIT} of ${changedFiles.length} changed files`, |
| 211 | ) |
| 212 | } |
| 213 | |
| 214 | // Get the content of each file before and after the commit |
| 215 | const diffs: CommitDiff[] = [] |
| 216 | for (const file of filesToProcess) { |
| 217 | try { |
| 218 | // Get content from parent commit (commit^) |
| 219 | const preContent = execFileSync( |
| 220 | 'git', |
| 221 | ['show', `${commitSha}^:${file}`], |
| 222 | { |
| 223 | cwd: repoPath, |
| 224 | }, |
| 225 | ).toString() |
| 226 | |
| 227 | // Get content after commit |
| 228 | const postContent = execFileSync( |
| 229 | 'git', |
| 230 | ['show', `${commitSha}:${file}`], |
| 231 | { |
| 232 | cwd: repoPath, |
| 233 | }, |
| 234 | ).toString() |
| 235 | |
| 236 | diffs.push({ |
| 237 | path: file, |
| 238 | preContent, |
| 239 | postContent, |
| 240 | }) |
| 241 | } catch (error) { |
| 242 | // File might not exist in parent commit (new file) or might be deleted |
| 243 | try { |
| 244 | const postContent = execFileSync( |