(
state: AttributionState,
changes: ReadonlyArray<{
path: string
type: 'modified' | 'created' | 'deleted'
oldContent: string
newContent: string
mtime?: number
}>,
)
| 428 | * large git diffs (e.g., jj operations that touch hundreds of thousands of files). |
| 429 | */ |
| 430 | export function trackBulkFileChanges( |
| 431 | state: AttributionState, |
| 432 | changes: ReadonlyArray<{ |
| 433 | path: string |
| 434 | type: 'modified' | 'created' | 'deleted' |
| 435 | oldContent: string |
| 436 | newContent: string |
| 437 | mtime?: number |
| 438 | }>, |
| 439 | ): AttributionState { |
| 440 | // Create ONE copy of the Map, then mutate it for each file |
| 441 | const newFileStates = new Map(state.fileStates) |
| 442 | |
| 443 | for (const change of changes) { |
| 444 | const mtime = change.mtime ?? Date.now() |
| 445 | if (change.type === 'deleted') { |
| 446 | const normalizedPath = normalizeFilePath(change.path) |
| 447 | const existingState = newFileStates.get(normalizedPath) |
| 448 | const existingContribution = existingState?.assistantContribution ?? 0 |
| 449 | const deletedChars = change.oldContent.length |
| 450 | |
| 451 | newFileStates.set(normalizedPath, { |
| 452 | contentHash: '', |
| 453 | assistantContribution: existingContribution + deletedChars, |
| 454 | mtime, |
| 455 | }) |
| 456 | |
| 457 | logForDebugging( |
| 458 | `Attribution: Tracked deletion of ${normalizedPath} (${deletedChars} chars removed, total contribution: ${existingContribution + deletedChars})`, |
| 459 | ) |
| 460 | } else { |
| 461 | const newFileState = computeFileModificationState( |
| 462 | newFileStates, |
| 463 | change.path, |
| 464 | change.oldContent, |
| 465 | change.newContent, |
| 466 | mtime, |
| 467 | ) |
| 468 | if (newFileState) { |
| 469 | const normalizedPath = normalizeFilePath(change.path) |
| 470 | newFileStates.set(normalizedPath, newFileState) |
| 471 | |
| 472 | logForDebugging( |
| 473 | `Attribution: Tracked ${newFileState.assistantContribution} chars for ${normalizedPath}`, |
| 474 | ) |
| 475 | } |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | return { |
| 480 | ...state, |
| 481 | fileStates: newFileStates, |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | /** |
| 486 | * Calculate final attribution for staged files. |
nothing calls this directly
no test coverage detected