2.8 KiB
ADR-013: Non-destructive adoption merge for existing subrepos
Status: Accepted
Date: 2026-04
Update (2026-05): The standalone josh-sync adopt <target> command described below has been folded into josh-sync onboard <target> as the adopt strategy (selectable via --mode=adopt, targets[].history_lock: preserve, or auto-detection when the subrepo has any branches). josh-sync adopt is retained as a thin CLI alias. The state file moved from <target>/adopt.json to <target>/onboard.json with a .strategy field; the legacy file is still read for backward compatibility. See the guide's Onboarding section.
Context
Existing subrepos often already have real history, open PRs, and developer clones.
The original import then reset onboarding path establishes Josh-compatible
history by force-pushing the Josh-filtered monorepo history onto the subrepo.
That is correct for empty replacement repos, but it rewrites the active subrepo
branch and invalidates local clones.
We need a path for active subrepos where history must remain available on the same branch and where open PR branches should stay based on the existing history.
Decision
Add josh-sync adopt <target> as a separate workflow from onboard and reset.
Adoption imports the subrepo content into the monorepo, waits for the import PR
to merge, then creates one merge commit on each configured subrepo branch:
- First parent: Josh-filtered monorepo HEAD
- Second parent: existing subrepo HEAD
- Tree: Josh-filtered monorepo tree
Josh follows first-parent history back to the monorepo, while Git still keeps the old subrepo history reachable through parent 2. The push is a normal fast-forward push from the existing subrepo HEAD to the adoption merge commit. No force-push is used.
Before creating the adoption merge, josh-sync requires the existing subrepo tree to match the Josh-filtered monorepo tree. If the trees differ, adoption aborts and the user must merge the import PR or reconcile the branch contents first.
Adoption state is stored separately at <target>/adopt.json on the
josh-sync-state branch.
Consequences
Positive:
- Existing subrepo history remains on the active branch.
- Existing clones can fast-forward instead of hard-resetting or re-cloning.
- Open PR branches remain based on reachable history.
- Josh-compatible ancestry is established without a destructive rewrite.
Negative:
- Adoption adds one synthetic merge commit to each adopted subrepo branch.
- Strict "no subrepo commit at all" adoption is impossible if the existing subrepo branch must become connected to the Josh-filtered history without a rewrite. A commit is needed to join the two histories.
- Tree equality is strict. If the monorepo import differs from the subrepo content, adoption stops until the user resolves the mismatch.