[Home](/content/site-root.html)

Log in

# Merge pull request \#99 from entireio/simplify/de-slop

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

Soph·1w ago·38 files·+821 added/-1,649 removed

Simplify: remove pass-through layers, dead code, and copy-pasted helpers

## Changes

38

- Mclient.go+55/-89

- cmd

- git-sync-bench

- Mmain.go+1/-9

- git-sync

- Mbootstrap.go+4/-10

- Mconvert\_sha256.go+1/-7

- Mflags.go+25/-10

- internal

- sha256convert

- Msha256convert.go+8/-33

- versioninfo

- Mversioninfo.go+8

- Mroot.go+1/-2

- Msyncplan.go+6/-15

- Mversion.go+1/-2

- internal

- auth

- Mauth.go+6/-7

- convert

- Mconvert.go-14

- gitproto

- Dconvert.go-60

- Dconvert\_test.go-211

- Mfetch.go+5/-63

- Mfetch\_test.go+10/-34

- Mpktline.go-6

- Mpktline\_test.go-27

- Mpush.go+22/-28

- Areaders.go+70

- Areaders\_test.go+183

- internalbridge

- Dconfig.go-149

- Dmodel.go-240

- Dmodel\_test.go-97

- planner

- Mcheckpoint.go-28

- Mtypes.go-37

- strategy

- bootstrap

- Mbootstrap.go+7/-59

- incremental

- Mincremental.go+2/-30

- Mincremental\_test.go-98

- materialized

- Mmaterialized\_test.go-99

- replicate

- Mreplicate.go+1/-31

- syncer

- Mprogress.go+5/-25

- Mprogress\_test.go-22

- Msyncer.go+30/-38

- Aresults.go+257

- Aresults\_test.go+97

- Mtypes.go-34

- unstable

- Mclient.go+16/-35

```
5 unmodified lines

6
7
8
9
9
10
11
12
23 unmodified lines

36
37
38
39
39
40
41
42
43
43
44
45
46
1 unmodified line

48
49
50
51
51
52
53
54
55
55
56
57
58
59
59
60
61
62
1 unmodified line

64
65
66
67
67
68
69
70
71
71
72
73
74
75
75
76
77
78
2 unmodified lines

81
82
83
84
84
85
86
87
87
88
89
90
89
90
91
92
93
94
95
96
97
98
92
99
100
94
101
102
96
97
103
104
99
100
101
102
103
104
105
106
107
108
109
110
105
106
107
113
114
108
109
110
116
111
112
118
113
114
120
115
116
122
123
124
125
126
127
128
129
130
131
132
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
52 unmodified lines

192
193
194
191
192
195
196
197
198
199
200
201
202
203
204
205
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
206
207
208
3 unmodified lines

212
213
214
245
246
247
248
215
216
217

5 unmodified lines

"fmt"
	"net/http"

"entire.io/entire/git-sync/internal/internalbridge"
	"entire.io/entire/git-sync/internal/syncer"
	"entire.io/entire/git-sync/internal/validation"
)

23 unmodified lines

if err != nil {
		return ProbeResult{}, err
	}
	result, err := internalbridge.Probe(ctx, cfg)
	result, err := syncer.Probe(ctx, cfg)
	if err != nil {
		return ProbeResult{}, fmt.Errorf("probe: %w", err)
	}
	return internalbridge.FromProbeResult(result), nil
	return fromProbeResult(result), nil
}

// Plan computes ref actions without pushing.
1 unmodified line

if err := req.Validate(); err != nil {
		return PlanResult{}, err
	}
	cfg, err := c.buildSyncConfig(ctx, req.Source, req.Target, req.Scope, req.Policy, req.CollectStats, true)
	cfg, err := c.buildSyncConfig(ctx, SyncRequest(req), true)
	if err != nil {
		return PlanResult{}, err
	}
	result, err := internalbridge.Run(ctx, cfg)
	result, err := syncer.Run(ctx, cfg)
	if err != nil {
		return PlanResult{}, fmt.Errorf("plan: %w", err)
	}
	return internalbridge.FromSyncResult(result), nil
	return fromSyncResult(result), nil
}

// Sync executes a sync between two remotes.
1 unmodified line

if err := req.Validate(); err != nil {
		return SyncResult{}, err
	}
	cfg, err := c.buildSyncConfig(ctx, req.Source, req.Target, req.Scope, req.Policy, req.CollectStats, false)
	cfg, err := c.buildSyncConfig(ctx, req, false)
	if err != nil {
		return SyncResult{}, err
	}
	result, err := internalbridge.Run(ctx, cfg)
	result, err := syncer.Run(ctx, cfg)
	if err != nil {
		return SyncResult{}, fmt.Errorf("sync: %w", err)
	}
	return internalbridge.FromSyncResult(result), nil
	return fromSyncResult(result), nil
}

// Replicate executes source-authoritative relay-only replication between two remotes.
2 unmodified lines

return c.Sync(ctx, req)
}

func (c *Client) buildProbeConfig(ctx context.Context, req ProbeRequest) (internalbridge.Config, error) {
func (c *Client) buildProbeConfig(ctx context.Context, req ProbeRequest) (syncer.Config, error) {
	sourceAuth, err := c.authFor(ctx, req.Source, SourceRole)
	if err != nil {
		return internalbridge.Config{}, err
		return syncer.Config{}, err
	}
	var target *internalbridge.Endpoint
	targetAuth := internalbridge.EndpointAuth{}
	cfg := syncer.Config{
		Source:             syncerEndpoint(req.Source, sourceAuth),
		HTTPClient:         c.httpClient,
		IncludeTags:        req.IncludeTags,
		AllRefs:            req.AllRefs,
		ExcludeRefPrefixes: append([]string(nil), req.ExcludeRefPrefixes...),
		ShowStats:          req.CollectStats,
		ProtocolMode:       string(req.Protocol),
	}
	if req.Target != nil {
		resolvedTargetAuth, err := c.authFor(ctx, *req.Target, TargetRole)
		targetAuth, err := c.authFor(ctx, *req.Target, TargetRole)
		if err != nil {
			return internalbridge.Config{}, err
			return syncer.Config{}, err
		}
		target = ptr(bridgeEndpoint(*req.Target))
		targetAuth = bridgeEndpointAuth(resolvedTargetAuth)
		cfg.Target = syncerEndpoint(*req.Target, targetAuth)
	}
	return internalbridge.ProbeConfig(
		bridgeEndpoint(req.Source),
		bridgeEndpointAuth(sourceAuth),
		target,
		targetAuth,
		internalbridge.ProtocolMode(req.Protocol),
		req.IncludeTags,
		req.AllRefs,
		req.CollectStats,
		req.ExcludeRefPrefixes,
		c.httpClient,
	), nil
	return cfg, nil
}

func (c *Client) buildSyncConfig(ctx context.Context, source Endpoint, target Endpoint, scope RefScope, policy SyncPolicy, collectStats, dryRun bool) (internalbridge.Config, error) {
	sourceAuth, err := c.authFor(ctx, source, SourceRole)
func (c *Client) buildSyncConfig(ctx context.Context, req SyncRequest, dryRun bool) (syncer.Config, error) {
	sourceAuth, err := c.authFor(ctx, req.Source, SourceRole)
	if err != nil {
		return internalbridge.Config{}, err
		return syncer.Config{}, err
	}
	targetAuth, err := c.authFor(ctx, target, TargetRole)
	targetAuth, err := c.authFor(ctx, req.Target, TargetRole)
	if err != nil {
		return internalbridge.Config{}, err
		return syncer.Config{}, err
	}
	return internalbridge.SyncConfig(
		bridgeEndpoint(source),
		bridgeEndpointAuth(sourceAuth),
		bridgeEndpoint(target),
		bridgeEndpointAuth(targetAuth),
		bridgeScope(scope),
		bridgePolicy(policy),
		collectStats,
		dryRun,
		c.httpClient,
	), nil
	return syncer.Config{
		Source:                 syncerEndpoint(req.Source, sourceAuth),
		Target:                 syncerEndpoint(req.Target, targetAuth),
		HTTPClient:             c.httpClient,
		Branches:               append([]string(nil), req.Scope.Branches...),
		Mappings:               validationMappings(req.Scope.Mappings),
		AllRefs:                req.Scope.AllRefs,
		ExcludeRefPrefixes:     append([]string(nil), req.Scope.ExcludeRefPrefixes...),
		ExcludeRefs:            append([]string(nil), req.Scope.ExcludeRefs...),
		IncludeTags:            req.Policy.IncludeTags,
		DryRun:                 dryRun,
		ShowStats:              req.CollectStats,
		Mode:                   string(req.Policy.Mode),
		ForceWithLease:         req.Policy.ForceWithLease,
		ForceBlind:             req.Policy.ForceBlind,
		Prune:                  req.Policy.Prune,
		BestEffort:             req.Policy.BestEffort,
		ProtocolMode:           string(req.Policy.Protocol),
		MaterializedMaxObjects: syncer.DefaultMaterializedMaxObjects,
	}, nil
}

func (c *Client) authFor(ctx context.Context, endpoint Endpoint, role EndpointRole) (EndpointAuth, error) {
52 unmodified lines

return nil
}

func bridgeEndpoint(ep Endpoint) internalbridge.Endpoint {
	return internalbridge.Endpoint{
func syncerEndpoint(ep Endpoint, auth EndpointAuth) syncer.Endpoint {
	return syncer.Endpoint{
		URL:                    ep.URL,
		Username:               auth.Username,
		Token:                  auth.Token,
		BearerToken:            auth.BearerToken,
		SkipTLSVerify:          auth.SkipTLSVerify,
		FollowInfoRefsRedirect: ep.FollowInfoRefsRedirect,
	}
}

func bridgeEndpointAuth(auth EndpointAuth) internalbridge.EndpointAuth {
	return internalbridge.EndpointAuth{
		Username:      auth.Username,
		Token:         auth.Token,
		BearerToken:   auth.BearerToken,
		SkipTLSVerify: auth.SkipTLSVerify,
	}
}

func bridgeScope(scope RefScope) internalbridge.RefScope {
	mappings := make([]internalbridge.RefMapping, 0, len(scope.Mappings))
	for _, mapping := range scope.Mappings {
		mappings = append(mappings, internalbridge.RefMapping{
			Source: mapping.Source,
			Target: mapping.Target,
		})
	}
	return internalbridge.RefScope{
		Branches:           append([]string(nil), scope.Branches...),
		Mappings:           mappings,
		AllRefs:            scope.AllRefs,
		ExcludeRefPrefixes: append([]string(nil), scope.ExcludeRefPrefixes...),
		ExcludeRefs:        append([]string(nil), scope.ExcludeRefs...),
	}
}

func bridgePolicy(policy SyncPolicy) internalbridge.SyncPolicy {
	return internalbridge.SyncPolicy{
		Mode:           internalbridge.OperationMode(policy.Mode),
		IncludeTags:    policy.IncludeTags,
		ForceWithLease: policy.ForceWithLease,
		ForceBlind:     policy.ForceBlind,
		Prune:          policy.Prune,
		BestEffort:     policy.BestEffort,
		Protocol:       internalbridge.ProtocolMode(policy.Protocol),
	}
}

func validateOperationMode(mode OperationMode) error {
	switch mode {
	case "", ModeSync, ModeReplicate:
3 unmodified lines

}
}

func ptr[T any](v T) *T {
	return &v
}

func validationMappings(mappings []RefMapping) []validation.RefMapping {
	out := make([]validation.RefMapping, 0, len(mappings))
	for _, mapping := range mappings {
```

Mclient.go+55/-89

```
382 unmodified lines

383
384
385
386
387
388
389
390
391
392
393
394
386
387
388
389

382 unmodified lines

return nil
	}
	slices.Sort(input)
	out := input[:0]
	var prev string
	for i, item := range input {
		if i == 0 || item != prev {
			out = append(out, item)
			prev = item
		}
	}
	return out
	return slices.Compact(input)
}

func normalizeRepoURL(raw string) (string, error) {
```

Mcmd/git-sync-bench/main.go+1/-9

