Electron 中的补丁
🌐 Patches in Electron
Electron 构建在两个主要的上游项目之上:Chromium 和 Node.js。这些项目各自也有若干依赖。我们尽最大努力按原样使用这些依赖,但有时如果不对这些上游依赖进行修改以适应我们的使用场景,我们无法实现目标。
🌐 Electron is built on two major upstream projects: Chromium and Node.js. Each of these projects has several of their own dependencies, too. We try our best to use these dependencies exactly as they are but sometimes we can't achieve our goals without patching those upstream dependencies to fit our use cases.
补丁理由
🌐 Patch justification
Electron 中的每一个补丁都是一种维护负担。当上游代码发生变化时,补丁可能会失效——有时甚至不会出现补丁冲突或编译错误。保持我们的补丁集最新且有效是一个持续的工作。因此,我们努力将补丁数量保持在最低限度。为此,每个补丁在提交信息中都必须说明其存在的理由。这个理由必须是以下之一:
🌐 Every patch in Electron is a maintenance burden. When upstream code changes, patches can break—sometimes without even a patch conflict or a compilation error. It's an ongoing effort to keep our patch set up-to-date and effective. So we strive to keep our patch count at a minimum. To that end, every patch must describe its reason for existence in its commit message. That reason must be one of the following:
- 该补丁是临时的,旨在(或已经)提交到上游,或者最终被移除。如果有的话,请附上上游的 PR 或代码评审的链接,或者提供一个用于以后验证补丁是否仍然需要的进程。
- 该补丁允许代码在 Electron 环境中编译,但无法提交到上游,因为它是特定于 Electron 的(例如,修补掉对 Chrome 的
Profile的引用)。请说明为什么无法在不使用补丁的情况下实现此更改(例如,通过子类化或复制代码)。 - 该补丁对 Electron 的功能进行了特定的更改,这些更改从根本上与上游不兼容。
总体而言,我们合作的所有上游项目都是友好的人,他们通常乐意接受重构,以使相关代码兼容 Electron 和上游项目。(例如,参见 Chromium 中的 这个 变更,这使我们能够移除做同样事情的补丁,或者 Node 中的 这个 变更,对 Node 来说没有实际作用,但修复了 Electron 中的一个错误。)我们应尽量将更改提交到上游,并避免长期存在的补丁。
🌐 In general, all the upstream projects we work with are friendly folks and are often happy to accept refactorings that allow the code in question to be compatible with both Electron and the upstream project. (See e.g. this change in Chromium, which allowed us to remove a patch that did the same thing, or this change in Node, which was a no-op for Node but fixed a bug in Electron.) We should aim to upstream changes whenever we can, and avoid indefinite-lifetime patches.
补丁系统
🌐 Patch system
如果你发现自己处于不幸的境地,必须进行只能通过修补上游项目才能进行的更改,那么你需要知道如何在 Electron 中管理补丁。
🌐 If you find yourself in the unfortunate position of having to make a change which can only be made through patching an upstream project, you'll need to know how to manage patches in Electron.
Electron 中对上游项目的所有补丁都包含在 patches/ 目录中。patches/ 的每个子目录包含多个补丁文件,以及一个 .patches 文件,该文件列出了应用补丁的顺序。可以将这些文件视为一系列 git 提交,它们在我们签出上游项目后应用在其之上。
🌐 All patches to upstream projects in Electron are contained in the patches/ directory. Each subdirectory of patches/ contains several patch files, along with a .patches file which lists the order in which the patches should be applied. Think of these files as making up a series of git commits that are applied on top of the upstream project after we check it out.
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 文件中。
🌐 To help manage these patch sets, we provide two tools: git-import-patches and git-export-patches. git-import-patches imports a set of patch files into a git repository by applying each patch in the correct order and creating a commit for each one. git-export-patches does the reverse; it exports a series of git commits in a repository into a set of files in a directory and an accompanying .patches file.
旁注:我们使用
.patches文件来维护已应用补丁的顺序,而不是在每个文件前加上类似001-的编号,是因为这样可以减少与补丁顺序相关的冲突。它能避免两次 PR 同时在序列末尾添加补丁且编号相同的情况,从而导致合并后出现重复标识符,同时在序列中间添加或删除补丁时也能减少频繁变动。
用法
🌐 Usage
添加新补丁
🌐 Adding a new patch
$ 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 中,这样它们就不会在其他人那里显示)。
🌐 Re-exporting patches will sometimes cause shasums in unrelated patches to change. This is generally harmless and can be ignored (but go ahead and add those changes to your PR, it'll stop them from showing up for other people).
编辑现有补丁
🌐 Editing an existing patch
$ 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。
🌐 Note that the ^ symbol can cause trouble on Windows. The workaround is to either quote it "[COMMIT_SHA]^" or avoid it [COMMIT_SHA]~1.
删除补丁
🌐 Removing a patch
$ 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 将标记在运行为 refs/patches/upstream-head 时的 HEAD 提交。这让你能够跟踪哪些提交来自 Electron 补丁(那些在 refs/patches/upstream-head 之后的提交)以及哪些提交属于上游(那些在 refs/patches/upstream-head 之前的提交)。
🌐 Note that git-import-patches will mark the commit that was HEAD when it was run as refs/patches/upstream-head. This lets you keep track of which commits are from Electron patches (those that come after refs/patches/upstream-head) and which commits are in upstream (those before refs/patches/upstream-head).
解决冲突
🌐 Resolving conflicts
在更新上游依赖时,补丁可能无法干净地应用。通常,git 可以通过三方合并自动解决冲突。你可以通过传递 -3 参数来指示 git-import-patches 使用三方合并算法:
🌐 When updating an upstream dependency, patches may fail to apply cleanly. Often, the conflict can be resolved automatically by git with a 3-way merge. You can instruct git-import-patches to use the 3-way merge algorithm by passing the -3 argument:
$ 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 继续应用其余的补丁。
🌐 If git-import-patches -3 encounters a merge conflict that it can't resolve automatically, it will pause and allow you to resolve the conflict manually. Once you have resolved the conflict, git add the resolved files and continue to apply the rest of the patches by running git am --continue.