Patch format
A patch is a unified diff plus a header of key-value lines. The header is what makes a series reviewable: it names the engine family, the revision the patch expects, what it touches and whether it may be skipped.
Anatomy of a patch
# bep-patch v1
id = "debug-hooks"
title = "Keep a debug port and symbols in dev builds"
family = "chromium"
base = "8f2c41d9"
requires = ["build-flags"]
touches = ["content/browser/devtools/", "build/config/compiler/"]
optional = true
tested = "ubuntu 24.04, clang 18, 12 jobs"
---
--- a/content/browser/devtools/host_impl.cc
+++ b/content/browser/devtools/host_impl.cc
@@ -148,6 +148,9 @@ bool HostImpl::Start() {
if (!settings_.enabled)
return false;
+ if (!base::debug::IsDebugBuild())
+ LOG(WARNING) << "debug hooks are enabled outside a debug build";
+
return transport_.Start();
}
The header ends at the first line containing only three dashes. Everything after it is passed to the three-way apply, unchanged. Unknown keys are rejected: a typo in family is an error, not a silently disabled patch.
| Key | Meaning |
|---|---|
id | Unique within a series, lowercase letters, digits and hyphens. Used by the lockfile and by bep patch drop. |
base | Upstream revision the hunk context was written against. A different base is allowed if --3way can resolve it. |
requires | Patches that must be applied first. Cycles stop the run with exit code 2 before any file is written. |
touches | Directory prefixes, used by bep series lint to warn about two patches that edit the same lines. |
optional | When true, a patch that no longer applies is reported as skipped and the run continues. Defaults to false. |
tested | Free-text note about the machine the author used. Not parsed. |
Ordering and the manifest
patches/series.toml lists patches by id and is applied top to bottom. An entry may also carry an override:
[debug-hooks]
optional = false
note = "our testers need the port"
[localization-minimal]
enabled = false
Order matters only for patches that touch the same lines. bep series order --check reads requires and fails if an entry appears before something it depends on. The resolved order and every patch hash go into bep.lock; a patch that changes afterwards is reported as modified on the next apply.
Three ways to change an engine
| Approach | Cost | Where it hurts |
|---|---|---|
| A long-lived fork branch | Rebase every upstream release | Review is a diff between two forks; the history hides which change caused a regression |
| Runtime hooks only | No rebuild at all | Cannot reach compile-time switches, which is where most of the size and debug paths live |
| A patch series (this tool) | Small per-change overhead | Needs discipline: one purpose per patch, a stated base, no reordering without a lockfile update |
The series wins for a team because it keeps the review unit small. It loses to runtime hooks when a change is genuinely runtime-only; we do not pretend otherwise, and the catalogue has no entries for things that can be done with a command-line switch.
Resolving a conflict
An apply either lands everything or writes nothing; a partial series is never left in the tree. When a hunk fails you have two options.
- Three-way.
bep apply --3wayuses the base recorded in the header to merge around the failure. Clean merges are written and reported as such; unresolved ones still stop the run. - Rejects.
bep apply --rejectwrites the successful hunks and leaves.rejfiles next to the originals, the waypatch(1)does. Fix the file, delete the.rej, thenbep apply --continue.
After fixing a conflict, update base in the patch header and the tested line. A patch that needed manual work with an old base will need it again next month.
A patch that has to change its own base every release is a patch that describes too much. Split it: the part that tracks upstream changes should be a small adaptation, the part that carries your intent should be stable.
Built-in catalogue
Installed with the binary, versioned with it. Copy an entry into your series to edit it; the copy stops tracking the catalogue.
| id | What it does | Default |
|---|---|---|
build-flags | Smaller optimisation set and faster link for local builds; release profile keeps the full set. | on |
debug-hooks | Symbols and a debug port in dev builds only, guarded by a define. | on |
devtools-extensions | Extension panels in the DevTools frontend, plus a panel reload command. | on |
headless-profile | Drops interactive UI paths used by test runners; not for daily use. | off |
localization-minimal | English UI strings only, saves about 900 files in the tree. | off |
crash-reporter-off | Removes the embedded crash reporter component from the build graph. | off |
Rules for the catalogue
- No patch may change behaviour in a release build unless a profile opts in.
- A patch that adds a debug surface must say how to turn it off at configuration time.
- Every catalogue entry ships with a test note and a command a reviewer can run.
- Patches never touch the vendored third-party directory; if a change needs it, it is the wrong change.