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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Local / Remote / Tags。只有一个仓库时,渲染和以前完全一样,
只有“活动仓库”那一个分组可以执行分支写操作。其它仓库的分组是**只读**的:可以点选
某一行把它切成活动仓库,但右键不再弹出任何菜单,避免在 B 仓库的行上误操作 A 仓库。

同一块引用面板还给每个仓库配一个颜色:工作区里有多个仓库时,仓库节点显示一个色点、
该仓库下的每条引用行左侧有一条细色条;只有一个仓库时完全不出现颜色,和改动前一致。
这次同时把引用行右键菜单补齐到 IntelliJ IDEA 的常见项:`Copy Branch Name`,以及
“Tracking Branch”子菜单(列出远程分支来设置上游、带一个清除项);提交右键的“重置到
此处”也补齐了 soft / mixed / hard 三档。

## 问题

工作区下可以有多个并列仓库,其中主仓库还常带一批链接工作树(位于主仓库目录内的
Expand Down Expand Up @@ -104,6 +110,69 @@ rename / delete)都只针对一个活动仓库,闭包直接从活动 `gitRep
与标题、闭包和本地化解耦,因此“只读仓库不提供任何条目”这条规则可以在没有 SwiftUI
宿主的情况下直接单元测试。视图只负责把每个条目映射成真实的 `LitheContextMenuItem`。

### 每个仓库一个颜色,定位用“完整仓库列表”而不是“可见仓库列表”

多仓库时,仓库节点带一个 8 色色点(`GitRepositoryColor`,定义在
`macos/Sources/Lithe/Views/Git/GitRepositoryColor.swift`),该仓库下的每条引用行左侧
有一条 2.5px 色条,方便一眼看出某条引用属于哪个仓库。

颜色按仓库在 `feature.availableRepositoryRoots` 里的**位置**取
`palette[index % 8]`,按标准化路径匹配,而不是按“当前可见的仓库”重新编号。原因是
`availableRepositoryRoots` 是**工作树过滤之前**的完整顺序列表:如果按可见集合编号,
隐藏工作树时剩余仓库的颜色会整体前移、颜色跟着行漂移,用户会以为引用换了仓库。正确做法
是把完整列表传给 `GitRepositoryColor.index(for:in:)`;错误做法是用
`GitRepositoryHierarchy.visibleRepositoryRoots(...)` 的结果去定色。

颜色只在可见仓库数 `> 1` 时出现:仓库节点判断 `GitRepositoryColor.isVisible(for:)`
(即完整列表 `count > 1`),单仓库布局把 `repositoryColorIndex` 传 `nil`,因此单仓库
工作区的渲染和改动前完全一样,没有色点也没有色条。

`GitReferenceRowView`(`GitLogView.swift`)因此新增 `repositoryColorIndex: Int?` 入参,
并且**必须纳入它的自定义 `==`**:只有把色号纳入相等比较,隐藏 / 显示工作树导
致某行的仓库色号变化时,那一行才会重建。存的是 `Int?` 色号而不是解析后的 `Color`,
是因为 `Color` 在这个部署目标(macOS 13)上不是稳定可比较的值,色号是纯数据、比较可靠。

### 引用行菜单补齐 Copy Branch Name 与 Tracking Branch

`GitReferenceRowMenu.entries(...)` 新增两个动作:本地分支和远程分支都加 `copyBranchName`
(标签不加),本地分支加 `trackingBranch`。视图侧:`copyBranchName` 直接把
`reference.shortName` 写进 `NSPasteboard`;`trackingBranch` 展开成一个子菜单,数据来自
活动仓库的远程引用(`feature.gitReferences.filter { $0.kind == .remote }`),已配置上游时
先显示当前上游和一个“Stop Tracking Branch”清除项,没有远程分支时显示禁用的占位项。

写操作复用 Rust Core 已经支持的 `setUpstream` / `unsetUpstream`(见
`rust/lithe-core/src/git/mod.rs`,Windows 端 `git-branches-api.ts` 已在用),macOS 侧只补
接线:`GitOperations` 协议 + `RustGitOperations` 实现(`setUpstream(branch:to:)` 把上游
引用和分支名传给 Core),`GitService`、`GitFeatureModel.setUpstream` /
`unsetUpstream` 依次透传。**没有改动 Rust Core**,也没有新增 Core 命令。

### 引用行的刷新比较必须包含远程分支

`GitReferenceRowView` 以 `.equatable()` 渲染,只有 `==` 判不相等的行才会重建。Tracking Branch
子菜单读的是活动仓库的远程分支列表,所以这份列表必须参与相等判断:否则一次只增删远程分支的
`refs` 刷新(本地分支、选中和执行状态都没变)不会让行失效,子菜单会继续用建行时捕获的旧列表
——远程分支已删还列着,或明明有远程分支却仍显示 “No Remote Branches”。

原来 `remoteBranches` 放在 `GitReferenceRowActions` 里,而 `==` 刻意忽略这个结构(它的闭包每次
body 都会重建,比较它会让所有行每帧都重建)。现在把它从 `actions` 移出,成为
`GitReferenceRowView` 自己的值属性;同时把整条相等契约抽成 `GitReferenceRowRenderKey`
(定义在 `GitReferenceRows.swift`),`==` 只比较这个 key。**新增任何被行或菜单读取的字段,都要
加进 key,否则改了对 UI 没有影响。**

非活动仓库分组的行是只读的、只触发选中,菜单不出现,所以它们传空列表,避免无谓的行重建。

### 提交右键的重置支持 soft / mixed / hard

`GitFeatureModel.resetCurrentBranch(to:)` 以前写死 `--mixed`,现在加
`mode: GitResetMode = .mixed` 参数(`GitResetMode` 定义在 `GitModels.swift`,`argument`
返回 `--soft` / `--mixed` / `--hard`)。默认值保持 `.mixed`,老调用点行为不变。

提交右键原来是一个“Reset Current Branch to Here…”项,现在改成同名的**子菜单**,
列出 soft / mixed / hard 三项,对应 Windows 的 `resetToCommit(repoPath, revision, mode)`。
三档都走既有的确认对话框;hard 的确认按钮标记为 `destructive`(`GitCommitOperationKind`
新增 `isDestructive`),并把确认文案改成“丢弃工作区更改”,确保硬重置需要用户明确确认。
`GitGraphRowActions.onReset` 的签名相应变成 `(GitCommit, GitResetMode) -> Void`。

## 考虑过的备选方案

- **在 Core 增加“聚合所有仓库引用”的新命令**:被否。把呈现层的仓库身份引入稳定契约,还要
Expand All @@ -119,6 +188,13 @@ rename / delete)都只针对一个活动仓库,闭包直接从活动 `gitRep
“先隐式切仓库”会让一次点击产生用户没预期的活动仓库变更,失败时更难回滚;只读更简单也更安全。
- **给写操作闭包传入目标 `repositoryRoot`**:被否。写操作链路上游(对话框、待处理请求)都以活动
仓库为上下文,逐个改签名会把仓库身份扩散到调用链各处;在菜单层直接不提供更集中。
- **按“当前可见仓库列表”的序号给仓库上色**:被否。隐藏工作树会让剩余仓库的色号整体前移,
同一行的颜色会漂移;必须用 `availableRepositoryRoots`(过滤前完整列表)定位。
- **在 Core 新增一个 macOS 专用的设置上游命令**:被否。Core 的 `setUpstream` / `unsetUpstream`
已经存在且被 Windows 使用,macOS 只需接线。
- **在引用行里保存解析后的 `Color` 而不是色号**:被否。部署目标是 macOS 13,`SwiftUI.Color`
在那里不是可靠可比较的值,放进 `GitReferenceRowView.==` 会导致刷新判断不稳定;存 `Int?`
色号更可靠,也顺带让单仓库的“无颜色”状态可表达。

