Skip to content

feat(windows): add merge, rebase, and conflict resolution UI - #107

Merged
1lck merged 11 commits into
1lck:preview/0.3.0from
yangboxuan726:feat/windows-git-merge-rebase-ui
Aug 16, 2026
Merged

feat(windows): add merge, rebase, and conflict resolution UI#107
1lck merged 11 commits into
1lck:preview/0.3.0from
yangboxuan726:feat/windows-git-merge-rebase-ui

Conversation

@yangboxuan726

Copy link
Copy Markdown
Contributor

功能

为 Windows 端补齐 merge / rebase / 冲突处理的完整工作流,对齐 macOS 参考端(其设计仿 IntelliJ IDEA)。此前 Windows 端没有任何 merge/rebase 入口与冲突界面,Core 侧全部能力(git.write merge/rebasegit.integrationPreflightgit.operationStateoperationContinue/Abort/Skipgit.conflictMarkers)未被使用。

实现(8 个提交)

  • 翻译层: platform.rs 新增 git_merge / git_rebase / git_integration_preflight / git_operation_state / git_operation_continue|abort|skip / git_conflict_markers 七组翻译,分支引用统一走 local_branch_reference 补全
  • 适配层: git_operation_state(空 kind → null)、git_integration_preflightgit_conflict_markers 结果适配
  • 数据层: 新 git-integration-api(preflight 先行、以 operationState 区分"冲突停止"与"真失败"、操作后无条件触发刷新); git.store 增加 operationState,挂入现有事件驱动刷新管线——终端发起的 merge 同样能被横幅如实反映
  • UI: 分支管理器非当前分支行的 "⋯" 菜单(Merge into Current Branch / Rebase Current Branch onto This / Delete Branch);Changes 列表顶部的操作横幅(标题、rebase Step N of M、冲突计数、Continue[有冲突时禁用]/Skip Commit[仅 rebase]/Abort);提交护栏(存在未解决冲突或暂存区含冲突标记时拦截并列出文件名)
  • 两枚修复: 菜单触发器的事件冒泡拦截、去掉 Button tooltip 包裹导致 Base UI 触发器失效的问题

验证

  • cargo test(src-tauri): 19/19(新增 4 组翻译测试)
  • bun test: 41/41(新增 operationState/preflight/markers 适配测试)
  • tsc --noEmit: 通过
  • 真机验收 T1–T8 全部通过(真实仓库逐用例操作 + git 状态断言): 菜单存在性、冲突合并横幅、解决冲突后 Continue 启用、Continue 完成双父合并、Abort 完整回滚、rebase Step/Skip/线性历史、提交护栏拦截、脏工作区预检(不进入合并状态)

已知边界(与 macOS 参考端行为一致或属既有课题)

  • 冲突文件解决但未暂存( M)时 Continue 即启用——与 macOS 相同;Core 侧 merge --continue 会拒绝并回显 git 报错,无误操作风险
  • 外部回滚(Abort)后已打开的编辑器缓冲不自动刷新——Windows 文件监听的既有课题,与本功能无关
  • 阻塞场景提示手动 stash(列出文件名),macOS 的 stash-and-continue 自动延迟恢复与 cherry-pick/revert 入口留作后续 PR

关联

…ispatcher

- translate git_merge and git_rebase into git.write with qualified
  refs/heads references, mirroring the checkout translations
- add git_integration_preflight, git_operation_state,
  git_operation_continue/abort/skip, and git_conflict_markers
  translations so the frontend can drive the shared core's conflict
  machinery
- map git.operationState responses into a UI-ready shape and collapse
  the no-operation case to null, matching the macOS model
- pass through integration preflight blocking paths and conflict
  marker paths for the upcoming banner and commit guard
- add git-integration-api with preflight-first merge/rebase that
  distinguishes conflict stops from real failures via operation state
- store operationState in the workspace git store and refresh it on
  every status pass so banner state stays true to the repository
- mirror the macOS banner: in-progress title, rebase step counters,
  conflict count, and Continue/Skip/Abort actions
- keep Continue disabled while conflicts remain and re-run resolution
  through the shared core, refreshing state after every attempt
- add a per-branch action menu with Merge into Current Branch and
  Rebase Current Branch onto This behind one confirmation each
- surface clean, conflict-stop, blocked-by-dirty, and failure outcomes
  with the shared core's preflight and operation state
- refuse to commit while the operation state still lists conflicted
  paths or staged files contain conflict markers, with the file names
  surfaced instead of Git's raw refusal
The action wrapper only stopped pointer-down propagation, so the click
that opened the menu also bubbled to the row's checkout handler, which
disabled the trigger and closed the menu instantly. Stop click
propagation like the retired delete button did.
…menu

Button wraps itself in a Tooltip component when given a tooltip prop,
so Base UI's render prop attached the menu trigger handlers to the
tooltip wrapper instead of the real button and clicks never opened the
menu. Pass only aria-label, like the working diff header trigger.
@yangboxuan726
yangboxuan726 requested a review from 1lck as a code owner August 16, 2026 08:07

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

整体架构方向合理,但下面几个错误处理和恢复路径需要确认并修正。

// A conflict stop exits non-zero like a real failure; the authoritative
// distinction is whether Git left an operation state behind.
const state = await getOperationState(repoPath);
if (state && state.kind === operation && state.conflictedPaths.length > 0) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: 这里不能用 conflictedPaths.length > 0 判断操作是否仍在进行。Rust Core 会独立报告 operation kind 和当前未合并路径;例如 rebase 因空提交、编辑器或 hook 失败而暂停时,operation state 仍存在但冲突列表可以为空。当前逻辑会返回普通 error 且不触发刷新,导致 Continue / Skip / Abort 横幅不出现。建议任何 kind 匹配的非空 state 都按 stopped/in-progress 处理并触发刷新,最好增加独立的 stopped outcome,并补一个空冲突列表的回归测试。

return await tauriInvoke<GitOperationState | null>("git_operation_state", {
repoPath: resolvedRepoPath,
});
} catch {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: 查询失败不能转换成 null。调用方会把 null 写回 store,当 Core 调用瞬时失败或仓库暂时不可访问时,这会把仍在进行的 merge/rebase 当作 idle 并隐藏恢复按钮。请传播结构化错误,或至少让刷新失败时保留最后一次已知 operation state。

repoPath: resolvedRepoPath,
});
return result.paths;
} catch {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: 这里返回空数组会让冲突标记保护 fail-open:调用方把 [] 当作安全并继续 commit。已经 stage 的 <<<<<<< 等文本不再是 unmerged index,Git 自身不会阻止提交。请把查询失败传播给提交面板并显示可重试错误;检查失败时不要执行 commit,同时补充 Core 查询失败的测试。

@1lck
1lck merged commit 8a86b6d into 1lck:preview/0.3.0 Aug 16, 2026
8 of 12 checks passed
@yangboxuan726
yangboxuan726 deleted the feat/windows-git-merge-rebase-ui branch August 16, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants