diff --git a/options.go b/options.go index 70687965b..6fafb8e0e 100644 --- a/options.go +++ b/options.go @@ -37,6 +37,9 @@ var ( type CloneOptions struct { // The (possibly remote) repository URL to clone from. URL string + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // Name of the remote to be added, by default `origin`. @@ -97,6 +100,9 @@ type PullOptions struct { SingleBranch bool // Limit fetching to the specified number of commits. Depth int + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // RecurseSubmodules controls if new commits of all populated submodules @@ -151,6 +157,9 @@ type FetchOptions struct { // Depth limit fetching to the specified number of commits from the tip of // each remote branch history. Depth int + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // Progress is where the human readable information sent by the server is @@ -195,6 +204,9 @@ type PushOptions struct { // RefSpecs specify what destination ref to update with what source // object. A refspec with empty src can be used to delete a reference. RefSpecs []config.RefSpec + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // Progress is where the human readable information sent by the server is @@ -247,6 +259,9 @@ type SubmoduleUpdateOptions struct { // the current repository but also in any nested submodules inside those // submodules (and so on). Until the SubmoduleRescursivity is reached. RecurseSubmodules SubmoduleRescursivity + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod } @@ -569,6 +584,9 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error { // ListOptions describes how a remote list should be performed. type ListOptions struct { + // Specify the transport to use. If nil, a default transport will + // be selected based on the URL's protocol. + Transport transport.Transport // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // InsecureSkipTLS skips ssl verify if protocal is https diff --git a/remote.go b/remote.go index 4a06106c5..8e7930bc9 100644 --- a/remote.go +++ b/remote.go @@ -103,7 +103,7 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) { return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name) } - s, err := newSendPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle) + s, err := newSendPackSession(r.c.URLs[0], o.Transport, o.Auth, o.InsecureSkipTLS, o.CABundle) if err != nil { return err } @@ -314,7 +314,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen o.RefSpecs = r.c.Fetch } - s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle) + s, err := newUploadPackSession(r.c.URLs[0], o.Transport, o.Auth, o.InsecureSkipTLS, o.CABundle) if err != nil { return nil, err } @@ -411,8 +411,8 @@ func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) { return false, nil } -func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.UploadPackSession, error) { - c, ep, err := newClient(url, auth, insecure, cabundle) +func newUploadPackSession(url string, trans transport.Transport, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.UploadPackSession, error) { + c, ep, err := newClient(url, trans, auth, insecure, cabundle) if err != nil { return nil, err } @@ -420,8 +420,8 @@ func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, return c.NewUploadPackSession(ep, auth) } -func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.ReceivePackSession, error) { - c, ep, err := newClient(url, auth, insecure, cabundle) +func newSendPackSession(url string, trans transport.Transport, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.ReceivePackSession, error) { + c, ep, err := newClient(url, trans, auth, insecure, cabundle) if err != nil { return nil, err } @@ -429,7 +429,7 @@ func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, ca return c.NewReceivePackSession(ep, auth) } -func newClient(url string, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.Transport, *transport.Endpoint, error) { +func newClient(url string, trans transport.Transport, auth transport.AuthMethod, insecure bool, cabundle []byte) (transport.Transport, *transport.Endpoint, error) { ep, err := transport.NewEndpoint(url) if err != nil { return nil, nil, err @@ -437,12 +437,14 @@ func newClient(url string, auth transport.AuthMethod, insecure bool, cabundle [] ep.InsecureSkipTLS = insecure ep.CaBundle = cabundle - c, err := client.NewClient(ep) - if err != nil { - return nil, nil, err + if trans == nil { + trans, err = client.NewClient(ep) + if err != nil { + return nil, nil, err + } } - return c, ep, err + return trans, ep, err } func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.UploadPackSession, @@ -1091,7 +1093,7 @@ func (r *Remote) List(o *ListOptions) (rfs []*plumbing.Reference, err error) { } func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Reference, err error) { - s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle) + s, err := newUploadPackSession(r.c.URLs[0], o.Transport, o.Auth, o.InsecureSkipTLS, o.CABundle) if err != nil { return nil, err } diff --git a/remote_test.go b/remote_test.go index 1efc9da70..013c9c670 100644 --- a/remote_test.go +++ b/remote_test.go @@ -14,6 +14,8 @@ import ( "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/v5/plumbing/transport/client" "github.com/go-git/go-git/v5/storage" "github.com/go-git/go-git/v5/storage/filesystem" "github.com/go-git/go-git/v5/storage/memory" @@ -336,6 +338,47 @@ func (s *RemoteSuite) TestFetchWithPackfileWriter(c *C) { c.Assert(mock.PackfileWriterCalled, Equals, true) } +type mockTransport struct { + transport.Transport + NewUploadPackSessionCalled bool + NewReceivePackSessionCalled bool +} + +func newMockTransport(url string) *mockTransport { + ep, _ := transport.NewEndpoint(url) + t, _ := client.NewClient(ep) + return &mockTransport{Transport: t} +} + +func (t *mockTransport) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) { + t.NewUploadPackSessionCalled = true + return t.Transport.NewUploadPackSession(ep, auth) +} + +func (t *mockTransport) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) { + t.NewReceivePackSessionCalled = true + return t.Transport.NewReceivePackSession(ep, auth) +} + +func (s *RemoteSuite) TestFetchWithTransport(c *C) { + url := s.GetLocalRepositoryURL(fixtures.ByTag("tags").One()) + mock := newMockTransport(url) + + r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ + URLs: []string{url}, + }) + + err := r.Fetch(&FetchOptions{ + RefSpecs: []config.RefSpec{ + config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), + }, + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDate(c *C) { url := s.GetBasicLocalRepositoryURL() s.doTestFetchNoErrAlreadyUpToDate(c, url) @@ -562,6 +605,32 @@ func (s *RemoteSuite) TestPushContextCanceled(c *C) { c.Assert(runtime.NumGoroutine(), Equals, numGoroutines) } +func (s *RemoteSuite) TestPushWithTransport(c *C) { + url, clean := s.TemporalDir() + defer clean() + + _, err := PlainInit(url, true) + c.Assert(err, IsNil) + + fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() + sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) + + mock := newMockTransport(url) + + r := NewRemote(sto, &config.RemoteConfig{ + Name: DefaultRemoteName, + URLs: []string{url}, + }) + + err = r.Push(&PushOptions{ + RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}, + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewReceivePackSessionCalled, Equals, true) +} + func (s *RemoteSuite) TestPushTags(c *C) { url, clean := s.TemporalDir() defer clean() @@ -1007,6 +1076,20 @@ func (s *RemoteSuite) TestListTimeout(c *C) { c.Assert(err, NotNil) } +func (s *RemoteSuite) TestListWithTransport(c *C) { + repo := fixtures.Basic().One() + remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{ + Name: DefaultRemoteName, + URLs: []string{repo.URL}, + }) + + mock := newMockTransport(repo.URL) + + _, err := remote.List(&ListOptions{Transport: mock}) + c.Assert(err, IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *RemoteSuite) TestUpdateShallows(c *C) { hashes := []plumbing.Hash{ plumbing.NewHash("0000000000000000000000000000000000000001"), diff --git a/repository.go b/repository.go index d3fbf9759..5bf6e707a 100644 --- a/repository.go +++ b/repository.go @@ -824,6 +824,7 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error { ref, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{ RefSpecs: c.Fetch, Depth: o.Depth, + Transport: o.Transport, Auth: o.Auth, Progress: o.Progress, Tags: o.Tags, diff --git a/repository_test.go b/repository_test.go index 2bc5c902c..0b8186df8 100644 --- a/repository_test.go +++ b/repository_test.go @@ -189,6 +189,19 @@ func (s *RepositorySuite) TestCloneContext(c *C) { c.Assert(err, Equals, context.Canceled) } +func (s *RepositorySuite) TestCloneWithTransport(c *C) { + url := s.GetBasicLocalRepositoryURL() + mock := newMockTransport(url) + + _, err := Clone(memory.NewStorage(), nil, &CloneOptions{ + URL: url, + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *RepositorySuite) TestCloneWithTags(c *C) { url := s.GetLocalRepositoryURL( fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(), @@ -882,6 +895,21 @@ func (s *RepositorySuite) TestFetchContext(c *C) { c.Assert(r.FetchContext(ctx, &FetchOptions{}), NotNil) } +func (s *RepositorySuite) TestFetchWithTransport(c *C) { + url := s.GetBasicLocalRepositoryURL() + mock := newMockTransport(url) + + r, _ := Init(memory.NewStorage(), nil) + _, err := r.CreateRemote(&config.RemoteConfig{ + Name: DefaultRemoteName, + URLs: []string{url}, + }) + c.Assert(err, IsNil) + + c.Assert(r.Fetch(&FetchOptions{Transport: mock}), IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *RepositorySuite) TestCloneWithProgress(c *C) { fs := memfs.New() @@ -1226,6 +1254,30 @@ func (s *RepositorySuite) TestPushContext(c *C) { c.Assert(err, NotNil) } +func (s *RepositorySuite) TestPushWithTransport(c *C) { + url, clean := s.TemporalDir() + defer clean() + + _, err := PlainInit(url, true) + c.Assert(err, IsNil) + + _, err = s.Repository.CreateRemote(&config.RemoteConfig{ + Name: "test2", + URLs: []string{url}, + }) + c.Assert(err, IsNil) + + mock := newMockTransport(url) + + err = s.Repository.Push(&PushOptions{ + RemoteName: "test2", + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewReceivePackSessionCalled, Equals, true) +} + // installPreReceiveHook installs a pre-receive hook in the .git // directory at path which prints message m before exiting // successfully. diff --git a/submodule.go b/submodule.go index a202a9b60..f54fcb442 100644 --- a/submodule.go +++ b/submodule.go @@ -243,7 +243,10 @@ func (s *Submodule) fetchAndCheckout( ctx context.Context, r *Repository, o *SubmoduleUpdateOptions, hash plumbing.Hash, ) error { if !o.NoFetch { - err := r.FetchContext(ctx, &FetchOptions{Auth: o.Auth}) + err := r.FetchContext(ctx, &FetchOptions{ + Transport: o.Transport, + Auth: o.Auth, + }) if err != nil && err != NoErrAlreadyUpToDate { return err } @@ -263,8 +266,9 @@ func (s *Submodule) fetchAndCheckout( refSpec := config.RefSpec("+" + hash.String() + ":" + hash.String()) err := r.FetchContext(ctx, &FetchOptions{ - Auth: o.Auth, - RefSpecs: []config.RefSpec{refSpec}, + Transport: o.Transport, + Auth: o.Auth, + RefSpecs: []config.RefSpec{refSpec}, }) if err != nil && err != NoErrAlreadyUpToDate && err != ErrExactSHA1NotSupported { return err diff --git a/submodule_test.go b/submodule_test.go index 4bae544d1..5e893c847 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -91,6 +91,25 @@ func (s *SubmoduleSuite) TestUpdate(c *C) { c.Assert(status.IsClean(), Equals, true) } +func (s *SubmoduleSuite) TestUpdateWithTransport(c *C) { + if testing.Short() { + c.Skip("skipping test in short mode.") + } + + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + mock := newMockTransport(sm.Config().URL) + + err = sm.Update(&SubmoduleUpdateOptions{ + Init: true, + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *SubmoduleSuite) TestRepositoryWithoutInit(c *C) { sm, err := s.Worktree.Submodule("basic") c.Assert(err, IsNil) diff --git a/worktree.go b/worktree.go index f23d9f170..8ad9adafe 100644 --- a/worktree.go +++ b/worktree.go @@ -74,6 +74,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { fetchHead, err := remote.fetch(ctx, &FetchOptions{ RemoteName: o.RemoteName, Depth: o.Depth, + Transport: o.Transport, Auth: o.Auth, Progress: o.Progress, Force: o.Force, diff --git a/worktree_test.go b/worktree_test.go index 79cbefd77..02526ae00 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -239,6 +239,28 @@ func (s *WorktreeSuite) TestPullProgressWithRecursion(c *C) { c.Assert(cfg.Submodules, HasLen, 2) } +func (s *WorktreeSuite) TestPullWithTransport(c *C) { + url := s.GetBasicLocalRepositoryURL() + mock := newMockTransport(url) + + r, _ := Init(memory.NewStorage(), memfs.New()) + + r.CreateRemote(&config.RemoteConfig{ + Name: DefaultRemoteName, + URLs: []string{url}, + }) + + w, err := r.Worktree() + c.Assert(err, IsNil) + + err = w.Pull(&PullOptions{ + Transport: mock, + }) + + c.Assert(err, IsNil) + c.Assert(mock.NewUploadPackSessionCalled, Equals, true) +} + func (s *RepositorySuite) TestPullAdd(c *C) { path := fixtures.Basic().ByTag("worktree").One().Worktree().Root()