Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

## [0.0.7] - 未发布

### Added
- **变更文件目录树切换**:Commit 视图活动 Changelist 文件列表、Log 视图选中提交的变更文件列表均支持「平铺 / 按目录分组(Group by Directory)」两态切换(工具栏 List/Tree 段控),偏好按视图 `webview.setState` 记忆。目录树由纯逻辑 [`engine/tree/file-tree.buildFileTree`](./src/engine/tree/file-tree.ts) host 侧构建随 payload 下发(复用 graph-layout「host 算、webview 渲」范式,规避内联脚本无法 import engine 的 Split-Brain),叶子以 `fileIndex` 回指扁平列表;支持单目录子链折叠(对齐 VS Code `explorer.compactFolders`);Commit 树形附目录级三态复选框。详见 [变更文件目录树](./docs/features/file-list-group-by-directory.md)。
- **Log 提交悬浮详情**:鼠标悬停 Log 提交行以浮层展示完整信息——所在本地/远程分支、标签、HEAD、完整提交消息(subject + body)、作者 `name <email>`、(与作者不同时)提交者、作者/提交时间(绝对 + 相对)、完整 SHA;复用 CI 状态浮层范式(置于虚拟滚动容器外、与 CI 浮层互斥),`i` 键开、`Esc` 关,滚动/刷新自动消隐。`git log` 取数扩展 `%cn/%cI/%b`(body 置末,NUL/RS 分隔容多行),正文上限截断以控 payload。详见 [Log 提交悬浮详情](./docs/features/log-commit-tooltip.md)。

### Changed
- **Commit 视图承接全部变更管理能力**(原 Changes 视图平移):活动 Changelist 文件单击看 Diff、右键(原生 QuickPick)执行 移动到 Changelist / 查看历史 / 暂存·撤销 Hunks / 加入 `.gitignore` / 丢弃改动;头部提供 Changelist 切换下拉 + ⋯ 管理菜单(新建 / 重命名 / 删除)。
- **Git 操作工具栏与未提交数角标迁至 Commit 视图**:原挂在 Changes 视图标题栏的 refresh / push / pull / fetch / patch 等动作迁到 Commit 视图标题栏(去掉与 webview 内按钮重复的 commit / commitAndPush);活动栏未提交数角标改由 Commit `WebviewView.badge` 承载,首帧未 resolve 以 `pendingBadge` 兜底。详见 [Commit 视图整合](./docs/features/commit-view-consolidation.md)。

### Removed
- **移除 CHANGES 树视图**:其展示的活动 Changelist 与 Commit 视图的 Active Changelist 完全重复(同源 `getChanges()` + `getGroups()`);全部独有能力已零回归平移入 Commit 视图(见 Changed),视图数由 7 降为 6。

### Fixed
- 修复 `vsce package` 打包因 ESLint `no-useless-assignment` 阻断:`engine/ci/remote-parser.ts` 中 `host`/`path` 的空串初值为 dead store(`hasScheme` 真假两分支均无条件重赋、解析失败处先 `return null`),改为带类型标注的纯声明消除,由 TS 确定赋值分析接管,运行时行为不变。

