Skip to content

feat(workspace): add file changes tracking panel#1789

Merged
piorpua merged 25 commits intomainfrom
feat/workspace-file-changes
Mar 29, 2026
Merged

feat(workspace): add file changes tracking panel#1789
piorpua merged 25 commits intomainfrom
feat/workspace-file-changes

Conversation

@kaizhou-lab
Copy link
Copy Markdown
Collaborator

Summary

  • Add a "Changes" tab to the Workspace panel showing cumulative file modifications by AI
  • Intercept file writes/deletes at fsBridge layer to capture before/after snapshots
  • Support M(modify)/A(create)/D(delete) status markers with line-level insertion/deletion stats

Changes

Snapshot Layer (Main Process)

  • src/process/bridge/fsBridge.ts — capture file content before write/delete, emit fileSnapshot.change IPC event
  • src/common/adapter/ipcBridge.ts — new fileSnapshot.change emitter channel
  • src/common/types/fileSnapshot.tsFileChangeEvent, FileChangeRecord types and mergeFileChange logic

State Management (Renderer)

  • Workspace/hooks/useFileChanges.ts — listen to IPC events, accumulate changes with merge logic, clear on conversation switch

UI (Renderer)

  • Workspace/components/WorkspaceTabBar.tsx — Arco Tabs for Files/Changes switching with badge count
  • Workspace/components/FileChangeList.tsx — change list with status markers, +/- stats, click-to-open DiffViewer
  • Workspace/index.tsx — integrate tab bar and conditional rendering

i18n

  • All 6 locales (en-US, zh-CN, ja-JP, zh-TW, ko-KR, tr-TR) updated with workspace.changes.* keys

Related Issue

Closes #1788

Test Plan

  • Open a conversation with workspace (Gemini/ACP/Codex)
  • Verify "Files" and "Changes" tabs render correctly
  • Let AI create/modify/delete files, verify Changes tab updates with correct M/A/D markers
  • Click a changed file, verify DiffViewer opens in Preview panel
  • Modify same file multiple times, verify cumulative diff (original → latest)
  • Switch conversation, verify changes list clears
  • Verify bun run test passes (merge logic unit tests)

Open Question

Current implementation captures changes by intercepting fsBridge.writeFile / removeEntry providers. 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.

zk added 24 commits March 27, 2026 15:11
…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
@kaizhou-lab kaizhou-lab marked this pull request as ready for review March 28, 2026 03:18
@piorpua piorpua added the bot:reviewing Review in progress (mutex) label Mar 29, 2026
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 29, 2026

Code Review:feat(workspace): add file changes tracking panel (#1789)

变更概述

本 PR 为 Workspace 面板新增 "Changes" 标签页,展示 AI 对工作区文件的增删改历史。主要分三层:主进程 WorkspaceSnapshotService(用 git 底层实现快照/对比逻辑)、IPC bridge 层(新增 fileSnapshot.* provider 组)、Renderer 层(useFileChanges hook + WorkspaceTabBar + FileChangeList 组件)。


方案评估

结论:✅ 方案合理

设计分层清晰,双模式(git-repo / snapshot)覆盖了有无 git 仓库两种场景,WorkspaceSnapshotService 复用 git 语义而非重造文件比对逻辑,符合项目架构边界。UI 参照 Sourcetree 风格的两栏布局对 git-repo 模式也很直观。有一处边界条件(空目录初始化)未被处理,详见下方问题清单。


问题清单

🟠 HIGH — 空工作区初始化崩溃(snapshot 模式)

文件src/process/services/WorkspaceSnapshotService.ts,第 350–356 行

问题代码

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 }
);

问题说明:当工作区目录为空(或所有文件都被 .gitignore 排除,如只含 node_modules/),git add . 不会暂存任何内容,随后 git commit 会报 "nothing to commit" 错误,导致 init 抛出异常。useFileChanges 会静默 catch 这个错误(打印 console.error 后不设置任何 UI 状态),用户在 Changes 面板看不到任何内容也没有错误提示。

