Electron 中的补丁
Electron 基于两个主要的上游项目构建:Chromium 和 Node.js。这些项目各自也有一些依赖项。我们尽力完全按照它们的原样使用这些依赖项,但有时为了满足我们的使用场景,我们不得不修改(打补丁)这些上游依赖项,否则无法达成目标。
补丁理由
Electron 中的每一个补丁都意味着维护负担。当上游代码发生变化时,补丁可能会失效——有时甚至不会出现补丁冲突或编译错误。保持我们的补丁集是最新的且有效的,这是一项持续的努力。因此,我们力求将补丁数量降至最低。为此,每个补丁都必须在其提交消息中说明其存在的理由。该理由必须是以下之一:
- 该补丁是临时的,旨在(或已)提交到上游,或最终将被移除。如果可能,请附上指向相关上游 PR 或代码审查的链接,或提供一个后续验证该补丁是否仍然需要的流程。
- 该补丁允许代码在 Electron 环境中编译,但由于它是 Electron 特有的(例如,移除对 Chrome 的
Profile的引用),因此无法上游。请提供有关为何无法通过其他方式(例如通过继承或复制代码)实现该更改的理由。 - 该补丁在 Electron 中进行了功能性更改,这些更改与上游存在根本性不兼容。
总的来说,我们合作的所有上游项目都非常友好,并且乐于接受能使代码同时兼容 Electron 和上游项目的重构。 (例如,参见 Chromium 中的此更改,它使我们能够移除一个执行相同功能的补丁,或者 Node 中的此更改,该更改对 Node 来说是无操作,但修复了 Electron 中的一个 bug。) 我们应该尽可能地上游更改,并避免使用永久性补丁。
补丁系统
如果您不幸需要修改一个只能通过打补丁到上游项目来完成的更改,您需要了解如何在 Electron 中管理补丁。
Electron 中所有对上游项目的补丁都包含在 patches/ 目录中。patches/ 的每个子目录都包含几个补丁文件,以及一个 .patches 文件,该文件列出了应用补丁的顺序。可以将这些文件看作是我们在检出上游项目后,在其之上应用的一系列 git 提交。
patches
├── config.json <-- this describes which patchset directory is applied to what project
├── chromium
│ ├── .patches
│ ├── accelerator.patch
│ ├── add_contentgpuclient_precreatemessageloop_callback.patch
│ ⋮
├── node
│ ├── .patches
│ ├── add_openssl_is_boringssl_guard_to_oaep_hash_check.patch
│ ├── build_add_gn_build_files.patch
│ ⋮
⋮
为了帮助管理这些补丁集,我们提供了两个工具:git-import-patches 和 git-export-patches。git-import-patches 会按正确的顺序应用每个补丁并将它们导入 git 仓库,为每个补丁创建一个提交。git-export-patches 则反其道而行之;它会将仓库中的一系列 git 提交导出为目录中的一系列文件和一个配套的 .patches 文件。
附注:我们使用
.patches文件来维护补丁的应用顺序,而不是为每个文件加上数字前缀(如001-),原因是为了减少与补丁顺序相关的冲突。它可以避免出现两个 PR 都以相同的编号在系列末尾添加一个补丁,结果两个都被合并导致重复标识符的情况,并且还能减少在系列中间添加或删除补丁时产生的额外变动。
用法
添加新补丁
$ cd src/third_party/electron_node
$ vim some/code/file.cc
$ git commit
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
git-export-patches 会忽略任何未提交的文件,因此如果您想导出更改,必须先创建一个提交。提交消息的主题行将用于生成补丁文件名,提交消息的正文应包含补丁存在的理由。
重新导出补丁有时会导致无关补丁的 sha1 值发生变化。这通常是无害的,可以忽略(但请随意将这些更改添加到您的 PR 中,这可以避免其他人看到它们)。
编辑现有补丁
$ cd src/v8
$ vim some/code/file.cc
$ git log
# Find the commit sha of the patch you want to edit.
$ git commit --fixup [COMMIT_SHA]
$ git rebase --autosquash -i [COMMIT_SHA]^
$ ../electron/script/git-export-patches -o ../electron/patches/v8
请注意,^ 符号 在 Windows 上可能会引起问题。解决方法是引用它 "[COMMIT_SHA]^" 或避免使用它 [COMMIT_SHA]~1。
移除补丁
$ vim src/electron/patches/node/.patches
# Delete the line with the name of the patch you want to remove
$ cd src/third_party/electron_node
$ git reset --hard refs/patches/upstream-head
$ ../../electron/script/git-import-patches ../../electron/patches/node
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
请注意,git-import-patches 会将运行时的 HEAD 提交标记为 refs/patches/upstream-head。这使您可以跟踪哪些提交是来自 Electron 补丁(那些在 refs/patches/upstream-head 之后的提交),哪些提交在上游(那些在 refs/patches/upstream-head 之前的提交)。
解决冲突
在更新上游依赖项时,补丁可能无法干净地应用。通常,git 可以通过三向合并自动解决冲突。您可以通过传递 -3 参数来指示 git-import-patches 使用三向合并算法。
$ cd src/third_party/electron_node
# If the patch application failed midway through, you can reset it with:
$ git am --abort
# And then retry with 3-way merge:
$ ../../electron/script/git-import-patches -3 ../../electron/patches/node
如果 git-import-patches -3 遇到无法自动解决的合并冲突,它将暂停并允许您手动解决冲突。一旦解决了冲突,请使用 git add 添加已解决的文件,然后通过运行 git am --continue 继续应用其余的补丁。