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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ out/
.DS_Store
Thumbs.db
*visual-check*
.archify/
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

## [Unreleased]

### Fixed

- **CI 状态与 Branches PR 徽标注入取数主机策略**:仅 github.com 与已配置 `github-enterprise.uri` 的实例主机发送取数请求;其余「GitHub 形态」远程(origin 指向任意第三方主机)视同非 GitHub(零徽标、零请求),杜绝把 GitHub/GHE token 发往任意主机的凭证外发面。

### Added

- **Branches 视图远程分支行右侧显示关联 GitHub Pull Request 状态徽标**(`#138 Merged` 形态 + 云图标按状态着色:Merged 紫 / Closed 红 / Open 绿 / Draft 弱化;悬停见 PR 号、标题与经反 SSRF 主机白名单校验的 Open on GitHub 链接)。分支 → PR 三级匹配(fork 全名 → 同仓 headRefName → 唯一兜底,歧义宁缺勿错);复用 CI 状态的 GitHub 认证与 remote/provider 单一事实源(`getGitHubContext()`),GraphQL 按 UPDATED_AT 倒序取最近 100 条全态 PR,TTL 60s 缓存 + 同实例引用相等守卫(无刷新循环、首轮无徽标先行不阻塞建树);未登录 / 非 GitHub 远程 / 配置关闭 → 零徽标零请求静默休眠。新增配置 `hyperGit.branches.pr.enabled`(默认开启);engine 纯逻辑 `engine/pr/` 四模块 + 全量单测。详见 [功能文档](./docs/features/branches-pr-status.md)。

### Build