Expand Down
2 changes: 1 addition & 1 deletion docs/.agents/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
- **表因**:用户截图反馈 Branches 视图中一组功能/工作分支无法框选多个、无法批量删除。
- **根因**:`hyperGit.branches` 经 `vscode.window.registerTreeDataProvider` 注册——该 API **不支持** `canSelectMany`,故视图天然单选;所有分支命令处理器亦只接收单个 `BranchNode`。多选能力(`canSelectMany: true`)仅 `createTreeView` 的 `TreeViewOptions` 支持。
- **处理方式**:改用 `createTreeView('hyperGit.branches', { treeDataProvider, canSelectMany: true })`(句柄入 subscriptions)。批量命令处理器签名扩展为 `(clickedNode, selectedNodes[])`——VS Code 多选树的 `view/item/context` 命令第 2 实参即完整选区数组。新增纯逻辑 `engine/ref/selection.collectBranchRefs`(谓词过滤 + shortName 去重 + 「点击在选区之外则以点击项为准」)与 `engine/ref/cleanup.partitionByMerged`/`formatBranchDeleteConfirm`,使 `branchDelete`/`tagDelete`/`copyBranchRef`/`toggleFavorite` 批量化(删除仅一次 `git branch --merged` 分类、汇总成功/失败、末尾单次刷新)。`package.json` 对仅单目标命令(检出/合并/变基/重命名/比较等)追加 `&& !listMultiSelection` 在多选时隐藏。
- **后续防范**:① 需要承载 `.badge` 或 `canSelectMany` 等 `TreeViewOptions` 能力的视图,一律用 `createTreeView` 而非 `registerTreeDataProvider`(本仓 `hyperGit.changes` 已有先例)。② 多选命令正确性**只依赖处理器读取实参**(`clickedNode` + `selectedNodes[]`),不得依赖 `listMultiSelection` 上下文键——其对**自定义贡献视图**的可靠性无法确证,仅作菜单整洁的视觉优化;单目标命令因只读 `clickedNode` 即便该键失效仍安全。③ 「右键点击选区之外」须以点击项为准(手势目标优先),由归一化助手统一兜底。
- **后续防范**:① 需要承载 `canSelectMany` 等 `TreeViewOptions` 能力的 **TreeView**,一律用 `createTreeView` 而非 `registerTreeDataProvider`(本仓 `hyperGit.branches` 即此);`.badge` 则 `TreeView` 与 `WebviewView` **均支持**——`hyperGit.changes` 视图移除后(其活动 Changelist 与 Commit 视图重复),未提交数角标已迁至 Commit `WebviewView.badge`,注意 `WebviewView` 仅在 `resolveWebviewView` 后可置 badge,需 `pendingBadge` 兜底首帧未 resolve 的时序。② 多选命令正确性**只依赖处理器读取实参**(`clickedNode` + `selectedNodes[]`),不得依赖 `listMultiSelection` 上下文键——其对**自定义贡献视图**的可靠性无法确证,仅作菜单整洁的视觉优化;单目标命令因只读 `clickedNode` 即便该键失效仍安全。③ 「右键点击选区之外」须以点击项为准(手势目标优先),由归一化助手统一兜底。
- **同类问题影响**:所有以 `registerTreeDataProvider` 注册却后续需要多选/角标的自定义 TreeView;以及误把单目标命令在多选下直接作用于「点击项」造成的隐性误操作。

## #9 LOG 视图残留「已删分支」提交(实为工具注入的内部引用污染 `git log --all`)
Expand Down
3 changes: 3 additions & 0 deletions docs/.agents/knowledge-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
## 项目文档(docs/)
- [文档中心](../docs/README.md) — 文档与调研资产总索引。
- [Log 视图 CI 状态](../docs/features/log-ci-status.md) — 按提交显示 GitHub CI 最终状态(绿勾/红叉 + Tooltip 明细):认证、限流、懒加载、边界与配置。
- [Commit 视图整合](../docs/features/commit-view-consolidation.md) — 移除 CHANGES 视图,其文件操作/changelist 管理/Git 工具栏/未提交角标零回归平移入 Commit 视图。
- [变更文件目录树](../docs/features/file-list-group-by-directory.md) — Commit / Log 文件列表「平铺 ⇄ 按目录分组」切换:host 侧 `buildFileTree` 构树下发、compact 折叠、目录三态。
- [Log 提交悬浮详情](../docs/features/log-commit-tooltip.md) — 悬停提交行浮层展示 分支/标签/HEAD/完整消息/作者·提交者/时间/SHA;复用 CI 浮层范式、与其互斥。
- [实施状态总览](../docs/milestones/implementation-status.md) — M0-M5 交付记录 + API 限制 + M5 AI 设计 + 验证/发布(**实施看板**)。
- [工程实施方案](../docs/architecture/engineering-plan.md) — 路径 B 架构 + M0-M5 里程碑(**开发蓝图**)。
- [Git 功能矩阵](../docs/requirements/idea-feature-matrix.md) — 56 功能点 / 8 组(**验收基线**,参考 IDEA 等成熟实现)。
Expand Down
37 changes: 37 additions & 0 deletions docs/features/commit-view-consolidation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Commit 视图整合(移除 CHANGES,能力全量平移)