```
4 unmodified lines

5
6
7
8
8
9
10
25 unmodified lines

36
37
38
40
41
42
43
44
45
46
47
48
39
40
41
42
43
44
45
46

4 unmodified lines

"fmt"

gitsync "entire.io/entire/git-sync"
	"entire.io/entire/git-sync/internal/validation"
	"entire.io/entire/git-sync/unstable"
	"github.com/spf13/cobra"
)
25 unmodified lines

if branches != "" {
				req.Scope.Branches = splitCSV(branches)
			}
			for _, raw := range mappings {
				mapping, err := validation.ParseMapping(raw)
				if err != nil {
					return fmt.Errorf("parse mapping %q: %w", raw, err)
				}
				req.Scope.Mappings = append(req.Scope.Mappings, gitsync.RefMapping{
					Source: mapping.Source,
					Target: mapping.Target,
				})
			parsed, err := parseMappings(mappings)
			if err != nil {
				return err
			}
			req.Scope.Mappings = parsed

if req.Source.URL == "" || req.Target.URL == "" {
				return errors.New("bootstrap requires source and target repository URLs")
```

Mcmd/git-sync/bootstrap.go+4/-10

```
73 unmodified lines

74
75
76
77
78
79
80
81
82
83
77
78
79
80

73 unmodified lines

cmd.Flags().BoolVar(&req.SourceFollowInfoRefsRedirect, "source-follow-info-refs-redirect",
		envBool("GITSYNC_SOURCE_FOLLOW_INFO_REFS_REDIRECT"),
		"send follow-up source RPCs to the final /info/refs redirect host")
	addSecretFlag(cmd, &req.SourceAuth.Token, "source-token", "GITSYNC_SOURCE_TOKEN", "source token/password")
	cmd.Flags().StringVar(&req.SourceAuth.Username, "source-username",
		envOr("GITSYNC_SOURCE_USERNAME", "git"), "source basic auth username")
	addSecretFlag(cmd, &req.SourceAuth.BearerToken, "source-bearer-token", "GITSYNC_SOURCE_BEARER_TOKEN", "source bearer token")
	cmd.Flags().BoolVar(&req.SourceAuth.SkipTLSVerify, "source-insecure-skip-tls-verify",
		envBool("GITSYNC_SOURCE_INSECURE_SKIP_TLS_VERIFY"),
		"skip TLS certificate verification for the source")
	addSourceAuth(cmd, &req.SourceAuth)
	cmd.Flags().StringVar(&req.TargetDir, "target-dir", "", "directory to initialize as a SHA256 bare repository")

allRefsFlag(cmd, allRefsUsageScopeOnly, &req.AllRefs)
```

Mcmd/git-sync/convert\_sha256.go+1/-7

```
125 unmodified lines

126
127
128
129
129
130
131
132
46 unmodified lines

179
180
181
182
183
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
185
186
200
201
202
203
204
3 unmodified lines

208
209
210
196
211
212
213
214
3 unmodified lines

218
219
220
206
221
222
223
224
3 unmodified lines

228
229
230
216
231
232
218
233
234
220
235
236

125 unmodified lines

}

func newProtocolFlag() protocolModeFlag {
	return protocolModeFlag(protocolMode(envOr("GITSYNC_PROTOCOL", validation.ProtocolAuto)))
	return protocolModeFlag(envOr("GITSYNC_PROTOCOL", validation.ProtocolAuto))
}

func envOr(key, fallback string) string {
46 unmodified lines

return out
}

type protocolMode gitsync.ProtocolMode
type operationMode gitsync.OperationMode
// parseMappings converts raw --map values (src:dst form) into ref mappings.
// Returns nil (not an empty slice) when no mappings were given, matching the
// zero value the request structs start with.
func parseMappings(raw []string) ([]gitsync.RefMapping, error) {
	if len(raw) == 0 {
		return nil, nil
	}
	out := make([]gitsync.RefMapping, 0, len(raw))
	for _, value := range raw {
		mapping, err := validation.ParseMapping(value)
		if err != nil {
			return nil, fmt.Errorf("parse mapping %q: %w", value, err)
		}
		out = append(out, gitsync.RefMapping{Source: mapping.Source, Target: mapping.Target})
	}
	return out, nil
}

type protocolModeFlag protocolMode
type operationModeFlag operationMode
type protocolModeFlag gitsync.ProtocolMode
type operationModeFlag gitsync.OperationMode

func (p *protocolModeFlag) String() string { return string(*p) }
func (p *protocolModeFlag) Type() string   { return "string" }
3 unmodified lines

if err != nil {
		return fmt.Errorf("normalize protocol: %w", err)
	}
	*p = protocolModeFlag(protocolMode(gitsync.ProtocolMode(mode)))
	*p = protocolModeFlag(mode)
	return nil
}

3 unmodified lines

func (m *operationModeFlag) Set(value string) error {
	switch gitsync.OperationMode(value) {
	case gitsync.ModeSync, gitsync.ModeReplicate:
		*m = operationModeFlag(operationMode(value))
		*m = operationModeFlag(value)
		return nil
	default:
		return fmt.Errorf("unsupported mode %q", value)
3 unmodified lines

// defaultOperationMode returns the starting value for the --mode flag.
// Subcommands that pin a mode (sync, replicate) pass it in; plan passes ""
// and gets sync as the default, letting --mode override it.
func defaultOperationMode(defaultMode gitsync.OperationMode) operationMode {
func defaultOperationMode(defaultMode gitsync.OperationMode) operationModeFlag {
	if defaultMode != "" {
		return operationMode(defaultMode)
		return operationModeFlag(defaultMode)
	}
	return operationMode(gitsync.ModeSync)
	return operationModeFlag(gitsync.ModeSync)
}
```

Mcmd/git-sync/flags.go+25/-10

```
41 unmodified lines

42
43
44
45
45
46
47
903 unmodified lines

951
952
953
955
954
955
956
957
9 unmodified lines

967
968
969
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
970
971
972
552 unmodified lines

1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1559
1560
1561
1562
1563
1564
1565
1541
1542
1543

41 unmodified lines

formatcfg "github.com/go-git/go-git/v6/plumbing/format/config"
	"github.com/go-git/go-git/v6/plumbing/object"
	"github.com/go-git/go-git/v6/plumbing/storer"
	transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
	"github.com/go-git/go-git/v6/storage/filesystem"

gitsync "entire.io/entire/git-sync"
903 unmodified lines

SkipTLSVerify: req.SourceAuth.SkipTLSVerify,
	}, ep)
	httpClient := &http.Client{Transport: gitproto.NewHTTPTransport(req.SourceAuth.SkipTLSVerify)}
	conn := gitproto.NewHTTPConnWithClient(ep, "source", normalizeAuth(authMethod), httpClient)
	conn := gitproto.NewHTTPConnWithClient(ep, "source", authMethod, httpClient)
	conn.FollowInfoRefsRedirect = req.SourceFollowInfoRefsRedirect

mode := string(req.ProtocolMode)
9 unmodified lines

return conn, svc, refs, nil
}

func normalizeAuth(m auth.Method) gitproto.AuthMethod {
	if m == nil {
		return nil
	}
	// auth.Method and gitproto.AuthMethod share the same Authorizer signature.
	// Wrap so we can pass either *transporthttp.BasicAuth or *transporthttp.TokenAuth.
	if a, ok := m.(*transporthttp.BasicAuth); ok {
		return a
	}
	if a, ok := m.(*transporthttp.TokenAuth); ok {
		return a
	}
	return authAdapter{m: m}
}

type authAdapter struct{ m auth.Method }

func (a authAdapter) Authorizer(req *http.Request) error {
	if err := a.m.Authorizer(req); err != nil {
		return fmt.Errorf("authorize request: %w", err)
	}
	return nil
}

// translator walks the SHA1 source store, rewrites object content with
// SHA256-mapped hashes, and writes the result into the target bare repo
// via SetEncodedObject. The target storer is configured for SHA256 (see
552 unmodified lines

return b.String(), count, nil
}

// resolveCacheEntry holds a memoized (Hash, matchResult) pair from
// resolveMessageRef. Stored in t.resolveCache keyed by lowercased prefix.
type resolveCacheEntry struct {
	hash   plumbing.Hash
	result matchResult
}

// resolveMessageRef classifies a hex prefix against the reachable set.
// Returns matchUnique with the resolved SHA1 when exactly one commit
// or tag in scope matches; matchAmbiguous when more than one does;
// matchNone otherwise (no match, or the match is a blob/tree — those
// are filtered so incidental hex collisions on content hashes aren't
// rewritten).
// resolveCacheEntry holds a memoized (Hash, matchResult) pair from
// resolveMessageRef. Stored in t.resolveCache keyed by lowercased prefix.
type resolveCacheEntry struct {
	hash   plumbing.Hash
	result matchResult
}

func (t *translator) resolveMessageRef(prefix string) (plumbing.Hash, matchResult) {
	// Canonicalize to lowercase: hashPattern is case-insensitive so
	// the caller can match `ABCD1234` in a message, but reachable
```

Mcmd/git-sync/internal/sha256convert/sha256convert.go+8/-33

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

package versioninfo

import "fmt"

var (
	Version = "dev"
	Commit  = "none"
	Date    = "unknown"
)

// String renders the one-line build description shared by `git-sync version`
// and `git-sync --version`.
func String() string {
	return fmt.Sprintf("git-sync %s (commit %s, built %s)", Version, Commit, Date)
}
```

Mcmd/git-sync/internal/versioninfo/versioninfo.go+8

```
26 unmodified lines

27
28
29
30
31
30
31
32
33

26 unmodified lines

return cmd.Help()
		},
	}
	cmd.SetVersionTemplate(fmt.Sprintf("git-sync %s (commit %s, built %s)\n",
		versioninfo.Version, versioninfo.Commit, versioninfo.Date))
	cmd.SetVersionTemplate(versioninfo.String() + "\n")

cmd.AddCommand(newSyncCmd())
	cmd.AddCommand(newReplicateCmd())
```

Mcmd/git-sync/root.go+1/-2

```
4 unmodified lines

5
6
7
8
8
9
10
17 unmodified lines

28
29
30
32
31
32
33
34
19 unmodified lines

54
55
56
58
59
60
61
62
63
64
65
66
57
58
59
60
61
62
63
64
3 unmodified lines

68
69
70
77
78
79
80
71
72
73
74

4 unmodified lines

"fmt"

gitsync "entire.io/entire/git-sync"
	"entire.io/entire/git-sync/internal/validation"
	"entire.io/entire/git-sync/unstable"
	"github.com/spf13/cobra"
)
17 unmodified lines

sourceAuth  gitsync.EndpointAuth
		targetAuth  gitsync.EndpointAuth
		branches    string
		modeValue   = operationModeFlag(defaultOperationMode(defaultMode))
		modeValue   = defaultOperationMode(defaultMode)
		protocolVal = newProtocolFlag()
		legacyForce bool
		req         = unstable.SyncRequest{DryRun: dryRun}
19 unmodified lines

if req.Source.URL == "" || req.Target.URL == "" {
				return fmt.Errorf("%s requires source and target repository URLs", name)
3 unmodified lines

Auth: gitsync.StaticAuthProvider{Source: sourceAuth, Target: targetAuth},
			})

var (
				result unstable.Result
				err    error
			)
			var result unstable.Result
			ctx := cmd.Context()
			switch {
			case dryRun:
```

Mcmd/git-sync/syncplan.go+6/-15

```
11 unmodified lines

12
13
14
15
16
15
16
17
18

11 unmodified lines

Use:   "version",
		Short: "Show build information",
		Run: func(cmd *cobra.Command, _ []string) {
			fmt.Fprintf(cmd.OutOrStdout(), "git-sync %s (commit %s, built %s)\n",
				versioninfo.Version, versioninfo.Commit, versioninfo.Date)
			fmt.Fprintln(cmd.OutOrStdout(), versioninfo.String())
		},
	}
}
```

Mcmd/git-sync/version.go+1/-2

```
2 unmodified lines

3
4
5
6
6
7
8
9
10
11
12
13
14
15
16
16
17
18
19
20
21
17
18
19
20
21
22
23

2 unmodified lines

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os/exec"
	"strings"

transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"

"entire.io/entire/git-sync/internal/gitproto"
)

const defaultGitUsername = "git"

// Method authorizes outbound HTTP requests for a remote. It is satisfied
// by *transporthttp.BasicAuth and *transporthttp.TokenAuth, whose Authorizer
// methods replaced the Method interface that go-git removed in v6 alpha.2.
type Method interface {
	Authorizer(req *http.Request) error
}
// Method authorizes outbound HTTP requests for a remote. It aliases
// gitproto.AuthMethod so values returned by Resolve flow into gitproto
// connection constructors without conversion.
type Method = gitproto.AuthMethod

// Endpoint holds the authentication-related fields for a remote.
type Endpoint struct {
```

Minternal/auth/auth.go+6/-7

```
44 unmodified lines

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
48
49
50

44 unmodified lines

return out
}

// PlansToPushPlans converts planner BranchPlans to gitproto PushPlans.
func PlansToPushPlans(plans []planner.BranchPlan) []gitproto.PushPlan {
	out := make([]gitproto.PushPlan, len(plans))
	for i, p := range plans {
		out[i] = gitproto.PushPlan{
			TargetRef:  p.TargetRef,
			TargetHash: p.TargetHash,
			SourceHash: p.SourceHash,
			Delete:     p.Action == planner.ActionDelete,
		}
	}
	return out
}

// PlansToPushCommands converts planner BranchPlans directly to gitproto PushCommands.
// When forceBlind is true, non-delete commands send a zero expected-old so
// receive-pack overwrites regardless of current target value; see SyncPolicy.
```

Minternal/convert/convert.go-14

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

package gitproto

import (
	"errors"
	"fmt"
	"io"

"github.com/go-git/go-git/v6/plumbing"
)

// ToPushCommands converts a slice of PushPlans to PushCommands.
// Used by all strategy packages to avoid copy-pasting the conversion.
func ToPushCommands(plans []PushPlan) []PushCommand {
	cmds := make([]PushCommand, 0, len(plans))
	for _, p := range plans {
		cmd := PushCommand{Name: p.TargetRef, Old: p.TargetHash}
		if p.Delete {
			cmd.Delete = true
		} else {
			cmd.New = p.SourceHash
		}
		cmds = append(cmds, cmd)
	}
	return cmds
}

// PushPlan is a minimal interface for plan-to-command conversion.
type PushPlan struct {
	TargetRef  plumbing.ReferenceName
	TargetHash plumbing.Hash
	SourceHash plumbing.Hash
	Delete     bool
}

// LimitPackReader wraps a ReadCloser with a byte limit. Shared across strategies.
func LimitPackReader(r io.ReadCloser, maxBytes int64) io.ReadCloser {
	if maxBytes <= 0 {
		return r
	}
	return &packLimitRC{ReadCloser: r, max: maxBytes}
}

type packLimitRC struct {
	io.ReadCloser

max  int64
	read int64
}

func (r *packLimitRC) Read(p []byte) (int, error) {
	n, err := r.ReadCloser.Read(p)
	r.read += int64(n)
	if r.read > r.max {
		return n, fmt.Errorf("source pack exceeded max-pack-bytes limit (%d)", r.max)
	}
	if err != nil && !errors.Is(err, io.EOF) {
		return n, fmt.Errorf("read: %w", err)
	}
	return n, err //nolint:wrapcheck // io.EOF must pass through for io.Reader contract
}
```

Dinternal/gitproto/convert.go-60

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

package gitproto

import (
	"io"
	"strings"
	"testing"

"github.com/go-git/go-git/v6/plumbing"
)

func TestToPushCommands(t *testing.T) {
	hashA := plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	hashB := plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")

tests := []struct {
		name       string
		plan       PushPlan
		wantNew    plumbing.Hash
		wantOld    plumbing.Hash
		wantDelete bool
	}{
		{
			name: "create command",
			plan: PushPlan{
				TargetRef:  "refs/heads/main",
				TargetHash: plumbing.ZeroHash,
				SourceHash: hashA,
			},
			wantNew: hashA,
			wantOld: plumbing.ZeroHash,
		},
		{
			name: "update command",
			plan: PushPlan{
				TargetRef:  "refs/heads/main",
				TargetHash: hashA,
				SourceHash: hashB,
			},
			wantNew: hashB,
			wantOld: hashA,
		},
		{
			name: "delete command",
			plan: PushPlan{
				TargetRef:  "refs/heads/old-branch",
				TargetHash: hashA,
				Delete:     true,
			},
			wantOld:    hashA,
			wantDelete: true,
		},
	}

for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmds := ToPushCommands([]PushPlan{tt.plan})
			if len(cmds) != 1 {
				t.Fatalf("expected 1 command, got %d", len(cmds))
			}
			cmd := cmds[0]
			if cmd.Name != tt.plan.TargetRef {
				t.Errorf("Name = %s, want %s", cmd.Name, tt.plan.TargetRef)
			}
			if cmd.Old != tt.wantOld {
				t.Errorf("Old = %s, want %s", cmd.Old, tt.wantOld)
			}
			if cmd.Delete != tt.wantDelete {
				t.Errorf("Delete = %v, want %v", cmd.Delete, tt.wantDelete)
			}
			if !tt.wantDelete && cmd.New != tt.wantNew {
				t.Errorf("New = %s, want %s", cmd.New, tt.wantNew)
			}
		})
	}
}

func TestToPushCommandsMultiple(t *testing.T) {
	plans := []PushPlan{
		{TargetRef: "refs/heads/a", SourceHash: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")},
		{TargetRef: "refs/heads/b", SourceHash: plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")},
		{TargetRef: "refs/heads/c", TargetHash: plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc"), Delete: true},
	}
	cmds := ToPushCommands(plans)
	if len(cmds) != 3 {
		t.Fatalf("expected 3 commands, got %d", len(cmds))
	}
}

func TestToPushCommandsEmpty(t *testing.T) {
	cmds := ToPushCommands(nil)
	if len(cmds) != 0 {
		t.Fatalf("expected 0 commands for nil input, got %d", len(cmds))
	}
}

func TestLimitPackReaderWithinLimit(t *testing.T) {
	data := "hello world"
	rc := io.NopCloser(strings.NewReader(data))
	limited := LimitPackReader(rc, 1024)
	defer limited.Close()

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

func TestLimitPackReaderExceedsLimit(t *testing.T) {
	data := "this is more than ten bytes of data"
	rc := io.NopCloser(strings.NewReader(data))
	limited := LimitPackReader(rc, 10)
	defer limited.Close()

_, err := io.ReadAll(limited)
	if err == nil {
		t.Fatal("expected error when exceeding limit, got nil")
	}
	if !strings.Contains(err.Error(), "source pack exceeded max-pack-bytes limit") {
		t.Errorf("unexpected error message: %v", err)
	}
}

func TestLimitPackReaderZeroLimitPassesThrough(t *testing.T) {
	data := "unlimited data"
	rc := io.NopCloser(strings.NewReader(data))
	limited := LimitPackReader(rc, 0)
	defer limited.Close()

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

func TestLimitPackReaderNegativeLimitPassesThrough(t *testing.T) {
	data := "unlimited data"
	rc := io.NopCloser(strings.NewReader(data))
	limited := LimitPackReader(rc, -1)
	defer limited.Close()

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

func TestSortedUniqueHashes(t *testing.T) {
	hashA := plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	hashB := plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
	hashC := plumbing.NewHash("cccccccccccccccccccccccccccccccccccccccc")

tests := []struct {
		name  string
		input []plumbing.Hash
		want  []plumbing.Hash
	}{
		{
			name:  "deduplicates repeated hashes",
			input: []plumbing.Hash{hashA, hashB, hashA, hashC, hashB},
			want:  []plumbing.Hash{hashA, hashB, hashC},
		},
		{
			name:  "already sorted and unique is unchanged",
			input: []plumbing.Hash{hashA, hashB, hashC},
			want:  []plumbing.Hash{hashA, hashB, hashC},
		},
		{
			name:  "reverse order gets sorted",
			input: []plumbing.Hash{hashC, hashB, hashA},
			want:  []plumbing.Hash{hashA, hashB, hashC},
		},
		{
			name:  "single element",
			input: []plumbing.Hash{hashB},
			want:  []plumbing.Hash{hashB},
		},
		{
			name:  "empty input",
			input: []plumbing.Hash{},
			want:  []plumbing.Hash{},
		},
		{
			name:  "nil input",
			input: nil,
			want:  []plumbing.Hash{},
		},
	}

for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := SortedUniqueHashes(tt.input)
			if len(got) != len(tt.want) {
				t.Fatalf("len = %d, want %d", len(got), len(tt.want))
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Errorf("index %d: got %s, want %s", i, got[i], tt.want[i])
				}
			}
		})
	}
}
```

Dinternal/gitproto/convert\_test.go-211

```
27 unmodified lines

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
31
32
33
50
34
35
36
37
41 unmodified lines

79
80
81
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
82
83
84
85
86
87
148
88
89
90
91
92
93

27 unmodified lines

IsTag      bool
}

// FetchFeatures summarizes negotiated source fetch features used by strategies.
type FetchFeatures struct {
	Filter     bool
	IncludeTag bool
}

func (s *RefService) FetchFeatures() FetchFeatures {
	if s == nil || s.Protocol != "v2" || s.V2Caps == nil {
		return FetchFeatures{}
	}
	return FetchFeatures{
		Filter:     s.V2Caps.FetchSupports("filter"),
		IncludeTag: s.V2Caps.FetchSupports("include-tag"),
	}
}

// SupportsBootstrapBatch centralizes the source-side capability check for the
// batched bootstrap strategy.
func (s *RefService) SupportsBootstrapBatch() bool {
	return s != nil && s.Protocol == "v2" && s.FetchFeatures().Filter
	return s != nil && s.Protocol == "v2" && s.V2Caps != nil && s.V2Caps.FetchSupports("filter")
}

// FetchToStore fetches objects from source into the given store, using the
41 unmodified lines

}
}

// FetchCommitGraph fetches only the commit graph (tree:0 filter) for a ref.
// Requires v2 with filter support. Optional haves let the source skip commits
// already reachable from those hashes, which is valuable when planning later
// branches that share history with an already-planned trunk.
func (s *RefService) FetchCommitGraph(
	ctx context.Context,
	store storer.Storer,
	conn Conn,
	ref DesiredRef,
	haves []plumbing.Hash,
) error {
	if s.Protocol != "v2" {
		return errors.New("commit graph fetch requires protocol v2")
	}
	if !s.V2Caps.FetchSupports("filter") {
		return errors.New("source does not advertise fetch filter support")
	}

sortedHaves := SortedUniqueHashes(haves)
	cmdArgs := make([]string, 0, 4+len(sortedHaves))
	cmdArgs = append(cmdArgs,
		"ofs-delta",
		"no-progress",
		"filter tree:0",
		"want "+ref.SourceHash.String(),
	)
	for _, h := range sortedHaves {
		cmdArgs = append(cmdArgs, "have "+h.String())
	}
	cmdArgs = append(cmdArgs, "done")

body, err := EncodeCommand("fetch", s.V2Caps.RequestCapabilities(), cmdArgs)
	if err != nil {
		return err
	}
	reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, true, "upload-pack fetch")
	if err != nil {
		return err
	}
	defer ioutil.CheckClose(reader, &err)
	// Commit-graph fetches are short and not user-facing; skip progress.
	return storeV2FetchPack(store, reader, false, nil)
}

// FetchCommitParents fetches a commit graph (tree:0 filter) and
// FetchCommitParents fetches a commit graph (v2 fetch with tree:0 filter) and
// extracts (commit -> parent hashes) directly from the pack stream
// without materializing the full object set into a store. Peak memory
// during this call is dominated by the result map plus a small fixed
// delta-resolution cache; no commit content is retained.
//
// Same wire protocol as FetchCommitGraph (v2 fetch with tree:0 filter).
// Optional haves let the source skip commits already reachable from those
// hashes, which is valuable when planning later branches that share history
// with an already-planned trunk.
// Returns an empty map (not nil) when the source has no new commits.
func (s *RefService) FetchCommitParents(
	ctx context.Context,
```

Minternal/gitproto/fetch.go+5/-63

```
58 unmodified lines

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
62
63
64
51 unmodified lines

116
117
118
138
139
140
141
142
143
144
145
146
147
148
119
120
121
183 unmodified lines

305
306
307
338
308
309
340
310
311
312
313
314
315
316
317
318
346
319
320
321
322
323
324
325
353
326
327
328
329
330
331
332
333
334
335

58 unmodified lines

}
}

