/simplify: make non-branch ref push non-force too (one consistent policy) · Entire
/simplify: make non-branch ref push non-force too (one consistent policy)
0d500bc·
Soph·2w ago·2 files·+10 added/-3 removed
Altitude follow-up to the non-force checkpoint push: tryPushRefCommon (the v1-era per-ref pusher behind doPushRef) still force-pushed non-branch refs (+ref:ref), which contradicts the new fast-forward-only policy for per-checkpoint refs and was a latent footgun — a future caller routing a checkpoint ref through doPushRef would silently overwrite a divergence. The non-branch path has no production caller today (the v1 PrePush loop only pushes the v1 branch), so dropping the force is safe and unifies the policy: every checkpoint ref, branch or per-checkpoint, is fast-forward-only with doPushRef's fetch+rebase recovery on divergence.
Also clarify (comment) that explain_export's RefName-error branch is defensive — cid is already validated by NewCheckpointID, so it can't fire — rather than a swallowed live error.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
89a2a991d773View transcript
Changes
2
cmd/entire/cli
Mexplain_export.go+3
strategy
Mpush_common.go+7/-3
205 unmodified lines
206
207
208
209
210
211
212
213
214
205 unmodified lines
// then re-list. Falls through to the v1-branch fetch below otherwise.
if cpCfg, _ := settings.LoadCheckpointsConfig(ctx); checkpoint.PrimaryIsRefs(cpCfg) { //nolint:errcheck // fail-soft: bad config surfaces via Open elsewhere
if cid, err := id.NewCheckpointID(prefix); err == nil {
// cid is already validated by NewCheckpointID above, so RefName can't
// error here; the guard is defensive — fall back to the v1-branch path
// rather than fetch a malformed ref.
refName, refErr := checkpoint.RefName(cid)
if refErr != nil {
return nil, lookup
Mcmd/entire/cli/explain_export.go+3
298 unmodified lines
299
300
301
302
303
302
303
304
305
306
307
308
309
310
307
311
312
313
314
298 unmodified lines
// tryPushRefCommon attempts to push a ref. No timeout of its own —
// runs under doPushRef's shared budget. Branch refs use a bare branch-name
// refSpec so existing remote-tracking works; non-branch refs use a force
// refspec ("+refs/...:refs/...") with no tracking shadow.
// refSpec so existing remote-tracking works; non-branch refs use an explicit
// "refs/...:refs/..." refSpec with no tracking shadow. Neither forces: a
// non-fast-forward is rejected so doPushRef's fetch+rebase recovery runs (and a
// genuinely diverged ref is never silently overwritten — there is no
// server-side ref protection). This keeps one consistent non-force policy for
// every checkpoint ref, branch or per-checkpoint.
func tryPushRefCommon(ctx context.Context, remoteName string, ref plumbing.ReferenceName) (pushResult, error) {
refSpec := ref.Short()
if !ref.IsBranch() {
refSpec = "" + ref.String() + ":" + ref.String()
refSpec = ref.String() + ":" + ref.String()
}
// Span the actual `git push` subprocess: on a slow remote (e.g. a custom
Mcmd/entire/cli/strategy/push_common.go+7/-3