# Wire max/batch pack byte flags into sync and replicate

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

Replicate against a fresh target for a repo the size of linux currently
fails with "decode report-status: invalid pkt-len found: short pkt-line 4"  
because the receive-pack POST body hits a server limit (or times out)  
and the target closes the connection before writing a report-status.  
The fix is batching, which the bootstrap strategy already supports and  
which replicate's bootstrap-fallback already plumbs to the strategy via  
Config.BatchMaxPackBytes -- but the flags were only registered on the  
bootstrap subcommand. Replicate and sync silently ignored them.

## Changes:

- cmd/git-sync/main.go runSyncLike: register --max-pack-bytes and  
 --batch-max-pack-bytes. Update the usage string for sync, replicate,
 and plan accordingly.
- pkg/gitsync/unstable/client.go buildSyncConfig: forward MaxPackBytes
 and BatchMaxPackBytes from AdvancedOptions to syncer.Config. The
 fields already existed on AdvancedOptions; only bootstrap was using
 them.
- internal/syncer/integration_test.go: add TestRun_Integration-
ReplicateBootstrapBatchesWhenConfigured exercising a replicate call
 with BatchMaxPackBytes set, asserting it reaches the batched
 bootstrap path (batching=true, batch_count>=2) and leaves source
 and target heads matching. This guards the plumbing; regressing
 the forwarding would silently reintroduce the "flag has no effect"
 bug.
- CHANGELOG.md: document the added flags and the unstable
 buildSyncConfig forwarding.

Typical usage for a large initial replicate push:

```
git-sync replicate \
 --batch-max-pack-bytes 268435456 \
 --max-pack-bytes 10737418240 \
...  
```

splits the push into ~256 MiB batches, each of which the receive-pack
server processes and acknowledges before the next starts.

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

## Sessions

08a1fb563aa3View transcript

### Changes

4

- MCHANGELOG.md+7

- cmd/git-sync
  
  - Mmain.go+3/-1

- internal/syncer
  
  - Mintegration_test.go+42

- pkg/gitsync/unstable
  
  - Mclient.go+2

```
16 unmodified lines
```

- `gitsync.Client.Replicate` on the stable embedding surface.
- `gitsync.OperationMode`, `gitsync.ModeSync`, `gitsync.ModeReplicate`, and
  `SyncPolicy.Mode` for selecting the mode from library callers.
- `--max-pack-bytes` and `--batch-max-pack-bytes` flags on `sync`,
  `replicate`, and `plan`. Previously only `bootstrap` exposed them, but
  replicate's bootstrap-fallback path internally honors both — without
  these flags, users couldn't split a huge initial replicate push into
  tractable receive-pack POSTs for size-limited targets. The unstable
  library `buildSyncConfig` now forwards `MaxPackBytes` and
  `BatchMaxPackBytes` from `AdvancedOptions` to `syncer.Config`.

### Changed (stable API, breaking)

```  
MCHANGELOG.md+7
```

```
82 unmodified lines
```

```  
1716 unmodified lines
```

func TestRun_IntegrationReplicateBootstrapBatchesWhenConfigured(t *testing.T) {
        // Large bootstrap-relay pushes can overwhelm targets with body-size
        // limits (e.g. go-git-based receive-pack on raft-backed storage).
        // Replicate falls back through the bootstrap strategy for empty targets,
        // and must honor BatchMaxPackBytes so callers can split a huge push
        // into tractable receive-pack POSTs. Without this plumbing the replicate
        // CLI flag would be silently ignored.
        sourceRepo, sourceFS := newSourceRepo(t)
        makeLargeCommits(t, sourceRepo, sourceFS, 20, 200_000)

targetRepo, err := git.Init(memory.NewStorage())
        if err != nil {
            t.Fatalf("init target repo: %v", err)
        }

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo)
        targetServer := newSmartHTTPRepoServer(t, targetRepo)
        targetServer.receivePackThinCap = true
        defer sourceServer.Close()
        defer targetServer.Close()

result, err := Run(context.Background(), Config{
            Source:            Endpoint{URL: sourceServer.RepoURL()},
            Target:            Endpoint{URL: targetServer.RepoURL()},
            Mode:              modeReplicate,
            ProtocolMode:      protocolModeAuto,
            BatchMaxPackBytes: 500_000, // force > 1 batch for the generated pack
        })
        if err != nil {
            t.Fatalf("replicate with batched bootstrap failed: %v", err)
        }
        if result.OperationMode != modeReplicate {
            t.Fatalf("expected operation_mode=replicate, got %q", result.OperationMode)
        }
        if !result.Batching || result.BatchCount < 2 {
            t.Fatalf("expected batched bootstrap inside replicate, got batching=%t batch_count=%d result=%+v",
                result.Batching, result.BatchCount, result)
        }

assertHeadsMatch(t, sourceRepo, targetRepo, testBranch)
    }

func TestRun_IntegrationReplicateBootstrapsEmptyTarget(t *testing.T) {
        sourceRepo, sourceFS := newSourceRepo(t)
        makeCommits(t, sourceRepo, sourceFS, 2)
```
