处理混乱的拉取请求差异统计¶
子系统维护者通常会使用 git request-pull
作为向上游提交工作流程的一部分。通常,结果会包含一个清晰的差异统计 (diffstat),显示哪些文件将被修改以及每个文件修改了多少。然而,偶尔,一个开发历史相对复杂的仓库会产生一个巨大的差异统计 (diffstat),其中包含大量不相关的工作。结果看起来很难看,并且掩盖了拉取请求实际的作用。本文档描述了这种情况发生的原因以及如何解决;它源自 Linus Torvalds 的智慧,可在 Linus1 和 Linus2 中找到。
Git 开发历史是作为一系列提交进行的。简单来说,主线内核开发看起来是这样的
... vM --- vN-rc1 --- vN-rc2 --- vN-rc3 --- ... --- vN-rc7 --- vN
如果想查看两个点之间发生了什么变化,类似这样的命令就可以做到
$ git diff --stat --summary vN-rc2..vN-rc3
在这里,历史中有两个清晰的起点和终点;Git 本质上会从终点“减去”起点,并显示由此产生的差异。所请求的操作是明确的,并且很容易理解。
当子系统维护者创建一个分支并向其提交更改时,最简单的情况是历史记录看起来是这样的
... vM --- vN-rc1 --- vN-rc2 --- vN-rc3 --- ... --- vN-rc7 --- vN
|
+-- c1 --- c2 --- ... --- cN
如果该维护者现在使用 git diff
查看主线分支(我们称之为“linus”)和 cN 之间发生了什么变化,仍然有两个清晰的终点,结果也符合预期。因此,使用 git request-pull
生成的拉取请求也将符合预期。但是现在考虑一个稍微更复杂的开发历史
... vM --- vN-rc1 --- vN-rc2 --- vN-rc3 --- ... --- vN-rc7 --- vN
| |
| +-- c1 --- c2 --- ... --- cN
| /
+-- x1 --- x2 --- x3
我们的维护者在 vN-rc1 创建了一个分支,在 vN-rc2 创建了另一个分支;这两个分支随后合并到 c2。现在,为 cN 生成的拉取请求可能确实会变得混乱,开发人员也经常会因此感到困惑。
这里发生的情况是,git diff
操作不再有两个清晰的终点可用。最终汇聚到 cN 的开发始于两个不同的地方;为了生成差异统计 (diffstat),git diff
最终不得不选择其中一个,然后听天由命。如果差异统计 (diffstat) 从 vN-rc1 开始,它可能最终会包含从那里到第二个原始终点 (vN-rc2) 之间的所有更改,这肯定不是我们的维护者所希望的。由于差异统计 (diffstat) 中包含所有这些额外的“垃圾”,可能无法判断在导致 cN 的更改中实际发生了什么。
维护者通常会尝试解决这个问题,例如,通过对分支进行变基操作,或者与 linus 分支进行另一次合并,然后重新创建拉取请求。这种方法往往不会让拉取请求的接收方感到高兴;在向上游推送之前进行变基和/或合并是一种众所周知的会招致不满回应的方式。
那么该怎么做呢?当遇到这种情况时,最好的回应确实是与你打算将工作拉入的分支进行合并,但要私下进行,仿佛这是一件见不得人的事情。创建一个新的、临时的分支,并在那里进行合并
... vM --- vN-rc1 --- vN-rc2 --- vN-rc3 --- ... --- vN-rc7 --- vN
| | |
| +-- c1 --- c2 --- ... --- cN |
| / | |
+-- x1 --- x2 --- x3 +------------+-- TEMP
合并操作解决了由多个起始点引起的所有复杂问题,从而产生了一个只包含与主线分支差异的连贯结果。现在就可以生成包含所需信息的差异统计 (diffstat) 了
$ git diff -C --stat --summary linus..TEMP
保存此命令的输出,然后简单地删除 TEMP 分支;千万不要将其暴露给外界。将保存的差异统计 (diffstat) 输出编辑到混乱的拉取请求中,从而产生一个显示实际情况的结果。然后就可以将该请求发送到上游了。