Browser Engine Patcher

Home/Docs

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.

Updated 2026-10-09Applies to 2.8.1Reading time 9 minutes

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.

Header keys. id, title, family and base are required.
KeyMeaning
idUnique within a series, lowercase letters, digits and hyphens. Used by the lockfile and by bep patch drop.
baseUpstream revision the hunk context was written against. A different base is allowed if --3way can resolve it.
requiresPatches that must be applied first. Cycles stop the run with exit code 2 before any file is written.
touchesDirectory prefixes, used by bep series lint to warn about two patches that edit the same lines.
optionalWhen true, a patch that no longer applies is reported as skipped and the run continues. Defaults to false.
testedFree-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

ApproachCostWhere it hurts
A long-lived fork branchRebase every upstream releaseReview is a diff between two forks; the history hides which change caused a regression
Runtime hooks onlyNo rebuild at allCannot reach compile-time switches, which is where most of the size and debug paths live
A patch series (this tool)Small per-change overheadNeeds 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.

  1. Three-way. bep apply --3way uses 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.
  2. Rejects. bep apply --reject writes the successful hunks and leaves .rej files next to the originals, the way patch(1) does. Fix the file, delete the .rej, then bep 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.

idWhat it doesDefault
build-flagsSmaller optimisation set and faster link for local builds; release profile keeps the full set.on
debug-hooksSymbols and a debug port in dev builds only, guarded by a define.on
devtools-extensionsExtension panels in the DevTools frontend, plus a panel reload command.on
headless-profileDrops interactive UI paths used by test runners; not for daily use.off
localization-minimalEnglish UI strings only, saves about 900 files in the tree.off
crash-reporter-offRemoves the embedded crash reporter component from the build graph.off

Rules for the catalogue