59 lines
2.9 KiB
Markdown
59 lines
2.9 KiB
Markdown
# ADR-011: Linearize Fallback for Reverse Sync
|
|
|
|
**Status:** Accepted
|
|
**Date:** 2026-03
|
|
|
|
## Context
|
|
|
|
Josh-proxy rejects reverse sync pushes when the subrepo history contains merge commits whose parents it cannot map through the filter. This happens when:
|
|
|
|
1. A long-lived branch (e.g., `stage`) is merged into `main` via a merge commit
|
|
2. That branch contains auto-sync merge commits or criss-cross merges with `main`
|
|
3. Josh encounters the merge commit, tries to map both parents through the filter, and fails with a 500 error
|
|
|
|
This is a legitimate subrepo workflow — teams merge staging branches into main regularly. The sync tool should handle it without requiring teams to change their Git workflow.
|
|
|
|
### Alternatives considered
|
|
|
|
1. **Squash all commits into one**: Simple but destroys all commit granularity.
|
|
2. **Rewrite subrepo history**: Breaks josh's SHA mapping, forces all developers to re-clone.
|
|
3. **Linearize: cherry-pick regular commits, squash only merges**: Preserves individual commit granularity; only merge commits lose their multi-parent structure.
|
|
|
|
## Decision
|
|
|
|
When the direct push through josh-proxy fails, fall back to option 4: linearize the history by cherry-picking onto the josh-filtered base.
|
|
|
|
### How it works
|
|
|
|
1. Direct `git push` through josh-proxy is attempted first (existing behavior)
|
|
2. If it fails, create a temporary branch from `mono-filtered/<branch>`
|
|
3. Walk human commits (oldest-first, bot commits excluded) from the ancestry path:
|
|
- **Regular commits** (≤1 parent): `git cherry-pick <sha>` — preserves author, date, and message
|
|
- **Merge commits** (>1 parent): `git cherry-pick -m 1 <sha>` — applies the merge's diff relative to its first parent as a single commit
|
|
4. If cherry-pick conflicts (rare — usually due to ordering issues), fall back to `git diff | git apply` with the original author metadata
|
|
5. Push the linearized branch through josh-proxy
|
|
6. PR body includes a note explaining that merge commits were squashed
|
|
|
|
### What is preserved
|
|
|
|
- Individual non-merge commits (author, date, message, diff)
|
|
- The net effect of each merge commit (as a squashed single commit)
|
|
- The original commit list in the PR body for reference
|
|
|
|
### What is lost
|
|
|
|
- The multi-parent structure of merge commits (they become single-parent)
|
|
- The distinction between "changes introduced by the merge" vs "changes from each parent" — the merge is represented as its diff from first parent
|
|
|
|
## Consequences
|
|
|
|
**Positive:**
|
|
- Reverse sync no longer gets stuck on merge commits — handles the common case automatically
|
|
- Non-merge commits retain full granularity (no unnecessary squashing)
|
|
- Subrepo history is untouched — no rewriting, no broken sync relationship
|
|
- The PR body documents when linearization was used, so reviewers know
|
|
|
|
**Negative:**
|
|
- Merge commit semantics are lost on the monorepo side (they appear as regular commits)
|
|
- Adds complexity to the reverse sync path (two code paths: direct push and linearize fallback)
|