Skip to content

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 ​

另有 ci 10、style 9、revert 7,以及少量没有类型前缀的提交。

测试提交占将近四分之一,这是刻意的结果。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 个是中文。

一条提交信息的结构 ​

feat(gates): reject module-scope await in package sources
标题:类型(范围): 动词开头,写行为结果
Package modules that other code imports must not suspend evaluation at module scope.
问题与规则:为什么需要这次改动
A syntax-aware gate scans packages/*/*/src and excludes only executables (a shebang or a package bin target).
机制:写到评审需要的粒度,包括例外
The SenseVoice worker, the SSH helper entry, and the Inspector worker entry now run an explicit main().
附带修正:被新规则拦下的存量代码在同一提交里修好
Fixes #6099.
关联 issue
Validation: 305 focused tests passed, one skipped; … 100% statements, branches, functions, and lines. Removing the shutdown guard makes the new regression fail.
证据段:实际运行的检查、结果,以及负向对照:去掉修复后新测试会失败

前四行来自提交 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。

依据 deepseek-harness 仓库 master 分支及其 git 历史整理