Created
July 13, 2026 17:15
-
-
Save rgruesbeck/77e76370f985ff983a636df2234e94ce to your computer and use it in GitHub Desktop.
fast-rebase
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Upstream Sync Strategy: Moving to a High-Cadence Rebase Workflow | |
| ## Goal | |
| Shorten the time between an upstream release and our own release by moving | |
| from "merge upstream into develop, fix issues, release quarterly" to a | |
| high-cadence (weekly) sync process, automated where possible with a human | |
| in the loop for safety, backed by automated E2E tests to catch regressions | |
| introduced by unreleased upstream code. | |
| ## Rebase vs. Merge: the real trade-off | |
| **Rebase-onto-upstream** ("vendor branch" / "patch stack" model, used by | |
| Linux distros, Chromium embedders, many Android forks) keeps our | |
| customizations as a clean, readable patch series on top of upstream. | |
| - Benefits: our diff vs. upstream is always inspectable | |
| (`git log upstream/develop..develop` *is* our customizations), conflicts | |
| stay small because we resolve them weekly against fresh upstream, and old | |
| resolved conflicts don't come back to haunt us. | |
| - Costs: every rebase **rewrites history** and force-pushes our develop | |
| branch, which disrupts anyone with open feature branches or in-flight | |
| PRs. We also re-resolve the *same* conflicts every week unless we use | |
| `git rerere`. | |
| **Merge at high cadence** (weekly merges instead of quarterly) gets most of | |
| the "small conflicts, fast integration" benefit without rewriting history. | |
| The downside is history becomes a merge-lattice and our net diff vs. | |
| upstream is harder to see. | |
| **Middle path:** maintain our customizations as a rebased patch series | |
| internally, but cut releases from a separate, immutable branch so the | |
| rewritten branch is never something others build long-lived work on top | |
| of directly. | |
| **Recommendation:** if the team is small and the customization set is | |
| modest (roughly under ~50 commits of delta), weekly rebase is very | |
| automatable and gives the clearest picture of "what we changed." If many | |
| people branch off develop daily, weekly merges may cause less friction. | |
| The conflict-detection and E2E gating described below work for either | |
| choice. | |
| ## Recommended architecture (rebase variant) | |
| ### Branch model | |
| - `upstream/develop` — the upstream remote's develop branch. | |
| - `patches` — our rewritten branch, rebased weekly onto | |
| `upstream/develop`. This branch force-pushes; it is the source of truth | |
| for "our delta." | |
| - Feature work happens on short-lived branches off `patches`, merged via | |
| PR before the weekly rebase when possible. | |
| - Release branches/tags are cut from `patches` and are immutable, so | |
| releases are never affected by later rewrites. | |
| ### Weekly automated job (CI, e.g. GitHub Actions / GitLab CI) | |
| 1. `git fetch upstream`. | |
| 2. If upstream has no new commits, exit quietly. | |
| 3. Attempt `git rebase upstream/develop` on a fresh candidate branch | |
| (e.g. `rebase/2026-07-13`), with `git rerere` enabled and a shared | |
| rerere cache (commit the rerere cache to a side branch/artifact so CI | |
| reuses past resolutions — this is what makes weekly rebasing cheap). | |
| 4. **Clean rebase:** push the candidate branch, run the full E2E suite | |
| against it, and open a PR ("Weekly upstream sync — clean, E2E | |
| green/red"). A human approves, and the branch becomes the new | |
| `patches` (human-in-the-loop safety valve). | |
| 5. **Conflicts:** the job stops and opens an issue/PR with the | |
| conflicting commits/files, pinging the owning team. A human finishes | |
| the rebase locally, pushes the candidate, and CI re-runs E2E. | |
| ### Key mechanics | |
| - **`git rerere` with a shared cache** — records conflict resolutions so | |
| identical conflicts auto-resolve on later rebases. Without this, weekly | |
| rebasing is Groundhog Day. | |
| - **Curate the patch stack** — periodically squash/reorder our delta | |
| commits so each one is coherent and minimal. Fewer, cleaner patches | |
| means fewer conflicts. Upstream anything upstreamable — every patch we | |
| delete is conflicts we never see again. | |
| - **Isolate customizations structurally** where possible (separate files, | |
| plugin/hook points, config overlays instead of edits to upstream | |
| files). This is the highest-leverage long-term move: conflicts only | |
| happen where we touch upstream lines. | |
| - **E2E as the merge gate, not just a smoke test** — run it on the | |
| candidate branch before it becomes `patches`. Also run E2E nightly | |
| against raw `upstream/develop` so we learn about upstream breakage | |
| *before* our sync day, and can distinguish "upstream is broken" from | |
| "our rebase broke it." | |
| - **Track delta size as a metric** — number of patches, lines of diff vs. | |
| upstream, conflicts per sync. A rising trend signals accumulating fork | |
| debt. | |
| ## Tooling options for the automation | |
| 1. **Plain CI workflow (recommended starting point)** — a scheduled | |
| GitHub Action (~100 lines of bash) doing | |
| fetch → rebase-with-rerere → push candidate → open PR. Simple, | |
| transparent, easy to debug. Weekly cadence via `schedule: cron`. | |
| 2. **Existing sync tools** — GitHub's "Sync fork" API / `gh repo sync` | |
| (merge-based only), apps like Pull or Backstroke (merge-based, less | |
| actively maintained). Nothing off-the-shelf does rebase + | |
| human-in-the-loop well, which is why most forks hand-roll option 1. | |
| 3. **Agent-assisted conflict resolution** — have the CI job invoke an | |
| agent (e.g. `claude -p`) to propose conflict resolutions on the | |
| candidate branch, but always land the result via PR with E2E green + | |
| human review. Treat it strictly as a proposer, never an auto-merger. | |
| This can turn "human must do the rebase" into "human reviews a | |
| proposed resolution," a meaningful cadence win. | |
| ## Suggested rollout | |
| 1. **Now:** add the `upstream` remote, enable `rerere`, do one manual | |
| rebase to establish the clean patch stack. This first one is the | |
| painful one — it collapses years of merge history into a curated | |
| series. Consider `git rebase --onto` from the last upstream merge | |
| point rather than replaying full history. | |
| 2. **Week 2–3:** stand up the scheduled CI job that attempts the rebase | |
| and opens a PR; wire E2E to run on candidate branches. Add nightly | |
| E2E against raw upstream too. | |
| 3. **Then:** tighten cadence, add the shared rerere cache, optionally add | |
| agent-assisted conflict proposals, and start a standing effort to | |
| shrink the patch stack (upstream contributions, structural | |
| isolation). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment