feat(workspace): add file changes tracking panel#1789
Conversation
…t snapshot service - Add WorkspaceSnapshotService with dual-mode support: - git-repo mode: reuse existing .git, read branch name, compare against HEAD - snapshot mode: create isolated gitdir in temp dir for workspaces without .git - Use git.walk with content hashing to bypass "racy git" stat-cache issue - Replace IPC emitter with 5 providers: init/compare/getBaselineContent/getInfo/dispose - Rewrite useFileChanges hook for on-demand comparison instead of real-time events - Update FileChangeList: lazy diff loading, refresh button, loading state - Update WorkspaceTabBar: display current branch name for git-repo mode - Remove old snapshot interception code from fsBridge.ts - Add isomorphic-git dependency - Add i18n refresh key to all 6 locales - 19 unit tests passing
…or compare git.walk reads every file's content into memory and computes SHA-1 hashes, causing 17GB memory usage on large workspaces. statusMatrix uses stat-based comparison (mtime + size) which is O(n) stat calls with no file content reads.
…service isomorphic-git reads every file's content and computes SHA-1 hashes in JS, causing 78s latency and 862MB memory for 1829 files (17GB in real workspace). Native git via child_process: 91ms total, 2.6MB memory — 857x faster. - Use git diff --name-status + git ls-files for compare() - Use git show <ref>:<path> for getBaselineContent() - Use git init --bare + git add/commit for snapshot mode - Remove isomorphic-git dependency
Previously git-repo mode used HEAD commit as baseline, showing ALL uncommitted changes (possibly from days ago). Now both modes create a temp gitdir snapshot of the current working tree at conversation start, so only changes made DURING the conversation are shown.
Redesign Changes tab to show two collapsible sections like VS Code: - Staged Changes: files added to git index - Changes (unstaged): modified/new/deleted files Git-repo mode supports: stage, stage all, unstage, unstage all, discard Snapshot mode shows flat list with reset (revert) action - Add stageFile/stageAll/unstageFile/unstageAll/discardFile/resetFile to WorkspaceSnapshotService, IPC bridge, and useFileChanges hook - Parse git status --porcelain for staged/unstaged separation - Git-repo mode now uses real .git (no temp gitdir), compare via git status instead of snapshot baseline - Snapshot mode keeps temp gitdir for conversation-scoped changes - FileChangeList shows hover action buttons per file - Add i18n keys for all 6 locales
Replace collapsible sections with fixed two-panel split: - Top panel: unstaged changes with stage/discard actions - Bottom panel: staged changes with unstage action - Each panel has its own header bar and scrollable file list - Empty state text when a panel has no files - Snapshot mode: single flat panel (no staging concept)
Split refresh into two functions: - silentRefresh: updates data without loading spinner (git operations) - refreshChanges: shows loading indicator (manual refresh button) Stage/unstage/discard/reset now use silentRefresh so panels stay visible during the data update instead of flashing to a spinner.
- Add getBranches service method using git branch --format - Wire IPC bridge and provider for branch listing - Fetch branches after init in git-repo mode - Display branches in a read-only Tree dropdown with folder grouping - Current branch highlighted with check icon, ancestors auto-expanded
- Remove Arco Tree dependency for branch dropdown - Use lightweight recursive BranchList component with 12px indent - Fix folder key uniqueness issue causing broken layout on click - Fix branch trigger width to 100px to prevent TabBar layout shift - Use var(--color-bg-popup) for solid dropdown background
…moteAgent bridges
Code Review:feat(workspace): add file changes tracking panel (#1789)变更概述本 PR 为 Workspace 面板新增 "Changes" 标签页,展示 AI 对工作区文件的增删改历史。主要分三层:主进程 方案评估结论:✅ 方案合理 设计分层清晰,双模式( 问题清单🟠 HIGH — 空工作区初始化崩溃(snapshot 模式)文件: 问题代码: await execFileAsync('git', ['init', '--bare', gitdir]);
await execFileAsync('git', [...gitArgs, 'add', '.'], { cwd: workspacePath });
await execFileAsync(
'git',
[...gitArgs, '-c', 'user.name=AionUI', '-c', '[email protected]', 'commit', '-m', 'baseline'],
{ cwd: workspacePath }
);问题说明:当工作区目录为空(或所有文件都被 对于 AI 从零开始创建文件的场景(空工作区是初始状态),这会导致整个 Changes 功能失效。 修复建议:使用 await execFileAsync(
'git',
[...gitArgs, '-c', 'user.name=AionUI', '-c', '[email protected]',
'commit', '--allow-empty', '-m', 'baseline'],
{ cwd: workspacePath }
);🟡 MEDIUM — git worktree / 子模块被误判为 snapshot 模式文件: 问题代码: private async detectMode(workspacePath: string): Promise<'git-repo' | 'snapshot'> {
try {
const gitPath = path.join(workspacePath, '.git');
const stat = await fs.stat(gitPath);
return stat.isDirectory() ? 'git-repo' : 'snapshot';
} catch {
return 'snapshot';
}
}问题说明:git worktree 和 submodule 的 修复建议: private async detectMode(workspacePath: string): Promise<'git-repo' | 'snapshot'> {
try {
await execFileAsync('git', ['rev-parse', '--git-dir'], { cwd: workspacePath });
return 'git-repo';
} catch {
return 'snapshot';
}
}用 🔵 LOW — PR 描述与实现不符问题说明:PR description 描述的是旧方案(在 汇总
结论
本报告由本地 CONCLUSION: CONDITIONAL |
- Fix empty workspace crash: add --allow-empty to baseline commit so git commit does not fail when workspace has no trackable files - Fix git worktree/submodule misdetection: replace .git directory stat check with `git rev-parse --git-dir` which handles .git files Review follow-up for #1789
PR Fix 验证报告原始 PR: #1789
总结: ✅ 已修复 2 个 | ❌ 未能修复 0 个
|
Summary
Changes
Snapshot Layer (Main Process)
src/process/bridge/fsBridge.ts— capture file content before write/delete, emitfileSnapshot.changeIPC eventsrc/common/adapter/ipcBridge.ts— newfileSnapshot.changeemitter channelsrc/common/types/fileSnapshot.ts—FileChangeEvent,FileChangeRecordtypes andmergeFileChangelogicState Management (Renderer)
Workspace/hooks/useFileChanges.ts— listen to IPC events, accumulate changes with merge logic, clear on conversation switchUI (Renderer)
Workspace/components/WorkspaceTabBar.tsx— Arco Tabs for Files/Changes switching with badge countWorkspace/components/FileChangeList.tsx— change list with status markers, +/- stats, click-to-open DiffViewerWorkspace/index.tsx— integrate tab bar and conditional renderingi18n
workspace.changes.*keysRelated Issue
Closes #1788
Test Plan
bun run testpasses (merge logic unit tests)Open Question
Current implementation captures changes by intercepting
fsBridge.writeFile/removeEntryproviders. This covers all AI-initiated file operations but does NOT capture external edits (e.g., user editing files in their IDE). Need to discuss whether filesystem watching is needed.