跳至主要内容

Electron 中的补丁

Electron 基于两个主要的 upstream 项目:Chromium 和 Node.js。这两个项目本身也有各自的多个依赖项。我们尽力按原样使用这些依赖项,但有时为了满足我们的用例,不得不对 upstream 的依赖项进行一些补丁。

补丁说明

Electron 中的每个补丁都是维护负担。当 upstream 代码更改时,补丁可能会失效,有时甚至不会出现补丁冲突或编译错误。保持补丁集更新和有效是一个持续的过程。因此,我们努力将补丁数量保持在最低限度。为此,每个补丁都必须在其提交消息中描述其存在的原因。该理由必须是以下之一

  1. 该补丁是临时的,旨在(或已)提交到 upstream 或最终删除。如果可用,请提供指向 upstream PR 或代码审查的链接,或提供一个用于验证补丁在稍后日期是否仍然需要的过程。
  2. 该补丁允许代码在 Electron 环境中编译,但不能提交到 upstream,因为它与 Electron 相关(例如,修补掉对 Chrome 的 Profile 的引用)。请说明为什么不能在没有补丁的情况下实施更改(例如,通过子类化或复制代码)。
  3. 该补丁在功能方面做了 Electron 特定的更改,这些更改与 upstream 原则上不兼容。

总的来说,我们与之合作的所有 upstream 项目的开发人员都非常友好,并且经常乐于接受重构,这样可以使相关的代码与 Electron 和 upstream 项目都兼容。(例如,请参见 Chromium 中的更改,它使我们能够删除执行相同操作的补丁,或 Node 中的更改,它对 Node 是无操作的,但修复了 Electron 中的一个错误。)我们应该尽可能地将更改提交到 upstream,并避免使用无限期存在的补丁

补丁系统

如果你发现自己不幸地需要进行只能通过修补 upstream 项目来完成的更改,你需要了解如何在 Electron 中管理补丁。

Electron 中对 upstream 项目的所有补丁都包含在 patches/ 目录中。patches/ 的每个子目录都包含多个补丁文件,以及一个 .patches 文件,该文件列出了应用补丁的顺序。将这些文件看作是在我们检出 upstream 项目后应用在其之上的系列 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-patchesgit-export-patchesgit-import-patches 通过按正确顺序应用每个补丁并为每个补丁创建一个提交来将一组补丁文件导入到 git 存储库中。git-export-patches 执行相反的操作;它将存储库中的系列 git 提交导出到目录中的一组文件中,并附带一个 .patches 文件。

旁注:我们使用 .patches 文件来维护应用补丁的顺序,而不是在每个文件前面加上一个数字(例如 001-),因为这样做可以减少与补丁顺序相关的冲突。它避免了这种情况:两个 PR 都在系列的末尾添加了相同编号的补丁,最终都被合并,导致重复的标识符,并且当在系列的中间添加或删除补丁时,它也减少了 churn。

用法

添加新的补丁

$ 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 会忽略任何未提交的文件,因此如果你希望导出更改,则必须创建一个提交。提交消息的主题行将用于推导出补丁文件名,提交消息的主体应包含补丁存在的原因。

重新导出补丁有时会导致无关补丁中的 shasum 发生更改。这通常是无害的,可以忽略(但请将这些更改添加到你的 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

删除补丁

$ 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 之后的提交)以及哪些提交在 upstream(那些在 refs/patches/upstream-head 之前的提交)。

解决冲突

更新 upstream 依赖项时,补丁可能无法干净地应用。通常,git 可以使用 3 路合并来自动解决冲突。你可以通过传递 -3 参数来指示 git-import-patches 使用 3 路合并算法

$ 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 继续应用其余的补丁。