fix(ui): keep diff refreshes targeted

This commit is contained in:
Bohdan Triapitsyn
2026-08-02 21:46:22 +03:00
parent 75832876fa
commit 17c2d5ec36
14 changed files with 268 additions and 38 deletions
+4 -1
View File
@@ -252,7 +252,10 @@ Expected model:
- `GitView` / `DiffView` ensure current-directory Git state when visible
- explicit Git actions refresh status/branches/log as needed
- successful file-mutating tools can issue a one-shot Git refresh hint
- a mounted file-mutating tool issues a one-shot Git refresh hint when it transitions from active to successfully finalized; remounting historical completed tools does not replay the hint
- a successful dirty save from the in-app file editor issues a path-scoped Git refresh hint; clean autosave checks remain no-ops
- refresh hints with authoritative file paths invalidate only those cached and currently rendered diffs before status refresh; pathless tools request status reconciliation without broadly remounting DiffView
- targeted diff remounts preserve the user's current file-section anchor and intra-file offset before paint instead of resetting the stacked view to the top
- no root-level background Git polling
### PR
@@ -161,6 +161,32 @@ describe('useGitStore', () => {
expect(useGitStore.getState().getDiff('/repo', 'stale.ts')).toBe(null);
});
test('clears cached file contents when a git refresh hint invalidates diffs', () => {
setDirectoryStatus(createStatus(
{ 'src/index.ts': { insertions: 1, deletions: 1 } },
[{ path: 'src/index.ts', index: ' ', working_dir: 'M' }],
));
useGitStore.getState().setDiff('/repo', 'src/index.ts', { original: 'old', modified: 'stale' });
useGitStore.getState().clearDiffCache('/repo');
expect(useGitStore.getState().getDiff('/repo', 'src/index.ts')).toBe(null);
});
test('invalidates only the requested cached file contents', () => {
setDirectoryStatus(createStatus(undefined, [
{ path: 'src/first.ts', index: ' ', working_dir: 'M' },
{ path: 'src/second.ts', index: ' ', working_dir: 'M' },
]));
useGitStore.getState().setDiff('/repo', 'src/first.ts', { original: 'a', modified: 'b' });
useGitStore.getState().setDiff('/repo', 'src/second.ts', { original: 'c', modified: 'd' });
useGitStore.getState().clearDiffCache('/repo', ['src/first.ts']);
expect(useGitStore.getState().getDiff('/repo', 'src/first.ts')).toBe(null);
expect(useGitStore.getState().getDiff('/repo', 'src/second.ts')?.modified).toBe('d');
});
test('keeps the newest branch request when completions are reversed', async () => {
const requests = [createDeferred<Awaited<ReturnType<GitAPI['getGitBranches']>>>(), createDeferred<Awaited<ReturnType<GitAPI['getGitBranches']>>>()];
let index = 0;
+15 -5
View File
@@ -71,7 +71,7 @@ interface GitStore {
getDiff: (directory: string, filePath: string) => { original: string; modified: string; fetchedAt: number; isBinary?: boolean } | null;
setDiff: (directory: string, filePath: string, diff: { original: string; modified: string; isBinary?: boolean }, expectedRuntimeKey?: string) => void;
clearDiffCache: (directory: string) => void;
clearDiffCache: (directory: string, filePaths?: string[]) => void;
fetchAllDiffs: (directory: string, git: GitAPI) => Promise<void>;
prefetchDiffs: (directory: string, git: GitAPI, filePaths: string[], options?: { maxFiles?: number }) => Promise<void>;
@@ -974,15 +974,25 @@ export const useGitStore = create<GitStore>()(
set({ directories: evictGlobalDiffCachesIfNeeded(newDirectories) });
},
clearDiffCache: (directory) => {
clearDiffCache: (directory, filePaths) => {
bumpDiffFetchGeneration(directory);
startRequest(directory, 'diff');
const newDirectories = new Map(get().directories);
const dirState = newDirectories.get(directory);
if (dirState) {
newDirectories.set(directory, { ...dirState, diffCache: new Map() });
set({ directories: newDirectories });
if (!dirState || dirState.diffCache.size === 0) return;
const nextDiffCache = new Map(dirState.diffCache);
if (filePaths) {
for (const filePath of filePaths) {
nextDiffCache.delete(filePath);
}
} else {
nextDiffCache.clear();
}
if (nextDiffCache.size === dirState.diffCache.size) return;
newDirectories.set(directory, { ...dirState, diffCache: nextDiffCache });
set({ directories: newDirectories });
},
fetchAllDiffs: async (directory, git) => {