## 后果

Expand All @@ -130,6 +206,10 @@ rename / delete)都只针对一个活动仓库,闭包直接从活动 `gitRep
- 只在可见仓库数 `> 1` 时走分组渲染;开关关闭时被隐藏的是工作树仓库,活动仓库始终保留。
- 引用面板的 Local / Remote / Tags 展开状态在所有仓库间共享(与 Windows 一致),同名分组
(如 `feature`)的折叠状态在不同仓库间也是共享的——这是对 Windows 行为的对齐,非缺陷。
- 多仓库时每条引用行多一条 2.5px 色条、每个仓库节点多一个色点;单仓库工作区没有这些视觉元素。
颜色取自固定 8 色,超过 8 个仓库时按位置回绕,因此两个相距 8 个位置的仓库会同色。
- 引用行菜单新增 Copy Branch Name(本地 / 远程)和 Tracking Branch 子菜单;提交右键的重置
从单项变成 soft / mixed / hard 子菜单。这些项都复用现有写操作,不增加新的 Core 命令。

## 验证

Expand All @@ -146,25 +226,49 @@ rename / delete)都只针对一个活动仓库,闭包直接从活动 `gitRep
旧刷新返回后不能覆盖新页)。
- `macos/Tests/LitheTests/GitReferenceRowsBuilderTests.swift` 的 `Git reference row menu` 套件
校验菜单策略:只读行返回空条目,活动行的本地 / 远程 / 标签菜单项与启用状态符合预期,
分支操作进行中时写操作项被禁用。
分支操作进行中时写操作项被禁用,并新增 Copy Branch Name(本地 / 远程有、标签无)与
Tracking Branch(仅本地有)两条策略断言。
- `macos/Tests/LitheTests/GitReferenceRowRenderKeyTests.swift` 校验行的刷新契约:只增 / 只删
远程分支时 `GitReferenceRowRenderKey` 必须判不相等(保证子菜单重建),远程分支列表不变时
判相等(保证不白重建),行自身字段变化时仍然判不相等。
- `macos/Tests/LitheTests/GitRepositoryColorTests.swift` 校验配色:8 色互不相同且不透明,
N 个仓库拿到互不相同的色号,隐藏中间仓库时后续仓库色号不变(证明按完整仓库列表定色),
超过 8 个仓库回绕,单仓库不启用颜色,未知仓库回退到 0 号色。
- `macos/Tests/LitheTests/ContextMenuCoverageTests.swift` 更新提交右键断言:重置现在是子菜单,
可执行 mixed / hard 两项并回调到 `onReset(commit, mode)`。
- `macos/Tests/LitheTests/AppLocalizationTests.swift` 的英 / 中对照校验扩展到新增的
Copy Branch Name、Tracking Branch、Stop Tracking Branch、No Remote Branches 与
soft / mixed / hard 三档重置的标题和确认文案。
- 上游写操作复用 Core 已有的 `setUpstream` / `unsetUpstream`(`rust/lithe-core` 内已有测试
`git_write` 覆盖),macOS 侧只做接线,不新增 Rust 改动。
- git 控件本地化词条(含新增的两个开关文案)纳入
`macos/Tests/LitheTests/AppLocalizationTests.swift` 的英 / 中对照校验。
- 完整 macOS 编译与测试需要在 macOS 上运行:`./scripts/test-macos.sh`。
- `./scripts/verify-service-boundaries.sh`。
- **限制**:本次改动没有 macOS 工具链可用,Swift 代码和 UI 均未在本机编译或手工验证;
上述 Swift 测试由 macOS CI 执行。UI(仓库分组、工作树开关、跨仓库选中)未手工验证。
上述 Swift 测试由 macOS CI 执行。UI(仓库分组、工作树开关、跨仓库选中、仓库配色、新增
菜单项)未手工验证。