func TestFetchFeatures(t *testing.T) {
	v2Caps := &V2Capabilities{
		Caps: map[string]string{
			"fetch": "shallow filter include-tag",
		},
	}
	rs := &RefService{Protocol: "v2", V2Caps: v2Caps}
	features := rs.FetchFeatures()
	if !features.Filter || !features.IncludeTag {
		t.Fatalf("FetchFeatures() = %+v, want filter and include-tag enabled", features)
	}

rs = &RefService{Protocol: "v1"}
	features = rs.FetchFeatures()
	if features.Filter || features.IncludeTag {
		t.Fatalf("FetchFeatures() for v1 = %+v, want zero value", features)
	}
}

func TestSupportsBootstrapBatch(t *testing.T) {
	if (&RefService{Protocol: "v1"}).SupportsBootstrapBatch() {
		t.Fatal("v1 service should not support bootstrap batching")
51 unmodified lines

}
}

func TestProgressWriter(t *testing.T) {
	w := progressWriter(false, nil)
	if w != nil {
		t.Error("progressWriter(false, nil) should return nil")
	}
	w = progressWriter(true, nil)
	if w == nil {
		t.Error("progressWriter(true, nil) should return non-nil writer")
	}
}

func TestWrappedRCClose(t *testing.T) {
	// wrappedRC should close the underlying closer.
	called := false
183 unmodified lines

}
}

func TestFetchCommitGraphRequiresV2(t *testing.T) {
func TestFetchCommitParentsRequiresV2(t *testing.T) {
	rs := &RefService{Protocol: "v1"}
	err := rs.FetchCommitGraph(t.Context(), nil, nil, DesiredRef{}, nil)
	parents, err := rs.FetchCommitParents(t.Context(), nil, DesiredRef{}, nil)
	if err == nil {
		t.Fatal("expected error for non-v2 protocol")
	}
	if parents != nil {
		t.Fatalf("expected nil parents on error, got %v", parents)
	}
}

func TestFetchCommitGraphRequiresFilter(t *testing.T) {
func TestFetchCommitParentsRequiresFilter(t *testing.T) {
	caps := &V2Capabilities{
		Caps: map[string]string{
			"fetch": "shallow",
		},
	}
	rs := &RefService{Protocol: "v2", V2Caps: caps}
	err := rs.FetchCommitGraph(t.Context(), nil, nil, DesiredRef{}, nil)
	parents, err := rs.FetchCommitParents(t.Context(), nil, DesiredRef{}, nil)
	if err == nil {
		t.Fatal("expected error when filter not supported")
	}
	if parents != nil {
		t.Fatalf("expected nil parents on error, got %v", parents)
	}
}

func TestFetchPackV1ContextCanceled(t *testing.T) {
```

Minternal/gitproto/fetch\_test.go+10/-34

```
2 unmodified lines

3
4
5
6
6
7
8
183 unmodified lines

192
193
194
196
197
198
199
200
195
196
197

2 unmodified lines

import (
	"bufio"
	"bytes"
	"encoding/hex"
	"fmt"
	"io"

183 unmodified lines

}
}

// HashHex is a helper to encode a 20-byte hash as lowercase hex.
func HashHex(h [20]byte) string {
	return hex.EncodeToString(h[:])
}

// FormatPktLine encodes a single pkt-line from a string payload.
func FormatPktLine(s string) string {
	n := len(s) + 4
```

Minternal/gitproto/pktline.go-6

```
394 unmodified lines

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

394 unmodified lines

t.Errorf("payload = %q, want %q", payload, "ABCDEF")
	}
}

func TestHashHex(t *testing.T) {
	tests := []struct {
		name string
		hash [20]byte
		want string
	}{
		{
			name: "zero hash",
			hash: [20]byte{},
			want: "0000000000000000000000000000000000000000",
		},
		{
			name: "known hash",
			hash: [20]byte{0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
			want: "deadbeef0102030405060708090a0b0c0d0e0f10",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := HashHex(tt.hash)
			if got != tt.want {
				t.Errorf("HashHex() = %q, want %q", got, tt.want)
			}
		})
	}
}
```

Minternal/gitproto/pktline\_test.go-27

```
662 unmodified lines

663
664
665
666
666
667
668
669
2 unmodified lines

672
673
674
675
675
676
677
678
679
680
681
682
683
684
685
686
687
688
679
680
681
682
683
684
685
690
691
692
693
694
695
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
122 unmodified lines

826
827
828
825
826
827
828
829
830
831
832
833
834
829
830
831

662 unmodified lines

return
			case <-ticker.C:
				fmt.Fprintf(dest, "encoding pack: %s, elapsed %s\r",
					humanizeBytes(cw.Count()), time.Since(start).Round(time.Second))
					HumanBytes(cw.Count()), time.Since(start).Round(time.Second))
			}
		}
	}()
2 unmodified lines

close(stop)
		<-done
		fmt.Fprintf(dest, "encoded pack: %s in %s\n",
			humanizeBytes(cw.Count()), time.Since(start).Round(time.Second))
			HumanBytes(cw.Count()), time.Since(start).Round(time.Second))
	}
}

