13 · Git 与 PR 规范
提交、合并、审批与发布规范
.github/pull_request_template.md.github/review-ownership/.github/workflows/weighted-approval.yml.agents/skills/dsh-merging-stacked-prs/docs/cookbook/responding-to-pr-review-on-a-stack.md仓库从 2026-06-10 建立到 10 月 20 日共有 22,395 次提交。本章的统计取自最近 3,000 次非合并提交,以及 master 上最近 1,500 次第一父提交。
提交类型:Conventional Commits
测试提交占将近四分之一,这是刻意的结果。2026-06-16 的提交 67447fcdc3 在规则里写明:每个 PR 的提交历史(包括评审修复提交和回归测试提交)是有意保留的记录。仓库最早几次提交没有类型前缀(例如 "Initialize repo with README, AGENTS.md, and CLAUDE.md symlink"),6 月 12 日起改用 Conventional Commits。
- scope 写包名或产品区域。出现最多的是 web、desktop、cua、client、session、ptc、ui-chat、subagent、llm。门禁类改动用
gates、infra,决策记录用notes。 - 标题用动词原形开头,描述行为结果。例如
fix(web): keep model menu positioning local、feat(gates): reject module-scope await in package sources。标题长度中位数 64 字符,90% 在 82 字符以内。 - 破坏性变更加
!,例如refactor(experimental)!: classify optional packages and move prototypes,并配一份升级指南。 - revert 写恢复了什么行为:
revert: restore the bundled DeepSeek Flash display name,不写 "Revert 'xxx'"。 - 提交信息以英文为主。最近 4,000 个标题中约有 27 个是中文。
一条提交信息的结构
前四行来自提交 b77cc1c0f2;"Fixes" 行来自合入 #6103 的提交;Validation 段来自另一次修复提交。
Validation: 段不是每个提交都有,但出现时格式很一致:列出聚焦测试数量、跳过数、改动文件的覆盖率、选中的 snapshot、doc-sync 门禁数,有时还写明本地无法复现、要等下一次 CI 才能判定的部分。这正是"只汇报实际运行过的命令"在提交信息里的体现。
评审修复留在历史里
最近 4,000 个标题中约有 215 个提到 review,例如 fix(ui-chat): address review on single fold motion。部分修复针对自动评审机器人 ds-review-bot 的意见,提交正文会写明回应的是哪一轮。修复提交单独存在,评审者能只看新增的改动。
合并策略
master 最近 1,500 次第一父提交中,1,413 次是 "Merge pull request",86 次是 squash 风格的 "(#N)" 提交。规则经历了一次调整:
- 2026-06-16:规定 PR 一律用 merge commit 合并(
gh pr merge --merge),不用 squash 或 rebase,以保留 PR 内的提交历史。 - 2026-08-02:Note
native-github-stacks-and-optional-rebases放宽了"刷新"分支的方式:独立 PR 和 stack 都可以 merge-forward 或 rebase。改写远端历史必须用--force-with-lease,远端有变化就中止,禁止裸--force。依赖链上的多个 PR 必须使用 GitHub 原生 stack,用gh stack merge --merge整体落地。 - 修复放在引入问题的那一层。根
AGENTS.md要求独立改动拆成单独 PR,在问题扩散前修引入它的 PR。
分支命名
合并记录里的分支前缀依次是 worktree/(214)、fix/(193)、个人前缀如 turtle/(110)、feat/(88),以及 perf/、docs/、build/、refactor/。worktree/ 来自在独立 git worktree 里工作的 Agent 会话,发布也走这种分支,例如 worktree/release-dsh-0.2.1-rc.2。
PR 模板与标签
- Motivation:一句话说明问题,用
Fixes #NN或Related #NN关联同仓库 issue。 - Changes:分别写接口层面(命令、配置、API、协议、持久化格式)和可观察行为层面的变化,没有就写 None。
- Testing:每种测试方法一个条目,附折叠的 Proof 区放可复核的证据。
- 标签:每个 PR 一个
kind/*,所有相关的area/*,再加 GitHub 原生 Issue Type。
审批:加权评分
.github/review-ownership/ 和 weighted-approval 工作流计算审批分数,达到 2 分才算通过:
- 名单里的核心维护者每次批准记 2 分,其他有写权限的评审者记 1 分。
- 1 分的批准按评审者对被修改代码的所有权加权:用
git blame统计改动的旧生产代码行中有多少是他写的。所有权 0%、12.5%、25% 分别对应 1、1.5、2 分,上限 2 分。文档、测试、fixture、snapshot 和生成代码不计入。 - PR 作者按已合并的 PR 数获得
min(100, 合并数) × 11 / 1000分,最多 1.1 分;作者自己的 review 不计分。
这套机制让"真正写过这段代码的人"的批准更有分量,也避免一个新人自己批准自己。
发布节奏
发布提交只做版本号变更,例如 release(dsh): 0.2.1-rc.2 同时修改所有包的 package.json,所有包统一版本。标签格式是 dsh-v0.2.1-rc.2,节奏是 alpha.1 到 alpha.5,再到 rc.1、rc.2。vendor 的 Cordis 包单独发布:release(vendor): cordis 4.0.5, cosmokit 1.8.6, …。
可以借鉴
- 用 Conventional Commits,scope 写包名;标题写行为结果,不写"改了哪个文件"。
- 测试和评审修复作为独立提交保留,合并时不 squash 掉。
- 提交正文加
Validation:段,列出实际运行的检查和负向对照。 - 破坏性变更用
!标记,并配升级说明;改写远端历史只用--force-with-lease。