5. 发布补丁

迟早,您的工作将准备好提交给社区进行审查,并最终纳入主线内核。不出所料,内核开发社区已经发展出一套用于发布补丁的惯例和程序;遵循这些惯例和程序将使所有相关人员的工作变得更加轻松。本文档将尝试详细涵盖这些期望;更多信息还可以在文件 Documentation/process/submitting-patches.rstDocumentation/process/submit-checklist.rst 中找到。

5.1. 何时发布

总有一种诱惑,想等到补丁完全“准备好”再发布。对于简单的补丁,这没什么问题。然而,如果正在进行的工作很复杂,那么在工作完成之前从社区获得反馈将受益匪多。因此,您应该考虑发布正在进行的工作,甚至公开一个 git 树,以便感兴趣的开发人员可以随时了解您的工作进展。

在发布尚未准备好纳入的代码时,最好在发布内容中明确说明。还要提及任何尚未完成的主要工作以及任何已知问题。知道补丁不成熟的人会较少关注,但那些关注的人会抱着帮助您将工作引向正确方向的想法。

5.2. 创建补丁之前

在考虑向开发社区发送补丁之前,有一些事情应该做。其中包括

  • 尽可能测试代码。利用内核的调试工具,确保内核能以所有合理的配置选项组合进行构建,使用交叉编译器为不同架构进行构建等。

  • 确保您的代码符合内核编码风格指南。

  • 您的更改是否对性能有影响?如果是,您应该运行基准测试,显示您的更改的影响(或益处);结果摘要应包含在补丁中。

  • 确保您有权发布代码。如果此工作是为雇主完成的,雇主可能对该工作拥有权利,并且必须同意在 GPL 下发布。

一般来说,在发布代码之前多花一点心思,几乎总能很快得到回报。

5.3. 补丁准备

准备发布补丁可能是一项出乎意料的工作量,但再一次强调,即使短期内,试图在此处节省时间通常也是不明智的。

补丁必须针对特定版本的内核准备。一般来说,补丁应该基于 Linus 的 git 树中当前的主线版本。当基于主线时,应从一个众所周知的发布点(如稳定版或 -rc 版本)开始,而不是从主线的任意位置分支。

不过,为了促进更广泛的测试和审查,可能需要针对 -mm、linux-next 或子系统树制作版本。根据您的补丁所涉及的领域以及其他地方正在发生的事情,基于这些其他树的补丁可能需要大量工作来解决冲突和处理 API 更改。

只有最简单的更改应该格式化为单个补丁;其他所有更改都应该制作成一个逻辑上的系列。拆分补丁是一门艺术;一些开发人员花很长时间才弄清楚如何以社区期望的方式进行。然而,有一些经验法则可以提供很大帮助

  • 您发布的补丁系列几乎肯定不会是您工作版本控制系统中找到的更改系列。相反,您所做的更改需要以最终形式进行考虑,然后以有意义的方式进行拆分。开发人员感兴趣的是离散的、独立的更改,而不是您达到这些更改所采取的路径。

  • 每个逻辑上独立的更改都应该格式化为单独的补丁。这些更改可以很小(“为此结构添加一个字段”)或很大(例如,添加一个重要的新驱动程序),但它们在概念上应该很小,并且能够用一行描述。每个补丁都应该进行一个特定的更改,可以独立审查并验证其确实如其所说。

  • 重申上述指导原则:不要在同一个补丁中混合不同类型的更改。如果一个补丁同时修复了一个关键安全漏洞、重新排列了一些结构并重新格式化了代码,那么它很有可能被忽略,重要的修复将丢失。

  • 每个补丁都应该生成一个能够正确构建和运行的内核;如果您的补丁系列中途被中断,结果仍然应该是一个可工作的内核。当使用“git bisect”工具查找回归时,部分应用补丁系列是一个常见场景;如果结果是损坏的内核,您将使那些从事追踪问题这项崇高工作的开发人员和用户的生活更加艰难。

  • 但是不要过度。一位开发人员曾将一个文件的编辑作为 500 个单独的补丁发布——这一行为并没有让他成为内核邮件列表上最受欢迎的人。只要单个补丁仍然包含一个逻辑上的更改,它就可以合理地大。

  • 引入一个全新基础设施并将其作为一系列补丁添加,但直到系列中最后一个补丁才启用整个功能,这可能很诱人。如果可能,应避免这种诱惑;如果该系列引入了回归,二分查找会将最后一个补丁识别为导致问题的补丁,即使真正的错误在其他地方。只要可能,添加新代码的补丁应该立即激活该代码。

在“真正的工作”完成后,努力创建完美的补丁系列可能是一个令人沮丧的过程,需要花费相当多的时间和思考。然而,如果做得好,这些时间是值得的。

