工具入门 · 协作流程

PR 与 Git Worktree

这是团队每天都在用的两个工具:Pull Request(PR)决定一次改动能不能上线,git worktree决定多人(多会话)能不能不互相干扰地并行开发。全文结合 Akke 仓库最近 3 天的真实提交,不讲空泛的 Git 教程。

篇幅 2 章 · 约 6 分钟 受众 新加入项目的同事 主线 Akke 仓库真实 commit / CI 配置

Akke 是私有仓库、免费套餐,开不了 GitHub 原生的分支保护,push 到 main 会立即触发部署。所以团队用 PR 当作"合并前的检查点";同时因为经常同时开好几个 Claude Code 会话改代码,又需要 git worktree 来隔离工作区,不然谁的改动都可能覆盖别人的。

怎么用这篇 · 下面每一个分支名、commit、CI 规则都取自 Akke 仓库当前的真实状态,你可以自己 git log / git worktree list 复核。读完你应该能回答:这次改动该不该开 PR、该 squash 还是保留 merge、什么时候该开一个新 worktree。
01

Section 01 · Pull Request

合并前的检查点:谁改的、CI 有没有绿、要不要走人工/AI review

PR 不是 git 本身的功能,是 GitHub 加的一层协作流程:分支 push 上去 → CI 跑起来 → review → 合并。它在"直接 push 到 main"和"完全没有检查点"之间,插入一个可以被看见、被拦截的关卡。

不是所有改动都要走 PR

Akke 按风险给改动路径分层,规则写在仓库 CLAUDE.md 里:

CLAUDE.md · 改动走 PR 还是直推
高危路径必须走 PR:src/lib/llm/**、src/app/api/cron/**、
  vercel.json、supabase/migrations/**、.github/**、src/lib/auth/**
其余 src/ 代码:建议 PR,小 fix 直推可接受
docs/**、诊断脚本:直推

执法两道闸:.husky/pre-push 本地事前拦截 + CI 事后标红告警

最近 3 天的真实提交

Akke 的 commit message 统一用 type(scope): 描述,合并后常带 (#PR号)。这是过去 3 天里 CI 相关的真实记录:

类型CommitPR
perf(ci)fly-worker 合并 test+deploy 两个 job,省 ~1min/次部署
fix(ci)model-watch 日报 push 加 --no-verify,治每天挂在 pre-push 的 code 128#728
fix(ci)smoke-tuwen-weekly-auto.yml 一个 step name 加引号,治 Dependabot 解析失败#727
chore(ci)high-risk-path-guard 加 paths 白名单,非高危推送不启 runner#726
chore(ci)砍掉 CI 的 pull_request 触发,只留 push:main#725
真实事故 · #728 · 一个每天自动跑的日报脚本,push 结果时天天卡在本地 pre-push 钩子上(exit 128)——护栏不区分"人在推"还是"自动化在推",只要命中高危路径就拦。修法是给这条固定的自动化 push 加 --no-verify教训:pre-push 钩子对所有 push 一视同仁,写自动化脚本时要预先想好会不会撞上它。
真实决策 · #725 · Akke 私有库开不了分支保护,团队用 gh pr merge --auto 自动合并——CI 绿就合,根本不等 PR 侧那次 CI 跑完。结果 PR 触发的那份 CI"跑了个寂寞",干脆砍掉,只留 push 到 main 后触发。教训:CI 触发条件要跟真实的合并流程对齐,摆设的关卡不如没有。

Squash 还是保留 merge commit

Merge commit

commit 数多、每步都是独立可读的开发步骤——大 feature、需要保留可回滚粒度时用。

vs

Squash

chore / 小 fix / 多次 fixup 的整理型改动——没有独立回滚价值,压平成一条更干净。

仓库里真正的 merge commit 只有 23 条,而最近 100 条提交里有 43 条尾部带 (#PR号)——说明 Akke 的大部分 PR 走的是 squash merge,只有少数大 feature 才保留 merge 历史。

02

Section 02 · Git Worktree

同一个仓库、多个并行工作目录,专治"多会话互抢工作区"

git worktree 让同一个 .git 仓库同时在多个目录里 checkout 不同分支,不用来回 stash/checkout,也不用 clone 多份仓库。每个 worktree 有独立工作区,但共享同一份 stash 列表——这一点是后面坑的根源。

为什么 Akke 需要它 · 同一台机器上经常同时跑几个 Claude Code 会话,都挤在一个目录里,谁 checkout / commit 都会覆盖别人的工作区。团队真实记录过一次事故:HEAD 在几分钟内四次易手,落后 commit 数在 87 → 0 → 3 之间乱跳,排查后确认是并发会话互抢。

当前真实在用的 worktree

分支用途
perf/fly-worker-single-job主工作目录,fly-worker CI job 合并优化
feat/lark-sales-rehearsal飞书销售话术 AI 演练评测
docs/memory-rc-v1反向评论风控升级归档
feat/wecom-chat-spike企微客服 AI 代回设计 spike(只读探针)
feat/outbound-pipeline三通道外联产线,独立 Python worker

开一个新的走脚本封装:scripts/new-worktree.sh <name> —— 从 origin/main 切新分支建 worktree,并把 memory 文件软链回主目录(避免每个分支各写一份 memory 互相冲突),用完 git worktree remove 清理。

真实事故 · git stash 是仓库级全局共享的——不管你在哪个 worktree,stash 列表都是同一份。在 worktree-A 里 stash push,切到 worktree-B stash pop,改动会直接落进错误的分支,且没有任何警告。这次事故里差点把用户的 WIP 覆盖进主工作区,靠 reset --hard 才挽回。pop 前先 git stash show -p 确认归属,多 worktree 场景优先转成分支再 cherry-pick。
真实事故 · 在基于 origin/main 新建的 worktree 里改一个导出脚本,直接 cp 本地旧版文件覆盖,结果把 main 上已经存在的两个参数功能静默删掉了——开 PR 前 git diff origin/main 才发现少了 51 行。worktree 是全新 checkout,改文件前先确认不是覆盖了别人已有的功能。
高危路径必须走 PR,多会话并行必须开 worktree——两者都是给"改坏了不知道是谁改的"上的保险。