Skip to content

PATTERN Cited by 1 source

External feature-branch repo for monorepo patches

Intent

When an organization's primary monorepo lacks the branching support needed to track long-lived patches against an upstream OSS project, maintain the patches in a separate Git repository outside the monorepo, organized as named feature branches anchored to each upstream release tag. Merge forward each feature branch to the next upstream tag to upgrade; merge all feature branches together into a release-candidate branch to ship.

Motivation

Monorepos are optimized for a trunk-based workflow: all developers commit to one branch, a small number of release branches cut at fixed cadence. They typically don't support thousands of long-lived branches per OSS project the way a per-project Git repo does.

But tracking patches against upstream OSS requires exactly that shape — one branch per patch, each rebased onto each upstream release, the whole thing operationally long-lived. Shoving this into the monorepo either overloads the monorepo's branch infrastructure or degrades to stored patch files (which loses Git history and parallelism — see concepts/feature-branch-patch-management).

The resolution: keep the monorepo where it is, and put the OSS-patch tracking in an external Git repo that does support rich branching, based on the upstream OSS project's own Git repo so upstream tooling (gn, gclient, git cl) works out of the box.

Structure

  1. Clone the upstream OSS project's Git repo — not just the tarball. Upstream's own release tags, tooling, and review conventions come along for free.
  2. Create a base/<tag> branch at each upstream release you care aboutbase/7499 for Chromium M143, base/7559 for M145. These are the anchor branches; they contain no internal patches.
  3. For each internal patch family, create a <feature>/<tag> branch off the corresponding base/<tag>debug-tools/7499, hw-av1-fixes/7499, etc. Each branch has a clearly delineated purpose and owning team.
  4. On upgrade to a new upstream release tag, merge forward each <feature>/<old> into base/<new> to produce <feature>/<new>, resolving conflicts as normal Git merges. Highly parallelizable — N teams can upgrade their feature branches in parallel.
  5. Once all feature branches are merged forward and green, sequence-merge them together to produce the release-candidate branch r<new>. Build, test, and deploy against r<new>.
  6. Upstream submission — any feature branch is submit-ready against upstream: it's already formatted as Git commits against the upstream tree, just push it to the upstream remote.

Canonical disclosure

"We had two choices here: We could track patch files checked into source control and reapply them one by one in the correct order, or we could track patches in a separate repository that supported branching. In the end we chose to go with tracking feature branches in a separate Git repository. One of the reasons for this was to establish a good pipeline for making it very easy to submit feature branches and fixes upstream. By basing them on top of the libwebrtc Git repo, we could easily reuse existing upstream Chromium tools for building, testing, and submitting (gn, gclient, git cl, and more)." (sources/2026-04-09-meta-escaping-the-fork-webrtc-modernization)

Four named benefits

From the canonical disclosure:

  1. Highly parallelizable — each feature branch rebases independently of the others.
  2. Preserves Git history/contextgit log, git blame, git bisect all work on each branch as normal.
  3. Well-suited for LLM-driven auto-conflict-resolution — merge conflicts in Git are expressible as "this diff applied to that base" in a way LLMs can operate on. Meta explicitly names this as a motivating advantage for their architecture.
  4. Submit-ready upstream — any branch is a Git branch against upstream's own tree; upstream submission is a push + PR away.

When to reach for this pattern

  • You maintain long-lived patches against an OSS project's Git repo.
  • Your primary source tree doesn't naturally support the one-branch-per-patch-per-release discipline.
  • You want to keep the option of upstreaming each patch cheap.
  • You want auto-merge / LLM-assisted merge-conflict tools to work against your patches.

When not to

  • Your patches are too small to justify individual feature branches — a single internal-tweaks branch suffices.
  • Your main repo already has the branching support you need (Gerrit / GitLab / GitHub per-project repos already do this naturally).
  • You're upstreaming everything you carry, so there are no long-lived internal patches — just target upstream directly.

Consequences

  • Two source-control systems. The monorepo stays the primary source for internal code; the external repo is the dedicated patch-tracking system for one OSS project.
  • Branch hygiene matters. <feature>/<tag> per release means N patches × M tracked releases = N×M branches. Stale branches beyond a handful of releases should be garbage collected to avoid operational clutter.
  • Tooling investment. The upgrade workflow (merge-forward every branch, produce release candidate, build, test) typically becomes a scripted pipeline. Well worth it — the alternative is manual repo surgery on every upgrade.
  • Explicit coupling to upstream's tag cadence. The scheme only works as well as upstream's tag discipline; if upstream doesn't tag cleanly, anchor branches become harder to create.

Seen in

  • sources/2026-04-09-meta-escaping-the-fork-webrtc-modernization — canonical wiki instance. Meta's post-fork-retirement scheme for tracking residual internal WebRTC patches against Chromium tags (M143 = 7499, M145 = 7559). The pattern is explicitly architected to be LLM-friendly for future merge-conflict automation.
Last updated · 319 distilled / 1,201 read