> CHANGES 树视图与 Commit 视图顶部的「Active Changelist」展示同一份数据(同源 `service.getChanges()` + `registry.getGroups()`),属冗余。现移除 CHANGES 视图,其**全部独有能力零回归**平移入 Commit 视图,侧边栏视图数由 7 降为 6。

## 平移清单(零功能回归)

| 原 CHANGES 能力 | 迁入 Commit 视图后的形态 |
|---|---|
| 文件单击看 Diff | 文件行单击(勾选框除外)→ `commit/openFile` → 复用 `hyperGit.openDiff` |
| 单文件右键菜单 | 文件行右键 → `commit/fileMenu` → 原生 `showQuickPick`:Open Diff / Move to Changelist… / Show History / Stage·Unstage Hunks… / Add to .gitignore / Discard Changes(复用既有命令,含 discard 确认框) |
| Changelist 管理 | 头部 `<select>` 切换活动列表(`commit/setActive`)+ `⋯` 菜单(`commit/changelistMenu` → 新建 / 重命名 / 删除) |
| Git 操作工具栏 | 标题栏(`view/title`)承接 refresh / push / pull / fetch / pushDialog / updateProject / createPatch / applyPatch;去掉与 webview 内 Commit·Commit&Push 按钮重复的两项 |
| 活动栏未提交数角标 | 迁至 Commit `WebviewView.badge`(容器图标角标 = 各视图 badge 之和,总数不变) |

## 关键设计(复用与最小回归)