## 适用范围

- `macos/Sources/LitheGitModule/Models/GitModels.swift`
- `macos/Sources/LitheGitModule/Application/GitFeatureModel.swift`
- `macos/Sources/LitheGitModule/Services/GitService.swift`
- `macos/Sources/Lithe/Core/Rust/RustGitOperations.swift`
- `macos/Sources/Lithe/Views/Git/GitLogView.swift`
- `macos/Sources/Lithe/Views/Git/GitReferenceRows.swift`
- `macos/Sources/Lithe/Views/Git/GitGraphView.swift`
- `macos/Sources/Lithe/Views/Git/GitRepositoryColor.swift`
- `macos/Resources/en.lproj/Localizable.strings`
- `macos/Resources/zh-Hans.lproj/Localizable.strings`
- `macos/Tests/LitheGitModuleTests/GitModuleTests.swift`
- `macos/Tests/LitheTests/GitReferenceRowsBuilderTests.swift`
- `macos/Tests/LitheTests/GitReferenceRowRenderKeyTests.swift`
- `macos/Tests/LitheTests/GitRepositoryColorTests.swift`
- `macos/Tests/LitheTests/ContextMenuCoverageTests.swift`
- `macos/Tests/LitheTests/AppLocalizationTests.swift`

上游写操作复用 `rust/lithe-core/src/git/mod.rs` 已有的 `setUpstream` / `unsetUpstream`,
本笔记不改变这些命令的入参或契约。