- **开发依赖批量升级**(dependabot):`@vscode/vsce` 3.9.2 → **4.0.0**(#131)、`vitest` 4.1.10 → **5.0.1**(#127)、`ovsx` 1.1.1 → 1.2.0(#130)、`typescript-eslint` 8.68.0 → 8.70.0(#129)、`esbuild` 0.28.1 → 0.28.2(#128);两项 major 均把 Node 基线抬至 22(vitest 5 实际要求 ≥ 22.12),与 CI 的 `node-version: 22` 一致。五个 PR 共改 `pnpm-lock.yaml`,顺序合并必连环冲突,故合成单个 PR 只重生一次 lockfile,组合兼容性由 CI 在 `feature/1.x.x` 基线上一次验证。`pnpm-workspace.yaml` 的 `allowBuilds` 三项维持不变——`ovsx@1.2.0` 仍传递依赖 `@vscode/vsce@^3.7.1`,`keytar` 与 `@vscode/vsce-sign`(现 2.0.9 / 2.1.0 双版本)继续在依赖树中,删任一项都会重现 `ERR_PNPM_IGNORED_BUILDS`。
Expand Down
5 changes: 3 additions & 2 deletions docs/.agents/knowledge-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [文档中心](../docs/README.md) — 文档与调研资产总索引。
- [README(简体中文)](../docs/i18n/zh-CN/README.md) — 根 README 的中文版本(根路径为英文版,i18n 目录沉淀中文)。
- [Log 视图 CI 状态](../docs/features/log-ci-status.md) — 按提交显示 GitHub CI 最终状态(绿勾/红叉 + Tooltip 明细):认证、限流、懒加载、边界与配置。
- [Branches 视图远程分支 PR 状态徽标](../docs/features/branches-pr-status.md) — 远程分支行右侧显示关联 GitHub PR(`#138 Merged` + 图标按态着色):三级匹配、TTL 缓存与无循环刷新、静默边界与配置。
- [Commit 视图整合](../docs/features/commit-view-consolidation.md) — 移除 CHANGES 视图,其文件操作/changelist 管理/Git 工具栏/未提交角标零回归平移入 Commit 视图。
- [变更文件目录树](../docs/features/file-list-group-by-directory.md) — Commit / Log 文件列表「平铺 ⇄ 按目录分组」切换:host 侧 `buildFileTree` 构树下发、compact 折叠、目录三态。
- [分支前缀分组树](../docs/features/branch-tree-group-by-prefix.md) — Branches 视图 Local/Remote/Tags「平铺 ⇄ 按 `/` 前缀分组」切换:`buildRefTree` 构树、分支感知排序、compact 折叠、隐藏 `origin/HEAD`、按仓库持久化。
Expand All @@ -37,8 +38,8 @@
## 架构分层(src/)
> 依赖方向单向:`UI → Adapter → Engine`;`Agent` 以接口注入 `Engine`/`CommitPipeline`,不反向依赖 UI。

- `engine/` — 纯领域逻辑(零 vscode 依赖,Vitest 可测,15 模块):`model/`、`diff/`、`commit/`、`changelist/`、`log/`、`ref/`、`tree/`、`git-state/`、`scm-mapping/`、`ci/`、`merge/`、`rebase/`、`blame/`、`worktree/`、`agent/`。
- `adapter/` — 唯一接触 vscode API:`git-api.ts`(getAPI(1))、`GitRepositoryService`(活跃仓库唯一持有者,vscode.git API + execGit 双通道)、`ChangelistRegistry`、`BranchFavorites`、`ShelfService`、`CommitService`、命令注册组、`tree/`、`webview/`、`ci/`、`editor/`。
- `engine/` — 纯领域逻辑(零 vscode 依赖,Vitest 可测,16 模块):`model/`、`diff/`、`commit/`、`changelist/`、`log/`、`ref/`、`tree/`、`git-state/`、`scm-mapping/`、`ci/`、`pr/`、`merge/`、`rebase/`、`blame/`、`worktree/`、`agent/`。
- `adapter/` — 唯一接触 vscode API:`git-api.ts`(getAPI(1))、`GitRepositoryService`(活跃仓库唯一持有者,vscode.git API + execGit 双通道)、`ChangelistRegistry`、`BranchFavorites`、`ShelfService`、`CommitService`、命令注册组、`tree/`、`webview/`、`ci/`、`pr/`、`editor/`。
- `agent/` — AI 接缝(当前 5 接口,均 Null 实现,完整逻辑延后至 M5):`ILlmProvider`、`ICommitMessageProvider`、`IPreCommitInspector`、`IChangelistGrouper`、`IConflictResolver`(另有规划中的第 6 接缝 `IChatToolRegistrar`,详见[调研报告](../research/05-ai-agent-seams.md))。
- `shared/protocol.ts` — Webview ↔ Host 消息契约【单一事实源】。
- `infra/` — 日志(OutputChannel)/ 错误处理 / 事件总线 / 配置。
Expand Down
1 change: 1 addition & 0 deletions docs/assets/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
| [agentic-git-preferences-flow](features/agentic-git-preferences-flow.html) | workflow | [.mmd](../mermaid/features/agentic-git-preferences-flow.mmd) | [agentic-git-preferences.md](../../features/agentic-git-preferences.md) |
| [claude-code-config-flow](features/claude-code-config-flow.html) | workflow | [.mmd](../mermaid/features/claude-code-config-flow.mmd) | [claude-code-config.md](../../features/claude-code-config.md) |
| [log-ci-status-dataflow](features/log-ci-status-dataflow.html) | dataflow | [.mmd](../mermaid/features/log-ci-status-dataflow.mmd) | [log-ci-status.md](../../features/log-ci-status.md) |
| [branches-pr-status-dataflow](features/branches-pr-status-dataflow.html) | dataflow | [.mmd](../mermaid/features/branches-pr-status-dataflow.mmd) | [branches-pr-status.md](../../features/branches-pr-status.md) |

### research/(调研设计)
| 基名 | archify 类型 | 源 Mermaid | 嵌入文档 |
Expand Down
655 changes: 655 additions & 0 deletions docs/assets/architecture/features/branches-pr-status-dataflow.dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13,806 changes: 13,806 additions & 0 deletions docs/assets/architecture/features/branches-pr-status-dataflow.html

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/assets/mermaid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
| [agentic-git-preferences-flow.mmd](features/agentic-git-preferences-flow.mmd) | flowchart LR · 偏好流向 | [agentic-git-preferences.md](../../features/agentic-git-preferences.md) L10-31 | [agentic-git-preferences-flow.html](../architecture/features/agentic-git-preferences-flow.html) | agentic-git-preferences.md | 与 `package.json` `hyperGit.agent.*` 四项配置核实一致(baseBranch 默认空、三项 multilineText) | ✅ 2026-09-11 |
| [claude-code-config-flow.mmd](features/claude-code-config-flow.mmd) | flowchart LR · 配置流向 | [claude-code-config.md](../../features/claude-code-config.md) L17-43 | [claude-code-config-flow.html](../architecture/features/claude-code-config-flow.html) | claude-code-config.md | 与 `adapter/claude-commands.ts`(QuickPick / 缺失确认后 mkdir+写{})、`engine/agent/claude-path.ts` 纯函数核实一致 | ✅ 2026-09-11 |
| [log-ci-status-dataflow.mmd](features/log-ci-status-dataflow.mmd) | flowchart LR · CI 取数 | [log-ci-status.md](../../features/log-ci-status.md) L9-31 | [log-ci-status-dataflow.html](../architecture/features/log-ci-status-dataflow.html) | log-ci-status.md | 与 `engine/ci/` + `adapter/ci/` + `protocol.ts` 核实一致(防抖 200ms、批量 ≤100 oid、pending 30s TTL、openExternal 主机校验) | ✅ 2026-09-11 |
| [branches-pr-status-dataflow.mmd](features/branches-pr-status-dataflow.mmd) | flowchart LR · PR 徽标取数 | [branches-pr-status.md](../../features/branches-pr-status.md) 数据流节 | [branches-pr-status-dataflow.html](../architecture/features/branches-pr-status-dataflow.html) | branches-pr-status.md | 与 `engine/pr/` + `adapter/pr/github-pr-service.ts` + branches-tree 集成核实一致(TTL 60s、分页 ≤2×50、三级匹配、同实例引用相等守卫、safePrUrl 主机白名单) | ✅ 2026-10-16 |
| [log-commit-detail-panel-layout.mmd](features/log-commit-detail-panel-layout.mmd) | flowchart TB · 面板布局 | [log-commit-detail-panel.md](../../features/log-commit-detail-panel.md) L17-41 | [log-commit-detail-panel-layout.html](../architecture/features/log-commit-detail-panel-layout.html) | log-commit-detail-panel.md | 与 `log-webview.ts` 分栏实现(560px stacked 阈值、18%–75% / 15%–85% 钳制)及 `package.json` view/title 核实一致 | ✅ 2026-09-11 |
| [log-commit-detail-panel-dataflow.mmd](features/log-commit-detail-panel-dataflow.mmd) | flowchart LR · 取数数据流 | 同上 L50-65 | [log-commit-detail-panel-dataflow.html](../architecture/features/log-commit-detail-panel-dataflow.html) | 同上 | 与 `protocol.ts`(commitFiles 按 hash、commitDetail 按 forHash 校验)及 selectRow / requestPanelData / deselectRow 漏斗核实一致 | ✅ 2026-09-11 |
| [multi-root-repo-selection-toolbar.mmd](features/multi-root-repo-selection-toolbar.mmd) | flowchart LR · 切换入口 | [multi-root-repo-selection.md](../../features/multi-root-repo-selection.md) L7-18 | [multi-root-repo-selection-toolbar.html](../architecture/features/multi-root-repo-selection-toolbar.html) | multi-root-repo-selection.md | 修正漂移:按钮实为 `view/title` 原生标题栏项**直调命令** `hyperGit.selectRepository`,原「postMessage log/selectRepo」路径不存在,.mmd 已改 | ✅ 2026-09-11 |
Expand Down
25 changes: 25 additions & 0 deletions docs/assets/mermaid/features/branches-pr-status-dataflow.mmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
%% Mermaid 源图(单一事实源)· 溯源矩阵: docs/assets/mermaid/README.md
%% 源文档: docs/features/branches-pr-status.md(数据流节)
%% archify 成品: docs/assets/architecture/features/branches-pr-status-dataflow.html

flowchart LR
subgraph Tree["Branches 树(首轮)"]
A["refresh / getChildren"] --> B["先渲染(无徽标不阻塞)"]
A --> C["ensurePrData 异步 kick"]
end
subgraph Svc["GitHubPrService(adapter)"]
C --> D{"可用?
开关 / GitHub 远程 / 已登录"}
D -->|否| E["返回 null(零请求)"]
D -->|是| F["TTL 60s 缓存命中?"]
F -->|命中且数据未变| G["返回同一索引实例"]
F -->|未命中| H["GraphQL 分页 ≤2×50\nUPDATED_AT 倒序全态 PR"]
H --> I["buildPrIndex 预计算\nfork全名 / 同仓 / 唯一兜底"]
I --> J["写缓存(repoRoot + TTL)"]
end
J --> K{"索引 ≠ 快照?"}
G --> K
K -->|是| L["fire 一次二次渲染\n行右侧 #N 状态 + 图标着色"]
K -->|否| B
L --> M["悬停 Tooltip\nPR 号 / 标题 / Open on GitHub"]
E --> B
80 changes: 80 additions & 0 deletions docs/features/branches-pr-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Branches 视图远程分支 PR 状态徽标

> 在 Branches 视图(`hyperGit.branches`)**远程分支**行右侧显示关联 GitHub Pull Request 状态与
> PR 号(如 `#138 Merged`),云图标按状态着色(Merged 紫 / Closed 红 / Open 绿 / Draft 弱化);
> 悬停行以 Tooltip 展示 PR 号、标题与经主机白名单校验的 Open on GitHub 链接。数据源为 GitHub
> GraphQL(最近更新的全态 PR 列表),按 origin 远程主机自动判定 github.com / GitHub Enterprise。

## 数据流

<picture>
<source media="(prefers-color-scheme: dark)" srcset="../assets/architecture/features/branches-pr-status-dataflow.dark.svg">
<img src="../assets/architecture/features/branches-pr-status-dataflow.light.svg" alt="Branches 视图 PR 徽标数据流">
</picture>

> [交互版](../assets/architecture/features/branches-pr-status-dataflow.html) · [Mermaid 源图](../assets/mermaid/features/branches-pr-status-dataflow.mmd)(图资产溯源见 [图资产索引](../assets/mermaid/README.md))

## 刷新与终止性

- **首轮先行渲染**:树刷新即渲染(无徽标),`ensurePrData` 异步 kick 取索引,**不阻塞建树**。
- **无刷新循环**:索引就绪且与快照不同才 fire 一次二次渲染;TTL 命中与数据未变(`prsEqual`)均返回
**同一索引实例** → 引用相等守卫不再 fire。最坏每 TTL 到期多一轮渲染。
- **多根切换**:`setRepoRoot` 清快照;取数完成后 `repoRoot` 已变则丢弃(防串仓);服务端为携带 repoRoot 校验的单槽缓存,切库即失效重取。
- **登录/登出与取数源配置**:`GitHubAuth.onDidChange` 或 PR 开关 / `hyperGit.log.ci.*` / `github-enterprise.uri` 变化 → 服务推进代次(作废在途写回)+ 清缓存,经 `onDidInvalidate` 通知树清快照重取(登出即隐去徽标;切换取数仓后无需等待 git 事件)。

## 分支 → PR 匹配(三级优先,宁缺勿错)

剥 remote 前缀复用 `resolveRemoteBranch` 最长前缀匹配(remote 名可含 `/`):

| Tier | 规则 | 场景 |
|---|---|---|
| a | `shortName === ${headOwner}/${headRefName}`,且首段 remote 的 URL host/owner/repo 与本仓 host 及 head owner/head repo 一致(owner/repo 大小写不敏感;fork 与 base 同实例、保留 base 仓库名) | remote 本身以 fork owner 命名(如 `mylogin/feature`);撞名劫持(login 与 remote 名撞名、含 `/` remote 名与键同形、remote 指向同 owner 他仓或异 forge 实例)被身份校验排除 |
| b | 剥离后 `branch === headRefName && headOwner === repoOwner` | dependabot / 仓内功能分支(主场景) |
| c | 剥离后 `branch === headRefName` 且候选**唯一** | 专属 fork remote;歧义(多 fork 同名)→ 不显示 |

同一 headRefName 多条 PR(复用分支名)取 `updatedAt` 最近一条。

## 认证与安全

- 复用 **VS Code 内置 GitHub 认证**(`vscode.authentication`,范围 `repo`),凭证绝不经过 chat / 日志 / webview;与 CI 状态共享同一会话。
- **静默优先**:`peek({createIfNone:false})` 绝不弹窗;未登录 → 零徽标、零请求。登录入口复用 Graph 视图标题栏「登录 GitHub」按钮(`hyperGit.ci.signIn`);该按钮由 CI 状态驱动,需 `hyperGit.log.ci.enabled` 开启(默认开启)——「CI 关 / PR 开」的组合下 Branches 视图暂无独立登录入口。
- **反 SSRF**:Tooltip 链接经 `safePrUrl` 校验(https 且主机 ∈ {github.com, `*.github.com`, 仓库主机, api.仓库主机})后才入 `isTrusted` MarkdownString。
- **取数主机策略**:仅 github.com 与已配置 `github-enterprise.uri` 的实例主机取数;其余「GitHub 形态」远程(origin 指向任意第三方主机)视同非 GitHub——零徽标、零请求,杜绝 token 外发。

## 限流与性能

- **分页硬上限 100 条**(UPDATED_AT 倒序 × 2×50):高流量仓中掉出前 100 的旧分支 PR 静默不显示(宁缺勿错),不做配置项。
- **TTL 60s 缓存 + in-flight 去重**:最坏 2 请求/分钟(持续失败按 60s 退避,同口径限频);数据未变续期并返回同一实例。
- **限流冷却独立**:读取 `rateLimit{remaining,resetAt}` 与 `Retry-After`,剩余点数 <100 或 403 时冷却(期间只走缓存);与 CI 服务冷却互不联动。
- **降级**:失败/超时 → 走既有缓存(无缓存则无徽标)、60s 退避后重试;401 → 失效会话;断网/限流不崩溃、建树正常。

## 边界行为

| 场景 | 表现 |
|---|---|
| 非 GitHub 远程(GitLab 等) | 功能隐藏(零徽标、零请求) |
| 未登录 GitHub | 零徽标、零请求(登录后即取,无需 git 事件) |
| 配置关闭 | 零徽标、零请求 |
| 多 fork 同名分支(匹配歧义) | 不显示徽标(宁缺勿错) |
| 掉出最近 100 条 PR 的旧分支 | 静默无徽标 |
| Favorites 中的远程分支 | 同样显示徽标(图标收藏黄优先于 PR 状态色) |
| GitHub Enterprise | 按 origin 主机自动判定,使用 `github-enterprise` provider(需已配置 `github-enterprise.uri`) |

## 配置

| 键 | 默认 | 说明 |
|---|---|---|
| `hyperGit.branches.pr.enabled` | `true` | 总开关。关闭后零徽标、零请求。 |

provider 与取数仓判定复用 `hyperGit.log.ci.provider` / `hyperGit.log.ci.remote`(默认 auto:按 origin 主机自动判定;provider 钉死为与远程主机不符的值时徽标休眠,remote 可重定向取数仓)——详见 [Log 视图 CI 状态](./log-ci-status.md)。

## 实现

- 引擎层(纯逻辑,Vitest 可测):[`engine/pr/`](../../src/engine/pr) — `types.ts`(PullRequestVM)、`graphql-query.ts`($variables 防注入列表查询)、`model.ts`(宽容解析 + prBadge + prsEqual)、`match.ts`(三级匹配索引)。
- 适配层(唯一触碰 vscode/网络):[`adapter/pr/github-pr-service.ts`](../../src/adapter/pr/github-pr-service.ts)(缓存/分页/限流/safePrUrl);[`adapter/ci/github-ci-service.ts`](../../src/adapter/ci/github-ci-service.ts) 增 `getGitHubContext()`(remote/provider 单一事实源);[`adapter/ci/github-auth.ts`](../../src/adapter/ci/github-auth.ts) 增 `onDidChange` 透传。
- 渲染:[`adapter/tree/branches-tree.ts`](../../src/adapter/tree/branches-tree.ts) — `ensurePrData` 异步 kick、`describe` 追加 `#N 状态`、`prThemeColor` 图标着色(收藏黄/活动蓝优先);[`adapter/tree/tree-tooltip.ts`](../../src/adapter/tree/tree-tooltip.ts) — `mdTooltip` 增 `links` 链接行(仅含链接实例放开 isTrusted)。

## 验证

1. `pnpm run check-types` + `pnpm run lint` + `pnpm run test:unit`(含 `tests/unit/pr-*.test.ts`)全绿。
2. Extension Development Host(F5)在 GitHub 仓库:远程分支行右侧出现 `#N Open/Merged/Closed/Draft` + 图标着色;悬停见 PR 号/标题/可点击 Open on GitHub;收藏远程条目同样生效(黄标优先);未登录/非 GitHub/配置关闭 → 零徽标零请求;PR 状态变化 ≤60s(下次树刷新 + TTL 到期)自然更新;多根切换无串仓残留。
3 changes: 2 additions & 1 deletion docs/features/log-ci-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
- 范围 `repo`:覆盖私有仓库的 Checks(Actions)+ Commit Status 读取(`repo:status` 不覆盖 Checks API)。
- **静默优先**:加载时 `getSession({createIfNone:false})` 仅复用已有会话,**绝不自动弹窗**;仅当用户点击 Graph 标题栏「登录 GitHub」
图标按钮(`$(sign-in)`,`when: hyperGit.log.ciNeedsSignIn`,授权完成自动隐藏)时才以 `{createIfNone:true}` 触发原生授权 UI。未登录 → 仅显该按钮、不渲染图标、不发请求。
- **反 SSRF**:Tooltip 的跳转链接(`detailsUrl` / `targetUrl`,属「观察内容」)由 host 校验 `https` 且主机 ∈ {仓库主机、`*.github.com`} 后才 `openExternal`。
- **反 SSRF**:Tooltip 的跳转链接(`detailsUrl` / `targetUrl`,属「观察内容」)由 host 校验 `https` 且主机过共享白名单谓词(github.com、`*.github.com`、仓库主机、api.仓库主机)后才 `openExternal`。
- **取数主机策略**:仅 github.com 与已配置 `github-enterprise.uri` 的实例主机取数;其余「GitHub 形态」远程(origin 指向任意第三方主机)视同非 GitHub——零徽标、零请求,杜绝 token 外发(与 Branches PR 徽标同策略,见 [Branches 视图 PR 徽标](./branches-pr-status.md))。

## 限流与性能

Expand Down
Loading
Loading