(
updateFileHistoryState: (
updater: (prev: FileHistoryState) => FileHistoryState,
) => void,
filePath: string,
messageId: UUID,
)
| 84 | * its contents before the edit. |
| 85 | */ |
| 86 | export async function fileHistoryTrackEdit( |
| 87 | updateFileHistoryState: ( |
| 88 | updater: (prev: FileHistoryState) => FileHistoryState, |
| 89 | ) => void, |
| 90 | filePath: string, |
| 91 | messageId: UUID, |
| 92 | ): Promise<void> { |
| 93 | if (!fileHistoryEnabled()) { |
| 94 | return |
| 95 | } |
| 96 | |
| 97 | const trackingPath = maybeShortenFilePath(filePath) |
| 98 | |
| 99 | // Phase 1: check if backup is needed. Speculative writes would overwrite |
| 100 | // the deterministic {hash}@v1 backup on every repeat call — a second |
| 101 | // trackEdit after an edit would corrupt v1 with post-edit content. |
| 102 | let captured: FileHistoryState | undefined |
| 103 | updateFileHistoryState(state => { |
| 104 | captured = state |
| 105 | return state |
| 106 | }) |
| 107 | if (!captured) return |
| 108 | const mostRecent = captured.snapshots.at(-1) |
| 109 | if (!mostRecent) { |
| 110 | logError(new Error('FileHistory: Missing most recent snapshot')) |
| 111 | logEvent('tengu_file_history_track_edit_failed', {}) |
| 112 | return |
| 113 | } |
| 114 | if (mostRecent.trackedFileBackups[trackingPath]) { |
| 115 | // Already tracked in the most recent snapshot; next makeSnapshot will |
| 116 | // re-check mtime and re-backup if changed. Do not touch v1 backup. |
| 117 | return |
| 118 | } |
| 119 | |
| 120 | // Phase 2: async backup. |
| 121 | let backup: FileHistoryBackup |
| 122 | try { |
| 123 | backup = await createBackup(filePath, 1) |
| 124 | } catch (error) { |
| 125 | logError(error) |
| 126 | logEvent('tengu_file_history_track_edit_failed', {}) |
| 127 | return |
| 128 | } |
| 129 | const isAddingFile = backup.backupFileName === null |
| 130 | |
| 131 | // Phase 3: commit. Re-check tracked (another trackEdit may have raced). |
| 132 | updateFileHistoryState((state: FileHistoryState) => { |
| 133 | try { |
| 134 | const mostRecentSnapshot = state.snapshots.at(-1) |
| 135 | if ( |
| 136 | !mostRecentSnapshot || |
| 137 | mostRecentSnapshot.trackedFileBackups[trackingPath] |
| 138 | ) { |
| 139 | return state |
| 140 | } |
| 141 | |
| 142 | // This file has not already been tracked in the most recent snapshot, so we |
| 143 | // need to retroactively track a backup there. |
no test coverage detected