// humanizeBytes renders n in IEC units with one decimal place for KB+
// (e.g. "47.3 MB"). Anything below 1 KB is shown as raw bytes.
func humanizeBytes(n int64) string {
	const (
		kb = 1024
		mb = kb * 1024
		gb = mb * 1024
	)
	switch {
	case n < kb:
// HumanBytes renders a byte count in IEC-ish binary units (e.g. "47.3 MB"),
// scaling decimal precision down as the value grows. Shared by the progress
// and batching output across syncer, strategy, and gitproto.
func HumanBytes(n int64) string {
	const unit = 1024
	if n < unit {
		return fmt.Sprintf("%d B", n)
	case n < mb:
		return fmt.Sprintf("%.1f KB", float64(n)/float64(kb))
	case n < gb:
		return fmt.Sprintf("%.1f MB", float64(n)/float64(mb))
	default:
		return fmt.Sprintf("%.1f GB", float64(n)/float64(gb))
	}
	div, exp := int64(unit), 0
	for x := n / unit; x >= unit; x /= unit {
		div *= unit
		exp++
	}
	value := float64(n) / float64(div)
	suffix := []string{"KB", "MB", "GB", "TB", "PB"}[exp]
	if value >= 100 {
		return fmt.Sprintf("%.0f %s", value, suffix)
	}
	if value >= 10 {
		return fmt.Sprintf("%.1f %s", value, suffix)
	}
	return fmt.Sprintf("%.2f %s", value, suffix)
}

// PushPack pushes a pack stream (relay) to the target.
122 unmodified lines

return emptyPackSHA1
}

func progressWriter(verbose bool, dest io.Writer) io.Writer {
	if !verbose {
		return nil
	}
	if dest == nil {
		dest = os.Stderr
	}
	return dest
}

// progressSink returns a line-prefixing io.Writer suitable for
// sideband.Demuxer.Progress. When verbose is false it returns nil so the
// demuxer discards progress frames without allocating. Passing a non-nil
```

Minternal/gitproto/push.go+22/-28

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

package gitproto

import (
	"errors"
	"fmt"
	"io"
	"sync"
)

// This file holds shared io.ReadCloser wrappers for pack streams produced
// and consumed by this package.

// CloseOnce wraps a ReadCloser so repeated Close calls only close the
// underlying reader once. Strategies use it for pack readers that are closed
// both by PushPack and by the caller's retry/error cleanup, so double closes
// do not surface spurious failures. Passing an already-wrapped or nil reader
// returns it unchanged.
func CloseOnce(rc io.ReadCloser) io.ReadCloser {
	if rc == nil {
		return nil
	}
	if _, ok := rc.(*closeOnceReadCloser); ok {
		return rc
	}
	return &closeOnceReadCloser{ReadCloser: rc}
}

type closeOnceReadCloser struct {
	io.ReadCloser

once sync.Once
}

func (c *closeOnceReadCloser) Close() error {
	var err error
	c.once.Do(func() {
		err = c.ReadCloser.Close()
	})
	if err != nil {
		return fmt.Errorf("close pack reader: %w", err)
	}
	return nil
}

type packLimitRC struct {
	io.ReadCloser

max  int64
	read int64
}

Ainternal/gitproto/readers.go+70

package gitproto

import (
	"io"
	"strings"
	"testing"

"github.com/go-git/go-git/v6/plumbing"
)

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

got, err := io.ReadAll(limited)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if string(got) != data {
		t.Errorf("got %q, want %q", got, data)
	}
}

for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := SortedUniqueHashes(tt.input)
			if len(got) != len(tt.want) {
				t.Fatalf("len = %d, want %d", len(got), len(tt.want))
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Errorf("index %d: got %s, want %s", i, got[i], tt.want[i])
				}
			}
		})
	}
}

type countingCloser struct {
	io.Reader

closes int
}

func (c *countingCloser) Close() error {
	c.closes++
	return nil
}

func TestCloseOnce(t *testing.T) {
	if CloseOnce(nil) != nil {
		t.Fatal("CloseOnce(nil) should return nil")
	}

cc := &countingCloser{Reader: strings.NewReader("data")}
	wrapped := CloseOnce(cc)
	if again := CloseOnce(wrapped); again != wrapped {
		t.Fatal("CloseOnce should return an already-wrapped reader unchanged")
	}
	if err := wrapped.Close(); err != nil {
		t.Fatalf("Close() error: %v", err)
	}
	if err := wrapped.Close(); err != nil {
		t.Fatalf("second Close() error: %v", err)
	}
	if cc.closes != 1 {
		t.Fatalf("underlying closer closed %d times, want 1", cc.closes)
	}
}

func TestHumanBytes(t *testing.T) {
	t.Parallel()
	cases := []struct {
		in   int64
		want string
	}{
		{0, "0 B"},
		{42, "42 B"},
		{1023, "1023 B"},
		{1024, "1.00 KB"},
		{1500, "1.46 KB"},
		{int64(15 * 1024), "15.0 KB"},
		{int64(1024 * 1024), "1.00 MB"},
		{int64(150 * 1024 * 1024), "150 MB"},
		{int64(3) << 30, "3.00 GB"},
		{int64(2) << 40, "2.00 TB"},
		{int64(5) << 50, "5.00 PB"},
	}
	for _, c := range cases {
		if got := HumanBytes(c.in); got != c.want {
			t.Errorf("HumanBytes(%d) = %q, want %q", c.in, got, c.want)
		}
	}
}
```

Ainternal/gitproto/readers\_test.go+183

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

package internalbridge

import (
	"context"
	"net/http"

"entire.io/entire/git-sync/internal/syncer"
	"entire.io/entire/git-sync/internal/validation"
)

type ProtocolMode string
type OperationMode string

type Config struct {
	raw syncer.Config
}

const ProtocolAuto ProtocolMode = validation.ProtocolAuto
const ProtocolV1 ProtocolMode = validation.ProtocolV1
const ProtocolV2 ProtocolMode = validation.ProtocolV2

const ModeSync OperationMode = "sync"
const ModeReplicate OperationMode = "replicate"

type RefMapping struct {
	Source string
	Target string
}

type Endpoint struct {
	URL                    string
	FollowInfoRefsRedirect bool
}

type EndpointAuth struct {
	Username      string
	Token         string
	BearerToken   string
	SkipTLSVerify bool
}

type RefScope struct {
	Branches           []string
	Mappings           []RefMapping
	AllRefs            bool
	ExcludeRefPrefixes []string
	ExcludeRefs        []string
}

type SyncPolicy struct {
	Mode           OperationMode
	IncludeTags    bool
	ForceWithLease bool
	ForceBlind     bool
	Prune          bool
	BestEffort     bool
	Protocol       ProtocolMode
}

func ProbeConfig(source Endpoint, sourceAuth EndpointAuth, target *Endpoint, targetAuth EndpointAuth, protocol ProtocolMode, includeTags, allRefs, collectStats bool, excludeRefPrefixes []string, httpClient *http.Client) Config {
	cfg := syncer.Config{
		Source:             ToSyncerEndpoint(source, sourceAuth),
		HTTPClient:         httpClient,
		IncludeTags:        includeTags,
		AllRefs:            allRefs,
		ExcludeRefPrefixes: append([]string(nil), excludeRefPrefixes...),
		ShowStats:          collectStats,
		ProtocolMode:       protocolString(protocol),
	}
	if target != nil {
		cfg.Target = ToSyncerEndpoint(*target, targetAuth)
	}
	return Config{raw: cfg}
}

func SyncConfig(source Endpoint, sourceAuth EndpointAuth, target Endpoint, targetAuth EndpointAuth, scope RefScope, policy SyncPolicy, collectStats, dryRun bool, httpClient *http.Client) Config {
	return Config{raw: syncer.Config{
		Source:                 ToSyncerEndpoint(source, sourceAuth),
		Target:                 ToSyncerEndpoint(target, targetAuth),
		HTTPClient:             httpClient,
		Branches:               append([]string(nil), scope.Branches...),
		Mappings:               ToValidationMappings(scope.Mappings),
		AllRefs:                scope.AllRefs,
		ExcludeRefPrefixes:     append([]string(nil), scope.ExcludeRefPrefixes...),
		ExcludeRefs:            append([]string(nil), scope.ExcludeRefs...),
		IncludeTags:            policy.IncludeTags,
		DryRun:                 dryRun,
		ShowStats:              collectStats,
		Mode:                   operationModeString(policy.Mode),
		ForceWithLease:         policy.ForceWithLease,
		ForceBlind:             policy.ForceBlind,
		Prune:                  policy.Prune,
		BestEffort:             policy.BestEffort,
		ProtocolMode:           protocolString(policy.Protocol),
		MaterializedMaxObjects: syncer.DefaultMaterializedMaxObjects,
	}}
}

func Probe(ctx context.Context, cfg Config) (syncer.ProbeResult, error) {
	result, err := syncer.Probe(ctx, cfg.raw)
	if err != nil {
		return syncer.ProbeResult{}, err //nolint:wrapcheck // pass-through layer, caller wraps with context
	}
	return result, nil
}

func Run(ctx context.Context, cfg Config) (syncer.Result, error) {
	result, err := syncer.Run(ctx, cfg.raw)
	if err != nil {
		return syncer.Result{}, err //nolint:wrapcheck // pass-through layer, caller wraps with context
	}
	return result, nil
}

func ToSyncerEndpoint(endpoint Endpoint, auth EndpointAuth) syncer.Endpoint {
	return syncer.Endpoint{
		URL:                    endpoint.URL,
		Username:               auth.Username,
		Token:                  auth.Token,
		BearerToken:            auth.BearerToken,
		SkipTLSVerify:          auth.SkipTLSVerify,
		FollowInfoRefsRedirect: endpoint.FollowInfoRefsRedirect,
	}
}

func protocolString(mode ProtocolMode) string {
	if mode == "" {
		return string(ProtocolAuto)
	}
	return string(mode)
}

func operationModeString(mode OperationMode) string {
	if mode == "" {
		return string(ModeSync)
	}
	return string(mode)
}

func ToValidationMappings(mappings []RefMapping) []validation.RefMapping {
	out := make([]validation.RefMapping, 0, len(mappings))
	for _, mapping := range mappings {
		out = append(out, validation.RefMapping{
			Source: mapping.Source,
			Target: mapping.Target,
		})
	}
	return out
}
```

Dinternal/internalbridge/config.go-149

package internalbridge

import (
	"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/planner"
	"entire.io/entire/git-sync/internal/syncer"
)

type RefKind string

const (
	RefKindBranch RefKind = RefKind(planner.RefKindBranch)
	RefKindTag    RefKind = RefKind(planner.RefKindTag)
	RefKindOther  RefKind = RefKind(planner.RefKindOther)
)

type Action string

const (
	ActionCreate Action = Action(planner.ActionCreate)
	ActionUpdate Action = Action(planner.ActionUpdate)
	ActionDelete Action = Action(planner.ActionDelete)
	ActionSkip   Action = Action(planner.ActionSkip)
	ActionBlock  Action = Action(planner.ActionBlock)
	ActionWarn   Action = Action(planner.ActionWarn)
)

type RefResult struct {
	Branch     string  `json:"branch"`
	SourceRef  string  `json:"sourceRef"`
	TargetRef  string  `json:"targetRef"`
	SourceHash string  `json:"sourceHash"`
	TargetHash string  `json:"targetHash"`
	Kind       RefKind `json:"kind"`
	Action     Action  `json:"action"`
	Reason     string  `json:"reason"`
}

type RefPlan = RefResult

type RefInfo struct {
	Name string `json:"name"`
	Hash string `json:"hash"`
}

type ServiceStats struct {
	Name          string `json:"name"`
	Requests      int    `json:"requests"`
	RequestBytes  int64  `json:"requestBytes"`
	ResponseBytes int64  `json:"responseBytes"`
	Wants         int    `json:"wants"`
	Haves         int    `json:"haves"`
	Commands      int    `json:"commands"`
}

type SideBytes struct {
	Label       string `json:"label"`
	Bytes       int64  `json:"bytes"`
	Display     string `json:"display,omitempty"`
	ActiveNanos int64  `json:"activeNanos,omitempty"`
	IdleNanos   int64  `json:"idleNanos,omitempty"`
}

type Stats struct {
	Enabled      bool                     `json:"enabled"`
	Items        map[string]*ServiceStats `json:"items"`
	Sides        []SideBytes              `json:"sides,omitempty"`
	ElapsedNanos int64                    `json:"elapsedNanos,omitempty"`
}

type Measurement struct {
	Enabled            bool   `json:"enabled"`
	ElapsedMillis      int64  `json:"elapsedMillis"`
	PeakAllocBytes     uint64 `json:"peakAllocBytes"`
	PeakHeapInuseBytes uint64 `json:"peakHeapInuseBytes"`
	TotalAllocBytes    uint64 `json:"totalAllocBytes"`
	GCCount            uint32 `json:"gcCount"`
}

type ProbeResult struct {
	SourceURL     string      `json:"sourceUrl"`
	TargetURL     string      `json:"targetUrl,omitempty"`
	RequestedMode string      `json:"requestedMode"`
	Protocol      string      `json:"protocol"`
	RefPrefixes   []string    `json:"refPrefixes"`
	Capabilities  []string    `json:"sourceCapabilities"`
	TargetCaps    []string    `json:"targetCapabilities,omitempty"`
	Refs          []RefInfo   `json:"refs"`
	SourceHEAD    string      `json:"sourceHead,omitempty"`
	Stats         Stats       `json:"stats"`
	Measurement   Measurement `json:"measurement"`
}

type SyncCounts struct {
	Applied int `json:"applied"`
	Skipped int `json:"skipped"`
	Blocked int `json:"blocked"`
	Deleted int `json:"deleted"`
	Warned  int `json:"warned"`
}

type BatchSummary struct {
	Enabled bool `json:"enabled"`
	Planned int  `json:"planned"`
	Done    int  `json:"done"`
}

type ExecutionSummary struct {
	DryRun             bool         `json:"dryRun"`
	Protocol           string       `json:"protocol"`
	OperationMode      string       `json:"operationMode"`
	Relay              bool         `json:"relay"`
	TransferMode       string       `json:"transferMode"`
	Reason             string       `json:"reason"`
	BootstrapSuggested bool         `json:"bootstrapSuggested"`
	SourceHEAD         string       `json:"sourceHead,omitempty"`
	Batch              BatchSummary `json:"batch"`
}

type SyncResult struct {
	Refs        []RefResult      `json:"refs"`
	Counts      SyncCounts       `json:"counts"`
	Execution   ExecutionSummary `json:"execution"`
	Stats       Stats            `json:"stats"`
	Measurement Measurement      `json:"measurement"`
}

type PlanResult = SyncResult

func FromProbeResult(result syncer.ProbeResult) ProbeResult {
	out := ProbeResult{
		SourceURL:     result.SourceURL,
		TargetURL:     result.TargetURL,
		RequestedMode: result.RequestedMode,
		Protocol:      result.Protocol,
		RefPrefixes:   append([]string(nil), result.RefPrefixes...),
		Capabilities:  append([]string(nil), result.Capabilities...),
		TargetCaps:    append([]string(nil), result.TargetCaps...),
		Refs:          make([]RefInfo, 0, len(result.Refs)),
		SourceHEAD:    result.SourceHEAD.String(),
		Stats:         FromStats(result.Stats),
		Measurement:   FromMeasurement(result.Measurement),
	}
	for _, ref := range result.Refs {
		out.Refs = append(out.Refs, RefInfo{Name: ref.Name, Hash: ref.Hash.String()})
	}
	return out
}

func FromSyncResult(result syncer.Result) SyncResult {
	out := SyncResult{
		Refs: make([]RefResult, 0, len(result.Plans)),
		Counts: SyncCounts{
			Applied: result.Pushed,
			Skipped: result.Skipped,
			Blocked: result.Blocked,
			Deleted: result.Deleted,
			Warned:  result.Warned,
		},
		Execution: ExecutionSummary{
			DryRun:             result.DryRun,
			Protocol:           result.Protocol,
			OperationMode:      result.OperationMode,
			Relay:              result.Relay,
			TransferMode:       result.RelayMode,
			Reason:             result.RelayReason,
			BootstrapSuggested: result.BootstrapSuggested,
			SourceHEAD:         result.SourceHEAD.String(),
			Batch: BatchSummary{
				Enabled: result.Batching,
				Planned: result.PlannedBatchCount,
				Done:    result.BatchCount,
			},
		},
		Stats:       FromStats(result.Stats),
		Measurement: FromMeasurement(result.Measurement),
	}
	for _, plan := range result.Plans {
		out.Refs = append(out.Refs, RefResult{
			Branch:     plan.Branch,
			SourceRef:  plan.SourceRef.String(),
			TargetRef:  plan.TargetRef.String(),
			SourceHash: HashString(plan.SourceHash),
			TargetHash: HashString(plan.TargetHash),
			Kind:       RefKind(plan.Kind),
			Action:     Action(plan.Action),
			Reason:     plan.Reason,
		})
	}
	return out
}

func FromStats(stats syncer.Stats) Stats {
	out := Stats{Enabled: stats.Enabled, Items: make(map[string]*ServiceStats, len(stats.Items))}
	for key, item := range stats.Items {
		copyItem := *item
		out.Items[key] = &ServiceStats{
			Name:          copyItem.Name,
			Requests:      copyItem.Requests,
			RequestBytes:  copyItem.RequestBytes,
			ResponseBytes: copyItem.ResponseBytes,
			Wants:         copyItem.Wants,
			Haves:         copyItem.Haves,
			Commands:      copyItem.Commands,
		}
	}
	if len(stats.Sides) > 0 {
		out.Sides = make([]SideBytes, 0, len(stats.Sides))
		for _, side := range stats.Sides {
			out.Sides = append(out.Sides, SideBytes{
				Label:       side.Label,
				Bytes:       side.Bytes,
				Display:     side.Display,
				ActiveNanos: side.ActiveNanos,
				IdleNanos:   side.IdleNanos,
			})
		}
	}
	out.ElapsedNanos = stats.ElapsedNanos
	return out
}

func FromMeasurement(m syncer.Measurement) Measurement {
	return Measurement{
		Enabled:            m.Enabled,
		ElapsedMillis:      m.ElapsedMillis,
		PeakAllocBytes:     m.PeakAllocBytes,
		PeakHeapInuseBytes: m.PeakHeapInuseBytes,
		TotalAllocBytes:    m.TotalAllocBytes,
		GCCount:            m.GCCount,
	}
}

func HashString(hash plumbing.Hash) string {
	if hash.IsZero() {
		return ""
	}
	return hash.String()
}
```

Dinternal/internalbridge/model.go-240

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

package internalbridge

import (
	"testing"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/planner"
	"entire.io/entire/git-sync/internal/syncer"
)

func TestHashStringZeroHashIsEmpty(t *testing.T) {
	got := HashString(plumbing.ZeroHash)
	if got != "" {
		t.Fatalf("HashString(zero) = %q, want empty string", got)
	}
}

func TestFromProbeResultCopiesStableFields(t *testing.T) {
	got := FromProbeResult(syncer.ProbeResult{
		SourceURL:     "https://source.example/repo.git",
		TargetURL:     "https://target.example/repo.git",
		RequestedMode: "auto",
		Protocol:      "v2",
		RefPrefixes:   []string{"refs/heads/"},
		Capabilities:  []string{"ls-refs", "fetch"},
		TargetCaps:    []string{"report-status"},
		Refs: []syncer.RefInfo{
			{Name: "refs/heads/main", Hash: plumbing.NewHash("1111111111111111111111111111111111111111")},
		},
		Stats: syncer.Stats{
			Enabled: true,
			Items: map[string]*syncer.ServiceStats{
				"source": {Name: "source", Requests: 2, Wants: 3},
			},
		},
		Measurement: syncer.Measurement{Enabled: true, ElapsedMillis: 42},
	})

if got.SourceURL != "https://source.example/repo.git" || got.TargetURL != "https://target.example/repo.git" {
		t.Fatalf("unexpected URLs: %+v", got)
	}
	if len(got.Refs) != 1 || got.Refs[0].Hash != "1111111111111111111111111111111111111111" {
		t.Fatalf("unexpected refs: %+v", got.Refs)
	}
	if !got.Stats.Enabled || got.Stats.Items["source"].Requests != 2 || got.Measurement.ElapsedMillis != 42 {
		t.Fatalf("unexpected stats/measurement: %+v %+v", got.Stats, got.Measurement)
	}
}

func TestFromSyncResultShapesStableSummary(t *testing.T) {
	got := FromSyncResult(syncer.Result{
		Plans: []planner.BranchPlan{
			{
				Branch:     "main",
				SourceRef:  plumbing.ReferenceName("refs/heads/main"),
				TargetRef:  plumbing.ReferenceName("refs/heads/main"),
				SourceHash: plumbing.NewHash("1111111111111111111111111111111111111111"),
				TargetHash: plumbing.NewHash("2222222222222222222222222222222222222222"),
				Kind:       planner.RefKindBranch,
				Action:     planner.ActionUpdate,
				Reason:     "fast-forward",
			},
		},
		Pushed:             1,
		Skipped:            2,
		Blocked:            3,
		Deleted:            4,
		DryRun:             true,
		OperationMode:      "replicate",
		Relay:              true,
		RelayMode:          "incremental-relay",
		RelayReason:        "fast-forward",
		Batching:           true,
		BatchCount:         5,
		PlannedBatchCount:  6,
		TempRefs:           []string{"refs/gitsync/bootstrap/heads/main/1"},
		BootstrapSuggested: true,
		Protocol:           "v2",
	})

if len(got.Refs) != 1 || got.Refs[0].Branch != "main" {
		t.Fatalf("unexpected refs: %+v", got.Refs)
	}
	if got.Counts.Applied != 1 || got.Counts.Skipped != 2 || got.Counts.Blocked != 3 || got.Counts.Deleted != 4 {
		t.Fatalf("unexpected counts: %+v", got.Counts)
	}
	if !got.Execution.DryRun || !got.Execution.Relay || got.Execution.OperationMode != "replicate" || got.Execution.TransferMode != "incremental-relay" || got.Execution.Reason != "fast-forward" {
		t.Fatalf("unexpected execution summary: %+v", got.Execution)
	}
	if !got.Execution.Batch.Enabled || got.Execution.Batch.Done != 5 || got.Execution.Batch.Planned != 6 {
		t.Fatalf("unexpected batch summary: %+v", got.Execution.Batch)
	}
	if !got.Execution.BootstrapSuggested {
		t.Fatalf("expected bootstrap suggestion in execution summary")
	}
}
```

Dinternal/internalbridge/model\_test.go-97

```
304 unmodified lines

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
308
309
310

304 unmodified lines

return chain, nil
}

