7. 高级主题¶
到目前为止,希望您已经掌握了开发流程的运作方式。然而,仍有更多内容需要学习!本节将介绍一些对希望成为 Linux 内核开发流程常客的开发者有用的主题。
7.1. 使用 Git 管理补丁¶
内核的分布式版本控制始于2002年初,当时 Linus 首次试用专有的 BitKeeper 应用程序。尽管 BitKeeper 备受争议,但它所体现的软件版本管理方法无疑并非如此。分布式版本控制使得内核开发项目得以立即加速。目前,BitKeeper 有多种免费替代方案。无论好坏,内核项目已选择 Git 作为其首选工具。
使用 Git 管理补丁可以大大简化开发者的工作,尤其是在补丁数量增加时。Git 也有其粗糙之处并带来某些风险;它是一个年轻而强大的工具,仍在被其开发者不断完善。本文档不会尝试教读者如何使用 Git;那本身就足以写成一篇长篇文档。相反,本文的重点将放在 Git 如何特别是如何融入内核开发流程中。希望快速掌握 Git 的开发者可以在以下网址找到更多信息:
以及网络上各种教程。
首要任务是阅读上述网站并对 Git 的工作原理有一个扎实的理解,然后才能尝试使用它向他人提供补丁。使用 Git 的开发者应该能够获取主线仓库的副本,探索修订历史,向树提交更改,使用分支等。理解 Git 用于重写历史的工具(如 rebase)也很有用。Git 带有其自己的术语和概念;Git 的新用户应该了解引用(refs)、远程分支、索引(index)、快进合并(fast-forward merges)、推送(pushes)和拉取(pulls)、分离头(detached heads)等。这一切在开始时可能有点令人生畏,但只要稍加学习,这些概念并不难掌握。
在熟悉 Git 的过程中,使用 Git 生成通过电子邮件提交的补丁是一个很好的练习。
当你准备好上传 Git 树供他人查看时,你当然需要一个可以从中拉取(pull)的服务器。如果你有一个可以访问互联网的系统,使用 git-daemon 设置这样的服务器相对简单。否则,免费的公共托管网站(例如 GitHub)正在互联网上出现。经验丰富的开发者可以在 kernel.org 上获得账户,但这并不容易获得;请参阅 https://linuxkernel.org.cn/faq/ 获取更多信息。
正常的 Git 工作流程涉及大量分支的使用。每条开发线都可以分离到一个单独的“主题分支”中并独立维护。Git 中的分支成本低廉,没有理由不自由使用它们。无论如何,你不应该在你打算让其他人从中拉取(pull)的任何分支中进行开发。公开可用的分支应谨慎创建;当开发分支中的补丁完整并准备就绪时才合并它们——而不是在此之前。
Git 提供了一些强大的工具,可以让你重写开发历史。一个不便的补丁(例如,一个导致二分查找失败或有其他明显错误)可以在原地修复或完全从历史中移除。一个补丁系列可以被重写,就好像它是基于今天的主线编写的,即使你已经为此工作了数月。更改可以透明地从一个分支转移到另一个分支。诸如此类。明智地利用 Git 修正历史的能力有助于创建问题较少的干净补丁集。
然而,过度使用此功能可能会导致其他问题,而不仅仅是对创建完美项目历史的简单执着。重写历史将重写该历史中包含的更改,将一个经过测试(希望如此)的内核树变成一个未经测试的树。但除此之外,如果开发者对项目历史没有共同的看法,他们就无法轻松协作;如果你重写了其他开发者已经拉取到他们仓库中的历史,你将使那些开发者 laborious。因此,这里适用一个简单的经验法则:已经导出给其他人的历史之后通常应被视为不可变。
因此,一旦你将一系列更改推送到你的公共服务器,这些更改就不应该被重写。如果你尝试推送不会导致快进合并(即不共享相同历史的更改)的更改,Git 将尝试强制执行此规则。可以覆盖此检查,并且有时可能需要重写导出的树。例如,在树之间移动更改集以避免 linux-next 中的冲突。但此类操作应该很少见。这就是为什么开发应该在私有分支中进行(如有必要可以重写),并且只有在达到相当成熟的状态时才将其移动到公共分支的原因之一。
随着主线(或基于一系列更改的其他树)的推进,人们很容易倾向于与该树合并以保持领先地位。对于私有分支,rebase 是一种跟上另一个树的简单方法,但 rebase 不再是选项一旦树被导出到外部。一旦发生这种情况,就必须进行完整合并。偶尔合并很有意义,但过于频繁的合并可能会不必要地使历史变得混乱。在这种情况下,建议的技术是少合并,并且通常只在特定的发布点(例如主线 -rc 发布)进行合并。如果你对特定更改感到不安,你始终可以在私有分支中执行测试合并。Git 的“rerere”工具在这种情况下会很有用;它会记住合并冲突是如何解决的,这样你就不必重复相同的工作了。
关于像 Git 这样的工具,最常见的抱怨之一是:补丁从一个仓库大规模移动到另一个仓库,使得未经审查或不当的更改很容易潜入主线。内核开发者看到这种情况发生时往往会感到不高兴;上传包含未经审查或跑题补丁的 Git 树可能会影响你未来获取树的能力。引用 Linus 的话:
You can send me patches, but for me to pull a git patch from you, I
need to know that you know what you're doing, and I need to be able
to trust things *without* then having to go and check every
individual change by hand.
(https://lwn.net/Articles/224135/)。
为了避免这种情况,请确保给定分支中的所有补丁都与相关主题紧密相关;一个“驱动程序修复”分支不应该修改核心内存管理代码。最重要的是,不要使用 Git 树来绕过审查流程。定期向相关列表发布树的摘要,并在适当的时候,请求将该树包含在 linux-next 中。
如果或当其他人开始发送补丁以包含到您的树中时,请不要忘记审查它们。同时确保您维护正确的作者信息;Git 的“am”工具在这方面尽力而为,但如果补丁是通过第三方转发给您的,您可能需要向补丁添加“From:”行。
当请求拉取(pull)时,请务必提供所有相关信息:你的树在哪里,要拉取哪个分支,以及拉取将导致哪些更改。Git 的 `request-pull` 命令在这方面很有帮助;它将按照其他开发者期望的格式化请求,并且还会检查你是否记得将这些更改推送到公共服务器。
7.2. 审查补丁¶
有些读者肯定会反对将本节归入“高级主题”,理由是即使是初级内核开发者也应该审查补丁。确实,学习如何在内核环境中编程的最佳方式莫过于查看其他人发布的代码。此外,审查者总是供不应求;通过查看代码,你可以为整个流程做出重大贡献。
审查代码可能是一个令人生畏的前景,特别是对于那些可能对公开质疑经验更丰富的人发布的代码感到紧张的新内核开发者。然而,即使是最有经验的开发者编写的代码也可以改进。也许对审查者(所有审查者)最好的建议是:将审查意见表述为问题而不是批评。提问“此路径中锁是如何释放的?”总是比声明“这里的锁机制是错误的”效果更好。
在出现分歧时,另一个有用的技巧是请其他人发表意见。如果讨论在几次交流后陷入僵局,那么请其他审查者或维护者发表意见。通常,那些同意审查者意见的人除非被要求,否则会保持沉默。多人的意见具有指数级的更大权重。
不同的开发者会从不同的角度审查代码。有些人主要关注编码风格以及代码行是否有尾随空格。另一些人则主要关注补丁整体实现的更改是否对内核有利。还有一些人会检查是否存在问题锁、过度堆栈使用、可能的安全问题、与其他地方代码的重复、足够的文档、对性能的不利影响、用户空间 ABI 更改等。所有类型的审查,如果能使进入内核的代码变得更好,都是受欢迎和有价值的。
没有严格要求使用像 Reviewed-by
这样的特定标签。事实上,即使提供了标签,也更鼓励用简单英语进行的审查,因为它们信息量更大,例如“我查看了此提交的 A、B 和 C 方面,我认为它看起来不错。” 某种形式的审查消息或回复显然是必要的,否则维护者将根本不知道审查者是否查看了补丁!
最后但同样重要的是,补丁审查可能会变成一个负面过程,只专注于指出问题。请时不时地给予一些赞扬,特别是对于新手!