diff options
-rw-r--r-- | clients/common/common.go | 1 | ||||
-rw-r--r-- | clients/common_test.go | 4 | ||||
-rw-r--r-- | clients/http/git_upload_pack.go | 4 | ||||
-rw-r--r-- | common_test.go | 23 | ||||
-rw-r--r-- | config/config.go | 13 | ||||
-rw-r--r-- | config/refspec.go | 110 | ||||
-rw-r--r-- | config/refspec_test.go | 69 | ||||
-rw-r--r-- | core/reference.go | 114 | ||||
-rw-r--r-- | core/reference_test.go | 62 | ||||
-rw-r--r-- | options.go | 38 | ||||
-rw-r--r-- | remote.go | 100 | ||||
-rw-r--r-- | remote_test.go | 10 | ||||
-rw-r--r-- | repository.go | 130 | ||||
-rw-r--r-- | repository_test.go | 12 |
14 files changed, 365 insertions, 325 deletions
diff --git a/clients/common/common.go b/clients/common/common.go index 9099016..d128f3f 100644 --- a/clients/common/common.go +++ b/clients/common/common.go @@ -28,6 +28,7 @@ type GitUploadPackService interface { ConnectWithAuth(AuthMethod) error Info() (*GitUploadPackInfo, error) Fetch(*GitUploadPackRequest) (io.ReadCloser, error) + Disconnect() error } type AuthMethod interface { diff --git a/clients/common_test.go b/clients/common_test.go index 72d8e99..0381f63 100644 --- a/clients/common_test.go +++ b/clients/common_test.go @@ -76,6 +76,10 @@ func (s *dummyProtocolService) Fetch(r *common.GitUploadPackRequest) (io.ReadClo return nil, nil } +func (s *dummyProtocolService) Disconnect() error { + return nil +} + func typeAsString(v interface{}) string { return fmt.Sprintf("%T", v) } diff --git a/clients/http/git_upload_pack.go b/clients/http/git_upload_pack.go index 68ec238..79aec3f 100644 --- a/clients/http/git_upload_pack.go +++ b/clients/http/git_upload_pack.go @@ -134,3 +134,7 @@ func (s *GitUploadPackService) applyAuthToRequest(req *http.Request) { s.auth.setAuth(req) } + +func (s *GitUploadPackService) Disconnect() (err error) { + return nil +} diff --git a/common_test.go b/common_test.go index 3c373ec..386dc74 100644 --- a/common_test.go +++ b/common_test.go @@ -2,6 +2,7 @@ package git import ( "bytes" + "errors" "io" "io/ioutil" "os" @@ -21,27 +22,34 @@ type BaseSuite struct{} func (s *BaseSuite) SetUpTest(c *C) { clients.InstallProtocol("mock", func(end common.Endpoint) common.GitUploadPackService { - return &MockGitUploadPackService{conected: end} + return &MockGitUploadPackService{endpoint: end} }) } const RepositoryFixture = "mock://formats/packfile/fixtures/git-fixture.ref-delta" type MockGitUploadPackService struct { - conected common.Endpoint - auth common.AuthMethod + connected bool + endpoint common.Endpoint + auth common.AuthMethod } func (p *MockGitUploadPackService) Connect() error { + p.connected = true return nil } func (p *MockGitUploadPackService) ConnectWithAuth(auth common.AuthMethod) error { + p.connected = true p.auth = auth return nil } func (p *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) { + if !p.connected { + return nil, errors.New("not connected") + } + h := core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") c := common.NewCapabilities() @@ -62,9 +70,18 @@ func (p *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) { } func (p *MockGitUploadPackService) Fetch(*common.GitUploadPackRequest) (io.ReadCloser, error) { + if !p.connected { + return nil, errors.New("not connected") + } + return os.Open("formats/packfile/fixtures/git-fixture.ref-delta") } +func (p *MockGitUploadPackService) Disconnect() error { + p.connected = false + return nil +} + type packedFixture struct { url string packfile string diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..88172f4 --- /dev/null +++ b/config/config.go @@ -0,0 +1,13 @@ +package config + +type Config interface { + Remote(name string) *RemoteConfig + Remotes() []*RemoteConfig + SetRemote(*RemoteConfig) +} + +type RemoteConfig struct { + Name string + URL string + Fetch RefSpec +} diff --git a/config/refspec.go b/config/refspec.go new file mode 100644 index 0000000..2fe8d22 --- /dev/null +++ b/config/refspec.go @@ -0,0 +1,110 @@ +package config + +import ( + "strings" + + "gopkg.in/src-d/go-git.v4/core" +) + +const ( + refSpecWildcard = "*" + refSpecForce = "+" + refSpecSeparator = ":" +) + +// RefSpec is a mapping from local branches to remote references +// The format of the refspec is an optional +, followed by <src>:<dst>, where +// <src> is the pattern for references on the remote side and <dst> is where +// those references will be written locally. The + tells Git to update the +// reference even if it isn’t a fast-forward. +// eg.: "+refs/heads/*:refs/remotes/origin/*" +// +// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec +type RefSpec string + +// IsValid validates the RefSpec +func (s RefSpec) IsValid() bool { + spec := string(s) + if strings.Count(spec, refSpecSeparator) != 1 { + return false + } + + sep := strings.Index(spec, refSpecSeparator) + if sep == len(spec) { + return false + } + + ws := strings.Count(spec[0:sep], refSpecWildcard) + wd := strings.Count(spec[sep+1:len(spec)], refSpecWildcard) + return ws == wd && ws < 2 && wd < 2 +} + +// IsForceUpdate returns if update is allowed in non fast-forward merges +func (s RefSpec) IsForceUpdate() bool { + if s[0] == refSpecForce[0] { + return true + } + + return false +} + +// Src return the src side +func (s RefSpec) Src() string { + spec := string(s) + start := strings.Index(spec, refSpecForce) + 1 + end := strings.Index(spec, refSpecSeparator) + + return spec[start:end] +} + +// Match match the given core.ReferenceName against the source +func (s RefSpec) Match(n core.ReferenceName) bool { + if !s.isGlob() { + return s.matchExact(n) + } + + return s.matchGlob(n) +} + +func (s RefSpec) isGlob() bool { + return strings.Index(string(s), refSpecWildcard) != -1 +} + +func (s RefSpec) matchExact(n core.ReferenceName) bool { + return s.Src() == n.String() +} + +func (s RefSpec) matchGlob(n core.ReferenceName) bool { + src := s.Src() + name := n.String() + wildcard := strings.Index(src, refSpecWildcard) + + var prefix, suffix string + prefix = src[0:wildcard] + if len(src) < wildcard { + suffix = src[wildcard+1 : len(suffix)] + } + + return len(name) > len(prefix)+len(suffix) && + strings.HasPrefix(name, prefix) && + strings.HasSuffix(name, suffix) +} + +// Dst returns the destination for the given remote reference +func (s RefSpec) Dst(n core.ReferenceName) core.ReferenceName { + spec := string(s) + start := strings.Index(spec, refSpecSeparator) + 1 + dst := spec[start:len(spec)] + src := s.Src() + + if !s.isGlob() { + return core.ReferenceName(dst) + } + + name := n.String() + ws := strings.Index(src, refSpecWildcard) + wd := strings.Index(dst, refSpecWildcard) + match := name[ws : len(name)-(len(src)-(ws+1))] + + return core.ReferenceName(dst[0:wd] + match + dst[wd+1:len(dst)]) +} diff --git a/config/refspec_test.go b/config/refspec_test.go new file mode 100644 index 0000000..b0bb8f5 --- /dev/null +++ b/config/refspec_test.go @@ -0,0 +1,69 @@ +package config + +import ( + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git.v4/core" +) + +type RefSpecSuite struct{} + +var _ = Suite(&RefSpecSuite{}) + +func Test(t *testing.T) { TestingT(t) } + +func (s *RefSpecSuite) TestRefSpecIsValid(c *C) { + spec := RefSpec("+refs/heads/*:refs/remotes/origin/*") + c.Assert(spec.IsValid(), Equals, true) + + spec = RefSpec("refs/heads/*:refs/remotes/origin/") + c.Assert(spec.IsValid(), Equals, false) + + spec = RefSpec("refs/heads/master:refs/remotes/origin/master") + c.Assert(spec.IsValid(), Equals, true) + + spec = RefSpec("refs/heads/*") + c.Assert(spec.IsValid(), Equals, false) +} + +func (s *RefSpecSuite) TestRefSpecIsForceUpdate(c *C) { + spec := RefSpec("+refs/heads/*:refs/remotes/origin/*") + c.Assert(spec.IsForceUpdate(), Equals, true) + + spec = RefSpec("refs/heads/*:refs/remotes/origin/*") + c.Assert(spec.IsForceUpdate(), Equals, false) +} + +func (s *RefSpecSuite) TestRefSpecSrc(c *C) { + spec := RefSpec("refs/heads/*:refs/remotes/origin/*") + c.Assert(spec.Src(), Equals, "refs/heads/*") +} + +func (s *RefSpecSuite) TestRefSpecMatch(c *C) { + spec := RefSpec("refs/heads/master:refs/remotes/origin/master") + c.Assert(spec.Match(core.ReferenceName("refs/heads/foo")), Equals, false) + c.Assert(spec.Match(core.ReferenceName("refs/heads/master")), Equals, true) +} + +func (s *RefSpecSuite) TestRefSpecMatchBlob(c *C) { + spec := RefSpec("refs/heads/*:refs/remotes/origin/*") + c.Assert(spec.Match(core.ReferenceName("refs/tag/foo")), Equals, false) + c.Assert(spec.Match(core.ReferenceName("refs/heads/foo")), Equals, true) +} + +func (s *RefSpecSuite) TestRefSpecDst(c *C) { + spec := RefSpec("refs/heads/master:refs/remotes/origin/master") + c.Assert( + spec.Dst(core.ReferenceName("refs/heads/master")).String(), Equals, + "refs/remotes/origin/master", + ) +} + +func (s *RefSpecSuite) TestRefSpecDstBlob(c *C) { + spec := RefSpec("refs/heads/*:refs/remotes/origin/*") + c.Assert( + spec.Dst(core.ReferenceName("refs/heads/foo")).String(), Equals, + "refs/remotes/origin/foo", + ) +} diff --git a/core/reference.go b/core/reference.go index 5258146..1325c7e 100644 --- a/core/reference.go +++ b/core/reference.go @@ -34,16 +34,12 @@ const ( // ReferenceName reference name's type ReferenceName string -// AsRemote returns a new remote reference name using current one as base -func (r ReferenceName) AsRemote(remote string) ReferenceName { - return ReferenceName(refRemotePrefix + remote + "/" + r.alias()) -} - func (r ReferenceName) String() string { return string(r) } -func (r ReferenceName) alias() string { +// Short returns the short name of a ReferenceName +func (r ReferenceName) Short() string { parts := strings.Split(string(r), "/") return parts[len(parts)-1] } @@ -114,7 +110,7 @@ func (r *Reference) Target() ReferenceName { // IsBranch check if a reference is a branch func (r *Reference) IsBranch() bool { - return strings.HasPrefix(string(r.n), refHeadPrefix) || r.n == HEAD + return strings.HasPrefix(string(r.n), refHeadPrefix) } // IsNote check if a reference is a note @@ -225,107 +221,3 @@ func resolveReference(s ReferenceStorage, r *Reference, recursion int) (*Referen recursion++ return resolveReference(s, t, recursion) } - -const ( - refSpecWildcard = "*" - refSpecForce = "+" - refSpecSeparator = ":" -) - -// RefSpec is a mapping from local branches to remote references -// The format of the refspec is an optional +, followed by <src>:<dst>, where -// <src> is the pattern for references on the remote side and <dst> is where -// those references will be written locally. The + tells Git to update the -// reference even if it isn’t a fast-forward. -// eg.: "+refs/heads/*:refs/remotes/origin/*" -// -// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec -type RefSpec string - -// IsValid validates the RefSpec -func (s RefSpec) IsValid() bool { - spec := string(s) - if strings.Count(spec, refSpecSeparator) != 1 { - return false - } - - sep := strings.Index(spec, refSpecSeparator) - if sep == len(spec) { - return false - } - - ws := strings.Count(spec[0:sep], refSpecWildcard) - wd := strings.Count(spec[sep+1:len(spec)], refSpecWildcard) - return ws == wd && ws < 2 && wd < 2 -} - -// IsForceUpdate returns if update is allowed in non fast-forward merges -func (s RefSpec) IsForceUpdate() bool { - if s[0] == refSpecForce[0] { - return true - } - - return false -} - -// Src return the src side -func (s RefSpec) Src() string { - spec := string(s) - start := strings.Index(spec, refSpecForce) + 1 - end := strings.Index(spec, refSpecSeparator) - - return spec[start:end] -} - -// Match match the given ReferenceName against the source -func (s RefSpec) Match(n ReferenceName) bool { - if !s.isGlob() { - return s.matchExact(n) - } - - return s.matchGlob(n) -} - -func (s RefSpec) isGlob() bool { - return strings.Index(string(s), refSpecWildcard) != -1 -} - -func (s RefSpec) matchExact(n ReferenceName) bool { - return s.Src() == n.String() -} - -func (s RefSpec) matchGlob(n ReferenceName) bool { - src := s.Src() - name := n.String() - wildcard := strings.Index(src, refSpecWildcard) - - var prefix, suffix string - prefix = src[0:wildcard] - if len(src) < wildcard { - suffix = src[wildcard+1 : len(suffix)] - } - - return len(name) > len(prefix)+len(suffix) && - strings.HasPrefix(name, prefix) && - strings.HasSuffix(name, suffix) -} - -// Dst returns the destination for the given remote reference -func (s RefSpec) Dst(n ReferenceName) ReferenceName { - spec := string(s) - start := strings.Index(spec, refSpecSeparator) + 1 - dst := spec[start:len(spec)] - src := s.Src() - - if !s.isGlob() { - return ReferenceName(dst) - } - - name := n.String() - ws := strings.Index(src, refSpecWildcard) - wd := strings.Index(dst, refSpecWildcard) - match := name[ws : len(name)-(len(src)-(ws+1))] - - return ReferenceName(dst[0:wd] + match + dst[wd+1:len(dst)]) - -} diff --git a/core/reference_test.go b/core/reference_test.go index 3d5a9b2..b14f24c 100644 --- a/core/reference_test.go +++ b/core/reference_test.go @@ -14,11 +14,8 @@ const ( ExampleReferenceName ReferenceName = "refs/heads/v4" ) -func (s *ReferenceSuite) TestReferenceNameAsRemote(c *C) { - c.Assert( - ExampleReferenceName.AsRemote("foo").String(), - Equals, "refs/remotes/foo/v4", - ) +func (s *ReferenceSuite) TestReferenceNameShort(c *C) { + c.Assert(ExampleReferenceName.Short(), Equals, "v4") } func (s *ReferenceSuite) TestNewReferenceFromStrings(c *C) { @@ -121,58 +118,3 @@ func (s *ReferenceSuite) TestReferenceSliceIterForEachStop(c *C) { c.Assert(count, Equals, 1) } - -func (s *ReferenceSuite) TestRefSpecIsValid(c *C) { - spec := RefSpec("+refs/heads/*:refs/remotes/origin/*") - c.Assert(spec.IsValid(), Equals, true) - - spec = RefSpec("refs/heads/*:refs/remotes/origin/") - c.Assert(spec.IsValid(), Equals, false) - - spec = RefSpec("refs/heads/master:refs/remotes/origin/master") - c.Assert(spec.IsValid(), Equals, true) - - spec = RefSpec("refs/heads/*") - c.Assert(spec.IsValid(), Equals, false) -} - -func (s *ReferenceSuite) TestRefSpecIsForceUpdate(c *C) { - spec := RefSpec("+refs/heads/*:refs/remotes/origin/*") - c.Assert(spec.IsForceUpdate(), Equals, true) - - spec = RefSpec("refs/heads/*:refs/remotes/origin/*") - c.Assert(spec.IsForceUpdate(), Equals, false) -} - -func (s *ReferenceSuite) TestRefSpecSrc(c *C) { - spec := RefSpec("refs/heads/*:refs/remotes/origin/*") - c.Assert(spec.Src(), Equals, "refs/heads/*") -} - -func (s *ReferenceSuite) TestRefSpecMatch(c *C) { - spec := RefSpec("refs/heads/master:refs/remotes/origin/master") - c.Assert(spec.Match(ReferenceName("refs/heads/foo")), Equals, false) - c.Assert(spec.Match(ReferenceName("refs/heads/master")), Equals, true) -} - -func (s *ReferenceSuite) TestRefSpecMatchBlob(c *C) { - spec := RefSpec("refs/heads/*:refs/remotes/origin/*") - c.Assert(spec.Match(ReferenceName("refs/tag/foo")), Equals, false) - c.Assert(spec.Match(ReferenceName("refs/heads/foo")), Equals, true) -} - -func (s *ReferenceSuite) TestRefSpecDst(c *C) { - spec := RefSpec("refs/heads/master:refs/remotes/origin/master") - c.Assert( - spec.Dst(ReferenceName("refs/heads/master")).String(), Equals, - "refs/remotes/origin/master", - ) -} - -func (s *ReferenceSuite) TestRefSpecDstBlob(c *C) { - spec := RefSpec("refs/heads/*:refs/remotes/origin/*") - c.Assert( - spec.Dst(ReferenceName("refs/heads/foo")).String(), Equals, - "refs/remotes/origin/foo", - ) -} @@ -2,19 +2,23 @@ package git import ( "errors" + "fmt" "gopkg.in/src-d/go-git.v3/clients/common" + "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/core" ) const ( // DefaultRemoteName name of the default Remote, just like git command - DefaultRemoteName = "origin" + DefaultRemoteName = "origin" + DefaultSingleBranchRefSpec = "+refs/heads/%s:refs/remotes/%s/%[1]s" + DefaultRefSpec = "+refs/heads/*:refs/remotes/%s/*" ) var ( - ErrMissingURL = errors.New("URL field is required") - ErrMissingReferences = errors.New("references cannot be empty") + ErrMissingURL = errors.New("URL field is required") + ErrInvalidRefSpec = errors.New("invalid refspec") ) // RepositoryCloneOptions describe how a clone should be perform @@ -50,6 +54,22 @@ func (o *RepositoryCloneOptions) Validate() error { return nil } +func (o *RepositoryCloneOptions) refSpec(s core.ReferenceStorage) (config.RefSpec, error) { + var spec string + if o.SingleBranch { + head, err := core.ResolveReference(s, o.ReferenceName) + if err != nil { + return "", err + } + + spec = fmt.Sprintf(DefaultSingleBranchRefSpec, head.Name().Short(), o.RemoteName) + } else { + spec = fmt.Sprintf(DefaultRefSpec, o.RemoteName) + } + + return config.RefSpec(spec), nil +} + // RepositoryPullOptions describe how a pull should be perform type RepositoryPullOptions struct { // Name of the remote to be pulled @@ -77,18 +97,14 @@ func (o *RepositoryPullOptions) Validate() error { // RemoteFetchOptions describe how a fetch should be perform type RemoteFetchOptions struct { - // Remote branchs to fetch - References []*core.Reference - // Local references present on the local storage - LocalReferences []*core.Reference - // Limit fetching to the specified number of commits - Depth int + RefSpec config.RefSpec + Depth int } // Validate validate the fields and set the default values func (o *RemoteFetchOptions) Validate() error { - if len(o.References) == 0 { - return ErrMissingReferences + if !o.RefSpec.IsValid() { + return ErrInvalidRefSpec } return nil @@ -1,14 +1,19 @@ package git import ( + "io" + "gopkg.in/src-d/go-git.v4/clients" "gopkg.in/src-d/go-git.v4/clients/common" + "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/core" "gopkg.in/src-d/go-git.v4/formats/packfile" ) // Remote represents a connection to a remote repository type Remote struct { + config *config.RemoteConfig + Name string Endpoint common.Endpoint Auth common.AuthMethod @@ -79,20 +84,19 @@ func (r *Remote) Capabilities() *common.Capabilities { } // Fetch returns a reader using the request -func (r *Remote) Fetch(s core.ObjectStorage, o *RemoteFetchOptions) (err error) { +func (r *Remote) Fetch(s core.Storage, o *RemoteFetchOptions) (err error) { if err := o.Validate(); err != nil { return err } - req := &common.GitUploadPackRequest{} - req.Depth = o.Depth - - for _, ref := range o.References { - if ref.Type() != core.HashReference { - continue - } + refs, err := r.getWantedReferences(o.RefSpec) + if err != nil { + return err + } - req.Want(ref.Hash()) + req, err := r.buildRequest(s.ReferenceStorage(), o, refs) + if err != nil { + return err } reader, err := r.upSrv.Fetch(req) @@ -101,12 +105,85 @@ func (r *Remote) Fetch(s core.ObjectStorage, o *RemoteFetchOptions) (err error) } defer checkClose(reader, &err) + if err := r.updateObjectStorage(s.ObjectStorage(), reader); err != nil { + return err + } + + return r.updateLocalReferenceStorage(s.ReferenceStorage(), o.RefSpec, refs) +} + +func (r *Remote) getWantedReferences(spec config.RefSpec) ([]*core.Reference, error) { + var refs []*core.Reference + + return refs, r.Refs().ForEach(func(r *core.Reference) error { + if r.Type() != core.HashReference { + return nil + } + + if spec.Match(r.Name()) { + refs = append(refs, r) + } + + return nil + }) +} + +func (r *Remote) buildRequest( + s core.ReferenceStorage, o *RemoteFetchOptions, refs []*core.Reference, +) (*common.GitUploadPackRequest, error) { + req := &common.GitUploadPackRequest{} + req.Depth = o.Depth + + for _, ref := range refs { + req.Want(ref.Hash()) + } + + i, err := s.Iter() + if err != nil { + return nil, err + } + + err = i.ForEach(func(ref *core.Reference) error { + if ref.Type() != core.HashReference { + return nil + } + + req.Have(ref.Hash()) + return nil + }) + + return req, err +} + +func (r *Remote) updateObjectStorage(s core.ObjectStorage, reader io.Reader) error { stream := packfile.NewStream(reader) d := packfile.NewDecoder(stream) return d.Decode(s) } +func (r *Remote) updateLocalReferenceStorage( + local core.ReferenceStorage, spec config.RefSpec, refs []*core.Reference, +) error { + for _, ref := range refs { + if !spec.Match(ref.Name()) { + continue + } + + if ref.Type() != core.HashReference { + continue + } + + name := spec.Dst(ref.Name()) + n := core.NewHashReference(name, ref.Hash()) + if err := local.Set(n); err != nil { + return err + } + } + + return nil +} + // Head returns the Reference of the HEAD func (r *Remote) Head() *core.Reference { return r.upInfo.Head() @@ -126,3 +203,8 @@ func (r *Remote) Refs() core.ReferenceIter { i, _ := r.upInfo.Refs.Iter() return i } + +func (r *Remote) Disconnect() error { + r.upInfo = nil + return r.upSrv.Disconnect() +} diff --git a/remote_test.go b/remote_test.go index c369ab7..451f801 100644 --- a/remote_test.go +++ b/remote_test.go @@ -82,17 +82,13 @@ func (s *RemoteSuite) TestFetch(c *C) { c.Assert(err, IsNil) c.Assert(r.Connect(), IsNil) - sto := memory.NewObjectStorage() + sto := memory.NewStorage() err = r.Fetch(sto, &RemoteFetchOptions{ - References: []*core.Reference{ - core.NewReferenceFromStrings( - "refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", - ), - }, + RefSpec: DefaultRefSpec, }) c.Assert(err, IsNil) - c.Assert(sto.Objects, HasLen, 28) + c.Assert(sto.ObjectStorage().(*memory.ObjectStorage).Objects, HasLen, 28) } func (s *RemoteSuite) TestHead(c *C) { diff --git a/repository.go b/repository.go index 676c7b8..ada0bc4 100644 --- a/repository.go +++ b/repository.go @@ -11,8 +11,9 @@ import ( ) var ( - ErrObjectNotFound = errors.New("object not found") - ErrUnknownRemote = errors.New("unknown remote") + ErrObjectNotFound = errors.New("object not found") + ErrUnknownRemote = errors.New("unknown remote") + ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch") ) // Repository giturl string, auth common.AuthMethod repository struct @@ -58,35 +59,28 @@ func (r *Repository) Clone(o *RepositoryCloneOptions) error { return err } - var single core.ReferenceName - if o.SingleBranch { - single = o.ReferenceName - } + defer remote.Disconnect() - head, err := remote.Ref(o.ReferenceName, true) + spec, err := o.refSpec(remote.Info().Refs) if err != nil { return err } - refs, err := r.getRemoteRefences(remote, single) - if err != nil { - return err - } - - err = remote.Fetch(r.s.ObjectStorage(), &RemoteFetchOptions{ - References: refs, - Depth: o.Depth, + err = remote.Fetch(r.s, &RemoteFetchOptions{ + RefSpec: spec, + Depth: o.Depth, }) if err != nil { return err } - if err := r.createLocalReferences(head); err != nil { + head, err := remote.Ref(o.ReferenceName, true) + if err != nil { return err } - return r.createRemoteReferences(remote, refs) + return r.createReferences(head) } func (r *Repository) createRemote(name, url string, auth common.AuthMethod) (*Remote, error) { @@ -99,47 +93,7 @@ func (r *Repository) createRemote(name, url string, auth common.AuthMethod) (*Re return remote, nil } -func (r *Repository) getRemoteRefences( - remote *Remote, single core.ReferenceName, -) ([]*core.Reference, error) { - if single == "" { - return r.getAllRemoteRefences(remote) - } - - ref, err := remote.Ref(single, true) - if err != nil { - return nil, err - } - - refs := []*core.Reference{ref} - head, err := remote.Ref(core.HEAD, false) - if err != nil { - return nil, err - } - - if head.Target() == ref.Name() { - refs = append(refs, head) - } - - return refs, nil -} - -func (r *Repository) getAllRemoteRefences(remote *Remote) ([]*core.Reference, error) { - var refs []*core.Reference - i := remote.Refs() - defer i.Close() - - return refs, i.ForEach(func(ref *core.Reference) error { - if !ref.IsBranch() { - return nil - } - - refs = append(refs, ref) - return nil - }) -} - -func (r *Repository) createLocalReferences(ref *core.Reference) error { +func (r *Repository) createReferences(ref *core.Reference) error { if !ref.IsBranch() { // detached HEAD mode head := core.NewHashReference(core.HEAD, ref.Hash()) @@ -154,38 +108,6 @@ func (r *Repository) createLocalReferences(ref *core.Reference) error { return r.s.ReferenceStorage().Set(head) } -func (r *Repository) createRemoteReferences(remote *Remote, remoteRefs []*core.Reference) error { - for _, ref := range remoteRefs { - if err := r.createRemoteReference(remote, ref); err != nil { - return err - } - } - - return nil -} - -func (r *Repository) createRemoteReference(remote *Remote, ref *core.Reference) error { - name := ref.Name().AsRemote(remote.Name) - - var n *core.Reference - switch ref.Type() { - case core.HashReference: - n = core.NewHashReference(name, ref.Hash()) - case core.SymbolicReference: - n = core.NewSymbolicReference(name, ref.Target().AsRemote(remote.Name)) - target, err := remote.Ref(ref.Target(), false) - if err != nil { - return err - } - - if err := r.createRemoteReference(remote, target); err != nil { - return err - } - } - - return r.s.ReferenceStorage().Set(n) -} - // Pull incorporates changes from a remote repository into the current branch func (r *Repository) Pull(o *RepositoryPullOptions) error { if err := o.Validate(); err != nil { @@ -202,37 +124,21 @@ func (r *Repository) Pull(o *RepositoryPullOptions) error { return err } - refs, err := r.getLocalReferences() - if err != nil { + if err = remote.Connect(); err != nil { return err } - err = remote.Fetch(r.s.ObjectStorage(), &RemoteFetchOptions{ - References: []*core.Reference{head}, - LocalReferences: refs, - Depth: o.Depth, + defer remote.Disconnect() + + err = remote.Fetch(r.s, &RemoteFetchOptions{ + Depth: o.Depth, }) if err != nil { return err } - return r.createLocalReferences(head) -} - -func (r *Repository) getLocalReferences() ([]*core.Reference, error) { - var refs []*core.Reference - i := r.Refs() - defer i.Close() - - return refs, i.ForEach(func(ref *core.Reference) error { - if ref.Type() == core.SymbolicReference { - return nil - } - - refs = append(refs, ref) - return nil - }) + return r.createReferences(head) } // Commit return the commit with the given hash diff --git a/repository_test.go b/repository_test.go index c2b2ff2..2539a0b 100644 --- a/repository_test.go +++ b/repository_test.go @@ -95,12 +95,6 @@ func (s *RepositorySuite) TestClone(c *C) { c.Assert(branch, NotNil) c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - branch, err = r.Ref("refs/remotes/origin/HEAD", false) - c.Assert(err, IsNil) - c.Assert(branch, NotNil) - c.Assert(branch.Type(), Equals, core.SymbolicReference) - c.Assert(branch.Target().String(), Equals, "refs/remotes/origin/master") - branch, err = r.Ref("refs/remotes/origin/master", false) c.Assert(err, IsNil) c.Assert(branch, NotNil) @@ -182,12 +176,6 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) { c.Assert(branch, NotNil) c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - branch, err = r.Ref("refs/remotes/origin/HEAD", false) - c.Assert(err, IsNil) - c.Assert(branch, NotNil) - c.Assert(branch.Type(), Equals, core.SymbolicReference) - c.Assert(branch.Target().String(), Equals, "refs/remotes/origin/master") - branch, err = r.Ref("refs/remotes/origin/master", false) c.Assert(err, IsNil) c.Assert(branch, NotNil) |