// FirstParentChainFromMap walks a first-parent map from tip back to root.
// The map key is a commit hash, the value is its first parent hash.
// A zero-value parent marks the root. Returns the chain in root-to-tip order.
func FirstParentChainFromMap(parents map[plumbing.Hash]plumbing.Hash, tip plumbing.Hash) ([]plumbing.Hash, error) {
	chain := make([]plumbing.Hash, 0, len(parents))
	current := tip
	seen := make(map[plumbing.Hash]struct{}, len(parents))
	for {
		if _, ok := seen[current]; ok {
			return nil, fmt.Errorf("cycle detected at %s", current)
		}
		seen[current] = struct{}{}
		chain = append(chain, current)
		parent, ok := parents[current]
		if !ok {
			return nil, fmt.Errorf("commit %s not found in parent map", current)
		}
		if parent.IsZero() {
			break
		}
		current = parent
	}
	for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 {
		chain[i], chain[j] = chain[j], chain[i]
	}
	return chain, nil
}

// SampledCheckpointCandidates generates a set of candidate indices to probe,
// sorted from largest (preferred) to smallest.
func SampledCheckpointCandidates(lo, hi int, prevSpan int) []int {
```

Minternal/planner/checkpoint.go-28

```
221 unmodified lines

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
225
226
227

221 unmodified lines

return out
}

// DesiredSubset returns the subset of desired refs that match the given plans.
func DesiredSubset(
	desired map[plumbing.ReferenceName]DesiredRef,
	plans []BranchPlan,
) map[plumbing.ReferenceName]DesiredRef {
	out := make(map[plumbing.ReferenceName]DesiredRef, len(plans))
	for _, plan := range plans {
		if ref, ok := desired[plan.TargetRef]; ok {
			out[plan.TargetRef] = ref
		}
	}
	return out
}

// SingleDesired builds a single-entry desired ref map.
func SingleDesired(sourceRef, targetRef plumbing.ReferenceName, hash plumbing.Hash) map[plumbing.ReferenceName]DesiredRef {
	return map[plumbing.ReferenceName]DesiredRef{
		targetRef: {
			Kind:       RefKindBranch,
			Label:      targetRef.Short(),
			SourceRef:  sourceRef,
			TargetRef:  targetRef,
			SourceHash: hash,
		},
	}
}

// SingleHaveMap builds a single-entry have map for fetch negotiation.
func SingleHaveMap(hash plumbing.Hash) map[plumbing.ReferenceName]plumbing.Hash {
	if hash.IsZero() {
		return nil
	}
	return map[plumbing.ReferenceName]plumbing.Hash{
		plumbing.ReferenceName("refs/gitsync/have"): hash,
	}
}

// BootstrapTempRef returns the temporary ref name used during batched bootstrap.
func BootstrapTempRef(targetRef plumbing.ReferenceName) plumbing.ReferenceName {
	return plumbing.ReferenceName("refs/gitsync/bootstrap/heads/" + targetRef.Short())
```

Minternal/planner/types.go-37

```
13 unmodified lines

14
15
16
17
17
18
19
146 unmodified lines

166
167
168
170
169
170
171
172
14 unmodified lines

187
188
189
191
190
191
192
193
243 unmodified lines

437
438
439
441
440
441
442
443
17 unmodified lines

461
462
463
465
466
464
465
466
467
468
126 unmodified lines

595
596
597
599
598
599
600
601
80 unmodified lines

682
683
684
686
685
686
687
688
719 unmodified lines

1408
1409
1410
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1411
1412
1413
82 unmodified lines

1496
1497
1498
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550

13 unmodified lines

"regexp"
	"strconv"
	"strings"
	"sync"

git "github.com/go-git/go-git/v6"
	"github.com/go-git/go-git/v6/plumbing"
146 unmodified lines

return result, fmt.Errorf("fetch source pack: %w", err)
	}
	packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)
	packReader = closeOnce(packReader)
	packReader = gitproto.CloseOnce(packReader)

p.log("bootstrap pushing refs to target", "ref_count", len(plans))
	if p.OnPhase != nil {
14 unmodified lines

p.log("bootstrap retrying with batched mode after batchable push failure",
			"target_max_pack_bytes", autoBatch, "reason", reason)
		p.notice(fmt.Sprintf("%s — switching to batched mode (limit %s)",
			reason, humanBytes(autoBatch)))
			reason, gitproto.HumanBytes(autoBatch)))
		p.TargetMaxPack = autoBatch
		return executeBatched(ctx, p, plans, result)
	}
243 unmodified lines

if err != nil {
				return result, fmt.Errorf("fetch source batch pack for %s: %w", batch.Plan.TargetRef, err)
			}
			packReader = closeOnce(packReader)
			packReader = gitproto.CloseOnce(packReader)

// Peek at the PACK header (12 bytes) to get the object count.
			// If the estimated pack size (objectCount × calibrated
17 unmodified lines

"calibrated_bytes_per_object", calibratedBytesPerObject)
						p.notice(fmt.Sprintf(
							"estimated pack ~%s exceeds target limit %s — splitting %d → %d packs (~%s each)",
							humanBytes(estimated), humanBytes(p.TargetMaxPack),
							oldRemaining, newCount, humanBytes(perPack),
							gitproto.HumanBytes(estimated), gitproto.HumanBytes(p.TargetMaxPack),
							oldRemaining, newCount, gitproto.HumanBytes(perPack),
						))
						batch.Checkpoints = append(batch.Checkpoints[:idx], expanded...)
						subdivided = true
126 unmodified lines

"error", pushErr.Error())
						limitText := ""
						if limit > 0 {
							limitText = fmt.Sprintf(" (target limit %s)", humanBytes(limit))
							limitText = fmt.Sprintf(" (target limit %s)", gitproto.HumanBytes(limit))
						}
						reason := "target rejected pack"
						if abortedEarly {
80 unmodified lines

}
		} else {
			packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)
			packReader = closeOnce(packReader)
			packReader = gitproto.CloseOnce(packReader)
			cmds := convert.PlansToPushCommands(tailPlans, false)
			if err := p.TargetPusher.PushPack(ctx, cmds, packReader); err != nil {
				_ = packReader.Close()
719 unmodified lines

return limit, true
}

// humanBytes renders a byte count in IEC-ish binary units. Local copy
// because importing the syncer's formatter would invert the dependency
// direction; the bootstrap package is meant to be standalone.
func humanBytes(n int64) string {
	const unit = 1024
	if n < unit {
		return fmt.Sprintf("%d B", n)
	}
	div, exp := int64(unit), 0
	for x := n / unit; x >= unit; x /= unit {
		div *= unit
		exp++
	}
	value := float64(n) / float64(div)
	suffix := []string{"KB", "MB", "GB", "TB", "PB"}[exp]
	if value >= 100 {
		return fmt.Sprintf("%.0f %s", value, suffix)
	}
	if value >= 10 {
		return fmt.Sprintf("%.1f %s", value, suffix)
	}
	return fmt.Sprintf("%.2f %s", value, suffix)
}

func isTargetBodyLimitError(err error) bool {
	if err == nil {
		return false
82 unmodified lines

}
	p.Logger.Info(msg, args...)
}

type closeOnceReadCloser struct {
	io.ReadCloser

once sync.Once
}

func closeOnce(rc io.ReadCloser) io.ReadCloser {
	if rc == nil {
		return nil
	}
	if _, ok := rc.(*closeOnceReadCloser); ok {
		return rc
	}
	return &closeOnceReadCloser{ReadCloser: rc}
}
```

Minternal/strategy/bootstrap/bootstrap.go+7/-59

```
7 unmodified lines

8
9
10
11
11
12
13
47 unmodified lines

61
62
63
65
64
65
66
67
12 unmodified lines

80
81
82
84
83
84
85
86
4 unmodified lines

91
92
93
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

7 unmodified lines

"errors"
	"fmt"
	"io"
	"sync"

"github.com/go-git/go-git/v6/plumbing"

47 unmodified lines

return Result{}, fmt.Errorf("fetch source pack: %w", err)
		}
		packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)
		packReader = closeOnce(packReader)
		packReader = gitproto.CloseOnce(packReader)
		if err := p.TargetPusher.PushPack(ctx, cmds, packReader); err != nil {
			_ = packReader.Close()
			return Result{}, fmt.Errorf("push target refs: %w", err)
12 unmodified lines

return Result{}, fmt.Errorf("fetch source tag pack: %w", err)
		}
		packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)
		packReader = closeOnce(packReader)
		packReader = gitproto.CloseOnce(packReader)
		if err := p.TargetPusher.PushPack(ctx, cmds, packReader); err != nil {
			_ = packReader.Close()
			return Result{}, fmt.Errorf("push target refs: %w", err)
4 unmodified lines

return Result{}, nil
}

type closeOnceReadCloser struct {
	io.ReadCloser

once sync.Once
}

Minternal/strategy/incremental/incremental.go+2/-30

