From 320db9af8ba8b0046e833013504eb07c61a3573c Mon Sep 17 00:00:00 2001 From: Marcus Watkins Date: Wed, 12 May 2021 14:39:49 -0600 Subject: [PATCH] git: Add support for deepening shallow clones (#311) --- plumbing/transport/internal/common/common.go | 2 +- remote.go | 44 +++++++++++++++++++- remote_test.go | 26 ++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go index 75405c72f..fdb148f59 100644 --- a/plumbing/transport/internal/common/common.go +++ b/plumbing/transport/internal/common/common.go @@ -233,7 +233,7 @@ func (s *session) handleAdvRefDecodeError(err error) error { // UploadPack performs a request to the server to fetch a packfile. A reader is // returned with the packfile content. The reader must be closed after reading. func (s *session) UploadPack(ctx context.Context, req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) { - if req.IsEmpty() { + if req.IsEmpty() && len(req.Shallows) == 0 { return nil, transport.ErrEmptyUploadPackRequest } diff --git a/remote.go b/remote.go index 47af638ec..ca82a3d1a 100644 --- a/remote.go +++ b/remote.go @@ -350,6 +350,13 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen return nil, err } + if !req.Depth.IsZero() { + req.Shallows, err = r.s.Shallow() + if err != nil { + return nil, fmt.Errorf("existing checkout is not shallow") + } + } + req.Wants, err = getWants(r.s, refs) if len(req.Wants) > 0 { req.Haves, err = getHaves(localRefs, remoteRefs, r.s) @@ -367,6 +374,13 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen return nil, err } + if !updated { + updated, err = depthChanged(req.Shallows, r.s) + if err != nil { + return nil, fmt.Errorf("error checking depth change: %v", err) + } + } + if !updated { return remoteRefs, NoErrAlreadyUpToDate } @@ -374,6 +388,29 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen return remoteRefs, nil } +func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) { + after, err := s.Shallow() + if err != nil { + return false, err + } + + if len(before) != len(after) { + return true, nil + } + + bm := make(map[plumbing.Hash]bool, len(before)) + for _, b := range before { + bm[b] = true + } + for _, a := range after { + if _, ok := bm[a]; !ok { + return true, nil + } + } + + 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) if err != nil { @@ -778,6 +815,11 @@ func doCalculateRefs( } func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumbing.Hash, error) { + shallow := false + if s, _ := localStorer.Shallow(); len(s) > 0 { + shallow = true + } + wants := map[plumbing.Hash]bool{} for _, ref := range refs { hash := ref.Hash() @@ -786,7 +828,7 @@ func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumb return nil, err } - if !exists { + if !exists || shallow { wants[hash] = true } } diff --git a/remote_test.go b/remote_test.go index 2cd2a6eeb..1efc9da70 100644 --- a/remote_test.go +++ b/remote_test.go @@ -233,6 +233,32 @@ func (s *RemoteSuite) TestFetchWithDepth(c *C) { c.Assert(r.s.(*memory.Storage).Objects, HasLen, 18) } +func (s *RemoteSuite) TestFetchWithDepthChange(c *C) { + r := NewRemote(memory.NewStorage(), &config.RemoteConfig{ + URLs: []string{s.GetBasicLocalRepositoryURL()}, + }) + + s.testFetch(c, r, &FetchOptions{ + Depth: 1, + RefSpecs: []config.RefSpec{ + config.RefSpec("refs/heads/master:refs/heads/master"), + }, + }, []*plumbing.Reference{ + plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), + }) + c.Assert(r.s.(*memory.Storage).Commits, HasLen, 1) + + s.testFetch(c, r, &FetchOptions{ + Depth: 3, + RefSpecs: []config.RefSpec{ + config.RefSpec("refs/heads/master:refs/heads/master"), + }, + }, []*plumbing.Reference{ + plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"), + }) + c.Assert(r.s.(*memory.Storage).Commits, HasLen, 3) +} + func (s *RemoteSuite) testFetch(c *C, r *Remote, o *FetchOptions, expected []*plumbing.Reference) { err := r.Fetch(o) c.Assert(err, IsNil)