# Push git-refs checkpoints fast-forward-only (no force)

There is no server-side ref protection, so force-pushing per-checkpoint refs by default risks a buggy or racing client silently clobbering good remote history. Switch `batchForcePushRefs` -> `batchPushRefs` using a plain ref:ref refspec (fast-forward-only): per-checkpoint refs normally advance by fast-forward so the common case still succeeds, while a genuine non-fast-forward divergence (e.g. the same checkpoint written differently elsewhere) is REJECTED rather than overwritten. On rejection the pre-push logs and leaves the refs queued (not overwritten); reconciling a diverged ref is deferred, and a future rewrite path (e.g. OPF) can use `--force-with-lease` where it must replace a ref.

## Sessions

## Changes

- cmd/entire/cli/strategy

- Mmanual_commit_push.go+5/-3
  - Mpush_common.go+15/-11
  - Mrefs_push_test.go+53/-13

```go
163 unmodified lines

pushCtx, pushSpan := perf.Start(ctx, "push_checkpoint_refs")
pushErr := batchForcePushRefs(pushCtx, ps.pushTarget(), existing)
pushErr := batchPushRefs(pushCtx, ps.pushTarget(), existing)
pushSpan.End()
if pushErr != nil {
	// Leave the refs queued; the next pre-push retries. Non-fatal so the
	// user's push proceeds.
	logging.Warn(ctx, "git-refs pre-push: batch push failed; refs left queued for retry",
	// user's push proceeds. The push is fast-forward-only, so this can mean a
	// checkpoint ref diverged on the remote (non-fast-forward) — we leave it
	// queued rather than force-overwriting it.
	logging.Warn(ctx, "git-refs pre-push: checkpoint ref push failed (possible non-fast-forward divergence); refs left queued, not overwritten",
		slog.String("error", pushErr.Error()))
	return nil
}
```

```go
func batchForcePushRefs(ctx context.Context, target string, refs []plumbing.ReferenceName) error { ... }
```

```go
func batchPushRefs(ctx context.Context, target string, refs []plumbing.ReferenceName) error { ... }
```

```go
func TestBatchForcePushRefs(t *testing.T) { ... }
```

```go
func TestBatchPushRefs(t *testing.T) { ... }
```

```go
func TestBatchForcePushRefs_Empty(t *testing.T) { ... }
```

```go
func TestBatchPushRefs_RejectsNonFastForward(t *testing.T) { ... }
```
