* Get files that have changed since last index. * Uses git status as a fast path when available, falling back to full scan.
()
| 2933 | * Uses git status as a fast path when available, falling back to full scan. |
| 2934 | */ |
| 2935 | getChangedFiles(): { added: string[]; modified: string[]; removed: string[] } { |
| 2936 | const gitChanges = getGitChangedFiles(this.rootDir); |
| 2937 | |
| 2938 | if (gitChanges) { |
| 2939 | // === Git fast path === |
| 2940 | const added: string[] = []; |
| 2941 | const modified: string[] = []; |
| 2942 | const removed: string[] = []; |
| 2943 | |
| 2944 | // Deleted files — only report if tracked in DB |
| 2945 | for (const filePath of gitChanges.deleted) { |
| 2946 | const tracked = this.queries.getFileByPath(filePath); |
| 2947 | if (tracked) { |
| 2948 | removed.push(filePath); |
| 2949 | } |
| 2950 | } |
| 2951 | |
| 2952 | // Modified + added files — read + hash, compare with DB. Untracked (`??`) |
| 2953 | // files stay untracked in git even after indexing, so they must be |
| 2954 | // hash-compared like modified files instead of always counting as added — |
| 2955 | // otherwise status reports them as pending forever. (See issue #206.) |
| 2956 | for (const filePath of [...gitChanges.modified, ...gitChanges.added]) { |
| 2957 | const fullPath = path.join(this.rootDir, filePath); |
| 2958 | let content: string; |
| 2959 | try { |
| 2960 | content = fs.readFileSync(fullPath, 'utf-8'); |
| 2961 | } catch (error) { |
| 2962 | logDebug('Skipping unreadable file while detecting changes', { filePath, error: String(error) }); |
| 2963 | continue; |
| 2964 | } |
| 2965 | |
| 2966 | const contentHash = hashContent(content); |
| 2967 | const tracked = this.queries.getFileByPath(filePath); |
| 2968 | |
| 2969 | if (!tracked) { |
| 2970 | added.push(filePath); |
| 2971 | } else if (tracked.contentHash !== contentHash) { |
| 2972 | modified.push(filePath); |
| 2973 | } |
| 2974 | } |
| 2975 | |
| 2976 | return { added, modified, removed }; |
| 2977 | } |
| 2978 | |
| 2979 | // === Fallback: full scan (non-git project or git failure) === |
| 2980 | const currentFiles = new Set(scanDirectory(this.rootDir)); |
| 2981 | const trackedFiles = this.queries.getAllFiles(); |
| 2982 | |
| 2983 | // Build Map for O(1) lookups |
| 2984 | const trackedMap = new Map<string, FileRecord>(); |
| 2985 | for (const f of trackedFiles) { |
| 2986 | trackedMap.set(f.path, f); |
| 2987 | } |
| 2988 | |
| 2989 | const added: string[] = []; |
| 2990 | const modified: string[] = []; |
| 2991 | const removed: string[] = []; |
| 2992 |
no test coverage detected