```
13 unmodified lines

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
17
18
19

13 unmodified lines

"entire.io/entire/git-sync/internal/planner"
)

func TestPlansToPushPlans(t *testing.T) {
	hash1 := plumbing.NewHash("1111111111111111111111111111111111111111")
	hash2 := plumbing.NewHash("2222222222222222222222222222222222222222")
	hash3 := plumbing.NewHash("3333333333333333333333333333333333333333")

tests := []struct {
		name   string
		plans  []planner.BranchPlan
		expect []gitproto.PushPlan
	}{
		{
			name: "create plan",
			plans: []planner.BranchPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("main"),
					TargetHash: plumbing.ZeroHash,
					SourceHash: hash1,
					Action:     planner.ActionCreate,
				},
			},
			expect: []gitproto.PushPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("main"),
					TargetHash: plumbing.ZeroHash,
					SourceHash: hash1,
					Delete:     false,
				},
			},
		},
		{
			name: "update plan",
			plans: []planner.BranchPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("main"),
					TargetHash: hash1,
					SourceHash: hash2,
					Action:     planner.ActionUpdate,
				},
			},
			expect: []gitproto.PushPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("main"),
					TargetHash: hash1,
					SourceHash: hash2,
					Delete:     false,
				},
			},
		},
		{
			name: "delete plan",
			plans: []planner.BranchPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("old"),
					TargetHash: hash3,
					SourceHash: plumbing.ZeroHash,
					Action:     planner.ActionDelete,
				},
			},
			expect: []gitproto.PushPlan{
				{
					TargetRef:  plumbing.NewBranchReferenceName("old"),
					TargetHash: hash3,
					SourceHash: plumbing.ZeroHash,
					Delete:     true,
				},
			},
		},
		{
			name:   "empty input",
			plans:  []planner.BranchPlan{},
			expect: []gitproto.PushPlan{},
		},
	}

for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := convert.PlansToPushPlans(tt.plans)
			if len(got) != len(tt.expect) {
				t.Fatalf("expected %d push plans, got %d", len(tt.expect), len(got))
			}
			for i := range tt.expect {
				if got[i].TargetRef != tt.expect[i].TargetRef {
					t.Errorf("[%d] TargetRef = %s, want %s", i, got[i].TargetRef, tt.expect[i].TargetRef)
				}
				if got[i].TargetHash != tt.expect[i].TargetHash {
					t.Errorf("[%d] TargetHash = %s, want %s", i, got[i].TargetHash, tt.expect[i].TargetHash)
				}
				if got[i].SourceHash != tt.expect[i].SourceHash {
					t.Errorf("[%d] SourceHash = %s, want %s", i, got[i].SourceHash, tt.expect[i].SourceHash)
				}
				if got[i].Delete != tt.expect[i].Delete {
					t.Errorf("[%d] Delete = %v, want %v", i, got[i].Delete, tt.expect[i].Delete)
				}
			}
		})
	}
}

type fakeSourceService struct {
	fetchPack func(context.Context, gitproto.Conn, map[plumbing.ReferenceName]gitproto.DesiredRef, map[plumbing.ReferenceName]plumbing.Hash) (io.ReadCloser, error)
}
```

Minternal/strategy/incremental/incremental\_test.go-98

```
7 unmodified lines

8
9
10
11
11
12
13
14
15
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
16
17
18

7 unmodified lines

"github.com/go-git/go-git/v6/plumbing/object"
	"github.com/go-git/go-git/v6/plumbing/storer"

"entire.io/entire/git-sync/internal/convert"
	"entire.io/entire/git-sync/internal/gitproto"
	"entire.io/entire/git-sync/internal/planner"
	"entire.io/entire/git-sync/internal/syncertest"
)

func TestDefaultMaxMaterializedObjectsExported(t *testing.T) {
	// Verify the constant is exported and has a reasonable positive value.
	if DefaultMaxMaterializedObjects <= 0 {
```

Minternal/strategy/materialized/materialized\_test.go-99

```
5 unmodified lines

6
7
8
9
9
10
11
53 unmodified lines

65
66
67
69
68
69
70
71
9 unmodified lines

81
82
83
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

5 unmodified lines

"errors"
	"fmt"
	"io"
	"sync"

"github.com/go-git/go-git/v6/plumbing"

53 unmodified lines

return Result{}, fmt.Errorf("fetch source pack: %w", err)
		}
		packReader = gitproto.LimitPackReader(packReader, p.MaxPackBytes)
		packReader = closeOnce(packReader)
		packReader = gitproto.CloseOnce(packReader)
		if err := p.TargetPusher.PushPack(ctx, convert.PlansToPushCommands(updatePlans, false), packReader); err != nil {
			_ = packReader.Close()
			return Result{}, fmt.Errorf("push target refs: %w", err)
9 unmodified lines

return Result{Relay: true, RelayMode: "replicate", RelayReason: "replicate-overwrite-relay"}, nil
}

type closeOnceReadCloser struct {
	io.ReadCloser

once sync.Once
}

func closeOnce(rc io.ReadCloser) io.ReadCloser {
	if rc == nil {
		return nil
	}
	if _, ok := rc.(*closeOnceReadCloser); ok {
		return rc
	}
	// PushPack and the caller both close the relay reader; wrap it so retries and
	// error cleanup do not surface spurious double-close failures.
	return &closeOnceReadCloser{ReadCloser: rc}
}
```

Minternal/strategy/replicate/replicate.go+1/-31

```
7 unmodified lines

8
9
10
11
12
13
14
15
277 unmodified lines

293
294
295
294
296
297
298
299
2 unmodified lines

302
303
304
303
305
306
307
308
102 unmodified lines

411
412
413
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
414
415
416
2 unmodified lines

419
420
421
442
422
423

7 unmodified lines

"strings"
	"sync"
	"time"

"entire.io/entire/git-sync/internal/gitproto"
)

// progressReporter renders live per-side throughput to a writer (typically
277 unmodified lines

var rateText string
	if !done && instantBytesPerSec > 0 {
		rateText = formatBytes(int64(instantBytesPerSec)) + "/s"
		rateText = gitproto.HumanBytes(int64(instantBytesPerSec)) + "/s"
	} else {
		rateDur := fallbackDur
		if side.ActiveNanos > 0 {
2 unmodified lines

rateText = formatRate(side.Bytes, rateDur)
	}

rate := formatBytes(side.Bytes) + " @ " + rateText
	rate := gitproto.HumanBytes(side.Bytes) + " @ " + rateText
	if done {
		rate += doneMark
	}
102 unmodified lines

return (fi.Mode() & os.ModeCharDevice) != 0
}

// formatBytes renders byte counts in IEC-ish human units (binary base).
func formatBytes(n int64) string {
	const unit = 1024
	if n < unit {
		return fmt.Sprintf("%d B", n)
	}
	div, exp := int64(unit), 0
	for x := n / unit; x >= unit; x /= unit {
		div *= unit
		exp++
	}
	value := float64(n) / float64(div)
	suffix := []string{"KB", "MB", "GB", "TB", "PB"}[exp]
	if value >= 100 {
		return fmt.Sprintf("%.0f %s", value, suffix)
	}
	if value >= 10 {
		return fmt.Sprintf("%.1f %s", value, suffix)
	}
	return fmt.Sprintf("%.2f %s", value, suffix)
}

// formatRate renders a bytes/second average over the supplied duration.
// Returns "0 B/s" until the duration is large enough to be meaningful,
// avoiding misleadingly large rates from sub-millisecond samples.
2 unmodified lines

return "0 B/s"
	}
	rate := float64(bytes) / dur.Seconds()
	return formatBytes(int64(rate)) + "/s"
	return gitproto.HumanBytes(int64(rate)) + "/s"
}
```

Minternal/syncer/progress.go+5/-25

```
6 unmodified lines

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
10
11
12

6 unmodified lines

"time"
)

func TestFormatBytes(t *testing.T) {
	t.Parallel()
	cases := []struct {
		in   int64
		want string
	}{
		{0, "0 B"},
		{42, "42 B"},
		{1023, "1023 B"},
		{1024, "1.00 KB"},
		{1500, "1.46 KB"},
		{int64(15 * 1024), "15.0 KB"},
		{int64(1024 * 1024), "1.00 MB"},
		{int64(150 * 1024 * 1024), "150 MB"},
	}
	for _, c := range cases {
		if got := formatBytes(c.in); got != c.want {
			t.Errorf("formatBytes(%d) = %q, want %q", c.in, got, c.want)
		}
	}
}

func TestFormatRate(t *testing.T) {
	t.Parallel()
	const zeroRate = "0 B/s"
```

Minternal/syncer/progress\_test.go-22

```
436 unmodified lines

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
433 unmodified lines

900
901
902
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
903
904
905
906
84 unmodified lines

991
992
993
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
994
995
996
997
998
999
1000
1001
281 unmodified lines

1283
1284
1285
1292
1293
1286
1287
1288

436 unmodified lines

result.Deleted += deleted
}

// classifyPlans splits planned actions into pushable plans, tallying skips and
// blocks into result. Under dry-run, pushable plans are counted as skipped and
// none are returned. ActionWarn is not produced by planning; it is only set
// after a push by applyRejections.
func classifyPlans(plans []BranchPlan, dryRun bool, result *Result) []BranchPlan {
	pushPlans := make([]BranchPlan, 0, len(plans))
	for _, plan := range plans {
		switch plan.Action {
		case ActionCreate, ActionUpdate, ActionDelete:
			if dryRun {
				result.Skipped++
				continue
			}
			pushPlans = append(pushPlans, plan)
		case ActionSkip:
			result.Skipped++
		case ActionBlock:
			result.Blocked++
		case ActionWarn:
		}
	}
	return pushPlans
}

// tallyActions counts ref pushes and deletes from a classified plan slice.
// ActionWarn/Skip/Block don't contribute; rejections are tracked separately
// in Result.Warned via applyRejections.
433 unmodified lines

SourceHEAD: s.sourceService.HeadTarget,
	}

pushPlans := make([]BranchPlan, 0, len(plans))
	for _, plan := range plans {
		switch plan.Action {
		case ActionCreate, ActionUpdate, ActionDelete:
			if s.cfg.DryRun {
				result.Skipped++
				continue
			}
			pushPlans = append(pushPlans, plan)
		case ActionSkip:
			result.Skipped++
		case ActionBlock:
			result.Blocked++
		case ActionWarn:
			// not produced by planning; only set after a push by applyRejections.
		}
	}
	pushPlans := classifyPlans(plans, s.cfg.DryRun, &result)

if !s.cfg.DryRun && result.Blocked > 0 {
		return result, fmt.Errorf("blocked %d ref update(s); rerun with --force-with-lease (or --force-blind) where appropriate", result.Blocked)
84 unmodified lines

SourceHEAD:    s.sourceService.HeadTarget,
	}

pushPlans := make([]BranchPlan, 0, len(plans))
	relayPlans := make([]BranchPlan, 0, len(plans))
	for _, plan := range plans {
		switch plan.Action {
		case ActionCreate, ActionUpdate, ActionDelete:
			if s.cfg.DryRun {
				result.Skipped++
				continue
			}
			pushPlans = append(pushPlans, plan)
			if plan.Action != ActionDelete {
				relayPlans = append(relayPlans, plan)
			}
		case ActionSkip:
			result.Skipped++
		case ActionBlock:
			result.Blocked++
		case ActionWarn:
			// not produced by planning; only set after a push by applyRejections.
	pushPlans := classifyPlans(plans, s.cfg.DryRun, &result)
	relayPlans := make([]BranchPlan, 0, len(pushPlans))
	for _, plan := range pushPlans {
		if plan.Action != ActionDelete {
			relayPlans = append(relayPlans, plan)
		}
	}

281 unmodified lines

if s.target != nil {
		result.TargetURL = s.cfg.Target.URL
		result.TargetCaps = gitproto.AdvRefsCaps(s.target.adv)
		result.Stats = s.stats.snapshot()
		result.Measurement = s.measurementDone()
	}
	return result
}
```

Minternal/syncer/syncer.go+30/-38

package gitsync

import (
	"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/planner"
	"entire.io/entire/git-sync/internal/syncer"
)

// RefKind classifies a ref in results.
type RefKind string

const (
	RefKindBranch = RefKind(planner.RefKindBranch)
	RefKindTag    = RefKind(planner.RefKindTag)
	RefKindOther  = RefKind(planner.RefKindOther)
)

// Action is the planned or executed per-ref action.
type Action string

const (
	ActionCreate = Action(planner.ActionCreate)
	ActionUpdate = Action(planner.ActionUpdate)
	ActionDelete = Action(planner.ActionDelete)
	ActionSkip   = Action(planner.ActionSkip)
	ActionBlock  = Action(planner.ActionBlock)
	ActionWarn   = Action(planner.ActionWarn)
)