- **右键菜单复用 Log 范式**:沿用 Log 视图「webview 右键 → `postMessage` → host `showQuickPick` → 复用既有命令」模式(`log-webview.handleCommitMenu`),键盘可达、无 CSP/焦点陷阱、零 in-DOM 浮层成本。
- **命令实参统一**:文件级命令改为接受 `ChangeItem | 路径字符串`,host 经 `resolveChange` 回落 `service.getChanges()` 解析([`adapter/commands.ts`](../../src/adapter/commands.ts),单一事实源);`showHistory`/`ignorePath`/`partialStage`/`partialUnstage` 本就支持可选 `ChangeItem`,webview 直接传完整对象,故三处命令文件零改。
- **视图刷新**:删除 `tree.refresh()` 后,changelist/文件变更经 `registry.onDidChange` / `service.onDidChange` → `extension.refreshAll` → `commitView.refresh()` 驱动(原刷新链的自然替代)。
- **角标时序**:`WebviewView.badge` 仅在 `resolveWebviewView` 后可置,`CommitWebviewProvider.updateBadge` 在 view 未 resolve 时以 `pendingBadge` 暂存,resolve 后回填(见 [issue.md #8](../.agents/issue.md) 后续防范)。
- **命令面板**:迁移前这些命令即 `commandPalette: when:false`(唯一入口是 CHANGES 树),迁入后由 Commit webview 菜单承接,保持 webview-only,无面板暴露、无回归。

## 交互范围取舍

Commit 视图文件区以**活动 Changelist** 为提交目标(勾选集即提交范围);非活动 Changelist 通过头部下拉切换查看。相较旧树「同屏并列多 changelist」少了一处低频便利,换取窄侧栏下更聚焦的提交工作流。

## 实现

- 视图与菜单:[`package.json`](../../package.json)(删 `hyperGit.changes` 视图 / viewsWelcome / 全部 `view == hyperGit.changes` 菜单;`view/title` 迁至 `view == hyperGit.commit`)。
- 视图主体:[`adapter/webview/commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(groups/switcher/文件交互/菜单/角标/目录树)。
- 命令:[`adapter/commands.ts`](../../src/adapter/commands.ts)(`resolveChange` + 签名重构)。
- 装配:[`extension.ts`](../../src/extension.ts)(移除 tree/changesView,角标改由 `commitView.updateBadge`)。
- 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`CommitChangelistItem`、`commit/openFile|fileMenu|setActive|changelistMenu`)。

## 验证

`pnpm run check-types && pnpm run lint && pnpm run test:unit` 全绿;Extension Development Host(F5):无 CHANGES 视图;Commit 顶部可切换/管理 changelist;文件单击开 Diff、右键出菜单(含 discard 确认);标题栏 Git 动作可用;活动栏角标随未提交数增减;无 Git 仓库时降级不崩溃。
42 changes: 42 additions & 0 deletions docs/features/file-list-group-by-directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 变更文件目录树切换(Group by Directory)

> Commit 视图活动 Changelist 的文件列表、Log 视图选中提交的「Changed Files」列表,均支持在**平铺列表**与**按目录分组的树形**之间切换(对齐 IntelliJ / VS Code SCM)。默认平铺,树形为可选切换,偏好按视图各自记忆。

## 设计:host 算树、webview 渲染(单一事实源)

Webview 使用内联 `<script>` 字符串,无法 `import` engine TS。为避免同一「路径→树」逻辑两处实现(Split-Brain),沿用提交图布局的成熟范式(`graph-layout` 于 host 计算、随 `GraphRowVM.layout` 下发):

```mermaid
flowchart LR
A["扁平文件路径[]<br/>(与 files[] 同序)"] -->|host| B["buildFileTree()<br/>engine/tree/file-tree"]
B -->|"随 payload 下发<br/>FileTreeNode[]"| C["webview 渲染"]
C --> D{"mode"}
D -->|flat| E["平铺:files[] 直接渲染"]
D -->|tree| F["树形:递归 FileTreeNode<br/>叶子取 files[fileIndex]"]
style B fill:#1f6feb,color:#fff
style F fill:#238636,color:#fff
```

- **视图无关的 `FileTreeNode`**([`shared/protocol.ts`](../../src/shared/protocol.ts)):目录带 `name/path/children`,叶子带 `fileIndex` 回指扁平 `files[]`。同一套渲染逻辑服务两视图,且不复制条目数据。
- **构建算法**([`engine/tree/file-tree.ts`](../../src/engine/tree/file-tree.ts),纯逻辑、Vitest 覆盖):`/` 分段建 trie;每级**目录在前、文件在后**,同类按名称数字感知升序、稳定(相等按插入序);**compact 折叠**单目录子链(如 `a/b/c` → `a/b`,遇含叶子或多子目录即停,对齐 VS Code `explorer.compactFolders` 默认开启);根级文件、同名异目录、空输入、重复路径(keep-first)均已覆盖。
- **切换零 host 往返**:平铺 `files[]` 与树 `tree[]` 同批下发,切换/折叠仅本地重渲。

## 两视图差异

| | Commit 视图 | Log 视图(详情面板) |
|---|---|---|
| 数据源 | 活动 Changelist 的 `CommitFileItem[]` | 选中提交 `diff-tree` 的 `LogCommitFileItem[]` |
| 建树路径 | 条目 `path`(仓库相对) | `CommitFileChange.path`(干净新路径,重命名归位到新目录) |
| 复选框 | 有:叶子勾选 + **目录三态**级联;Select All 基于全量文件 | 无(只读浏览) |
| 叶子单击 | 打开 Diff(`commit/openFile`) | 打开单文件 Diff(`log/openFile`,`data-path` 保持展示串不变) |
| 偏好持久化 | `setState({mode, collapsed, checked})` | `setState({dmode, dcollapsed, ...})` |

## 实现

- 引擎:[`engine/tree/file-tree.ts`](../../src/engine/tree/file-tree.ts)(+ [`tests/unit/file-tree.test.ts`](../../tests/unit/file-tree.test.ts),15 用例)。
- 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`FileTreeNode`;`CommitViewState.tree`;`log/commitFiles` payload `tree`)。
- 渲染:[`commit-webview.ts`](../../src/adapter/webview/commit-webview.ts)(List/Tree 段控、`renderFlat`/`renderTree`、`makeLeafRow`、目录三态 `updateDirStates`)、[`log-webview.ts`](../../src/adapter/webview/log-webview.ts)(`renderDetails` 分派、`renderDetailNode`)。

## 验证

`pnpm run test:unit`(含 `file-tree.test.ts`)全绿;Extension Development Host(F5):两视图 List/Tree 即时切换、刷新/切换提交后保持模式;树内目录可折叠、compact 生效;Commit 目录三态与 Select All 联动正确;树形叶子单击仍打开对应文件 Diff(含重命名项)。
41 changes: 41 additions & 0 deletions docs/features/log-commit-tooltip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Log 提交悬浮详情(Commit Tooltip)

> 鼠标悬停 Log 视图任一提交行,以浮层快速展示该提交的完整信息:所在**本地/远程分支、标签、HEAD**、**完整提交消息**(subject + body)、**作者**(`name <email>`)、(与作者不同时的)**提交者**、**作者/提交时间**(绝对 + 相对)、**完整 SHA**。

## 数据流与取数

引用标签(分支/标签/HEAD)本就随 `GraphRowVM.chips` 下发;本特性补齐正文与提交者,随首帧一并送达,故悬停**零往返、零延迟**。

```mermaid
flowchart LR
A["git log --format=<br/>…%cn %cI %s %b"] --> B["parseLogLines → RawCommit"]
B --> C["GraphRowVM<br/>+authorEmail/committer/body(capBody)"]
C -->|"log/graphData(首帧全量)"| D["webview model.rows"]
D -->|"悬停行 ~400ms"| E["buildCommitTip → #commit-tip"]
style A fill:#1f6feb,color:#fff
style E fill:#d29922,color:#fff
```

- **格式扩展**([`engine/log/log-line.ts`](../../src/engine/log/log-line.ts)):`LOG_GRAPH_FORMAT` 增 `%cn`(提交者)、`%cI`(提交时间)、`%b`(正文)。`%b` **置于末字段**:记录以 RS `%x1e` 终止、字段以 NUL `%x00` 分隔,二者不出现于 git 文本,故正文内换行/管道/反斜杠不被误判为边界;解析用 `slice(8).join(NUL)` 稳健重组末字段。
- **payload 控制**:正文 host 侧 `capBody` 截断至上限(超限省略号、裁尾空白),避免 1000 行图的 `postMessage` 膨胀;浮层内另设滚动区兜底长正文。

## 复用 CI 浮层范式

沿用既有 CI 状态浮层(`#ci-tip`)的自定义浮层设计,新增 `#commit-tip`:

- **置于 `#rows` 之外**:虚拟滚动重写行 DOM 时浮层不被销毁。
- **与 CI 浮层互斥**:悬停 `.ci` 图标只显 CI 浮层(提交浮层隐藏并取消在途显示);悬停行其余区域显提交浮层(显示前先 `hideTip()` 关 CI 浮层)。
- **延迟与消隐**:悬停 ~400ms 显、离开 ~200ms 隐;滚动、`log/graphData`(刷新/切范围)、`log/ciMeta`、`log/appendData`(整体重绘 `#rows`)均即时消隐并取消在途显示(避免对已脱离节点定位)。
- **键盘可达**:选中行按 `i` 打开、`Esc` 关闭(`stopPropagation` 避与 `Enter`→提交菜单冲突)。
- **无双重气泡**:移除提交行 chips 的原生 `title`,引用明细统一由本浮层展示。
- **XSS 安全**:分支名/标签/作者/正文等一切外部内容均经 `esc()` 转义后注入。

## 实现

- 数据:[`engine/log/log-line.ts`](../../src/engine/log/log-line.ts)(格式 + `RawCommit` + 解析;[`tests/unit/log-line.test.ts`](../../tests/unit/log-line.test.ts)/[`log-query.test.ts`](../../tests/unit/log-query.test.ts) 更新含多行/空正文/提交者异于作者用例)。
- 协议:[`shared/protocol.ts`](../../src/shared/protocol.ts)(`GraphRowVM` 增 `authorEmail/committerName/committerDate/body`)。
- 浮层:[`adapter/webview/log-webview.ts`](../../src/adapter/webview/log-webview.ts)(`#commit-tip` + `buildCommitTip`/`positionCommitTip`/`scheduleShowCommit`/`hideCommitTip`、行 hover 处理、`fmtAbs`/`fmtRel`)。

## 验证

`pnpm run test:unit` 全绿;Extension Development Host(F5):悬停提交行 ~400ms 出浮层含各引用组 + 消息(多行保留、超长滚动 + 截断)+ 作者/提交者/时间/完整 SHA;悬停 CI 图标只出 CI 浮层(互斥);chips 无原生气泡;滚动/切换范围/刷新自动消隐;`i` 开 `Esc` 关且不误触菜单;合并 / 无正文 / detached HEAD / 多引用均正常。
Loading