内核维护者手册 - 变基和合并【ChatGPT】

发布时间 2023-12-08 21:25:16作者: 摩斯电码

重基和合并

一般来说,维护一个子系统需要熟悉 Git 源代码管理系统。Git 是一个功能强大的工具,具有许多特性;通常情况下,对于这类工具来说,使用这些特性有正确和错误的方式。本文特别关注重基和合并的使用。维护者在错误地使用这些工具时经常会遇到麻烦,但实际上避免问题并不是那么困难。

总的来说,需要注意的一点是,与许多其他项目不同,内核社区并不害怕在其开发历史中看到合并提交。事实上,考虑到项目的规模,要避免合并几乎是不可能的。一些维护者遇到的问题源于希望避免合并,而另一些问题则来自过于频繁地进行合并。

重基

"重基" 是指在存储库中更改一系列提交的历史的过程。有两种不同类型的操作被称为重基,因为它们都是使用 git rebase 命令完成的,但它们之间存在重大差异:

  • 更改一系列补丁构建的父(起始)提交。例如,重基操作可以将构建在先前内核发布版上的一组补丁,改为基于当前发布版。在下文中,我们将称这种操作为 "重定父"。

  • 通过修复(或删除)损坏的提交、添加补丁、向提交更改日志添加标签,或更改应用提交的顺序来更改一组补丁的历史。在下文中,这种类型的操作将被称为 "历史修改"。

术语 "重基" 将用于指代上述两种操作。正确使用重基可以产生更清晰、更清晰的开发历史;错误使用则可能使历史变得模糊,并引入错误。

有一些经验法则可以帮助开发人员避免重基的最严重危险:

  • 已经暴露给你的私有系统之外的世界的历史通常不应该被更改。其他人可能已经拉取了你的树的副本并在其基础上进行了构建;修改你的树将给他们带来困扰。如果工作需要重基,通常意味着它还没有准备好提交到公共存储库。

  • 也就是说,总会有例外情况。一些树(例如 linux-next)经常被重基,开发人员知道不要在其基础上进行工作。开发人员有时会暴露一个不稳定的分支供他人测试或用于自动化测试服务。如果以这种方式暴露了一个可能不稳定的分支,请确保潜在用户知道不要在其基础上进行工作。

  • 不要重基包含其他人创建的历史的分支。如果你从另一个开发人员的存储库中拉取了更改,那么你现在是他们历史的监护人。你不应该更改它。除了少数例外情况,例如这样的树中的一个损坏提交应该明确地被还原,而不是通过历史修改消失。

  • 没有充分理由的情况下不要重定父树。仅仅因为在一个更新的基础上或避免与上游存储库的合并并不是一个普遍的好理由。

  • 如果必须重定父存储库,不要随意选择某个随机的内核提交作为新的基础。内核在发布点之间通常处于相对不稳定的状态;在这些点上进行开发增加了遇到意外错误的机会。当一个补丁系列必须移动到一个新的基础时,选择一个稳定点(例如一个 -rc 发布)作为新的基础。

  • 了解重定父补丁系列(或进行重大历史修改)会改变其开发环境,并且很可能使之前进行的大部分测试失效。重定父的补丁系列通常应该像新代码一样被对待,并从头开始重新测试。

合并

合并是内核开发过程中常见的操作;5.1 开发周期包括 1,126 个合并提交,几乎占总数的 9%。内核工作积累在 100 多个不同的子系统树中,每个树可能包含多个主题分支;每个分支通常是独立开发的。因此,任何给定分支进入上游存储库之前通常需要至少一个合并。

许多项目要求拉取请求中的分支基于当前主干,以便历史记录中不出现合并提交。内核不是这样的项目;为了避免合并,对分支进行重基很可能会导致问题。

子系统维护者发现自己需要执行两种类型的合并:来自较低级别子系统树的合并和来自其他树(兄弟树或主线)的合并。在这两种情况下,应遵循不同的最佳实践。

从较低级别树合并

较大的子系统往往有多个级别的维护者,较低级别的维护者向较高级别发送拉取请求。对这样的拉取请求进行处理几乎肯定会生成一个合并提交;这是应该的。事实上,子系统维护者可能希望使用 --no-ff 标志来强制添加一个合并提交,即使在正常情况下不会创建合并提交,以便记录合并的原因。对于任何类型的合并,合并的变更日志应说明合并的原因。对于较低级别树,"为什么" 通常是对将随该拉取而来的更改的摘要。

所有级别的维护者都应该在他们的拉取请求上使用签名标签,上游维护者在拉取分支时应该验证这些标签。不这样做会威胁整个开发过程的安全性。

根据上述规则,一旦你将别人的历史合并到你的树中,即使在其他情况下你可能有能力这样做,也不能对该分支进行重基。

从兄弟树或上游树合并

从下游合并是常见且不值得注意的,但是当需要将分支推送到上游时,从其他树合并往往是一个警示信号。这样的合并需要仔细考虑和充分的理由,否则很可能会导致随后的拉取请求被拒绝。

自然而然地想要将主分支合并到一个存储库中;这种类型的合并通常被称为 "回合合并"。回合合并可以帮助确保与并行开发没有冲突,并且通常会给人一种更新的、温暖的感觉。但几乎所有情况下都应该避免这种诱惑。

为什么呢?回合合并会使你自己的分支的开发历史变得混乱。它们会显著增加你遇到社区其他地方的错误的机会,并且使得很难确保你正在管理的工作是稳定的并且准备好被推送到上游。频繁的合并也可能掩盖你的树中的开发过程问题;它们可能隐藏与其他树的交互,这些交互在一个良好管理的分支中(通常)不应该发生。

尽管如此,偶尔确实需要回合合并;当发生这种情况时,一定要在提交消息中记录为什么需要这样做。与此同时,合并到一个众所周知的稳定点,而不是随机的提交。即使在这种情况下,你也不应该回合合并到你的直接上游树之上;如果真的需要更高级别的回合合并,上游树应该先执行。

合并相关问题的一个最常见原因是维护者在发送拉取请求之前明显地对一个补丁系列进行了重基,通常是到一个随机的提交。这样的系列经过充分测试的机会相对较低 - 拉取请求被处理的机会也相对较低。

相反,如果重基限制在私有树中,提交基于一个众所周知的起始点,并且经过了充分测试,那么出现问题的可能性就很低了。

最后

在开发周期开始时,与主线合并以获取树中其他地方的更改和修复是相对常见的。与往常一样,这样的合并应该选择一个众所周知的发布点,而不是某个随机的地方。如果在合并窗口期间,你的上游分支完全合并到主线中,你可以使用以下命令将其向前推进:

git merge --ff-only v5.2-rc1

上述指南只是指南。总会有需要不同解决方案的情况,这些指南不应阻止开发人员在需要时采取正确的行动。但是,人们应该始终考虑是否真的需要采取非正常的行动,并准备解释为什么需要这样做。