errors: expose ErrTargetRefMoved / RefRejectedError on the public API

main

Commit

nodo3mo ago

Embedders that mirror into a target currently can only tell a benign racing concurrent push apart from a real push failure by substring-matching the free-form receive-pack error message. That is fragile: the git wire protocol carries no structured code for a report-status "ng" reason, so every consumer re-implements the same brittle matching, and broad markers like "non-fast-forward" are ambiguous (a legitimately non-fast-forward update that wasn't force-pushed looks identical to a race).

Give the classification a home in the library, where the protocol context lives. Sync/Replicate now report a typed *RefRejectedError (carrying the rejected Ref and the raw server Reason) for per-ref receive-pack rejections, reachable with errors.As. Rejections git-sync can prove are concurrent target-ref moves -- entire-server's compare-and-swap rejection ("remote ref has changed") and git's --force-with-lease lease miss ("stale info") -- additionally satisfy errors.Is(err, ErrTargetRefMoved).

The move classification is deliberately narrower than leaseFailureMarkers: "non-fast-forward" / "fetch first" are excluded so a real "needs --force" rejection is never silently treated as a benign, retryable race.

The typed error is layered over the existing annotateLeaseFailure wrap, so the message text and the underlying *packp.CommandStatusErr are preserved unchanged -- existing string and errors.As checks keep working, and IsLeaseFailure (used by the BestEffort path) is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com