// RefResult describes the outcome for a single ref.
type RefResult struct {
	Branch     string  `json:"branch"`
	SourceRef  string  `json:"sourceRef"`
	TargetRef  string  `json:"targetRef"`
	SourceHash string  `json:"sourceHash"`
	TargetHash string  `json:"targetHash"`
	Kind       RefKind `json:"kind"`
	Action     Action  `json:"action"`
	Reason     string  `json:"reason"`
}

// RefPlan is a planned ref action; it has the same shape as RefResult.
type RefPlan = RefResult

// RefInfo is a ref name and its hash as reported by a probe.
type RefInfo struct {
	Name string `json:"name"`
	Hash string `json:"hash"`
}

// ServiceStats aggregates request counters for one git service.
type ServiceStats struct {
	Name          string `json:"name"`
	Requests      int    `json:"requests"`
	RequestBytes  int64  `json:"requestBytes"`
	ResponseBytes int64  `json:"responseBytes"`
	Wants         int    `json:"wants"`
	Haves         int    `json:"haves"`
	Commands      int    `json:"commands"`
}

// SideBytes reports transfer volume and timing for one side of a sync.
type SideBytes struct {
	Label       string `json:"label"`
	Bytes       int64  `json:"bytes"`
	Display     string `json:"display,omitempty"`
	ActiveNanos int64  `json:"activeNanos,omitempty"`
	IdleNanos   int64  `json:"idleNanos,omitempty"`
}

// Stats aggregates transfer statistics when CollectStats was requested.
type Stats struct {
	Enabled      bool                     `json:"enabled"`
	Items        map[string]*ServiceStats `json:"items"`
	Sides        []SideBytes              `json:"sides,omitempty"`
	ElapsedNanos int64                    `json:"elapsedNanos,omitempty"`
}

// Measurement reports process resource usage when measurement was enabled.
type Measurement struct {
	Enabled            bool   `json:"enabled"`
	ElapsedMillis      int64  `json:"elapsedMillis"`
	PeakAllocBytes     uint64 `json:"peakAllocBytes"`
	PeakHeapInuseBytes uint64 `json:"peakHeapInuseBytes"`
	TotalAllocBytes    uint64 `json:"totalAllocBytes"`
	GCCount            uint32 `json:"gcCount"`
}

// ProbeResult is the outcome of a Probe.
type ProbeResult struct {
	SourceURL     string      `json:"sourceUrl"`
	TargetURL     string      `json:"targetUrl,omitempty"`
	RequestedMode string      `json:"requestedMode"`
	Protocol      string      `json:"protocol"`
	RefPrefixes   []string    `json:"refPrefixes"`
	Capabilities  []string    `json:"sourceCapabilities"`
	TargetCaps    []string    `json:"targetCapabilities,omitempty"`
	Refs          []RefInfo   `json:"refs"`
	SourceHEAD    string      `json:"sourceHead,omitempty"`
	Stats         Stats       `json:"stats"`
	Measurement   Measurement `json:"measurement"`
}

// SyncCounts tallies per-ref outcomes of a sync.
type SyncCounts struct {
	Applied int `json:"applied"`
	Skipped int `json:"skipped"`
	Blocked int `json:"blocked"`
	Deleted int `json:"deleted"`
	Warned  int `json:"warned"`
}

// BatchSummary reports batched push progress.
type BatchSummary struct {
	Enabled bool `json:"enabled"`
	Planned int  `json:"planned"`
	Done    int  `json:"done"`
}

// ExecutionSummary describes how a sync was executed.
type ExecutionSummary struct {
	DryRun             bool         `json:"dryRun"`
	Protocol           string       `json:"protocol"`
	OperationMode      string       `json:"operationMode"`
	Relay              bool         `json:"relay"`
	TransferMode       string       `json:"transferMode"`
	Reason             string       `json:"reason"`
	BootstrapSuggested bool         `json:"bootstrapSuggested"`
	SourceHEAD         string       `json:"sourceHead,omitempty"`
	Batch              BatchSummary `json:"batch"`
}

// SyncResult is the outcome of a Sync or Replicate.
type SyncResult struct {
	Refs        []RefResult      `json:"refs"`
	Counts      SyncCounts       `json:"counts"`
	Execution   ExecutionSummary `json:"execution"`
	Stats       Stats            `json:"stats"`
	Measurement Measurement      `json:"measurement"`
}

// PlanResult is the outcome of a Plan; it has the same shape as SyncResult.
type PlanResult = SyncResult

func fromStats(stats syncer.Stats) Stats {
	out := Stats{
		Enabled:      stats.Enabled,
		Items:        make(map[string]*ServiceStats, len(stats.Items)),
		ElapsedNanos: stats.ElapsedNanos,
	}
	for key, item := range stats.Items {
		out.Items[key] = &ServiceStats{
			Name:          item.Name,
			Requests:      item.Requests,
			RequestBytes:  item.RequestBytes,
			ResponseBytes: item.ResponseBytes,
			Wants:         item.Wants,
			Haves:         item.Haves,
			Commands:      item.Commands,
		}
	}
	if len(stats.Sides) > 0 {
		out.Sides = make([]SideBytes, 0, len(stats.Sides))
		for _, side := range stats.Sides {
			out.Sides = append(out.Sides, SideBytes{
				Label:       side.Label,
				Bytes:       side.Bytes,
				Display:     side.Display,
				ActiveNanos: side.ActiveNanos,
				IdleNanos:   side.IdleNanos,
			})
		}
	}
	return out
}

func hashString(hash plumbing.Hash) string {
	if hash.IsZero() {
		return ""
	}
	return hash.String()
}
```

Aresults.go+257

package gitsync

import (
	"testing"

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync/internal/planner"
	"entire.io/entire/git-sync/internal/syncer"
)

Aresults\_test.go+97

```
2 unmodified lines

3
4
5
6
7
6
7
8
146 unmodified lines

155
156
157
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

2 unmodified lines

import (
	"context"
	"errors"

"entire.io/entire/git-sync/internal/internalbridge"
)

// ProtocolMode controls source-side protocol negotiation.
146 unmodified lines

Policy       SyncPolicy `json:"policy"`
	CollectStats bool       `json:"collectStats"`
}

type RefKind = internalbridge.RefKind

const (
	RefKindBranch RefKind = internalbridge.RefKindBranch
	RefKindTag    RefKind = internalbridge.RefKindTag
	RefKindOther  RefKind = internalbridge.RefKindOther
)

type Action = internalbridge.Action

const (
	ActionCreate Action = internalbridge.ActionCreate
	ActionUpdate Action = internalbridge.ActionUpdate
	ActionDelete Action = internalbridge.ActionDelete
	ActionSkip   Action = internalbridge.ActionSkip
	ActionBlock  Action = internalbridge.ActionBlock
	ActionWarn   Action = internalbridge.ActionWarn
)

type RefResult = internalbridge.RefResult
type RefPlan = internalbridge.RefPlan
type RefInfo = internalbridge.RefInfo
type ServiceStats = internalbridge.ServiceStats
type Stats = internalbridge.Stats
type Measurement = internalbridge.Measurement
type ProbeResult = internalbridge.ProbeResult
type SyncCounts = internalbridge.SyncCounts
type BatchSummary = internalbridge.BatchSummary
type ExecutionSummary = internalbridge.ExecutionSummary
type SyncResult = internalbridge.SyncResult
type PlanResult = internalbridge.PlanResult
```

Mtypes.go-34

```
7 unmodified lines

8
9
10
11
11
12
13
219 unmodified lines

233
234
235
237
236
237
238
239
32 unmodified lines

272
273
274
276
275
276
277
278
2 unmodified lines

281
282
283
285
284
285
286
287
24 unmodified lines

312
313
314
316
315
316
317
318
15 unmodified lines

334
335
336
338
337
338
339
340
17 unmodified lines

358
359
360
362
363
364
365
366
367
368
369
370
371
372
373
374
375
361
377
378
379
380
381
382
383
384
385
386
387
388
362
363
364
365
366
367
368
369
370
371
372
392
373
374
394
375
376
377
378
379
399
380
381

7 unmodified lines

"github.com/go-git/go-git/v6/plumbing"

"entire.io/entire/git-sync"
	"entire.io/entire/git-sync/internal/internalbridge"
	"entire.io/entire/git-sync/internal/syncer"
	"entire.io/entire/git-sync/internal/validation"
)
219 unmodified lines

ShowStats:          req.Options.CollectStats,
		MeasureMemory:      req.Options.MeasureMemory,
		Progress:           req.Options.Progress,
		ProtocolMode:       protocolString(req.Protocol),
		ProtocolMode:       string(req.Protocol),
		Verbose:            req.Options.Verbose,
	}
	if req.Target != nil {
32 unmodified lines

ShowStats:              req.Options.CollectStats,
		MeasureMemory:          req.Options.MeasureMemory,
		Progress:               req.Options.Progress,
		Mode:                   operationModeString(req.Policy.Mode),
		Mode:                   string(req.Policy.Mode),
		ForceWithLease:         req.Policy.ForceWithLease,
		ForceBlind:             req.Policy.ForceBlind,
		Prune:                  req.Policy.Prune,
2 unmodified lines

TargetMaxPackBytes:     req.Options.TargetMaxPackBytes,
		TargetMaxRefUpdates:    req.Options.TargetMaxRefUpdates,
		MaterializedMaxObjects: maxObjects,
		ProtocolMode:           protocolString(req.Policy.Protocol),
		ProtocolMode:           string(req.Policy.Protocol),
		Verbose:                req.Options.Verbose,
		BootstrapStrategy:      req.Options.BootstrapStrategy,
	}, nil
24 unmodified lines

MaxPackBytes:        req.Options.MaxPackBytes,
		TargetMaxPackBytes:  req.Options.TargetMaxPackBytes,
		TargetMaxRefUpdates: req.Options.TargetMaxRefUpdates,
		ProtocolMode:        protocolString(req.Protocol),
		ProtocolMode:        string(req.Protocol),
		Verbose:             req.Options.Verbose,
		BootstrapStrategy:   req.Options.BootstrapStrategy,
	}, nil
15 unmodified lines

ShowStats:          req.Options.CollectStats,
		MeasureMemory:      req.Options.MeasureMemory,
		Progress:           req.Options.Progress,
		ProtocolMode:       protocolString(req.Protocol),
		ProtocolMode:       string(req.Protocol),
		Verbose:            req.Options.Verbose,
	}, nil
}
17 unmodified lines

return syncerEndpoint(endpoint, auth), nil
}

func protocolString(mode gitsync.ProtocolMode) string {
	if mode == "" {
		return string(gitsync.ProtocolAuto)
	}
	return string(mode)
}

func operationModeString(mode gitsync.OperationMode) string {
	if mode == "" {
		return string(gitsync.ModeSync)
	}
	return string(mode)
}

func syncerEndpoint(endpoint gitsync.Endpoint, auth gitsync.EndpointAuth) syncer.Endpoint {
	return internalbridge.ToSyncerEndpoint(
		internalbridge.Endpoint{
			URL:                    endpoint.URL,
			FollowInfoRefsRedirect: endpoint.FollowInfoRefsRedirect,
		},
		internalbridge.EndpointAuth{
			Username:      auth.Username,
			Token:         auth.Token,
			BearerToken:   auth.BearerToken,
			SkipTLSVerify: auth.SkipTLSVerify,
		},
	)
	return syncer.Endpoint{
		URL:                    endpoint.URL,
		Username:               auth.Username,
		Token:                  auth.Token,
		BearerToken:            auth.BearerToken,
		SkipTLSVerify:          auth.SkipTLSVerify,
		FollowInfoRefsRedirect: endpoint.FollowInfoRefsRedirect,
	}
}

func validationMappings(mappings []gitsync.RefMapping) []validation.RefMapping {
	bridgeMappings := make([]internalbridge.RefMapping, 0, len(mappings))
	out := make([]validation.RefMapping, 0, len(mappings))
	for _, mapping := range mappings {
		bridgeMappings = append(bridgeMappings, internalbridge.RefMapping{
		out = append(out, validation.RefMapping{
			Source: mapping.Source,
			Target: mapping.Target,
		})
	}
	return internalbridge.ToValidationMappings(bridgeMappings)
	return out
}
```

Munstable/client.go+16/-35
