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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

- **Branches 视图分支就地更新(不 checkout)**:右键单个分支 **Update Branch**、多选批量更新、标题栏 Pull 按钮升级为选区感知(无选区保持原 Pull 语义,Commit 视图 Pull 不受影响)。底层原语 `git fetch <remote> refs/heads/<upstream>:refs/heads/<branch>`——git 原生 fast-forward-only,diverged / ahead-only 分支被安全拒绝(附真实 stderr 原因),**永不改写本地提交**(src 侧全限定防 `+` 前缀上游名被解析为 force);当前分支自动改走 `repo.pull()`(含冲突处理通道);无上游分支透明跳过;无 remote 配置提示 `No remote repository configured` 提前返回(不误报 no upstream)。五桶对账汇总(updated / already up to date / pulled / skipped / failed,选中数 = 各桶之和),up-to-date 经前后 `objectname` 短 sha 前缀比对判定(fetch 静默 no-op 无输出;缩写长度随对象数伸缩,全等比对会误报)。upstream 解析不信任树节点数据(API 路径 `refToRaw` 不填 upstream/head),统一以一次 `for-each-ref` 取新鲜快照;`origin/x` 拆分复用 `resolveRemoteBranch` 最长前缀匹配(remote 名可含 `/`)。engine 纯逻辑 `planBranchUpdates` / `formatBranchUpdateResult` / `classifyFetchOutcome` 落于 `engine/ref/branch-update.ts`,全量单测覆盖。

### Fixed

- **Graph 视图过滤条件不再随重启 / 切库丢失(全部 7 个维度按仓库持久化)**:authors、File Path、Grep、Merge Mode、Date Filter、Message Regex 此前仅存于内存,重启 VS Code 或切换仓库后即被清空。现在沿用 scope/dmode 的按仓库 workspaceState 范式持久化——authors 独立 key(`hyperGit.log.authors:<repoRoot>`),其余 6 维组合 key(`hyperGit.log.filter:<repoRoot>`,单 key 原子读写防半恢复态,dateFrom/dateTo 经 ISO 串往返),重启与切库往返后自动恢复为上次设置;过滤生效期间标题栏过滤图标点亮为填充态(Graph Filter Active,`$(filter-filled)`,经 `hyperGit.log.filterActive` context key 与常态图标互斥呈现)——恢复的过滤不再是无声约束;重开「Filter by Author…」即预勾选已选 exact 作者并继续增删(pattern 项不参与预勾选、重开后不可见,确认新选择会将其移除);空选确认与 Clear Filter 的「清除」同样持久化,重启不复活;脏持久化数据经 engine 纯函数逐字段校验免疫。**附带行为变更**:① SCM 变更列表右键「Show History」由整替过滤器改为与其余过滤命令一致的增量式——文件历史现与活跃过滤相交,而非静默清空后者(M3 期遗留不一致);② 任何过滤对话框(Filter Graph 聚合入口 / Author / Path / Grep / Merge Mode / Date / Message Regex)或 Commit 文件右键菜单打开期间发生切库后确认,不再把旧仓库语境的选择误写新仓库(rootAtStart 切库守卫全覆盖);③ issue #107 的「切库清空 host 级过滤」限制消除——每个仓库记忆自己的完整过滤条件;④ Filter by Path 选中当前仓库外的文件(多根工作区选他仓文件 / 单仓导航到仓外)时以告警拒绝,不再落入 `../…` 垃圾 path 过滤——该垃圾 path 会随持久化成为跨重启的图加载 fatal 错误态。

## [0.0.21] - 2026-10-23 — Filter by Author 候选建议 · 多选过滤 · 元字符作者修复

### Added
Expand Down
8 changes: 8 additions & 0 deletions docs/.agents/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,11 @@
- **处理方式**:删除循环改三桶(成功 / 服务端已无此 ref / 真失败)——「服务端已无此 ref」经 engine 纯逻辑 `isRemoteRefMissingError`(stderr 优先,匹配 `remote ref does not exist` / `delete of non-existent ref` 变体)识别,按达成收尾并把本地陈旧 tracking ref 纳入既有 `branch -D -r` 清理;真失败经 stderr 优先的 `gitErrorText`(`engine/git-state/git-error.ts` 单一事实源)提取原因,由 `formatRemoteDeleteResult` 汇总为通知(失败走 `showGitError`,联动 Show Output 看完整 stderr)。确认弹窗/受保护主干硬阻断/上游软警示逻辑不动。
- **后续防范**:① 批量操作的 `catch` 必须保留可行动的失败原因(execGit 拒绝值已挂 `.stderr`,消费即可),`grep -rn 'catch {' src/adapter/` 应逐个确认非吞错;② remote-tracking ref 与服务端存在时滞,任何「对远程分支的操作」都要考虑「本地有、服务端无」的陈旧态——删除/比较/PR 匹配等场景遇到 `does not exist` 类报错应先判陈旧再判失败;③ 视图列表的「幽灵分支」根治靠 `fetch --prune`(Prune Remotes 命令已有),删除路径的容错只是补救。
- **同类问题影响**:所有列出 remote-tracking ref 并提供远程删除的 Git GUI;凡把服务端拒绝一概当失败、不区分「目标态已达成」的实现,以及任何丢弃 stderr 的批量 CLI 包装。

