# Simplify after review

`1ce7b69`→[main](/content/gh/entireio/git-sync/commits/main/index.html)·

Soph·2mo ago·4 files·+23 added/-24 removed

- Use cobra's MarkFlagsMutuallyExclusive for the lease/blind pair
  instead of a hand-rolled RunE check.
- Pull lease-failure markers to a package-level var; loop the
  contains-checks for symmetry and to keep the list in one place.
- Trim PlansToPushCommands doc; lease semantics already live on
  SyncPolicy.
- Shorten the legacy --force migration message.

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

## Sessions

604f682ccdc6View transcript

## Changes

4

- cmd/git-sync

- Mmain\_test.go+1/-1

- Msyncplan.go+2/-4

- internal

- convert

- Mconvert.go+2/-9

- gitproto

- Mpush.go+18/-10

```
594 unmodified lines

if err == nil {

t.Fatal("expected --force-with-lease and --force-blind together to be rejected")
}
if !strings.Contains(err.Error(), "mutually exclusive") {
if !strings.Contains(err.Error(), "force-with-lease") || !strings.Contains(err.Error(), "force-blind") {

t.Fatalf("unexpected error: %v", err)
}
}
```

Mcmd/git-sync/main\_test.go+1/-1

```
42 unmodified lines

SilenceUsage:  true,
RunE: func(cmd *cobra.Command, args []string) error {
if legacyForce {
return errors.New("--force has been removed; use --force-with-lease (previous --force semantics: receive-pack rejects updates where the target moved during the run) or --force-blind (true overwrite, matches `git push --force`)")
}
if req.Policy.ForceWithLease && req.Policy.ForceBlind {
return errors.New("--force-with-lease and --force-blind are mutually exclusive")
}
req.Policy.Mode = gitsync.OperationMode(modeValue)
req.Policy.Protocol = gitsync.ProtocolMode(protocolVal)
}

if err := cmd.Flags().MarkHidden("force"); err != nil {
panic(err)
}
cmd.MarkFlagsMutuallyExclusive("force-with-lease", "force-blind")
cmd.Flags().BoolVar(&req.Policy.Prune, "prune", false, "delete managed target refs that no longer exist on source")

```

Mcmd/git-sync/syncplan.go+2/-4

```
59 unmodified lines

// PlansToPushCommands converts planner BranchPlans directly to gitproto PushCommands.
//
// When forceBlind is true, the expected-old hash is zeroed for non-delete
// commands. This tells receive-pack to overwrite regardless of the current
// target tip — matching `git push --force` semantics. Delete commands always
// carry the captured target hash so the server can confirm what it is removing.
//
// When forceBlind is false (the default), commands carry the target hash
// captured at session start; receive-pack rejects updates where the target
// moved during the run, providing per-run `--force-with-lease` protection.
// When forceBlind is true, non-delete commands send a zero expected-old so
// receive-pack overwrites regardless of current target value; see SyncPolicy.
func PlansToPushCommands(plans []planner.BranchPlan, forceBlind bool) []gitproto.PushCommand {
out := make([]gitproto.PushCommand, len(plans))
for i, p := range plans {
```

Minternal/convert/convert.go+2/-9

```
104 unmodified lines

return req, hasDelete, hasUpdates, nil
// annotateLeaseFailure wraps a CommandStatusErr whose status looks like a
// lease failure (target ref moved during the sync, or the captured expected-old
// no longer matches) with a hint pointing users at the retry/override path.
// Other receive-pack errors pass through unchanged.
// leaseFailureMarkers are receive-pack ng reason substrings that indicate the
// captured target tip didn't match what was on the server at push time. Match
// is case-insensitive. CommandStatusErr.Status is a free-form string in go-git,
// so substring matching is the only option absent upstream sentinels.
var leaseFailureMarkers = []string{
"stale info",
"fetch first",
"non-fast-forward",
"does not match",
}

// annotateLeaseFailure wraps a lease-failure CommandStatusErr with a retry/
// override hint. Other receive-pack errors pass through unchanged.
func annotateLeaseFailure(err error) error {
var cs *packp.CommandStatusErr
if !errors.As(err, &cs) {
return err
}
status := strings.ToLower(cs.Status)
if !strings.Contains(status, "stale info") &&
!strings.Contains(status, "fetch first") &&
!strings.Contains(status, "non-fast-forward") &&
!strings.Contains(status, "does not match") {
return err
for _, marker := range leaseFailureMarkers {
if strings.Contains(status, marker) {
return fmt.Errorf("%w (target ref %s moved or differs from session start; rerun, or use --force-blind to overwrite)", err, cs.ReferenceName)
}
}
return fmt.Errorf("%w (target ref %s moved or differs from session start; rerun, or use --force-blind to overwrite)", err, cs.ReferenceName)
}
return err
}

// sendReceivePack encodes and POSTs a receive-pack request, then decodes the report.
```

Minternal/gitproto/push.go+18/-10
