Migrate from go-git v5 to v6 · Entire
Log in
Migrate from go-git v5 to v6
749b51f→main·
Soph·3mo ago·41 files·+642 added/-749 removed
Switch to go-git/go-git/v6 v6.0.0-alpha.1 and go-billy/v6. The v6 API removes the transport session abstraction (UploadPackSession, ReceivePackSession) and changes Endpoint to embed url.URL. Rather than adapting to v6's new session model, the v1 fetch and push paths were rewritten to use direct HTTP protocol handling via PostRPCStream, which eliminates the dual-stack transport problem called out in the rewrite memo and aligns with the goal of owning the smart HTTP layer directly.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
Sessions
b5b72cefafb3View transcript
Changes
41
cmd/git-sync
Mmain.go+1/-1
Mmain_test.go+35/-119
Mgo.mod+14/-17
Mgo.sum+34/-72
internal
auth
Mauth.go+9/-9
Mauth_test.go+20/-15
Mentiredb.go+5/-11
convert
Mconvert.go+1/-1
gitproto
Mcapability.go+1/-1
Mcapability_test.go+1/-1
Mconvert.go+1/-1
Mconvert_test.go+1/-1
Mfetch.go+74/-79
Mfetch_helpers_test.go+1/-1
Mfetch_test.go+13/-18
Mpktline.go+6/-7
Mpush.go+81/-89
Mpush_test.go+110/-45
Mrefs.go+7/-7
Mrefs_test.go+3/-3
Msmarthttp.go+31/-14
Msmarthttp_test.go+2/-2
planner
Mbenchmark_test.go+3/-3
Mcheckpoint.go+3/-3
Mmapping.go+1/-1
Mplanner.go+3/-3
Mplanner_test.go+6/-6
Mrelay.go+3/-3
Mtypes.go+1/-1
strategy
bootstrap
Mbootstrap.go+7/-7
Mbootstrap_test.go+3/-3
incremental
Mincremental.go+1/-1
Mincremental_test.go+1/-1
materialized
Mmaterialized.go+3/-3
Mmaterialized_test.go+1/-1
syncer
Mauth_test.go+2/-11
Mbenchmark_test.go+10/-10
Mgit_http_backend_test.go+1/-1
Mintegration_test.go+134/-169
Mlive_bootstrap_test.go+1/-1
Msyncer.go+7/-7
8 unmodified lines
9
10
11
12
12
13
14
15
8 unmodified lines
"os"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/syncer"
)
Mcmd/git-sync/main.go+1/-1
13 unmodified lines
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
17
18
19
20
21
22
23
24
25
26
27
56 unmodified lines
84
85
86
91
87
88
89
90
84 unmodified lines
175
176
177
182
178
179
180
181
99 unmodified lines
281
282
283
288
289
284
285
286
287
288
289
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
290
318
291
292
293
294
5 unmodified lines
300
301
302
330
331
332
333
334
335
303
304
305
337
306
307
308
309
310
311
312
313
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
314
315
316
5 unmodified lines
322
323
324
372
373
374
375
376
377
325
326
327
379
380
381
382
383
328
329
330
331
385
386
387
388
332
333
334
335
390
391
392
393
394
395
396
397
398
336
337
338
339
340
404
405
341
342
343
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
344
345
346
425
426
347
348
349
350
351
352
428
429
430
431
432
433
434
435
436
437
353
354
13 unmodified lines
"testing"
"time"
billy "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/transport"
transportclient "github.com/go-git/go-git/v5/plumbing/transport/client"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
transportserver "github.com/go-git/go-git/v5/plumbing/transport/server"
"github.com/go-git/go-git/v5/storage/memory"
billy "github.com/go-git/go-billy/v6"
"github.com/go-git/go-billy/v6/memfs"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/object"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
"github.com/go-git/go-git/v6/storage/memory"
"github.com/soph/git-sync/internal/syncer"
)
56 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 3)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
84 unmodified lines
t.Helper()
fs := memfs.New()
repo, err := git.Init(memory.NewStorage(), fs)
repo, err := git.Init(memory.NewStorage(), git.WithWorkTree(fs))
if err != nil {
t.Fatalf("init source repo: %v", err)
}
99 unmodified lines
}
func (s *smartHTTPRepoServer) handleInfoRefs(w http.ResponseWriter, r *http.Request) {
service := r.URL.Query().Get("service")
if service != transport.UploadPackServiceName && service != transport.ReceivePackServiceName {
service := transport.Service(r.URL.Query().Get("service"))
if service != transport.UploadPackService && service != transport.ReceivePackService {
http.Error(w, "missing service", http.StatusBadRequest)
return
}
session, err := s.newSession(service)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var ar *packp.AdvRefs
switch service {
case transport.UploadPackServiceName:
ar, err = session.(transport.UploadPackSession).AdvertisedReferencesContext(r.Context())
case transport.ReceivePackServiceName:
ar, err = session.(transport.ReceivePackSession).AdvertisedReferencesContext(r.Context())
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ar.Prefix = [][]byte{
[]byte("# service=" + service),
pktline.Flush,
}
var buf bytes.Buffer
if err := ar.Encode(&buf); err != nil {
if err := transport.AdvertiseReferences(r.Context(), s.repo.Storer, &buf, service, false); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
5 unmodified lines
}
func (s *smartHTTPRepoServer) handleUploadPack(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer r.Body.Close()
var buf bytes.Buffer
wc := nopWriteCloser{&buf}
session, err := s.newSession(transport.UploadPackServiceName)
err := transport.UploadPack(r.Context(), s.repo.Storer, r.Body, wc, &transport.UploadPackOptions{
StatelessRPC: true,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
req := packp.NewUploadPackRequest()
if err := req.Decode(bytes.NewReader(body)); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
resp, err := session.(transport.UploadPackSession).UploadPack(r.Context(), req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var buf bytes.Buffer
if err := resp.Encode(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/x-git-upload-pack-result")
if _, err := w.Write(buf.Bytes()); err != nil {
s.t.Fatalf("write upload-pack response: %v", err)
5 unmodified lines
s.receivePacks++
s.mu.Unlock()
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer r.Body.Close()
var buf bytes.Buffer
wc := nopWriteCloser{&buf}
session, err := s.newSession(transport.ReceivePackServiceName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err := transport.ReceivePack(r.Context(), s.repo.Storer, r.Body, wc, &transport.ReceivePackOptions{
StatelessRPC: true,
})
req := packp.NewReferenceUpdateRequest()
if err := req.Decode(bytes.NewReader(body)); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
w.Header().Set("Content-Type", "application/x-git-receive-pack-result")
if buf.Len() > 0 {
_, _ = w.Write(buf.Bytes())
}
report, err := session.(transport.ReceivePackSession).ReceivePack(r.Context(), req)
if report != nil {
var buf bytes.Buffer
if encErr := report.Encode(&buf); encErr == nil {
w.Header().Set("Content-Type", "application/x-git-receive-pack-result")
_, _ = w.Write(buf.Bytes())
}
}
if err != nil {
return
}
}
func (s *smartHTTPRepoServer) newSession(service string) (interface{}, error) {
loader := transportserver.MapLoader{}
// nopWriteCloser wraps an io.Writer to satisfy io.WriteCloser.
type nopWriteCloser struct{ io.Writer }
endpoint, err := transport.NewEndpoint(s.RepoURL())
if err != nil {
return nil, err
}
loader[endpoint.String()] = s.repo.Storer
srv := transportserver.NewServer(loader)
switch service {
case transport.UploadPackServiceName:
return srv.NewUploadPackSession(endpoint, nil)
case transport.ReceivePackServiceName:
return srv.NewReceivePackSession(endpoint, nil)
default:
return nil, fmt.Errorf("unknown service %q", service)
}
}
func (nopWriteCloser) Close() error { return nil }
func TestMain(m *testing.M) {
originalHTTP := transportclient.Protocols["http"]
originalHTTPS := transportclient.Protocols["https"]
customHTTP := transporthttp.NewTransport(&transporthttp.TransportOptions{
Client: &http.Client{},
})
transport.Register("http", customHTTP)
transport.Register("https", customHTTP)
customHTTP := transporthttp.NewClient(&http.Client{})
transportclient.InstallProtocol("http", customHTTP)
transportclient.InstallProtocol("https", customHTTP)
code := m.Run()
transportclient.InstallProtocol("http", originalHTTP)
transportclient.InstallProtocol("https", originalHTTPS)
os.Exit(code)
os.Exit(m.Run())
}
Mcmd/git-sync/main_test.go+35/-119
2 unmodified lines
3
4
5
6
7
6
7
8
9
10
11
12
12
14
15
16
13
14
15
16
17
19
18
19
20
22
23
24
25
26
27
28
29
30
31
21
22
23
24
25
26
27
28
29
2 unmodified lines
go 1.25.2
require (
github.com/go-git/go-billy/v5 v5.8.0
github.com/go-git/go-git/v5 v5.17.0
github.com/go-git/go-billy/v6 v6.0.0-20260410103409-85b6241850b5
github.com/go-git/go-git/v6 v6.0.0-alpha.1
github.com/zalando/go-keyring v0.2.8
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/danieljoos/wincred v1.2.3 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.43.0 // indirect
)
Mgo.mod+14/-17
1
2
3
1
2
6
7
3
4
5
6
7
8
12
13
14
15
9
10
11
12
13
14
15
16
17
21
22
18
19
20
21
27
28
29
30
31
32
33
34
22
23
24
25
26
27
28
29
30
31
32
33
39
40
41
42
43
44
34
35
36
37
38
46
47
39
40
50
51
52
53
54
55
56
57
41
42
43
44
60
61
62
63
64
65
66
45
46
47
48
49
70
50
51
52
74
75
53
54
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
55
56
57
58
59
60
61
62
63
64
65
66
67
68
103
104
105
106
69
70
71
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
github.com/go-git/go-billy/v6 v6.0.0-20260410103409-85b6241850b5 h1:r5Y4Hn9QwQj+u6vN0Ib1MipHkanYaG8Zj0kxsnv8Bu4=
github.com/go-git/go-billy/v6 v6.0.0-20260410103409-85b6241850b5/go.mod h1:CdBVp7CXl9l3sOyNEog46cP1Pvx/hjCe9AD0mtaIUYU=
github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67 h1:3hutPZF+/FBjR/9MdsLJ7e1mlt9pwHgwxMW7CrbmWII=
github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67/go.mod h1:xKt0pNHST9tYHvbiLxSY27CQWFwgIxBJuDrOE0JvbZw=
github.com/go-git/go-git/v6 v6.0.0-alpha.1 h1:tGohX5luKeO50DZD0/Zqd5dYjJzKtub91S4I+1qFVIs=
github.com/go-git/go-git/v6 v6.0.0-alpha.1/go.mod h1:qtzfNHlFsnq6vCw54aT4KvFWPK5bsOpTVtFC2sysdB8=
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs=
github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Mgo.sum+34/-72
5 unmodified lines
6
7
8
9
10
9
10
11
12
13
13 unmodified lines
27
28
29
30
30
31
32
33
44 unmodified lines
78
79
80
81
82
81
82
83
84
85
2 unmodified lines
88
89
90
91
91
92
93
94
95
95
96
97
98
99
100
99
100
101
102
103
5 unmodified lines
"os/exec"
"strings"
"github.com/go-git/go-git/v5/plumbing/transport"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
)
// Endpoint holds the authentication-related fields for a remote.
13 unmodified lines
if ep == nil {
return nil, nil
}
if ep.Protocol != "http" && ep.Protocol != "https" {
if ep.Scheme != "http" && ep.Scheme != "https" {
return nil, nil
}
if username, password, ok, err := LookupEntireDBCredential(raw, ep); err != nil {
44 unmodified lines
}
username := values["username"]
if username == "" {
if ep.User != "" {
username = ep.User
if ep.User != nil && ep.User.Username() != "" {
username = ep.User.Username()
} else {
username = "git"
}
2 unmodified lines
}
func credentialFillInput(ep *transport.Endpoint) string {
if ep == nil || ep.Host == "" {
if ep == nil || ep.Hostname() == "" {
return ""
}
var b strings.Builder
fmt.Fprintf(&b, "protocol=%s\nhost=%s\n", ep.Protocol, ep.Host)
fmt.Fprintf(&b, "protocol=%s\nhost=%s\n", ep.Scheme, ep.Hostname())
if path := strings.TrimPrefix(ep.Path, "/"); path != "" {
fmt.Fprintf(&b, "path=%s\n", path)
}
if ep.User != "" {
fmt.Fprintf(&b, "username=%s\n", ep.User)
if ep.User != nil && ep.User.Username() != "" {
fmt.Fprintf(&b, "username=%s\n", ep.User.Username())
}
b.WriteString("\n")
return b.String()
Minternal/auth/auth.go+9/-9
3 unmodified lines
4
5
6
7
8
9
10
1 unmodified line
12
13
14
14
15
15
16
17
18
19
101 unmodified lines
121
122
123
123
124
125
126
124
125
126
127
128
129
130
131
132
11 unmodified lines
144
145
146
144
147
148
149
150
2 unmodified lines
153
154
155
153
154
155
156
157
158
159
160
161
162
163
68 unmodified lines
232
233
234
230
235
236
237
238
180 unmodified lines
419
420
421
417
422
423
424
425
426
422
427
428
429
430
21 unmodified lines
452
453
454
450
455
456
457
458
459
455
460
461
462
463
3 unmodified lines
"context"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"strconv"
1 unmodified line
"testing"
"time"
"github.com/go-git/go-git/v5/plumbing/transport"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
"github.com/zalando/go-keyring"
)
101 unmodified lines
func TestCredentialFillInput(t *testing.T) {
ep := &transport.Endpoint{
Protocol: "https",
Host: "github.com",
Path: "/owner/repo.git",
User: "myuser",
URL: url.URL{
Scheme: "https",
Host: "github.com",
Path: "/owner/repo.git",
User: url.User("myuser"),
},
}
got := credentialFillInput(ep)
11 unmodified lines
}
func TestCredentialFillInputEmptyHost(t *testing.T) {
ep := &transport.Endpoint{Protocol: "https"}
ep := &transport.Endpoint{URL: url.URL{Scheme: "https"}}
got := credentialFillInput(ep)
if got != "" {
t.Errorf("expected empty string for empty host, got %q", got)
2 unmodified lines
func TestCredentialFillInputNoUser(t *testing.T) {
ep := &transport.Endpoint{
Protocol: "https",
Host: "example.com",
Path: "/repo.git",
URL: url.URL{
Scheme: "https",
Host: "example.com",
Path: "/repo.git",
},
}
got := credentialFillInput(ep)
want := "protocol=https\nhost=example.com\npath=repo.git\n\n"
68 unmodified lines
t.Fatal(err)
}
sshEP := &transport.Endpoint{Protocol: "ssh", Host: "example.com", Path: "/repo.git"}
sshEP := &transport.Endpoint{URL: url.URL{Scheme: "ssh", Host: "example.com", Path: "/repo.git"}}
tests := []struct {
name string
180 unmodified lines
}{
{
name: "https host",
ep: &transport.Endpoint{Protocol: "https", Host: "example.com"},
ep: &transport.Endpoint{URL: url.URL{Scheme: "https", Host: "example.com"}},
want: "https://example.com",
},
{
name: "http host with port",
ep: &transport.Endpoint{Protocol: "http", Host: "example.com", Port: 8080},
ep: &transport.Endpoint{URL: url.URL{Scheme: "http", Host: "example.com:8080"}},
want: "http://example.com:8080",
},
{
21 unmodified lines
}{
{
name: "host without port",
ep: &transport.Endpoint{Host: "example.com"},
ep: &transport.Endpoint{URL: url.URL{Host: "example.com"}},
want: "example.com",
},
{
name: "host with port",
ep: &transport.Endpoint{Host: "example.com", Port: 8080},
ep: &transport.Endpoint{URL: url.URL{Host: "example.com:8080"}},
want: "example.com:8080",
},
{
Minternal/auth/auth_test.go+20/-15
13 unmodified lines
14
15
16
17
17
18
19
20
38 unmodified lines
59
60
61
62
62
63
64
65
65
66
67
68
69
70
71
72
69
70
71
72
1 unmodified line
74
75
76
80
81
82
83
77
78
79
80
13 unmodified lines
"strings"
"time"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v6/plumbing/transport"
"github.com/zalando/go-keyring"
)
38 unmodified lines
}
func endpointBaseURL(ep *transport.Endpoint) string {
if ep == nil || ep.Host == "" {
if ep == nil || ep.Hostname() == "" {
return ""
}
scheme := ep.Protocol
scheme := ep.Scheme
if scheme == "" {
scheme = "https"
}
host := ep.Host
if ep.Port > 0 {
host = fmt.Sprintf("%s:%d", host, ep.Port)
}
host := ep.Host // includes port if present in url.URL
return scheme + "://" + host
}
1 unmodified line
if ep == nil {
return ""
}
if ep.Port > 0 {
return fmt.Sprintf("%s:%d", ep.Host, ep.Port)
}
return ep.Host
return ep.Host // includes port if present in url.URL
}
func lookupEntireDBToken(host, baseURL string, skipTLS bool) (string, error) {
Minternal/auth/entiredb.go+5/-11
3 unmodified lines
4
5
6
7
7
8
9
10
3 unmodified lines
package convert
import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/gitproto"
"github.com/soph/git-sync/internal/planner"
Minternal/convert/convert.go+1/-1
5 unmodified lines
6
7
8
9
9
10
11
12
5 unmodified lines
"sort"
"strings"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
)
// V2Capabilities represents a parsed protocol v2 capability advertisement.
Minternal/gitproto/capability.go+1/-1
2 unmodified lines
3
4
5
6
6
7
8
9
2 unmodified lines
import (
"testing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
)
func TestV2CapabilitiesFetchSupports(t *testing.T) {
Minternal/gitproto/capability_test.go+1/-1
3 unmodified lines
4
5
6
7
7
8
9
10
3 unmodified lines
"fmt"
"io"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
// ToPushCommands converts a slice of PushPlans to PushCommands.
Minternal/gitproto/convert.go+1/-1
4 unmodified lines
5
6
7
8
8
9
10
11
4 unmodified lines
"strings"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
func TestToPushCommands(t *testing.T) {
Minternal/gitproto/convert_test.go+1/-1
1
2
3
4
5
6
7
8
9
10
10
11
12
13
14
15
16
17
18
11
12
13
14
15
16
17
18
19
20
21
22
89 unmodified lines
112
113
114
114
115
116
117
118
43 unmodified lines
162
163
164
164
165
166
167
168
32 unmodified lines
201
202
203
203
204
205
206
207
73 unmodified lines
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
2 unmodified lines
329
330
331
291
332
333
293
334
335
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
336
337
315
316
317
338
339
340
341
322
342
343
344
345
346
347
348
349
350
4 unmodified lines
355
356
357
333
358
359
335
360
361
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
362
363
357
358
359
360
364
365
363
364
365
366
367
368
366
367
368
369
370
371
372
373
374
375
376
377
64 unmodified lines
442
443
444
439
440
441
442
443
444
445
446
447
448
449
package gitproto
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/utils/ioutil"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/format/packfile"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/plumbing/transport"
"github.com/go-git/go-git/v6/utils/ioutil"
)
// DesiredRef describes a single ref we want to fetch from source.
89 unmodified lines
if err != nil {
return err
}
reader, err := PostRPCStream(ctx, conn, transport.UploadPackServiceName, body, true, "upload-pack fetch")
reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, true, "upload-pack fetch")
if err != nil {
return err
}
43 unmodified lines
if err != nil {
return err
}
reader, err := PostRPCStream(ctx, conn, transport.UploadPackServiceName, body, true, "upload-pack fetch")
reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, true, "upload-pack fetch")
if err != nil {
return err
}
32 unmodified lines
if err != nil {
return nil, err
}
reader, err := PostRPCStream(ctx, conn, transport.UploadPackServiceName, body, true, "upload-pack fetch")
reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, true, "upload-pack fetch")
if err != nil {
return nil, err
}
73 unmodified lines
// --- V1 fetch implementation ---
// buildV1UploadPackBody encodes a v1 upload-pack request body for stateless-rpc HTTP.
func buildV1UploadPackBody(
adv *packp.AdvRefs,
desired map[plumbing.ReferenceName]DesiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
includeTags bool,
) ([]byte, *capability.List, error) {
wants := collectWants(desired)
haves := SortedUniqueHashes(refValues(targetRefs))
if len(wants) == 0 {
return nil, nil, git.NoErrAlreadyUpToDate
}
req := packp.NewUploadRequest()
req.Wants = wants
if adv.Capabilities.Supports(capability.NoProgress) {
_ = req.Capabilities.Set(capability.NoProgress)
}
if includeTags && adv.Capabilities.Supports(capability.IncludeTag) {
_ = req.Capabilities.Set(capability.IncludeTag)
}
// Prefer sideband64k over sideband (issue #4).
if sb := PreferredSideband(adv.Capabilities); sb != "" {
_ = req.Capabilities.Set(sb)
}
if adv.Capabilities.Supports(capability.OFSDelta) {
_ = req.Capabilities.Set(capability.OFSDelta)
}
var buf bytes.Buffer
if err := req.Encode(&buf); err != nil {
return nil, nil, fmt.Errorf("encode upload-request: %w", err)
}
uphav := &packp.UploadHaves{Haves: haves, Done: true}
if err := uphav.Encode(&buf); err != nil {
return nil, nil, fmt.Errorf("encode upload-haves: %w", err)
}
return buf.Bytes(), req.Capabilities, nil
}
func fetchToStoreV1(
ctx context.Context,
store storer.Storer,
2 unmodified lines
desired map[plumbing.ReferenceName]DesiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
) error {
session, err := conn.Transport.NewUploadPackSession(conn.Endpoint, conn.Auth)
body, caps, err := buildV1UploadPackBody(adv, desired, targetRefs, hasTag(desired))
if err != nil {
return fmt.Errorf("open source upload-pack session: %w", err)
return err
}
defer session.Close()
req := packp.NewUploadPackRequestFromCapabilities(adv.Capabilities)
for _, ref := range desired {
req.Wants = append(req.Wants, ref.SourceHash)
}
req.Wants = SortedUniqueHashes(req.Wants)
req.Haves = SortedUniqueHashes(refValues(targetRefs))
if len(req.Wants) == 0 {
return git.NoErrAlreadyUpToDate
}
if adv.Capabilities.Supports(capability.NoProgress) {
_ = req.Capabilities.Set(capability.NoProgress)
}
if hasTag(desired) && adv.Capabilities.Supports(capability.IncludeTag) {
_ = req.Capabilities.Set(capability.IncludeTag)
}
reader, err := session.UploadPack(ctx, req)
reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, false, "upload-pack fetch")
if err != nil {
if errors.Is(err, transport.ErrEmptyUploadPackRequest) {
return git.NoErrAlreadyUpToDate
}
return fmt.Errorf("source upload-pack: %w", err)
}
defer ioutil.CheckClose(reader, &err)
sbReader := buildSidebandReader(req.Capabilities, reader, nil)
// Decode server response (ACK/NAK) then read pack with sideband demux.
var srvResp packp.ServerResponse
if decErr := srvResp.Decode(reader); decErr != nil {
return fmt.Errorf("decode server response: %w", decErr)
}
sbReader := buildSidebandReader(caps, reader, nil)
return packfile.UpdateObjectStorage(store, sbReader)
}
4 unmodified lines
desired map[plumbing.ReferenceName]DesiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
) (io.ReadCloser, error) {
session, err := conn.Transport.NewUploadPackSession(conn.Endpoint, conn.Auth)
body, caps, err := buildV1UploadPackBody(adv, desired, targetRefs, hasTag(desired))
if err != nil {
return nil, fmt.Errorf("open source upload-pack session: %w", err)
return nil, err
}
req := packp.NewUploadPackRequestFromCapabilities(adv.Capabilities)
for _, ref := range desired {
req.Wants = append(req.Wants, ref.SourceHash)
}
req.Wants = SortedUniqueHashes(req.Wants)
req.Haves = SortedUniqueHashes(refValues(targetRefs))
if len(req.Wants) == 0 {
_ = session.Close()
return nil, git.NoErrAlreadyUpToDate
}
if adv.Capabilities.Supports(capability.NoProgress) {
_ = req.Capabilities.Set(capability.NoProgress)
}
if hasTag(desired) && adv.Capabilities.Supports(capability.IncludeTag) {
_ = req.Capabilities.Set(capability.IncludeTag)
}
reader, err := session.UploadPack(ctx, req)
reader, err := PostRPCStream(ctx, conn, transport.UploadPackService, body, false, "upload-pack fetch")
if err != nil {
_ = session.Close()
if errors.Is(err, transport.ErrEmptyUploadPackRequest) {
return nil, git.NoErrAlreadyUpToDate
}
return nil, fmt.Errorf("source upload-pack: %w", err)
}
return &sessionRC{
Reader: buildSidebandReader(req.Capabilities, reader, nil),
closeFn: func() error {
_ = reader.Close()
return session.Close()
},
var srvResp packp.ServerResponse
if decErr := srvResp.Decode(reader); decErr != nil {
_ = reader.Close()
return nil, fmt.Errorf("decode server response: %w", decErr)
}
return &wrappedRC{
Reader: buildSidebandReader(caps, reader, nil),
Closer: reader,
}, nil
}
64 unmodified lines
io.Closer
}
type sessionRC struct {
io.Reader
closeFn func() error
}
func (r *sessionRC) Close() error {
if r.closeFn == nil {
return nil
}
return r.closeFn()
}
Minternal/gitproto/fetch.go+74/-79
2 unmodified lines
3
4
5
6
6
7
8
9
2 unmodified lines
import (
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
func TestCollectWants(t *testing.T) {
Minternal/gitproto/fetch_helpers_test.go+1/-1
4 unmodified lines
5
6
7
8
9
10
8
9
10
11
12
13
119 unmodified lines
133
134
135
136
137
136
137
138
139
139
140
141
141
142
143
144
144
145
146
147
148
149
150
150
151
152
153
154
155
156
157
158
159
160
152
153
154
155
156
157
158
159
160
4 unmodified lines
"io"
"testing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/sideband"
)
func TestCapabilities(t *testing.T) {
119 unmodified lines
}
}
func TestSessionRCClose(t *testing.T) {
// With closeFn
func TestWrappedRCClose(t *testing.T) {
// wrappedRC should close the underlying closer.
called := false
rc := &sessionRC{
rc := &wrappedRC{
Reader: bytes.NewBufferString("data"),
closeFn: func() error {
Closer: closerFunc(func() error {
called = true
return nil
},
}),
}
if err := rc.Close(); err != nil {
t.Fatalf("Close() error: %v", err)
}
if !called {
t.Error("closeFn was not called")
t.Error("underlying closer was not called")
}
// With nil closeFn
rc = &sessionRC{
Reader: bytes.NewBufferString("data"),
closeFn: nil,
}
if err := rc.Close(); err != nil {
t.Fatalf("Close() with nil closeFn error: %v", err)
}
}
type closerFunc func() error
func (f closerFunc) Close() error { return f() }
func TestDecodeV2LSRefs(t *testing.T) {
// Build a valid ls-refs response:
// Each line: "<hash> <refname>\n"
Minternal/gitproto/fetch_test.go+13/-18
6 unmodified lines
7
8
9
10
10
11
12
13
91 unmodified lines
105
106
107
108
109
108
109
110
111
113
112
113
114
115
116
118
117
118
119
120
122
121
122
123
124
125
127
126
127
128
129
6 unmodified lines
"fmt"
"io"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/go-git/go-git/v6/plumbing/format/pktline"
)
// PacketType represents the type of a pkt-line packet.
91 unmodified lines
// EncodeCommand builds a pkt-line encoded v2 command request.
func EncodeCommand(command string, capArgs, cmdArgs []string) ([]byte, error) {
var buf bytes.Buffer
enc := pktline.NewEncoder(&buf)
if err := enc.EncodeString("command=" + command + "\n"); err != nil {
if _, err := pktline.Writef(&buf, "command=%s\n", command); err != nil {
return nil, err
}
for _, arg := range capArgs {
if err := enc.EncodeString(arg + "\n"); err != nil {
if _, err := pktline.Writef(&buf, "%s\n", arg); err != nil {
return nil, err
}
}
if len(cmdArgs) > 0 {
if _, err := buf.WriteString("0001"); err != nil {
if err := pktline.WriteDelim(&buf); err != nil {
return nil, err
}
for _, arg := range cmdArgs {
if err := enc.EncodeString(arg + "\n"); err != nil {
if _, err := pktline.Writef(&buf, "%s\n", arg); err != nil {
return nil, err
}
}
}
if err := enc.Flush(); err != nil {
if err := pktline.WriteFlush(&buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
Minternal/gitproto/pktline.go+6/-7
1
2
3
4
5
6
7
8
9
9
10
11
12
13
14
10
11
12
13
14
15
16
17
18
19
31 unmodified lines
51
52
53
52
53
54
55
54
55
56
57
58
59
60
61
62
63
64
65
66
59
60
61
62
63
64
65
66
67
68
69
11 unmodified lines
81
82
83
87
88
84
85
86
87
88
93
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
6 unmodified lines
142
143
144
106
145
146
147
148
110
111
149
150
151
152
153
154
155
156
157
158
159
160
161
162
5 unmodified lines
168
169
170
123
171
172
173
174
175
176
130
131
132
133
134
135
136
137
177
178
179
180
181
142
143
182
183
184
185
186
187
145
146
147
148
188
189
190
191
4 unmodified lines
196
197
198
159
199
200
201
202
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
203
204
205
206
package gitproto
import (
"bytes"
"context"
"fmt"
"io"
"os"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/format/packfile"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/plumbing/transport"
)
// PushCommand represents a single ref update command.
31 unmodified lines
return PushObjects(ctx, p.Conn, p.Adv, commands, store, hashes, p.Verbose)
}
// preparePush opens a receive-pack session and builds the base request with
// sideband negotiation. Shared by PushObjects, PushPack, and PushCommands.
func preparePush(
conn *Conn,
// buildUpdateRequest builds the receive-pack update request.
func buildUpdateRequest(
adv *packp.AdvRefs,
commands []PushCommand,
verbose bool,
) (transport.ReceivePackSession, *packp.ReferenceUpdateRequest, bool, bool, error) {
session, err := conn.Transport.NewReceivePackSession(conn.Endpoint, conn.Auth)
if err != nil {
return nil, nil, false, false, fmt.Errorf("open target receive-pack session: %w", err)
}
req := packp.NewReferenceUpdateRequestFromCapabilities(adv.Capabilities)
req.Progress = progressWriter(verbose)
) (*packp.UpdateRequests, bool, bool, error) {
req := packp.NewUpdateRequests()
if sb := PreferredSideband(adv.Capabilities); sb != "" {
_ = req.Capabilities.Set(sb)
}
if adv.Capabilities.Supports(capability.ReportStatus) {
_ = req.Capabilities.Set(capability.ReportStatus)
}
hasDelete := false
hasUpdates := false
11 unmodified lines
if hasDelete {
if !adv.Capabilities.Supports(capability.DeleteRefs) {
_ = session.Close()
return nil, nil, false, false, fmt.Errorf("target does not support delete-refs")
return nil, false, false, fmt.Errorf("target does not support delete-refs")
}
_ = req.Capabilities.Set(capability.DeleteRefs)
}
return session, req, hasDelete, hasUpdates, nil
_ = verbose // progress handling is server-side in HTTP mode
return req, hasDelete, hasUpdates, nil
}
// sendReceivePack encodes and POSTs a receive-pack request, then decodes the report.
func sendReceivePack(
ctx context.Context,
conn *Conn,
req *packp.UpdateRequests,
packData io.Reader,
) error {
var buf bytes.Buffer
if err := req.Encode(&buf); err != nil {
return fmt.Errorf("encode update-request: %w", err)
}
if packData != nil {
if _, err := io.Copy(&buf, packData); err != nil {
return fmt.Errorf("buffer pack data: %w", err)
}
}
reader, err := PostRPCStream(ctx, conn, transport.ReceivePackService, buf.Bytes(), false, "receive-pack push")
if err != nil {
return fmt.Errorf("target receive-pack: %w", err)
}
defer reader.Close()
// Unwrap sideband if negotiated.
var respReader io.Reader = reader
if req.Capabilities.Supports(capability.Sideband64k) {
respReader = sideband.NewDemuxer(sideband.Sideband64k, reader)
} else if req.Capabilities.Supports(capability.Sideband) {
respReader = sideband.NewDemuxer(sideband.Sideband, reader)
}
if req.Capabilities.Supports(capability.ReportStatus) {
report := packp.NewReportStatus()
if err := report.Decode(respReader); err != nil {
return fmt.Errorf("decode report-status: %w", err)
}
if err := report.Error(); err != nil {
return err
}
}
return nil
}
// PushObjects pushes locally-materialized objects to the target.
6 unmodified lines
hashes []plumbing.Hash,
verbose bool,
) error {
session, req, _, hasUpdates, err := preparePush(conn, adv, commands, verbose)
req, _, hasUpdates, err := buildUpdateRequest(adv, commands, verbose)
if err != nil {
return err
}
defer session.Close()
return executePush(ctx, session, store, req, hashes, hasUpdates, !adv.Capabilities.Supports(capability.OFSDelta))
if !hasUpdates {
return sendReceivePack(ctx, conn, req, nil)
}
useRefDeltas := !adv.Capabilities.Supports(capability.OFSDelta)
var packBuf bytes.Buffer
enc := packfile.NewEncoder(&packBuf, store, useRefDeltas)
if _, err := enc.Encode(hashes, 10); err != nil {
return fmt.Errorf("encode packfile: %w", err)
}
return sendReceivePack(ctx, conn, req, &packBuf)
}
// PushPack pushes a pack stream (relay) to the target.
5 unmodified lines
pack io.ReadCloser,
verbose bool,
) error {
// Validate no deletes in pack push before opening session.
for _, cmd := range commands {
if cmd.Delete {
return fmt.Errorf("pack push only supports create and update actions")
}
}
session, req, _, _, err := preparePush(conn, adv, commands, verbose)
if err != nil {
return err
}
defer session.Close()
req.Packfile = pack
report, err := session.ReceivePack(ctx, req)
req, _, _, err := buildUpdateRequest(adv, commands, verbose)
if err != nil {
_ = pack.Close()
return err
}
if closeErr := pack.Close(); closeErr != nil {
return closeErr
err = sendReceivePack(ctx, conn, req, pack)
closeErr := pack.Close()
if err != nil {
return err
}
if report != nil {
return report.Error()
}
return nil
return closeErr
}
// PushCommands sends ref update commands without a pack (for ref-only changes).
4 unmodified lines
commands []PushCommand,
verbose bool,
) error {
session, req, _, _, err := preparePush(conn, adv, commands, verbose)
req, _, _, err := buildUpdateRequest(adv, commands, verbose)
if err != nil {
return err
}
defer session.Close()
return executePush(ctx, session, nil, req, nil, false, false)
}
func executePush(
ctx context.Context,
session transport.ReceivePackSession,
store storer.Storer,
req *packp.ReferenceUpdateRequest,
hashes []plumbing.Hash,
sendPack bool,
useRefDeltas bool,
) error {
if !sendPack {
report, err := session.ReceivePack(ctx, req)
if err != nil {
return err
}
if report != nil {
return report.Error()
}
return nil
}
rd, wr := io.Pipe()
req.Packfile = rd
done := make(chan error, 1)
go func() {
enc := packfile.NewEncoder(wr, store, useRefDeltas)
if _, err := enc.Encode(hashes, 10); err != nil {
done <- wr.CloseWithError(err)
return
}
done <- wr.Close()
}()
report, err := session.ReceivePack(ctx, req)
if err != nil {
_ = rd.Close()
return err
}
if err := <-done; err != nil {
return err
}
if report != nil {
return report.Error()
}
return nil
return sendReceivePack(ctx, conn, req, nil)
}
func progressWriter(verbose bool) io.Writer {
Minternal/gitproto/push.go+81/-89
2 unmodified lines
3
4
5
6
6
7
8
9
10
10
11
12
11
12
13
14
15
16
17
15 unmodified lines
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
36
37
38
74
75
76
77
78
79
80
81
82
83
4 unmodified lines
88
89
90
91
92
93
94
95
96
97
98
99
55
56
57
58
59
100
101
102
103
104
105
106
107
108
109
3 unmodified lines
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
6 unmodified lines
181
182
183
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
2 unmodified lines
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/transport"
)
func TestOpenV2PackStreamCloseClosesBody(t *testing.T) {
15 unmodified lines
}
}
// fakeReceivePackServer returns an httptest.Server that responds to
// git-receive-pack POST requests. If reportErr is non-empty, the
// report-status will indicate failure.
func fakeReceivePackServer(t *testing.T, reportErr string) *httptest.Server {
t.Helper()
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Consume the request body.
_, _ = io.Copy(io.Discard, r.Body)
_ = r.Body.Close()
w.Header().Set("Content-Type", "application/x-git-receive-pack-result")
w.WriteHeader(http.StatusOK)
if reportErr != "" {
// Write a minimal report-status with an error.
report := packp.NewReportStatus()
report.UnpackStatus = reportErr
_ = report.Encode(w)
}
// If no reportErr, write nothing -- PushPack will not try to
// decode report-status when the capability is not negotiated.
}))
}
func connForServer(t *testing.T, srv *httptest.Server) *Conn {
t.Helper()
ep, err := transport.NewEndpoint(srv.URL + "/repo.git")
if err != nil {
t.Fatalf("parse endpoint: %v", err)
}
return NewConn(ep, "test", nil, srv.Client().Transport)
}
func TestPushPackClosesPackOnSuccess(t *testing.T) {
srv := fakeReceivePackServer(t, "")
defer srv.Close()
pack := &trackingReadCloser{ReadCloser: io.NopCloser(bytes.NewBufferString("PACK"))}
conn := &Conn{
Transport: fakeTransport{receivePackSession: &fakeReceivePackSession{}},
}
conn := connForServer(t, srv)
adv := packp.NewAdvRefs()
adv.Capabilities = capability.NewList()
err := PushPack(context.Background(), conn, adv, []PushCommand{{
Name: "refs/heads/main",
New: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
}}, pack, false)
if err != nil {
t.Fatalf("PushPack returned error: %v", err)
4 unmodified lines
}
func TestPushPackClosesPackOnReceivePackError(t *testing.T) {
// Server that returns HTTP 500 so the POST fails.
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.Copy(io.Discard, r.Body)
_ = r.Body.Close()
http.Error(w, "receive-pack failed", http.StatusInternalServerError)
}))
defer srv.Close()
pack := &trackingReadCloser{ReadCloser: io.NopCloser(bytes.NewBufferString("PACK"))}
conn := &Conn{
Transport: fakeTransport{receivePackSession: &fakeReceivePackSession{
err: errors.New("receive-pack failed"),
}},
}
conn := connForServer(t, srv)
adv := packp.NewAdvRefs()
adv.Capabilities = capability.NewList()
err := PushPack(context.Background(), conn, adv, []PushCommand{{
Name: "refs/heads/main",
New: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
}}, pack, false)
if err == nil {
t.Fatal("expected PushPack to return an error")
3 unmodified lines
}
}
func TestBuildUpdateRequest(t *testing.T) {
adv := packp.NewAdvRefs()
_ = adv.Capabilities.Set(capability.ReportStatus)
_ = adv.Capabilities.Set(capability.DeleteRefs)
_ = adv.Capabilities.Set(capability.Sideband64k)
req, hasDelete, hasUpdates, err := buildUpdateRequest(adv, []PushCommand{
{Name: "refs/heads/main", New: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")},
{Name: "refs/heads/old", Old: plumbing.NewHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), Delete: true},
}, false)
if err != nil {
t.Fatalf("buildUpdateRequest: %v", err)
}
if !hasDelete {
t.Error("expected hasDelete = true")
}
if !hasUpdates {
t.Error("expected hasUpdates = true")
}
if len(req.Commands) != 2 {
t.Fatalf("expected 2 commands, got %d", len(req.Commands))
}
if !req.Capabilities.Supports(capability.ReportStatus) {
t.Error("expected report-status capability")
}
}
func TestBuildUpdateRequestDeleteWithoutCapability(t *testing.T) {
adv := packp.NewAdvRefs()
// No delete-refs capability.
_, _, _, err := buildUpdateRequest(adv, []PushCommand{
{Name: "refs/heads/old", Old: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), Delete: true},
}, false)
if err == nil {
t.Fatal("expected error when target does not support delete-refs")
}
}
func TestPushPackRejectsDeletes(t *testing.T) {
pack := &trackingReadCloser{ReadCloser: io.NopCloser(bytes.NewBufferString("PACK"))}
// PushPack should reject delete commands before even trying to connect.
adv := packp.NewAdvRefs()
adv.Capabilities = capability.NewList()
// Use a nil-transport conn -- we should never reach the network.
ep, _ := transport.NewEndpoint("https://example.com/repo.git")
conn := &Conn{Endpoint: ep, HTTP: &http.Client{}}
err := PushPack(context.Background(), conn, adv, []PushCommand{
{Name: "refs/heads/old", Delete: true},
}, pack, false)
if err == nil {
t.Fatal("expected error for delete in pack push")
}
}
type trackingReadCloser struct {
io.ReadCloser
closed bool
6 unmodified lines
}
return nil
}
type fakeTransport struct {
receivePackSession transport.ReceivePackSession
}
func (t fakeTransport) NewUploadPackSession(*transport.Endpoint, transport.AuthMethod) (transport.UploadPackSession, error) {
return nil, errors.New("not implemented")
}
func (t fakeTransport) NewReceivePackSession(*transport.Endpoint, transport.AuthMethod) (transport.ReceivePackSession, error) {
return t.receivePackSession, nil
}
type fakeReceivePackSession struct {
err error
}
func (s *fakeReceivePackSession) AdvertisedReferences() (*packp.AdvRefs, error) {
return packp.NewAdvRefs(), nil
}
func (s *fakeReceivePackSession) AdvertisedReferencesContext(context.Context) (*packp.AdvRefs, error) {
return s.AdvertisedReferences()
}
func (s *fakeReceivePackSession) ReceivePack(context.Context, *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
if s.err != nil {
return nil, s.err
}
return nil, nil
}
func (s *fakeReceivePackSession) Close() error { return nil }
Minternal/gitproto/push_test.go+110/-45
5 unmodified lines
6
7
8
9
10
11
9
10
11
12
13
14
16 unmodified lines
31
32
33
34
34
35
36
37
27 unmodified lines
65
66
67
68
68
69
70
71
42 unmodified lines
114
115
116
117
117
118
119
120
13 unmodified lines
134
135
136
137
137
138
139
140
5 unmodified lines
"fmt"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/transport"
)
// RefService encapsulates the result of source ref discovery and the negotiated
16 unmodified lines
return refs, &RefService{Protocol: "v1", V1Adv: adv}, nil
case "auto", "v2":
data, err := RequestInfoRefs(ctx, conn, transport.UploadPackServiceName, "version=2")
data, err := RequestInfoRefs(ctx, conn, transport.UploadPackService, "version=2")
if err != nil {
return nil, nil, err
}
27 unmodified lines
}
// AdvertisedRefsV1 fetches and decodes v1 advertised refs for the given service.
func AdvertisedRefsV1(ctx context.Context, conn *Conn, service string) (*packp.AdvRefs, error) {
func AdvertisedRefsV1(ctx context.Context, conn *Conn, service transport.Service) (*packp.AdvRefs, error) {
data, err := RequestInfoRefs(ctx, conn, service, "")
if err != nil {
return nil, err
42 unmodified lines
}
func listSourceRefsV1(ctx context.Context, conn *Conn) (*packp.AdvRefs, []*plumbing.Reference, error) {
adv, err := AdvertisedRefsV1(ctx, conn, transport.UploadPackServiceName)
adv, err := AdvertisedRefsV1(ctx, conn, transport.UploadPackService)
if err != nil {
return nil, nil, err
}
13 unmodified lines
if err != nil {
return nil, err
}
data, err := PostRPC(ctx, conn, transport.UploadPackServiceName, body, true, "upload-pack ls-refs")
data, err := PostRPC(ctx, conn, transport.UploadPackService, body, true, "upload-pack ls-refs")
if err != nil {
return nil, err
}
Minternal/gitproto/refs.go+7/-7
3 unmodified lines
4
5
6
7
8
9
7
8
9
10
11
12
3 unmodified lines
"context"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
)
func TestRefHashMap(t *testing.T) {
Minternal/gitproto/refs_test.go+3/-3
7 unmodified lines
8
9
10
11
12
13
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
13 unmodified lines
47
48
49
35
50
51
52
53
16 unmodified lines
70
71
72
58
59
73
74
75
76
77
78
79
80
81
66
82
83
84
85
4 unmodified lines
90
91
92
77
93
94
95
96
11 unmodified lines
108
109
110
95
111
112
113
114
13 unmodified lines
128
129
130
115
116
131
132
133
134
135
136
137
121
122
138
139
140
141
142
5 unmodified lines
148
149
150
134
151
152
153
154
7 unmodified lines
"io"
"net/http"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/transport"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
)
// httpError checks an HTTP response status and returns an error for non-2xx responses.
func httpError(res *http.Response) error {
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusMultipleChoices {
return nil
}
var reason string
if res.Body != nil {
var buf bytes.Buffer
if n, _ := buf.ReadFrom(res.Body); n > 0 {
reason = buf.String()
}
}
return fmt.Errorf("http %d: %s %s", res.StatusCode, res.Request.URL.Redacted(), reason)
}
// StatsPhaseHeader is the HTTP header used to annotate requests with the
// current git-sync stats phase for round-trip tracking.
const StatsPhaseHeader = "X-Git-Sync-Stats-Phase"
13 unmodified lines
return &Conn{
Label: label,
Endpoint: ep,
Transport: transporthttp.NewClient(httpClient),
Transport: transporthttp.NewTransport(&transporthttp.TransportOptions{Client: httpClient}),
HTTP: httpClient,
Auth: auth,
}
16 unmodified lines
}
// RequestInfoRefs fetches /info/refs for the given service.
func RequestInfoRefs(ctx context.Context, conn *Conn, service, gitProtocol string) ([]byte, error) {
url := fmt.Sprintf("%s/info/refs?service=%s", conn.Endpoint.String(), service)
func RequestInfoRefs(ctx context.Context, conn *Conn, service transport.Service, gitProtocol string) ([]byte, error) {
svc := service.String()
url := fmt.Sprintf("%s/info/refs?service=%s", conn.Endpoint.String(), svc)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "*/*")
req.Header.Set("User-Agent", capability.DefaultAgent())
req.Header.Set(StatsPhaseHeader, service+" info-refs")
req.Header.Set(StatsPhaseHeader, svc+" info-refs")
if gitProtocol != "" {
req.Header.Set("Git-Protocol", gitProtocol)
}
4 unmodified lines
return nil, err
}
defer res.Body.Close()
if err := transporthttp.NewErr(res); err != nil {
if err := httpError(res); err != nil {
return nil, err
}
// Bound the read to prevent unbounded memory allocation (issue #9).
11 unmodified lines
// PostRPC sends a buffered POST to the given service and returns the full response body.
// Responses are bounded to prevent unbounded memory allocation (issue #9).
func PostRPC(ctx context.Context, conn *Conn, service string, body []byte, v2 bool, phase string) ([]byte, error) {
func PostRPC(ctx context.Context, conn *Conn, service transport.Service, body []byte, v2 bool, phase string) ([]byte, error) {
reader, err := PostRPCStream(ctx, conn, service, body, v2, phase)
if err != nil {
return nil, err
13 unmodified lines
// PostRPCStream sends a POST to the given service and returns the response body
// as a streaming reader. Caller must close the returned ReadCloser.
func PostRPCStream(ctx context.Context, conn *Conn, service string, body []byte, v2 bool, phase string) (io.ReadCloser, error) {
url := fmt.Sprintf("%s/%s", conn.Endpoint.String(), service)
func PostRPCStream(ctx context.Context, conn *Conn, service transport.Service, body []byte, v2 bool, phase string) (io.ReadCloser, error) {
svc := service.String()
url := fmt.Sprintf("%s/%s", conn.Endpoint.String(), svc)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", fmt.Sprintf("application/x-%s-request", service))
req.Header.Set("Accept", fmt.Sprintf("application/x-%s-result", service))
req.Header.Set("Content-Type", fmt.Sprintf("application/x-%s-request", svc))
req.Header.Set("Accept", fmt.Sprintf("application/x-%s-result", svc))
req.Header.Set("User-Agent", capability.DefaultAgent())
req.Header.Set(StatsPhaseHeader, phase)
if v2 {
5 unmodified lines
if err != nil {
return nil, err
}
if err := transporthttp.NewErr(res); err != nil {
if err := httpError(res); err != nil {
_ = res.Body.Close()
return nil, err
}
Minternal/gitproto/smarthttp.go+31/-14
6 unmodified lines
7
8
9
10
11
10
11
12
13
14
6 unmodified lines
"testing"
"time"
func TestNewConn(t *testing.T) {
Minternal/gitproto/smarthttp_test.go+2/-2
3 unmodified lines
4
5
6
7
8
9
7
8
9
10
11
12
3 unmodified lines
"fmt"
"testing"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/storage/memory"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/storage/memory"
)
func BenchmarkBuildDesiredRefs(b *testing.B) {
Minternal/planner/benchmark_test.go+3/-3
3 unmodified lines
4
5
6
7
8
9
7
8
9
10
11
12
3 unmodified lines
"fmt"
"sort"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/object"
"github.com/go-git/go-git/v6/plumbing/storer"
)
// BootstrapBatch holds the checkpoint plan for a single branch during batched bootstrap.
Minternal/planner/checkpoint.go+3/-3
3 unmodified lines
4
5
6
7
7
8
9
10
3 unmodified lines
"fmt"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
// NormalizeMapping validates and normalizes a single ref mapping.
Minternal/planner/mapping.go+1/-1
4 unmodified lines
5
6
7
8
9
10
8
9
10
11
12
13
4 unmodified lines
"fmt"
"sort"
// PlanConfig holds configuration for plan generation.
Minternal/planner/planner.go+3/-3
5 unmodified lines
6
7
8
9
10
11
12
13
14
9
10
11
12
13
14
15
16
17
5 unmodified lines
"testing"
"time"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/storage/memory"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/object"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/storage/memory"
)
func TestSelectBranches(t *testing.T) {
Minternal/planner/planner_test.go+6/-6
1
2
3
4
5
6
4
5
6
7
8
9
package planner
import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
)
// CanBootstrapRelay checks whether all desired target refs are absent on the target,
Minternal/planner/relay.go+3/-3
5 unmodified lines
6
7
8
9
9
10
11
12
5 unmodified lines
"sort"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
// RefKind distinguishes branch refs from tag refs.
Minternal/planner/types.go+1/-1
14 unmodified lines
15
16
17
18
19
20
21
22
18
19
20
21
22
23
24
25
491 unmodified lines
517
518
519
520
520
521
522
523
523
524
525
526
14 unmodified lines
"strconv"
"strings"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/storage/memory"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/storage/memory"
"github.com/soph/git-sync/internal/convert"
"github.com/soph/git-sync/internal/gitproto"
491 unmodified lines
return "", "", false
}
ep := conn.Endpoint
if ep.Protocol != "http" && ep.Protocol != "https" {
if ep.Scheme != "http" && ep.Scheme != "https" {
return "", "", false
}
if !strings.EqualFold(ep.Host, "github.com") {
if !strings.EqualFold(ep.Hostname(), "github.com") {
return "", "", false
}
path := strings.TrimSuffix(strings.Trim(ep.Path, "/"), ".git")
Minternal/strategy/bootstrap/bootstrap.go+7/-7
8 unmodified lines
9
10
11
12
13
14
12
13
14
15
16
17
8 unmodified lines
"net/http/httptest"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/plumbing/transport"
"github.com/soph/git-sync/internal/gitproto"
"github.com/soph/git-sync/internal/planner"
Minternal/strategy/bootstrap/bootstrap_test.go+3/-3
7 unmodified lines
8
9
10
11
11
12
13
14
7 unmodified lines
"fmt"
"io"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/convert"
"github.com/soph/git-sync/internal/gitproto"
Minternal/strategy/incremental/incremental.go+1/-1
6 unmodified lines
7
8
9
10
10
11
12
13
6 unmodified lines
"io"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/convert"
"github.com/soph/git-sync/internal/gitproto"
Minternal/strategy/incremental/incremental_test.go+1/-1
6 unmodified lines
7
8
9
10
11
12
10
11
12
13
14
15
6 unmodified lines
"context"
"fmt"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/soph/git-sync/internal/convert"
"github.com/soph/git-sync/internal/gitproto"
Minternal/strategy/materialized/materialized.go+3/-3
2 unmodified lines
3
4
5
6
6
7
8
9
2 unmodified lines
import (
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/convert"
"github.com/soph/git-sync/internal/gitproto"
Minternal/strategy/materialized/materialized_test.go+1/-1
10 unmodified lines
11
12
13
14
15
14
15
16
17
18
62 unmodified lines
81
82
83
84
85
86
84
85
86
49 unmodified lines
136
137
138
142
143
144
139
140
141
31 unmodified lines
173
174
175
182
183
184
10 unmodified lines
"testing"
"time"
"github.com/go-git/go-git/v5/plumbing/transport"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
"github.com/soph/git-sync/internal/auth"
)
62 unmodified lines
t.Fatalf("new endpoint: %v", err)
}
credHost := ep.Host
if ep.Port > 0 {
credHost = ep.Host + ":8080"
}
writeEntireDBHostsFile(t, configDir, credHost, "test-user")
// Store token with a future expiration so it's not treated as expired (issue #7).
futureExpiry := time.Now().Unix() + 3600
49 unmodified lines
t.Fatalf("new endpoint: %v", err)
}
credHost := ep.Host
if ep.Port > 0 {
credHost = ep.Host + ":" + itoa(ep.Port)
}
writeEntireDBHostsFile(t, configDir, credHost, "test-user")
// Expired token: expiration in the past
if err := auth.WriteStoredToken("entire:"+credHost, "test-user", "expired-token|1"); err != nil {
31 unmodified lines
}
}
func itoa(n int) string {
return fmt.Sprintf("%d", n)
}
Minternal/syncer/auth_test.go+2/-11
6 unmodified lines
7
8
9
10
11
12
13
14
15
10
11
12
13
14
15
16
17
18
7 unmodified lines
26
27
28
29
29
30
31
32
23 unmodified lines
56
57
58
59
59
60
61
62
31 unmodified lines
94
95
96
97
97
98
99
100
31 unmodified lines
132
133
134
135
135
136
137
138
6 unmodified lines
"testing"
"time"
billy "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/storage/memory"
billy "github.com/go-git/go-billy/v6"
"github.com/go-git/go-billy/v6/memfs"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/storage/memory"
)
func BenchmarkRunBootstrapEmptyTarget(b *testing.B) {
7 unmodified lines
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
b.Fatalf("init target repo: %v", err)
}
23 unmodified lines
sourceRepo, sourceFS := newBenchRepo(b)
makeBenchCommits(b, sourceRepo, sourceFS, 2)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
b.Fatalf("init target repo: %v", err)
}
31 unmodified lines
sourceRepo, sourceFS := newBenchRepo(b)
makeBenchCommits(b, sourceRepo, sourceFS, 3)
func newBenchRepo(b *testing.B) (*git.Repository, billy.Filesystem) {
b.Helper()
fs := memfs.New()
repo, err := git.Init(memory.NewStorage(), fs)
repo, err := git.Init(memory.NewStorage(), git.WithWorkTree(fs))
if err != nil {
b.Fatalf("init repo: %v", err)
}
Minternal/syncer/benchmark_test.go+10/-10
14 unmodified lines
15
16
17
18
18
19
20
21
14 unmodified lines
"sync"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
"github.com/soph/git-sync/internal/gitproto"
"github.com/soph/git-sync/internal/planner"
bstrap "github.com/soph/git-sync/internal/strategy/bootstrap"
Minternal/syncer/git_http_backend_test.go+1/-1
14 unmodified lines
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
6 unmodified lines
41
42
43
45
44
45
46
47
40 unmodified lines
88
89
90
92
91
92
93
94
33 unmodified lines
128
129
130
132
131
132
133
134
66 unmodified lines
201
202
203
205
204
205
206
207
68 unmodified lines
276
277
278
280
279
280
281
282
61 unmodified lines
344
345
346
348
347
348
349
350
42 unmodified lines
393
394
395
397
396
397
398
399
46 unmodified lines
446
447
448
450
449
450
451
452
12 unmodified lines
465
466
467
469
468
469
470
471
55 unmodified lines
527
528
529
531
530
531
532
533
29 unmodified lines
563
564
565
567
566
567
568
569
44 unmodified lines
614
615
616
618
617
618
619
620
24 unmodified lines
645
646
647
649
648
649
650
651
24 unmodified lines
676
677
678
680
679
680
681
682
96 unmodified lines
779
780
781
783
782
783
784
785
34 unmodified lines
820
821
822
824
823
824
825
826
41 unmodified lines
868
869
870
872
871
872
873
874
81 unmodified lines
956
957
958
960
959
960
961
962
140 unmodified lines
1103
1104
1105
1107
1106
1107
1108
1109
43 unmodified lines
1153
1154
1155
1157
1156
1157
1158
1159
51 unmodified lines
1211
1212
1213
1215
1214
1215
1216
1217
107 unmodified lines
1325
1326
1327
1329
1330
1328
1329
1330
1331
1332
19 unmodified lines
1352
1353
1354
1356
1355
1356
1357
1358
134 unmodified lines
1493
1494
1495
1497
1498
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1503
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1531
1515
1516
1517
1518
1 unmodified line
1520
1521
1522
1539
1523
1524
1525
1 unmodified line
1527
1528
1529
1547
1530
1531
1532
1533
1534
1552
1535
1536
1537
1538
18 unmodified lines
1557
1558
1559
1577
1578
1579
1580
1581
1560
1561
1562
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1563
1596
1564
1565
1566
1567
1568
1569
1570
1571
3 unmodified lines
1575
1576
1577
1606
1578
1579
1580
1581
28 unmodified lines
1610
1611
1612
1641
1613
1643
1614
1615
1616
1617
1618
1648
1619
1620
1621
1622
32 unmodified lines
1655
1656
1657
1687
1688
1658
1659
1660
1661
3 unmodified lines
1665
1666
1667
1698
1668
1669
1670
1671
1672
1703
1673
1674
1675
1676
35 unmodified lines
1712
1713
1714
1745
1746
1747
1748
1749
1715
1716
1751
1752
1753
1754
1755
1717
1718
1719
1720
1721
1722
1723
1724
1725
1757
1726
1727
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1728
1729
1730
1731
1 unmodified line
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1780
1748
1749
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1750
1751
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1752
1753
1754
1755
1809
1810
1756
1812
1813
1814
1815
1816
1817
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1823
1767
1768
1769
1770
1771
1772
1773
1830
1831
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1833
1834
1835
1790
1791
1792
1793
1794
1837
1795
1796
1797
1798
1839
1840
1841
1842
1843
1844
1845
1846
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
89 unmodified lines
1903
1904
1905
1945
1946
1906
1907
1908
1948
1949
1950
1909
1910
1911
1912
1913
1914
1954
1955
1915
1916
1917
1918
1919
1920
1921
1922
1923
14 unmodified lines
"testing"
"time"
billy "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/packfile"
"github.com/go-git/go-git/v5/plumbing/format/pktline"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/revlist"
"github.com/go-git/go-git/v5/plumbing/transport"
transportclient "github.com/go-git/go-git/v5/plumbing/transport/client"
transporthttp "github.com/go-git/go-git/v5/plumbing/transport/http"
transportserver "github.com/go-git/go-git/v5/plumbing/transport/server"
"github.com/go-git/go-git/v5/storage/memory"
billy "github.com/go-git/go-billy/v6"
"github.com/go-git/go-billy/v6/memfs"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/format/packfile"
"github.com/go-git/go-git/v6/plumbing/format/pktline"
"github.com/go-git/go-git/v6/plumbing/object"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v6/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v6/plumbing/revlist"
"github.com/go-git/go-git/v6/plumbing/transport"
transporthttp "github.com/go-git/go-git/v6/plumbing/transport/http"
"github.com/go-git/go-git/v6/storage/memory"
"github.com/soph/git-sync/internal/auth"
"github.com/soph/git-sync/internal/gitproto"
"github.com/soph/git-sync/internal/planner"
6 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 6)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
40 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeLargeCommits(t, sourceRepo, sourceFS, 20, 200_000)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
33 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 2)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
66 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 4)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
68 unmodified lines
t.Fatalf("resolve unrelated head: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
61 unmodified lines
t.Fatalf("resolve source head: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
42 unmodified lines
t.Fatalf("resolve source head: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
46 unmodified lines
t.Fatalf("resolve source head: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
12 unmodified lines
defer targetServer.Close()
failDeleteOnce := true
targetServer.commandHook = func(req *packp.ReferenceUpdateRequest) *packp.ReportStatus {
targetServer.commandHook = func(req *packp.UpdateRequests) *packp.ReportStatus {
if !failDeleteOnce || len(req.Commands) != 1 {
return nil
}
55 unmodified lines
t.Fatalf("set lightweight tag: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
29 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 3)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
44 unmodified lines
t.Fatalf("set source tag: %v", err)
}
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
24 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 2)
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 10)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
96 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 3)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
34 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 2)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
41 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 4)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
81 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 1)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
140 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 2)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
43 unmodified lines
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 4)
targetRepo, err := git.Init(memory.NewStorage(), nil)
targetRepo, err := git.Init(memory.NewStorage())
if err != nil {
t.Fatalf("init target repo: %v", err)
}
51 unmodified lines
t.Helper()
fs := memfs.New()
repo, err := git.Init(memory.NewStorage(), fs)
repo, err := git.Init(memory.NewStorage(), git.WithWorkTree(fs))
if err != nil {
t.Fatalf("init source repo: %v", err)
}
107 unmodified lines
type metricKind string
const (
serviceUploadPack = transport.UploadPackServiceName
serviceReceivePack = transport.ReceivePackServiceName
serviceUploadPack = string(transport.UploadPackService)
serviceReceivePack = string(transport.ReceivePackService)
metricInfoRefs metricKind = "info_refs"
metricPack metricKind = "pack"
19 unmodified lines
receivePackBodyLimit int64
receivePackNoThin bool
commandHook func(*packp.ReferenceUpdateRequest) *packp.ReportStatus
commandHook func(*packp.UpdateRequests) *packp.ReportStatus
mu sync.Mutex
metrics []exchangeMetric
134 unmodified lines
return
}
session, err := s.newSession(service)
if err != nil {
var buf bytes.Buffer
svc := transport.Service(service)
if err := transport.AdvertiseReferences(r.Context(), s.repo.Storer, &buf, svc, false); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var ar *packp.AdvRefs
switch service {
case serviceUploadPack:
ar, err = session.(transport.UploadPackSession).AdvertisedReferencesContext(r.Context())
case serviceReceivePack:
ar, err = session.(transport.ReceivePackSession).AdvertisedReferencesContext(r.Context())
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if service == serviceReceivePack && s.receivePackNoThin {
_ = ar.Capabilities.Set(capability.Capability("no-thin"))
}
ar.Prefix = [][]byte{
[]byte("# service=" + service),
pktline.Flush,
}
var buf bytes.Buffer
if err := ar.Encode(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
// For no-thin, we need to decode, modify capabilities, and re-encode
ar := packp.NewAdvRefs()
if err := ar.Decode(bytes.NewReader(buf.Bytes())); err == nil {
_ = ar.Capabilities.Set(capability.Capability("no-thin"))
buf.Reset()
_ = ar.Encode(&buf)
}
}
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", service))
if _, err := w.Write(buf.Bytes()); err != nil {
s.tb.Fatalf("write advertised refs: %v", err)
s.tb.Fatalf("write advertised refs: %v", err)
}
s.recordMetric(service, metricInfoRefs, 0, int64(buf.Len()), 0, 0)
1 unmodified line
func (s *smartHTTPRepoServer) handleInfoRefsV2(w http.ResponseWriter, r *http.Request) {
var buf bytes.Buffer
enc := pktline.NewEncoder(&buf)
lines := []string{
"version 2\n",
"ls-refs=unborn\n",
1 unmodified line
"agent=test-server\n",
}
for _, line := range lines {
if err := enc.EncodeString(line); err != nil {
if _, err := pktline.WriteString(&buf, line); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
if err := enc.Flush(); err != nil {
if err := pktline.WriteFlush(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
18 unmodified lines
return
}
session, err := s.newSession(serviceUploadPack)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
wantCount := strings.Count(string(body), "want ")
haveCount := strings.Count(string(body), "have ")
req := packp.NewUploadPackRequest()
if err := req.Decode(bytes.NewReader(body)); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
var buf bytes.Buffer
if err := resp.Encode(&buf); err != nil {
reader := io.NopCloser(bytes.NewReader(body))
writer := nopWriteCloser{&buf}
if err := transport.UploadPack(r.Context(), s.repo.Storer, reader, writer, &transport.UploadPackOptions{
StatelessRPC: true,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
3 unmodified lines
s.tb.Fatalf("write upload-pack response: %v", err)
}
s.recordMetric(serviceUploadPack, metricPack, int64(len(body)), int64(buf.Len()), len(req.Wants), strings.Count(string(body), "have "))
s.recordMetric(serviceUploadPack, metricPack, int64(len(body)), int64(buf.Len()), wantCount, haveCount)
}
func (s *smartHTTPRepoServer) handleUploadPackV2(w http.ResponseWriter, r *http.Request, body []byte) {
28 unmodified lines
}
var buf bytes.Buffer
enc := pktline.NewEncoder(&buf)
for _, ref := range refs {
if err := enc.EncodeString(ref.Hash().String() + " " + ref.Name().String() + "\n"); err != nil {
if _, err := pktline.WriteString(&buf, ref.Hash().String()+" "+ref.Name().String()+"\n"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
if err := enc.Flush(); err != nil {
if err := pktline.WriteFlush(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
32 unmodified lines
}
var buf bytes.Buffer
pkt := pktline.NewEncoder(&buf)
if err := pkt.EncodeString("packfile\n"); err != nil {
if _, err := pktline.WriteString(&buf, "packfile\n"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
3 unmodified lines
end = pack.Len()
}
payload := append([]byte{1}, pack.Bytes()[offset:end]...)
if err := pkt.Encode(payload); err != nil {
if _, err := pktline.Write(&buf, payload); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
if err := pkt.Flush(); err != nil {
if err := pktline.WriteFlush(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
35 unmodified lines
return
}
session, err := s.newSession(serviceReceivePack)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
hasPack := bytes.Contains(body, []byte("PACK"))
req := packp.NewReferenceUpdateRequest()
if err := req.Decode(bytes.NewReader(body)); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// For no-PACK requests, handle manually since transport.ReceivePack
// expects a packfile when there are create/update commands.
if !hasPack {
req := packp.NewUpdateRequests()
if err := req.Decode(bytes.NewReader(body)); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !bytes.Contains(body, []byte("PACK")) {
if s.commandHook != nil {
if report := s.commandHook(req); report != nil {
var buf bytes.Buffer
if err := report.Encode(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", serviceReceivePack))
if _, err := w.Write(buf.Bytes()); err != nil {
s.tb.Fatalf("write receive-pack command hook response: %v", err)
}
s.recordMetric(serviceReceivePack, metricPack, int64(len(body)), int64(buf.Len()), 0, 0)
s.writeReceivePackReport(w, report, req.Capabilities, len(body))
return
}
}
1 unmodified line
report := packp.NewReportStatus()
report.UnpackStatus = "ok"
for _, cmd := range req.Commands {
status := "ok"
if cmd.New.IsZero() {
if err := s.repo.Storer.RemoveReference(cmd.Name); err != nil {
status = err.Error()
}
} else {
if err := s.repo.Storer.SetReference(plumbing.NewHashReference(cmd.Name, cmd.New)); err != nil {
status = err.Error()
}
}
report.CommandStatuses = append(report.CommandStatuses, &packp.CommandStatus{
ReferenceName: cmd.Name,
Status: "ok",
Status: status,
})
if cmd.New.IsZero() {
if err := s.repo.Storer.RemoveReference(cmd.Name); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
continue
}
if err := s.repo.Storer.SetReference(plumbing.NewHashReference(cmd.Name, cmd.New)); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
var buf bytes.Buffer
if err := report.Encode(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", serviceReceivePack))
if _, err := w.Write(buf.Bytes()); err != nil {
s.tb.Fatalf("write receive-pack command response: %v", err)
}
s.recordMetric(serviceReceivePack, metricPack, int64(len(body)), int64(buf.Len()), 0, 0)
s.writeReceivePackReport(w, report, req.Capabilities, len(body))
return
}
report, err := session.(transport.ReceivePackSession).ReceivePack(r.Context(), req)
var buf bytes.Buffer
if report != nil {
if err := report.Encode(&buf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
reader := io.NopCloser(bytes.NewReader(body))
writer := nopWriteCloser{&buf}
rpErr := transport.ReceivePack(r.Context(), s.repo.Storer, reader, writer, &transport.ReceivePackOptions{
StatelessRPC: true,
})
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", serviceReceivePack))
if _, err := w.Write(buf.Bytes()); err != nil {
s.tb.Fatalf("write receive-pack response: %v", err)
}
if err != nil {
if rpErr != nil {
return
}
s.recordMetric(serviceReceivePack, metricPack, int64(len(body)), int64(buf.Len()), 0, 0)
}
func (s *smartHTTPRepoServer) newSession(service string) (interface{}, error) {
loader := transportserver.MapLoader{}
// writeReceivePackReport encodes a report-status, optionally wrapped in
// sideband (matching what transport.ReceivePack would produce), and writes it.
func (s *smartHTTPRepoServer) writeReceivePackReport(w http.ResponseWriter, report *packp.ReportStatus, caps *capability.List, bodyLen int) {
var buf bytes.Buffer
var writer io.Writer = &buf
useSideband := false
if caps != nil && !caps.Supports(capability.NoProgress) {
if caps.Supports(capability.Sideband64k) {
writer = sideband.NewMuxer(sideband.Sideband64k, &buf)
useSideband = true
} else if caps.Supports(capability.Sideband) {
writer = sideband.NewMuxer(sideband.Sideband, &buf)
useSideband = true
}
}
endpoint, err := transport.NewEndpoint(s.RepoURL())
if err != nil {
return nil, err
wc := nopWriteCloser{writer}
if err := report.Encode(wc); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
loader[endpoint.String()] = s.repo.Storer
if useSideband {
_ = pktline.WriteFlush(&buf)
}
srv := transportserver.NewServer(loader)
switch service {
case serviceUploadPack:
return srv.NewUploadPackSession(endpoint, nil)
case serviceReceivePack:
return srv.NewReceivePackSession(endpoint, nil)
default:
return nil, fmt.Errorf("unknown service %q", service)
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", serviceReceivePack))
if _, err := w.Write(buf.Bytes()); err != nil {
s.tb.Fatalf("write receive-pack report: %v", err)
}
s.recordMetric(serviceReceivePack, metricPack, int64(bodyLen), int64(buf.Len()), 0, 0)
}
// nopWriteCloser wraps an io.Writer to satisfy io.WriteCloser.
type nopWriteCloser struct{ io.Writer }
func (nopWriteCloser) Close() error { return nil }
func (s *smartHTTPRepoServer) recordMetric(service string, kind metricKind, in, out int64, wants, haves int) {
s.mu.Lock()
defer s.mu.Unlock()
89 unmodified lines
}
func TestMain(m *testing.M) {
originalHTTP := transportclient.Protocols["http"]
originalHTTPS := transportclient.Protocols["https"]
originalHTTP, _ := transport.Get("http")
originalHTTPS, _ := transport.Get("https")
customHTTP := transporthttp.NewClient(&http.Client{})
transportclient.InstallProtocol("http", customHTTP)
transportclient.InstallProtocol("https", customHTTP)
customHTTP := transporthttp.NewTransport(&transporthttp.TransportOptions{Client: &http.Client{}})
transport.Register("http", customHTTP)
transport.Register("https", customHTTP)
code := m.Run()
transportclient.InstallProtocol("http", originalHTTP)
transportclient.InstallProtocol("https", originalHTTPS)
if originalHTTP != nil {
transport.Register("http", originalHTTP)
}
if originalHTTPS != nil {
transport.Register("https", originalHTTPS)
}
os.Exit(code)
}
Minternal/syncer/integration_test.go+134/-169
6 unmodified lines
7
8
9
10
10
11
12
13
6 unmodified lines
"path/filepath"
"testing"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v6/plumbing"
)
const liveLinuxEnv = "GITSYNC_E2E_LIVE_LINUX"
Minternal/syncer/live_bootstrap_test.go+1/-1
12 unmodified lines
13
14
15
16
17
18
19
20
21
16
17
18
19
20
21
22
23
24
329 unmodified lines
354
355
356
357
357
358
359
360
12 unmodified lines
"sort"
"strings"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/storage/memory"
git "github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/plumbing"
"github.com/go-git/go-git/v6/plumbing/protocol/packp"
"github.com/go-git/go-git/v6/plumbing/storer"
"github.com/go-git/go-git/v6/plumbing/transport"
"github.com/go-git/go-git/v6/storage/memory"
"github.com/soph/git-sync/internal/auth"
"github.com/soph/git-sync/internal/convert"
329 unmodified lines
if err != nil {
return nil, fmt.Errorf("create target transport: %w", err)
}
s.targetAdv, err = gitproto.AdvertisedRefsV1(ctx, s.targetConn, transport.ReceivePackServiceName)
s.targetAdv, err = gitproto.AdvertisedRefsV1(ctx, s.targetConn, transport.ReceivePackService)
if err != nil {
return nil, fmt.Errorf("list target refs: %w", err)
}
Minternal/syncer/syncer.go+7/-7