对于 AI 从零开始创建文件的场景(空工作区是初始状态),这会导致整个 Changes 功能失效。

修复建议:使用 --allow-empty 创建 baseline commit:

await execFileAsync(
  'git',
  [...gitArgs, '-c', 'user.name=AionUI', '-c', '[email protected]',
   'commit', '--allow-empty', '-m', 'baseline'],
  { cwd: workspacePath }
);

🟡 MEDIUM — git worktree / 子模块被误判为 snapshot 模式

文件src/process/services/WorkspaceSnapshotService.ts,第 187–195 行

问题代码

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 的 .git文件(内容为 gitdir: ...),不是目录。stat.isDirectory() 返回 false,导致这类工作区被误识别为 snapshot 模式,git 操作(stage/discard/branch)全部不可用。

修复建议

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';
  }
}

git rev-parse --git-dir 判断,git 自己能正确处理 worktree / submodule 的 .git 文件。


🔵 LOW — PR 描述与实现不符

问题说明:PR description 描述的是旧方案(在 fsBridge 层截获写入、emit fileSnapshot.change 事件、FileChangeEvent/FileChangeRecord/mergeFileChange 类型),与实际合入的实现完全不同。实际实现使用 git 驱动的快照/对比服务 + IPC provider(轮询模型)。建议更新 PR description,避免后续 git log 或 code archaeology 时产生误导。


汇总

# 严重级别 文件 问题
1 🟠 HIGH WorkspaceSnapshotService.ts:350 空工作区 git commit 失败,Changes 功能静默失效
2 🟡 MEDIUM WorkspaceSnapshotService.ts:187 git worktree/submodule 被误判为 snapshot 模式
3 🔵 LOW PR description 描述与实际实现不符,需更新

结论

⚠️ 有条件批准 — 核心逻辑设计扎实,测试覆盖较充分,问题 #1 修复简单(加 --allow-empty)、问题 #2 修复成本低(换一条 git 命令),建议处理后合并。


本报告由本地 pr-review skill 生成,包含完整项目上下文,无截断限制。

CONCLUSION: CONDITIONAL
IS_CRITICAL_PATH: false
PR_NUMBER: 1789

@piorpua piorpua added bot:ready-to-fix CONDITIONAL review done, waiting for bot fix bot:fixing Fix in progress (mutex) and removed bot:reviewing Review in progress (mutex) labels Mar 29, 2026
@piorpua piorpua removed the bot:ready-to-fix CONDITIONAL review done, waiting for bot fix label Mar 29, 2026
- 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
@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 29, 2026

PR Fix 验证报告

原始 PR: #1789
修复方式: 直接推送到 feat/workspace-file-changes

# 严重级别 文件 问题 修复方式 状态
1 🟠 HIGH `WorkspaceSnapshotService.ts:350` 空工作区 `git commit` 失败,Changes 功能静默失效 添加 `--allow-empty` 标志,允许空目录创建 baseline commit ✅ 已修复
2 🟡 MEDIUM `WorkspaceSnapshotService.ts:187` git worktree/submodule 被误判为 snapshot 模式 将 `.git` 目录 stat 检查替换为 `git rev-parse --git-dir`,正确处理 .git 文件 ✅ 已修复

总结: ✅ 已修复 2 个 | ❌ 未能修复 0 个

🔵 LOW 级别问题已跳过(不阻塞合并,修复优先级低)。

@piorpua piorpua enabled auto-merge (squash) March 29, 2026 05:58
@piorpua piorpua added bot:done Auto-merged by bot and removed bot:fixing Fix in progress (mutex) labels Mar 29, 2026
@piorpua piorpua merged commit b00b450 into main Mar 29, 2026
18 of 21 checks passed
@piorpua piorpua deleted the feat/workspace-file-changes branch March 29, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:done Auto-merged by bot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(workspace): add file changes tracking panel

2 participants