不改变 `git.references`、`git.historyPage`、`workspace.repositories` 的 JSON 契约;不改提交图,
也不改顶部 `BranchSwitcherPopover`(仍为单仓库)。
1 change: 1 addition & 0 deletions docs/development/platform-parity-matrix.csv
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ git-diff-review,版本控制,Git,Diff 与变更审查,已实现,待验证,已实
git-history,版本控制,Git,提交历史与图谱,已实现,待验证,已实现,待验证,Git,分页浏览提交历史、分支图谱和提交详情,确认日期、作者和文件列表一致。,,macos/Sources/Lithe/Views/Git,windows/tauri/src/features/git
git-rebase-stash,版本控制,Git,Rebase 与 Stash,已实现,待验证,已实现,待验证,Git,执行交互式 Rebase 和 Stash 保存/恢复,确认中断、冲突和继续操作。,,macos/Sources/Lithe/Views/Git,windows/tauri/src/features/git
git-worktrees,版本控制,Git,Worktree 管理,已实现,待验证,已实现,待验证,Git,列出、创建、切换和删除 Worktree,确认路径、分支和安全检查。,,macos/Sources/Lithe/Views/Git,windows/tauri/src/features/git
git-multi-repository-references,版本控制,Git,多仓库引用面板:分组、配色与引用操作,已实现,待验证,已实现,待验证,Git,在多仓库工作区打开 Git Log:确认按仓库分组、仓库配色、非活动仓库分组只读,点其它仓库的引用会切换活动仓库且只加载一次,Pull 弹窗可选择远程分支与策略。,,macos/Sources/Lithe/Views/Git,windows/tauri/src/features/git
github-prs,协作,GitHub,Pull Request 列表与详情,已实现,待验证,已实现,待验证,GitHub,使用测试仓库验证 PR 列表、筛选、详情、分支比较和浏览器跳转。,,macos/Sources/Lithe/Views/GitHub; macos/Sources/Lithe/Services/GitHub,windows/tauri/src/features/github; shared/contracts/application-boundary.md
github-reviews-comments,协作,GitHub,Review 与评论,已实现,待验证,已实现,待验证,GitHub,创建、查看和回复 Review/评论,确认权限错误和网络失败不会丢失草稿。,,macos/Sources/Lithe/Views/GitHub,windows/tauri/src/features/github
ai-commit-generation,AI,AI 提交信息,Provider 配置与提交信息生成,已实现,待验证,已实现,待验证,Git / AI,用相同 diff、规则和 Provider 配置比较请求计划、取消、错误和生成文本。,,macos/Sources/Lithe/Application/Features/CommitWorkflowCoordinator.swift; macos/Sources/Lithe/Platform/MacOS/AI,windows/tauri/src/features/git/services/ai-commit-service.ts; shared/contracts/ai-commit.md
Expand Down
9 changes: 5 additions & 4 deletions docs/development/platform-parity-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

- 最后复核:2026-09-24
- 盘点状态:initial-static-inventory(根据 macOS Views/Application/Services、Windows features/extensions 和共享契约的代码入口进行初版盘点;未替代真实运行验收。)
- 功能项:81
- macOS:实现:✅ 69 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 2 平台专属;验证:✔️ 0 已验证,🔍 72 待验证,— 9 不适用
- Windows:实现:✅ 75 已实现,🟡 4 部分实现,❌ 2 未实现,🧩 0 平台专属;验证:✔️ 0 已验证,🔍 79 待验证,— 2 不适用
- 功能项:82
- macOS:实现:✅ 70 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 2 平台专属;验证:✔️ 0 已验证,🔍 73 待验证,— 9 不适用
- Windows:实现:✅ 76 已实现,🟡 4 部分实现,❌ 2 未实现,🧩 0 平台专属;验证:✔️ 0 已验证,🔍 80 待验证,— 2 不适用

## 实现状态定义

Expand Down Expand Up @@ -76,7 +76,7 @@
</details>

<details>
<summary><strong>版本控制</strong> · 6 个能力点</summary>
<summary><strong>版本控制</strong> · 7 个能力点</summary>

| 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
Expand All @@ -86,6 +86,7 @@
| Git | **提交历史与图谱**<br><sub>git-history</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Git`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/git`</sub> | Git | 分页浏览提交历史、分支图谱和提交详情,确认日期、作者和文件列表一致。 | |
| Git | **Rebase 与 Stash**<br><sub>git-rebase-stash</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Git`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/git`</sub> | Git | 执行交互式 Rebase 和 Stash 保存/恢复,确认中断、冲突和继续操作。 | |
| Git | **Worktree 管理**<br><sub>git-worktrees</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Git`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/git`</sub> | Git | 列出、创建、切换和删除 Worktree,确认路径、分支和安全检查。 | |
| Git | **多仓库引用面板:分组、配色与引用操作**<br><sub>git-multi-repository-references</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Git`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/git`</sub> | Git | 在多仓库工作区打开 Git Log:确认按仓库分组、仓库配色、非活动仓库分组只读,点其它仓库的引用会切换活动仓库且只加载一次,Pull 弹窗可选择远程分支与策略。 | |

</details>

Expand Down
Loading
Loading