# Explain relay selection decisions

`d971ec0`→[main](/content/gh/entireio/git-sync/commits/main/index.html)· Soph·3mo ago·4 files·+68 added/-39 removed

## Sessions

896f021c4767View transcript

## Changes

4

- MREADME.md+2
- cmd/git-sync
  - Mmain_test.go+3
- internal/syncer
  - Mintegration_test.go+6
  - Msyncer.go+57/-39

```
88 unmodified lines

That is the intended way to compare the bootstrap relay path against the normal sync path on the same fixture or test repo.

`plan` and `sync` JSON output also include `relay`, `relay_mode`, and `relay_reason` so automation can tell whether a relay path was chosen and why.

When `sync` sees that all managed target refs are absent and the run is compatible with bootstrap semantics, it automatically uses the bootstrap relay path instead of the normal decode-and-repack sync path.

`sync` also uses a narrow incremental relay path for fast-forward branch updates and tag creation when there is no prune/delete, no force, and the target does not advertise `no-thin`. This now includes multi-branch batches, branch-to-branch mappings, and create-only tags. Tag retargeting and other more complex updates still fall back to the normal local decode-and-repack path.
```

MREADME.md+2

```
120 unmodified lines

if result["bootstrap_suggested"] != true {

t.Fatalf("expected bootstrap_suggested=true, got %#v", result["bootstrap_suggested"])
}
if result["relay_reason"] != "empty-target-managed-refs" {

t.Fatalf("expected relay_reason for bootstrap suggestion, got %#v", result["relay_reason"])
}
plans, ok := result["plans"].([]any)
if !ok || len(plans) == 0 {

t.Fatalf("expected plan entries, got %#v", result["plans"])
}
```

Mcmd/git-sync/main_test.go+3

```
58 unmodified lines

if !result.Relay {

t.Fatalf("expected sync to auto-switch to relay bootstrap on empty target")
}
if result.RelayReason != "empty-target-managed-refs" {

t.Fatalf("expected bootstrap relay reason, got %+v", result)
}

assertHeadsMatch(t, sourceRepo, targetRepo, testBranch)
```

Minternal/syncer/integration_test.go+6

```
101 unmodified lines

if !cfg.DryRun {
if result.Blocked > 0 {
return result, fmt.Errorf("blocked %d ref update(s); rerun with --force where appropriate", result.Blocked)
}
result.RelayReason = relayFallbackReason(cfg, pushPlans, targetAdv)

if !cfg.DryRun && canIncrementalRelay(cfg, pushPlans, targetAdv) {
relayPlans := append([]BranchPlan(nil), pushPlans...)
desiredRelay := desiredSubsetForPlans(desiredRefs, relayPlans)
packReader, err := sourceService.FetchPack(ctx, sourceConn, desiredRelay, targetRefMap)
if err != nil {
return result, fmt.Errorf("fetch source pack: %w", err)
}
d
} else if !cfg.DryRun && len(pushPlans) > 0 {
if err := pushToTarget(ctx, repo, targetConn, targetAdv, pushPlans, targetRefMap, cfg.Verbose); err != nil {
return result, fmt.Errorf("push target refs: %w", err)
}
}
```

Minternal/syncer/syncer.go+57/-39
