Files
josh-sync/docs/adr/011-linearize-fallback-reverse.md
SBPro 65cc8eaf8a Trim overengineered docs and ADR
Remove redundant step-by-step subsections from workflow guide (the
table already covers it), tighten ADR-011 alternatives, and remove
version pins that go stale.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:55:53 +03:00

2.9 KiB

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)