## #20 Graph 视图 Filter by Author 过滤选择随重启丢失(未持久化)

- **表因**:Graph 视图「Filter Graph… → Filter by Author…」多选作者后,重启 VS Code(或切换仓库再切回)过滤被清空,需从头重选。
- **根因**:过滤状态仅存于 `LogWebviewProvider` 内存字段 `private filter: LogFilter = {}`,`setFilter/clearFilter` 只改内存不落盘;切库 handler 还会显式 `this.filter = {}`。同文件的 scope/dmode 早有按仓库 workspaceState 持久化范式(`loadRepoScopedPrefs`),filter 维度从未接入。另有一处硬冲突:`hyperGit.showHistory` 用 `setFilter({ path })` 整替 filter 对象(M3 期遗留,早于其余 5 个过滤命令统一的 `{ ...f, <维度> }` 增量范式),若接入持久化会静默清空已保存的 authors。
- **处理方式**:① `engine/log/log-filter.ts` 新增纯函数 `parsePersistedAuthors`(JSON 串 → `AuthorTerm[]`,元素级剔除脏项,永不抛异常);② `LogWebviewProvider` 新增 `authorsKey(root)`(`hyperGit.log.authors:<root>`,同 scope/dmode 命名范式)与唯一写入点 `persistAuthors`(`setFilter/clearFilter` 汇聚,`update(key, undefined)` 即删 key,空选清除同样持久化);恢复并入 `loadRepoScopedPrefs`(构造期 + 切库共用),切库语义由「全清」改为「清非持久维度 + 恢复新仓库持久值」;③ `showHistory` 改增量式 `setFilter({ ...getFilter(), path })`,与其余过滤命令同构;④ Review 收敛补充:`pickAuthors` settle 增切库守卫(rootAtStart 同范式——环境驱动切库不夺焦点、QuickPick 不隐藏,取数窗口外确认会把旧仓库勾选经 persistAuthors 误写新仓库持久化 key),候选集外的持久化 exact 作者以 ghost 项预勾选呈现(selectedItems 只能来自 items,scope 已切换 / shortlog 失败降级时缺项者确认即被静默丢出持久化 key),`multi-root-repo-selection.md` 已知限制同步新切库语义,wiring 级测试(`history-commands-filter-wiring.test.ts`)钉住 showHistory 增量式与 settle 守卫;同构守卫同步应用于 `logFilterPath`(对话框 await 期间切库会按新仓库 root 求出跨仓库垃圾相对路径——预存竞态,收敛门范围外观察,经用户裁定收口;越仓选择同产 `../…` 垃圾 path 且经持久化升级为跨重启粘性污染——增 rel 路径段级越仓校验(`'..'` / `'../'` 前缀;较 showBlame 的 `startsWith('..')` 不误杀仓内 `..` 前缀文件)+ 告警拦截,wiring 测试补越仓与不误杀用例);⑤ 其余 6 个过滤维度(path/grep/mergeMode/dateFrom/dateTo/messageRegex)经用户决策按同范式扩展持久化:组合 key `hyperGit.log.filter:<repoRoot>`(单 key 原子读写防半恢复态),dateFrom/dateTo ISO 串往返,engine 层 `serializePersistableFilter/parsePersistedFilter` 逐字段校验(归一化:空串、mergeMode 'all' 不落盘),issue #107 的「切库清空 host 级过滤」限制随之消除(multi-root 文档已知限制同步移除,按仓库状态表与导语同步补录过滤 key);增量 Review 复核后同范式守卫补齐 grep/mergeMode/date/regex 四命令、commit 文件菜单(`handleFileMenu`——菜单 await 期间切库时 change 属旧仓库语境,任何动作不派发)与聚合入口 `logFilter`(其 Clear Filter 项直接执行,await 期间切库会删切换后仓库的持久化 key),全部过滤对话框与 Commit 文件菜单的 await 窗口至此收口(Graph 提交行右键菜单 `handleCommitMenu` 为预存未守卫路径,不在本条收口范围);wiring 测试同步补齐 date/regex(正常 + 切库守卫 + 清除分支)与 mergeMode 正常路径用例;⑥ 经用户决策采纳 Review 改进建议:过滤激活态可见指示——engine 纯函数 `hasActiveFilter`(与持久化归一化同口径:空串 / mergeMode 'all' / 空 authors 不算生效)驱动 `hyperGit.log.filterActive` context key(setFilter/clearFilter/loadRepoScopedPrefs 三处同步),标题栏过滤图标常态(`$(filter)`)/填充(`$(filter-filled)`)双命令互斥呈现(detailTree/detailFlat 同范式,`logFilterOn` 与 `logFilter` 共用聚合 handler)。
- **后续防范**:① 视图 UI 状态凡期望跨会话存活,一律挂到既有的按仓库 memento 范式(key 带 repoRoot、读时校验、写时 undefined 删 key),勿新造存储;② 新增过滤维度命令必须沿用 `{ ...getFilter(), <维度> }` 增量式,整替 filter 会静默摧毁其他维度(可 grep `setFilter({` 检查是否带 spread);③ 持久化读写要过脏数据校验纯函数(engine 层),adapter 只做编排;④ 跨 await 的确认回调(QuickPick settle 等)写仓库语境数据前复核 repoRoot 未变——持久化把瞬态竞态升级为跨重启的粘性污染。
- **同类问题影响**:其余 6 个过滤维度已随后续决策按同范式扩展持久化(见处理方式⑤);所有「内存态 + 仓库语境」的视图组件在接入持久化前都存在同样的重启丢失。
4 changes: 2 additions & 2 deletions docs/features/multi-root-repo-selection.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 多根工作区仓库切换(Multi-root Repository Selection)

> 多根工作区(multi-root)含多个 Git 仓库时,**Graph 标题栏出现「切换仓库」图标按钮**(`$(repo)`,对标 Git Graph 的 repo selector;仓库路径常驻 `WebviewView.description` 副标题),Command Palette 亦可直达。切换的是**全局活跃仓库**:Graph / Branches / Commit(changelist)/ Stash / Shelf / Worktrees / 未提交角标**全部视图跟随**,changelist 分配、分支收藏、分支分组偏好、Shelf 存储、最近提交消息**按仓库隔离记忆**,重开窗口回到上次操作的仓库。
> 多根工作区(multi-root)含多个 Git 仓库时,**Graph 标题栏出现「切换仓库」图标按钮**(`$(repo)`,对标 Git Graph 的 repo selector;仓库路径常驻 `WebviewView.description` 副标题),Command Palette 亦可直达。切换的是**全局活跃仓库**:Graph / Branches / Commit(changelist)/ Stash / Shelf / Worktrees / 未提交角标**全部视图跟随**,changelist 分配、分支收藏、分支分组偏好、Shelf 存储、最近提交消息、Graph 过滤条件**按仓库隔离记忆**,重开窗口回到上次操作的仓库。

## 交互形态

Expand Down Expand Up @@ -51,6 +51,7 @@
| 分支分组偏好 | `hyperGit.branchesGrouping:${repoRoot}` | 同上 + context key 同步 |
| Shelf 存储 | `shelves/<basename>.<sha1[:8]>/` | 目录随 `service.repoRoot` **动态求值**(零快照零双源) |
| 最近提交消息 | `hyperGit.recentCommitMessages:${repoRoot}` | 动态 key + 旧全局 key 回落一次(平滑迁移) |
| Graph 过滤条件(authors + path/grep/mergeMode/date/regex) | `hyperGit.log.authors:${repoRoot}`、`hyperGit.log.filter:${repoRoot}` | `loadRepoScopedPrefs` 换 key 重载(构造期 + 切库,engine 纯函数校验脏数据) |
| Graph 视图状态(scope/选中/详情模式) | webview `vscode.getState()` v2 `byRepo` 分区 | `graphData` 到达时按 `repoRoot` 换装载 |
| Commit 勾选集/视图模式 | 同上 | 同上(勾选是相对路径集合,跨仓库本就错位) |

Expand All @@ -69,6 +70,5 @@

## 已知限制

- 仓库切换时 Graph 的 host 级过滤条件(author/path/grep 等)清空——过滤条件是旧仓库语境的产物,跨仓库无意义;
- 多仓库历史混仓 Shelf 数据归属迁移时的当前活跃仓库(无法按仓库归因);
- CI 状态按 commit hash 缓存(跨仓库理论碰撞概率可忽略,行为正确)。
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,12 @@
"category": "Hyper Git",
"icon": "$(filter)"
},
{
"command": "hyperGit.logFilterOn",
"title": "Graph Filter Active — Filter Graph…",
"category": "Hyper Git",
"icon": "$(filter-filled)"
},
{
"command": "hyperGit.logFilterAuthor",
"title": "Filter by Author…",
Expand Down Expand Up @@ -949,7 +955,12 @@
},
{
"command": "hyperGit.logFilter",
"when": "view == hyperGit.log",
"when": "view == hyperGit.log && !hyperGit.log.filterActive",
"group": "navigation@2"
},
{
"command": "hyperGit.logFilterOn",
"when": "view == hyperGit.log && hyperGit.log.filterActive",
"group": "navigation@2"
},
{
Expand Down Expand Up @@ -1436,6 +1447,10 @@
"command": "hyperGit.log.detailFlat",
"when": "view == hyperGit.log"
},
{
"command": "hyperGit.logFilterOn",
"when": "view == hyperGit.log"
},
{
"command": "hyperGit.commit.detailTree",
"when": "view == hyperGit.commit"
Expand Down
Loading
Loading