01

## 同步状态条

接线即可

替换现在标题行里那个孤零零的圆点。四个格子回答四个问题:我在哪个分支、有多少要推、有多少要拉、这两个数字还能不能信。

web-terminal develop ● 3 未提交
/Users/yiukai/Documents/git/web-terminal
Upstream origin/develop
待推送 ↑ 9 9 个 commit 只在本地
待拉取 ↓ 0 存疑 上次 fetch 在 19 天前 — 这个 0 不可信

「存疑」标记是这块最重要的设计。`ahead/behind` 比的是本地缓存的 `@{u}`,不 fetch 就永远不会变 —— 没有这个标记,面板会自信地告诉你已经同步,而这正是它此刻在做的事。 规则:`FETCH_HEAD` 的 mtime 超过阈值(建议 1 小时)就给 ↓ 挂上存疑,并把 Fetch 按钮点亮。 Fetch 是只读的,不 pull、不 merge —— 符合写侧一贯的克制。

02

## 提交列表 · 推送分界线

新增

你要的不是「有 9 个没推」这个数字,而是哪 9 个。复用现有的 commit 列表,给未推送的加左侧竖线和 ↑,在边界上画出 upstream 的位置 —— 一眼看完,零点击。

Recent commits ↑ 9 未推送
↑553a00c8mdocs(progress): log the three leftover fixes + live verification
↑d92caed22mMerge fix-tunnel-leftovers: gitignore-swallowed source, host identifiers in logs
↑2a602d531mfix(tunnel): close the three leftovers from the renewal-deadlock fix
↑befe6771hMerge fix-cert-renew-deadlock: break the expired-leaf renewal deadlock
↑7064a391hchore: drop accidentally committed node_modules symlinks
↑0970c621hdocs(progress): log the expired-leaf renewal deadlock fix + live self-heal proof
↑5509c812hfix(tunnel): recover an expired leaf over plain HTTPS, not mTLS
↑f3f4d8b2hfix(tunnel): break the expired-leaf renewal deadlock
↑b1bc50c3hfix(session): give the PTY a UTF-8 locale so tmux stops mangling CJK
origin/develop
1dbed546ddocs(progress): log the zero-touch enrollment + control-panel session
675de779dfeat(control-panel): web admin UI for the zero-touch tunnel
7c1d43310dMerge feat/zero-touch-enrollment: zero-touch tunnel enrollment (host + phone)
0b35dc010dfeat(android): wire zero-touch device enrollment + fix renew/cache (B-track)
显示最近 20 条

分界线不是装饰 —— 它是 `origin/develop` 在时间轴上的真实位置。分支没有 upstream 时不画线,改在顶部显示「无 upstream」(见 ④)。

03

## Worktree 面板

重做

现在这块叫 BRANCH、只有一行。按「每个 session 一个 worktree」的规矩,它很快会变成 4–5 行,而且每一行要能独立回答:哪个分支、在哪、干净不干净、推没推、里面跑着谁。这是编辑器给不了的视角 —— Cursor 一次只看得见一个 worktree。

Worktrees 2
develop 当前 ● 3 ↑ 9
/Users/yiukai/Documents/git/web-terminal
2 sessions
worktree-project-detail-git-mockup 干净 无 upstream
.claude/worktrees/project-detail-git-mockup
1 session

「无 upstream」必须显式标出。新建的 worktree 分支天然没有 upstream,`ahead/behind` 全是 `undefined` —— 绝不能因为「没有数字」就渲染成绿色的「已同步」。这是这套 UI 最容易犯的错。

04

## 状态变体

规格

同步条的全部形态。做设计稿的意义一半在这里 —— 边界情况不定死,实现时就会各写各的。

已同步 · 刚 fetch
✓ 已同步
↑0 ↓0 且 fetch 在阈值内。唯一可以显示绿色的情况。
待推送
↑ 9
主状态。数字用 accent,提交列表同步高亮。
待拉取
↓ 3
用中性色而非警告色 —— 落后不是错误,只是信息。
数据陈旧
存疑
FETCH_HEAD 超过 1 小时。挂在 ↓ 上,不挂在 ↑ 上 —— ↑ 不依赖 fetch,永远准。
无 upstream
无 upstream
新 worktree 分支的默认状态。不显示 ↑↓,不显示绿色。
HEAD 游离
detached HEAD
不显示分支名和 ↑↓,只显示短 sha。push 按钮禁用。

只有一个状态配用绿色:↑0 ↓0 且刚 fetch 过。其余一律中性或警示 —— 绿色在这里的含义是「我确认过了,你不用管」,给错了就是骗人。

05

## 数据来源与成本

编号 = 建议实现顺序。01 和 02 共用同一份数据,应该一个 PR 一起做。

元素 | 数据来源 | 后端现状
--- | --- | ---
分支名 | `.git/HEAD`(不 spawn 进程) | ✓ 已有
↑ ahead / ↓ behind | `git rev-list --count --left-right @{u}...HEAD` | 助手已有,`ProjectDetail` 没接
upstream 名字 | `git rev-parse --abbrev-ref @{u}` | 缺
未推送的 sha 集合 | `git log @{u}..HEAD --format=%H` | 缺
上次 fetch 时间 | `.git/FETCH_HEAD` 的 mtime(不 spawn) | 缺
Fetch 按钮 | `git fetch --no-tags`,只读 | 缺(需新路由)
未提交数量 | `git status --porcelain` 的行数 | 只有布尔 `dirty`,没有计数
worktree 列表 | `git worktree list --porcelain` | ✓ 已有
每个 worktree 的 dirty / ↑↓ | 同上,逐 worktree 跑一次 | 缺 — 成本最高的一项

成本控制。只有分支名和 fetch 时间是读文件、免费的;其余每项都要 spawn 一次 `git`,而这台机器同时在跑 Claude Code 和构建。三条便宜的对策:按仓库去重(N 个 tab 同一个 repo 只查一次)、盯 `.git/HEAD` / `.git/index` / `.git/refs` 的 mtime 变了才查、页面隐藏时暂停。逐 worktree 的 ↑↓ 建议懒加载 —— 展开那一行才算。

范围外。这一页不加 reset、不加切分支 checkout、不加 clean、不加 force-push。 stage / commit / push 已经在 `diff.ts` 里,维持现状。 这块面板的职责是告诉你发生了什么,不是替代编辑器去处理它。