aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clients/common/common.go1
-rw-r--r--clients/common_test.go4
-rw-r--r--clients/http/git_upload_pack.go4
-rw-r--r--common_test.go23
-rw-r--r--config/config.go13
-rw-r--r--config/refspec.go110
-rw-r--r--config/refspec_test.go69
-rw-r--r--core/reference.go114
-rw-r--r--core/reference_test.go62
-rw-r--r--options.go38
-rw-r--r--remote.go100
-rw-r--r--remote_test.go10
-rw-r--r--repository.go130
-rw-r--r--repository_test.go12
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",
- )
-}
diff --git a/options.go b/options.go
index 0c1fefb..414b7f0 100644
--- a/options.go
+++ b/options.go
@@ -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
diff --git a/remote.go b/remote.go
index 7e25ef7..9b07223 100644
--- a/remote.go
+++ b/remote.go
@@ -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)