( stdout: string, )
| 198 | * - Files ≤1MB: parsed but limited to MAX_LINES_PER_FILE lines |
| 199 | */ |
| 200 | export function parseGitDiff( |
| 201 | stdout: string, |
| 202 | ): Map<string, StructuredPatchHunk[]> { |
| 203 | const result = new Map<string, StructuredPatchHunk[]>() |
| 204 | if (!stdout.trim()) return result |
| 205 | |
| 206 | // Split by file diffs |
| 207 | const fileDiffs = stdout.split(/^diff --git /m).filter(Boolean) |
| 208 | |
| 209 | for (const fileDiff of fileDiffs) { |
| 210 | // Stop after MAX_FILES |
| 211 | if (result.size >= MAX_FILES) break |
| 212 | |
| 213 | // Skip files larger than 1MB |
| 214 | if (fileDiff.length > MAX_DIFF_SIZE_BYTES) { |
| 215 | continue |
| 216 | } |
| 217 | |
| 218 | const lines = fileDiff.split('\n') |
| 219 | |
| 220 | // Extract filename from first line: "a/path/to/file b/path/to/file" |
| 221 | const headerMatch = lines[0]?.match(/^a\/(.+?) b\/(.+)$/) |
| 222 | if (!headerMatch) continue |
| 223 | const filePath = headerMatch[2] ?? headerMatch[1] ?? '' |
| 224 | |
| 225 | // Find and parse hunks |
| 226 | const fileHunks: StructuredPatchHunk[] = [] |
| 227 | let currentHunk: StructuredPatchHunk | null = null |
| 228 | let lineCount = 0 |
| 229 | |
| 230 | for (let i = 1; i < lines.length; i++) { |
| 231 | const line = lines[i] ?? '' |
| 232 | |
| 233 | // StructuredPatchHunk header: @@ -oldStart,oldLines +newStart,newLines @@ |
| 234 | const hunkMatch = line.match( |
| 235 | /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/, |
| 236 | ) |
| 237 | if (hunkMatch) { |
| 238 | if (currentHunk) { |
| 239 | fileHunks.push(currentHunk) |
| 240 | } |
| 241 | currentHunk = { |
| 242 | oldStart: parseInt(hunkMatch[1] ?? '0', 10), |
| 243 | oldLines: parseInt(hunkMatch[2] ?? '1', 10), |
| 244 | newStart: parseInt(hunkMatch[3] ?? '0', 10), |
| 245 | newLines: parseInt(hunkMatch[4] ?? '1', 10), |
| 246 | lines: [], |
| 247 | } |
| 248 | continue |
| 249 | } |
| 250 | |
| 251 | // Skip binary file markers and other metadata |
| 252 | if ( |
| 253 | line.startsWith('index ') || |
| 254 | line.startsWith('---') || |
| 255 | line.startsWith('+++') || |
| 256 | line.startsWith('new file') || |
| 257 | line.startsWith('deleted file') || |
no test coverage detected