Files
josh-sync/docs/adr/003-force-with-lease-forward.md
sync-bot b35703d271 Import josh-sync from subrepo/main
Sync-Origin: import/josh-sync/20260528-041502
2026-05-28 04:15:02 +01:00

1.6 KiB

ADR-003: Force-with-Lease for Forward Sync

Status: Accepted Date: 2026-01

Context

Forward sync pushes monorepo changes to the subrepo. If someone pushes directly to the subrepo between when josh-sync reads its HEAD and when josh-sync pushes, a naive git push would overwrite their work. A git push --force would be worse — it would silently destroy concurrent changes.

Decision

Use git push --force-with-lease=refs/heads/<branch>:<expected-sha> for all forward sync pushes. The expected SHA is recorded at the start of the sync operation (the "lease").

How it works

  1. Record subrepo HEAD SHA before any operations: subrepo_sha=$(subrepo_ls_remote "$branch")
  2. Perform merge of monorepo changes onto subrepo state
  3. Push with explicit lease: --force-with-lease=refs/heads/main:<subrepo_sha>
  4. If the subrepo HEAD changed since step 1, git rejects the push
  5. Josh-sync reports lease-rejected and retries on the next run

Consequences

Positive:

  • Never overwrites concurrent changes — git atomically checks the expected SHA
  • Explicit SHA lease (not just "current tracking ref") prevents stale-ref bugs
  • Failed leases are retried on the next sync run — no data loss, just delay
  • Works correctly with josh-proxy's SHA mapping

Negative:

  • Lease-rejected means the sync run did work that gets discarded (clone, merge, etc.)
  • Persistent lease failures indicate a concurrent push pattern that needs investigation
  • Requires the --force-with-lease flag with explicit SHA — the shorthand form (--force-with-lease without =) is unsafe because it uses the local tracking ref, which may be stale