* Get files that have changed since last index. * Uses git status as a fast path when available, falling back to full scan.
()
| 2078 | * Uses git status as a fast path when available, falling back to full scan. |
| 2079 | */ |
| 2080 | getChangedFiles(): { added: string[]; modified: string[]; removed: string[] } { |
| 2081 | const gitChanges = getGitChangedFiles(this.rootDir); |
| 2082 | |
| 2083 | if (gitChanges) { |
| 2084 | // === Git fast path === |
| 2085 | const added: string[] = []; |
| 2086 | const modified: string[] = []; |
| 2087 | const removed: string[] = []; |
| 2088 | |
| 2089 | // Deleted files — only report if tracked in DB |
| 2090 | for (const filePath of gitChanges.deleted) { |
| 2091 | const tracked = this.queries.getFileByPath(filePath); |
| 2092 | if (tracked) { |
| 2093 | removed.push(filePath); |
| 2094 | } |
| 2095 | } |
| 2096 | |
| 2097 | // Modified + added files — read + hash, compare with DB. Untracked (`??`) |
| 2098 | // files stay untracked in git even after indexing, so they must be |
| 2099 | // hash-compared like modified files instead of always counting as added — |
| 2100 | // otherwise status reports them as pending forever. (See issue #206.) |
| 2101 | for (const filePath of [...gitChanges.modified, ...gitChanges.added]) { |
| 2102 | const fullPath = path.join(this.rootDir, filePath); |
| 2103 | let content: string; |
| 2104 | try { |
| 2105 | content = fs.readFileSync(fullPath, 'utf-8'); |
| 2106 | } catch (error) { |
| 2107 | logDebug('Skipping unreadable file while detecting changes', { filePath, error: String(error) }); |
| 2108 | continue; |
| 2109 | } |
| 2110 | |
| 2111 | const contentHash = hashContent(content); |
| 2112 | const tracked = this.queries.getFileByPath(filePath); |
| 2113 | |
| 2114 | if (!tracked) { |
| 2115 | added.push(filePath); |
| 2116 | } else if (tracked.contentHash !== contentHash) { |
| 2117 | modified.push(filePath); |
| 2118 | } |
| 2119 | } |
| 2120 | |
| 2121 | return { added, modified, removed }; |
| 2122 | } |
| 2123 | |
| 2124 | // === Fallback: full scan (non-git project or git failure) === |
| 2125 | const currentFiles = new Set(scanDirectory(this.rootDir)); |
| 2126 | const trackedFiles = this.queries.getAllFiles(); |
| 2127 | |
| 2128 | // Build Map for O(1) lookups |
| 2129 | const trackedMap = new Map<string, FileRecord>(); |
| 2130 | for (const f of trackedFiles) { |
| 2131 | trackedMap.set(f.path, f); |
| 2132 | } |
| 2133 | |
| 2134 | const added: string[] = []; |
| 2135 | const modified: string[] = []; |
| 2136 | const removed: string[] = []; |
| 2137 |
no test coverage detected