资讯动态

Zulip 提交纪律指南:如何写出干净、连贯、可审查的 Git 提交历史

发布时间:2026/9/12 2:13:07 来源:尧图企业网站定制
Zulip 提交纪律指南如何写出干净、连贯、可审查的 Git 提交历史【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulipZulip 是一个大型开源团队聊天服务代码库横跨 Django 后端、TypeScript/Handlebars 前端与大量运维脚本任何一次合并都可能影响数百名协作者。为了在这种规模下保持代码可审查、可回滚、可追溯Zulip 社区严格遵循 Git 项目自身的提交纪律实践——每个提交是一个最小的连贯想法Each commit is a minimal coherent idea。本文以 docs/contributing/commit-discipline.md 为主体结合仓库内真实的 lint 规则、Git 钩子脚本与 Git 工具链系统讲解如何构造提交结构、编写高质量提交消息并最终形成一条干净的提交历史。为什么提交纪律如此重要提交纪律需要付出额外的心力但它带来三方面直接收益让代码审查者更容易发现 bug。一个提交只做一件事时diff 的语义边界清晰审查者可以集中精力验证这个改动是否正确而不是在混杂的改动中寻找问题。让提交历史成为更有价值的资源。后来的开发者通过git log阅读历史时能理解代码为什么是现在这个样子这本身就是预防 bug 的重要手段。让发布管理与回归排查更高效。每个提交以清晰的组件前缀开头维护者可以快速 skim 历史定位回归来源。Zulip 在 docs/git/overview.md 中明确采用rebase 导向的工作流不使用 merge commit用git fetchgit rebase或git pull --rebase替代git pull。这一策略避免了合并分支时产生的大量杂讯提交让提交历史保持可读性——副作用是许多被合并的 PR 在 GitHub 界面上显示为closed而非merged这是 Zulip 团队有意接受的结果。每个提交必须连贯coherent连贯意味着一个提交是自洽的整体可以从以下几点检验必须通过测试。某个改动所需的测试更新应放在同一个提交里而不是事后追加一个修复上个提交弄坏的测试的提交。不能让 Zulip 变得更糟。例如先加后端能力而不加对应的前端入口是可以接受的但只加前端组件而没有任何后端支撑则不行。应能独立安全部署。如果某个提交单独部署不安全必须在提交消息中详细解释原因可以使用[manual]标记。因此先实现一个新 API 端点、再在后续提交补安全校验的做法应当避免——安全检查必须从第一个提交就存在。错误处理应与可能触发它的代码一起提交。TODO 注释应出现在引入该问题或功能的那个提交里而不是散落在后续提交中。这些规则保证了历史中的每一个节点都是可构建、可运行、可回滚的状态这正是大型项目长期演进的根基。提交应尽量最小化minimal只要可能就找到能够与项目其余部分分离的复杂度块拆成独立的小提交准备工作提交如果需要重构代码、为既有功能补测试、重命名变量或函数以及任何不改变产品功能的改动都应拆成一系列可以独立于新功能合并的预备提交。移动代码要单独提交把代码从一个文件移到另一个文件应当与功能改动、甚至与文件内的重构分开放在不同的提交里。两种不同的重构放进不同提交。两个不同的功能放进不同提交。自我检验信号如果你发现自己在写一个读起来像一堆不太相关的事情的清单的提交消息那就应该拆成多个提交。何时不必过度最小化全新功能不必为每个子特性单独开提交。例如从零写一个新工具完全可以一个初始提交带上丰富的选项与特性。但 2000 行的大块新代码审查起来并不愉快仍应把提交拆成可审查的单位。不必把后端提交与前端提交分开尽管后端往往可以单独自洽Zulip 允许前后端改动共处一个提交。写一条干净的提交历史宁可提交偏小过细的提交事后很容易 squash 合并反之则很难拆分。所以倾向于小提交代码审查者会告诉你哪些需要 squash。修 bug 用 amend 而不是追加如果一个提交不通过测试通常应该git commit --amend修正该提交而不是在其上再写一个修复测试的提交。PR 内的多个提交不要把多个改动混进同一个提交但在一个 PR 里按各自提交包含多个相关改动是受欢迎的。如果发现一个与手头工作只有部分相关的小问题懒得单独建 PR可以把它作为 PR 中的额外提交追加提交消息里要清楚说明 bug也可以单独开 PR。无论如何不要把无关改动 squash 进同一个提交——审查者会要求你拆开。推荐的开发节奏开始实现功能后才发现需要先重构时建议git stash暂存部分功能先完成重构并提交再git stash pop恢复并继续实现功能。这样历史始终保持线性清晰。当你的 PR 提交历史不符合这些规范时用git rebase -i修复。仓库中 docs/git/fixing-commits.md 给出了完整操作清单修改最近一次提交消息git commit --amend -m New message修改最近一次提交内容改完文件后git add file再git commit --amend修改更早的提交消息git rebase -i HEAD~5把pick改为reword删除旧提交git rebase -i HEAD~n把pick改为drop合并多个提交git rebase -i HEAD~n把pick改为squash重排提交顺序在git rebase -i中直接调整行序整理后推送git push origin my-feature-branch注意号表示强制推送。提交消息的两段式结构提交消息由两部分组成summary摘要一行简述改动description描述进一步说明改动内容、动机以及为何能改进项目。在 Zulip 中summary 又分为两个部分一两个词描述被改动的代码库区域组件前缀一句话概括你的改动。以下是一个优秀提交消息的完整示例tests: Remove ignored realm_str parameter from message send test.In commit 8181ec4, we removed therealm_stras a parameter forsend_message_backend. This removes a missed test that included this as a parameter for that endpoint/function.提交消息是你与审查者、以及未来贡献者沟通的关键载体其重要性不亚于你写的代码。Summary 第一部分组件前缀第一部分只能是1–2 个小写单词后跟一个:用来描述提交改动的产品区域。这些前缀对维护者做发布管理和排查回归至关重要。常见示例settings、message feed、compose、left sidebar、right sidebar、recent指Recent conversations、search、markdown、tooltips、popovers、drafts、integrations、email、docs、help、api docs。当能简洁地写得更具体时如emoji、spoilers、polls会更好但简单的settings:优于对某个具体设置的长篇描述。如果改动无法干净地映射到产品区域CSS 专用改动可用css也可以使用主要被修改的文件或技术子系统名不要全路径如realm_icon而非zerver/lib/realm_icon.py。其他提示一律小写如settings不要Settings如果很难为改动找到 1–2 词的描述请重新考虑你的提交结构是否合理永远不要用bug、fix、refactor这类泛化词。Summary 第二部分祈使句第二部分是一个完整的句子简要总结你的改动规则如下以祈使语气的动词开头如 fix、add、change、rename使用正确的大小写与标点避免缩写与首字母缩略词保持简洁、不写多余细节。例如 Change X and update tests/docs 应写成 Change X——因为如前文所述每个提交本就预期带上了必要的测试与文档更新让对 Zulip 代码库熟悉、但未参与你这项工作的人也能读懂整个 summary两部分合计不超过 72 个字符。优秀 summary 示例provision: Improve performance of installing npm.channel: Discard all HTTP responses while reloading.integrations: Add GitLab integration.typeahead: Rename compare_by_popularity() for clarity.typeahead: Convert to ES6 module.tests: Compile Handlebars templates with source maps.blueslip: Add feature to time common operations.gather_subscriptions: Fix exception handling bad input.channel_settings: Fix save/discard widget on narrow screens.好坏对比好gather_subscriptions: Fix exception handling bad input.不好gather_subscriptions was broken——没有说明坏在哪里也不符合格式规范Fix exception when given bad input——无法看出改了代码库的哪一部分非祈使语气gather_subscriptions: Fixing exception when given bad input. / gather_subscriptions: Fixed exception when given bad input.仓库中的自动化验证Zulip 用 gitlint 把上述规范落成可执行的规则。仓库根目录的 .gitlint 配置[general] ignoretitle-trailing-punctuation, body-min-length, body-is-missing extra-pathtools/lib/gitlint_rules.py [title-match-regex] regex^(.:\ )?[A-Z].\.$ [title-max-length] line-length72 [body-max-line-length] line-length76其中title-match-regex强制 title 以大写字母开头、以句点结尾并允许可选的前缀:部分title-max-length把 title 限制在 72 字符以内与文档要求一致body-max-line-length将正文限制在 76 字符。此外tools/lib/gitlint_rules.py 定义了自定义规则ImperativeMoodidZ1它会忽略区段标签:前缀检查 title 第一个单词是否为祈使语气内置了如 adds/adding/added→add、fixes/fixing/fixed→fix、renames/renaming/renamed→rename 等数十组时态纠正表一旦发现 Fixing/Fixed 之类非祈使形式就会报错。这些规则通过两个钩子自动运行tools/commit-msg作为commit-msg钩子在每次提交时对提交消息执行 gitlint若不符合风格指南会打印 WARNINGtools/pre-commit作为pre-commit钩子对暂存区中改动的文件运行 Zulip 的 ./tools/lint跳过 gitlint 与 provision 检查无论 lint 结果如何都会放行提交但会打印提示。通过 tools/setup-git-repo 脚本可一键安装这两个钩子详见 docs/git/zulip-tools.md安装后.git/hooks中会看到pre-commit - ../../tools/pre-commit的软链。此外 tools/commit-message-lint 会在本地或 CI 中对merge-base HEAD与 upstream 分支之间所有新提交批量运行gitlint --commits。提交描述description正文应解释改动的原因why与方式how。与优秀代码注释一样它为现在的审查者和一年后阅读此改动的开发者提供上下文与动机。哪些信息放在哪里提交消息放与理解这个提交做了什么、新旧版本差异相关的信息尤其包括为什么新版本更好或不差于旧版本代码注释或代码本身放与未来阅读并理解新版本代码相关的信息无需对比旧版本PR 描述/讨论放对审查有帮助的信息例如你否决或正在考虑的其他方案、注意到的可疑之处、不确定是否正确解决的错误。例如如果你有一个预期会在审查过程中解决的问题把它作为 PR 评论挂在改动相关位置问题解决后记得把决策背后的推理更新到代码注释和/或提交描述中。有时更好的方案是改进代码而非写说明如果信息是对某个计算或函数的描述先考虑当前使用的抽象——更好的变量名或函数名往往比一段散文解释更有效如果信息描述的是你顺带做的额外改动考虑它是否能从其余改动中分离——如果可以应当拆成独立提交、配上独立提交消息。审查和整合一系列写得好的提交远比审查同一批改动挤在一个提交里容易得多。关闭 GitHub issue当你修复了一个 GitHub issue 时在提交消息中标记使代码合并后 issue 被自动关闭同时提交也永久引用了它解决的问题。Zulip 偏好的写法是让提交消息的最后一段形如Fixes #123.。注意避免Partially fixes #1234.这类写法——GitHub 的正则表达式会忽略 partially 从而直接关闭 issueFixes part of #1234.是更好的替代。描述的目的summary 与 description 合在一起应向另一位 Zulip 开发者可能不熟悉你改动的具体文件/子系统解释清楚为什么这个提交改进了项目——既要说明它完成了什么也要说明它为什么不会破坏人们可能担心它破坏的东西包含另一位开发者验证你的改动正确性所需的重要调查/推理链。例如删除某个函数参数时消息里应写明可以安全移除该参数因为它始终为 False或该行为必须移除因为……——审查者会据此逐环验证提供背景上下文。好的模式是Previously, when X happened, this caused Y to happen, which resulted in ... 再描述负面结果不要包含从 diff 中显而易见的信息如改动文件/函数的名单、或我更新了测试之类的事实避免不必要的个人叙述如First I tried X或I changed Y。致谢其他贡献者与其他备注可以在提交消息末尾空一行后加Co-authored-by:行来注明共同作者Co-authored-by: Greg Price gregzulip.com功劳应当归于应得之人接手他人未完成工作的逐步指南见 docs/contributing/continuing-unfinished-work.md。也可以添加Reported-by:、Debugged-by:、Suggested-by:等备注但 Zulip 通常不这么做。-mention 不属于提交消息GitHub 不会因此通知被提及者。想通知某人请在 PR 讨论串中 -mention。格式指南描述与 summary 之间用空行分隔。大多数工具包括 GitHub会把不空行的提交消息渲染错乱使用完整句子和段落正确使用标点与大小写段落之间用一个空行分隔检查拼写与语法错误——提交消息是重要的技术写作英文错误会分散审查者对你观点的注意力每行折行到约 68 字符、不超过 70 字符保证在普通终端的git log中易读链接可以更长gitlint 对链接的抱怨可忽略可以配置EDITOR环境变量或git config --global core.editor选择编辑器并让编辑器自动在 70 列折行。用工具辅助阅读与维护历史掌握提交纪律的另一面是会读历史。仓库中的 docs/git/reading-history.md 给出实用技巧用git log -p时在分页器里按/搜索^c即可用n/N快速跳转到下一个/上一个提交因为git log默认格式中每行以c开头的正是每条日志的第一行git log --stat -p可别名如git lsp在跳转时同时显示提交消息与受影响文件列表用git log A..B过滤范围如git log --stat -p upstream/main..查看自己相对上游的提交用git log PATHS过滤到特定文件/目录用git log -G PATTERN过滤包含某模式的改动行用git log -S PATTERNpickaxe过滤新增/删除某模式的提交git log --graph --oneline --decorate --boundary可在终端获得紧凑的单行历史视图。这些技巧与提交纪律相辅相成写干净的提交让历史可读会读历史又让你更理解为什么要写干净的提交。从小提交起步、用git rebase -i打磨结构、用 gitlint 钩子自动把关消息格式——遵循这套纪律你的 PR 不仅更容易通过审查也会成为未来开发者理解 Zulip 演进脉络的宝贵资产。【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价