跳转到主要内容

Electron 中的补丁

Electron 构建于两大重要上游项目:Chromium 和 Node.js。这两个项目各自也有多个依赖项。我们尽最大努力严格按照原样使用这些依赖项,但有时为了满足我们的使用场景,我们不得不对这些上游依赖项进行打补丁,否则无法实现目标。

补丁理由

Electron 中的每一个补丁都是一种维护负担。当上游代码发生更改时,补丁可能会中断——有时甚至不会出现补丁冲突或编译错误。保持我们的补丁集最新且有效是一项持续的努力。因此,我们力求将补丁数量降至最低。为此,每个补丁都必须在其提交消息中说明其存在的理由。该理由必须是以下之一:

  1. 该补丁是临时的,并且旨在(或已经)提交到上游,或者最终会被移除。如果可用,请包含上游 PR 或代码审查的链接,或者一份用于验证补丁在稍后是否仍然需要的程序。
  2. 该补丁允许代码在 Electron 环境中编译,但由于它是 Electron 特定的(例如,打补丁移除对 Chrome 的 Profile 的引用),因此无法上游。包含关于为什么无法通过打补丁来实现更改的原因(例如,通过子类化或复制代码)。
  3. 该补丁对 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-patchesgit-export-patchesgit-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 会忽略任何未提交的文件,因此如果您希望更改被导出,则必须创建一个提交。提交消息的主题行将用于派生补丁文件名,提交消息的正文应包含补丁存在的理由。

重新导出补丁有时会导致不相关补丁中的 shasums 发生变化。这通常是无害的,可以忽略(但请继续将这些更改添加到您的 PR 中,这样可以避免它们出现在其他人的 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 可以通过三向合并自动解决冲突。您可以指示 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 来继续应用剩余的补丁。