5.4. 补丁格式和变更日志

现在您有了一系列完美的补丁可以发布,但工作还没有完全完成。每个补丁都需要格式化成一条消息,以便快速清晰地向外界传达其目的。为此,每个补丁将由以下部分组成

  • 可选的“From”行,指明补丁的作者。此行仅在您通过电子邮件转发他人补丁时才需要,但如有疑问,添加它也无妨。

  • 补丁作用的单行描述。这条消息应该足以让读者在没有其他上下文的情况下弄清楚补丁的范围;它是在“简短形式”变更日志中显示的行。此消息通常以相关子系统名称开头,后跟补丁的目的。例如

    gpio: fix build on CONFIG_GPIO_SYSFS=n
    
  • 一个空行,后面是补丁内容的详细描述。此描述可以根据需要尽可能长;它应该说明补丁的作用以及为什么应该将其应用于内核。

  • 一个或多个标签行,其中至少包含补丁作者的 Signed-off-by: 行。标签将在下面更详细地描述。

上述各项共同构成了补丁的变更日志。编写良好的变更日志是一项至关重要但常被忽视的艺术;值得花时间再讨论一下这个问题。编写变更日志时,您应该记住会有许多不同的人阅读您的文字。其中包括需要决定补丁是否应被纳入的子系统维护者和审查员、试图决定补丁是否应回溯到其他内核的发行商和其他维护者、怀疑补丁是否是他们正在追踪的问题原因的漏洞猎人、想知道内核如何更改的用户等等。一个好的变更日志以最直接和简洁的方式向所有这些人传达所需的信息。

为此,摘要行应尽可能在单行限制内描述更改的效果和动机。详细描述可以随后阐述这些主题并提供任何所需的额外信息。如果补丁修复了一个错误,如果可能,请引用引入该错误的提交(在引用提交时请同时提供提交 ID 和标题)。如果问题与特定的日志或编译器输出相关联,请包含该输出以帮助其他人寻找相同问题的解决方案。如果更改旨在支持后续补丁中的其他更改,请说明。如果内部 API 发生更改,请详细说明这些更改以及其他开发人员应如何响应。总的来说,您越能设身处地为将阅读您的变更日志的每个人着想,该变更日志(以及整个内核)就会越好。

毋庸置疑,变更日志应该是将更改提交到版本控制系统时使用的文本。它后面将是

  • 补丁本身,采用统一(“-u”)补丁格式。使用 diff 的 “-p” 选项会将函数名与更改关联起来,使生成的补丁更易于他人阅读。

上面已经简要提及的标签用于提供补丁是如何产生的见解。它们在 Documentation/process/submitting-patches.rst 文档中详细描述;下面是简要总结。

一个标签用于引用引入补丁所修复问题的早期提交

Fixes: 1f2e3d4c5b6a ("The first line of the commit specified by the first 12 characters of its SHA-1 ID")

另一个标签用于链接包含额外背景或详细信息的网页,例如导致该补丁的早期讨论,或由该补丁实现规范的文档

Link: https://example.com/somewhere.html  optional-other-stuff

许多维护者在应用补丁时也会添加此标签,以链接到补丁的最新公开审查发布;这通常由 b4 等工具或像‘配置 Git’中所述的 git 钩子自动完成。

如果 URL 指向补丁正在修复的公开错误报告,请使用“Closes:”标签代替

Closes: https://example.com/issues/1234  optional-other-stuff

一些错误跟踪系统能够在应用带有此类标签的提交时自动关闭问题。一些监控邮件列表的机器人也可以跟踪此类标签并采取某些行动。禁止使用私有错误跟踪系统和无效 URL。

另一种标签用于记录谁参与了补丁的开发。每种标签都使用这种格式

tag: Full Name <email address>  optional-other-stuff

常用标签有

  • Signed-off-by: 这是开发人员证明其有权提交补丁以纳入内核的声明。它是一份对《开发者原始贡献证书》的认可,其全文可在 Documentation/process/submitting-patches.rst 中找到。没有适当签名的代码不能合并到主线中。

  • Co-developed-by: 表明该补丁由多位开发人员共同创建;当多人协作一个补丁时,它用于向共同作者(除了 From: 标签归属的作者之外)致谢。每个 Co-developed-by: 后面必须紧跟着相应的共同作者的 Signed-off-by:。详细信息和示例可在 Documentation/process/submitting-patches.rst 中找到。

  • Acked-by: 表示另一位开发人员(通常是相关代码的维护者)同意该补丁适合纳入内核。

  • Tested-by: 表明指定人员已测试该补丁并发现其正常工作。

  • Reviewed-by: 指定的开发人员已审查补丁的正确性;更多详情请参阅 Documentation/process/submitting-patches.rst 中的审查员声明。

  • Reported-by: 指明报告了此补丁所修复问题的一位用户;此标签用于表彰那些测试我们的代码并在代码不正确时告知我们的(常被低估的)人。请注意,除非报告在网上不可用,否则此标签后面应跟一个指向该报告的 Closes: 标签。如果补丁修复了所报告问题的一部分,则可以使用 Link: 标签代替 Closes:。

  • Suggested-by: 标签表明补丁的想法是由指定人员提出的,并确保将功劳归于该想法的提出者。这有望激励他们在未来再次帮助我们。

  • Cc: 指定人员收到了补丁的副本并有机会对其发表评论。

在您的补丁中添加上述标签时请务必小心,因为除 Cc:、Reported-by: 和 Suggested-by: 外,所有标签都需要获得指定人员的明确许可。对于这三个标签,如果该人员根据 lore 档案或提交历史记录使用该姓名和电子邮件地址为 Linux 内核做出了贡献——并且在 Reported-by: 和 Suggested-by: 的情况下是在公开场合进行的报告或建议——那么隐式许可就足够了。请注意,bugzilla.kernel.org 在此意义上是一个公共场所,但那里使用的电子邮件地址是私有的;因此,除非该人员在早期的贡献中使用了这些地址,否则不要在标签中公开它们。

5.5. 发送补丁

在您发送补丁之前,还有几件事需要注意

  • 您确定您的邮件客户端不会损坏补丁吗?那些被邮件客户端随意添加空格或进行换行的补丁将无法在另一端应用,并且通常不会被详细检查。如果存有任何疑问,请将补丁发送给自己,并确认它完整无损地显示。

    Documentation/process/email-clients.rst 包含一些关于如何使特定邮件客户端适用于发送补丁的有用提示。

  • 您确定您的补丁没有愚蠢的错误吗?您应该始终使用 scripts/checkpatch.pl 检查补丁并解决它提出的问题。请记住,checkpatch.pl 虽然体现了对内核补丁外观的相当多的思考,但它并不比您聪明。如果修复 checkpatch.pl 的抱怨会使代码变差,请不要这样做。

补丁应始终以纯文本形式发送。请不要以附件形式发送;那样会使审阅者在回复中引用补丁的某些部分变得更加困难。相反,只需将补丁直接放入您的消息中。

邮寄补丁时,重要的是将副本发送给任何可能对其感兴趣的人。与其他一些项目不同,内核鼓励人们宁可多发也不要少发;不要以为相关人员会在邮件列表中看到您的发布。特别是,副本应发送给

  • 受影响子系统的维护者。如前所述,MAINTAINERS 文件是查找这些人的首选之地。

  • 一直在同一领域工作的其他开发人员——特别是那些现在可能正在那里工作的开发人员。使用 git 查看还有谁修改了您正在处理的文件会有所帮助。

  • 如果您正在回复错误报告或功能请求,请也抄送原发帖人。

  • 发送一份副本到相关的邮件列表,或者,如果没有其他适用的,发送到 linux-kernel 列表。

  • 如果您正在修复一个错误,请考虑该修复是否应该包含在下一个稳定更新中。如果是,stable@vger.kernel.org 应该收到一份补丁副本。此外,在补丁本身的标签中添加“Cc: stable@vger.kernel.org”;这将在您的修复进入主线时通知稳定团队。

在选择补丁接收者时,最好有一个您认为最终会接受并合并该补丁的人。虽然可以直接将补丁发送给 Linus Torvalds 并让他合并,但通常不会这样做。Linus 很忙,并且有子系统维护者负责内核的特定部分。通常您会希望该维护者合并您的补丁。如果没有明确的维护者,Andrew Morton 常常是最后的补丁目标。

补丁需要好的主题行。补丁行的规范格式如下所示

[PATCH nn/mm] subsys: one-line description of the patch

其中“nn”是补丁的序号,“mm”是系列中的补丁总数,“subsys”是受影响子系统的名称。显然,对于单个独立的补丁,可以省略 nn/mm。

如果您有大量补丁系列,通常会发送一个介绍性描述作为第零部分。不过,这种惯例并非普遍遵循;如果您使用它,请记住介绍中的信息不会进入内核变更日志。因此,请确保补丁本身具有完整的变更日志信息。

一般来说,多部分补丁的第二部分及后续部分应作为对第一部分的回复发送,以便它们在接收端全部串联起来。像 git 和 quilt 这样的工具具有发送带有正确线程的补丁集的命令。但是,如果您有一个很长的系列并且正在使用 git,请避免使用 --chain-reply-to 选项,以免创建异常深的嵌套。