Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 501a972

Browse files
committed
Remote.Fetch base on RefSpec, improvement of the responsabilities separation
1 parent c83ba5f commit 501a972

14 files changed

+365
-325
lines changed

clients/common/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type GitUploadPackService interface {
2828
ConnectWithAuth(AuthMethod) error
2929
Info() (*GitUploadPackInfo, error)
3030
Fetch(*GitUploadPackRequest) (io.ReadCloser, error)
31+
Disconnect() error
3132
}
3233

3334
type AuthMethod interface {

clients/common_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ func (s *dummyProtocolService) Fetch(r *common.GitUploadPackRequest) (io.ReadClo
7676
return nil, nil
7777
}
7878

79+
func (s *dummyProtocolService) Disconnect() error {
80+
return nil
81+
}
82+
7983
func typeAsString(v interface{}) string {
8084
return fmt.Sprintf("%T", v)
8185
}

clients/http/git_upload_pack.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,7 @@ func (s *GitUploadPackService) applyAuthToRequest(req *http.Request) {
134134

135135
s.auth.setAuth(req)
136136
}
137+
138+
func (s *GitUploadPackService) Disconnect() (err error) {
139+
return nil
140+
}

common_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package git
22

33
import (
44
"bytes"
5+
"errors"
56
"io"
67
"io/ioutil"
78
"os"
@@ -21,27 +22,34 @@ type BaseSuite struct{}
2122

2223
func (s *BaseSuite) SetUpTest(c *C) {
2324
clients.InstallProtocol("mock", func(end common.Endpoint) common.GitUploadPackService {
24-
return &MockGitUploadPackService{conected: end}
25+
return &MockGitUploadPackService{endpoint: end}
2526
})
2627
}
2728

2829
const RepositoryFixture = "mock://formats/packfile/fixtures/git-fixture.ref-delta"
2930

3031
type MockGitUploadPackService struct {
31-
conected common.Endpoint
32-
auth common.AuthMethod
32+
connected bool
33+
endpoint common.Endpoint
34+
auth common.AuthMethod
3335
}
3436

3537
func (p *MockGitUploadPackService) Connect() error {
38+
p.connected = true
3639
return nil
3740
}
3841

3942
func (p *MockGitUploadPackService) ConnectWithAuth(auth common.AuthMethod) error {
43+
p.connected = true
4044
p.auth = auth
4145
return nil
4246
}
4347

4448
func (p *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) {
49+
if !p.connected {
50+
return nil, errors.New("not connected")
51+
}
52+
4553
h := core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
4654

4755
c := common.NewCapabilities()
@@ -62,9 +70,18 @@ func (p *MockGitUploadPackService) Info() (*common.GitUploadPackInfo, error) {
6270
}
6371

6472
func (p *MockGitUploadPackService) Fetch(*common.GitUploadPackRequest) (io.ReadCloser, error) {
73+
if !p.connected {
74+
return nil, errors.New("not connected")
75+
}
76+
6577
return os.Open("formats/packfile/fixtures/git-fixture.ref-delta")
6678
}
6779

80+
func (p *MockGitUploadPackService) Disconnect() error {
81+
p.connected = false
82+
return nil
83+
}
84+
6885
type packedFixture struct {
6986
url string
7087
packfile string

config/config.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package config
2+
3+
type Config interface {
4+
Remote(name string) *RemoteConfig
5+
Remotes() []*RemoteConfig
6+
SetRemote(*RemoteConfig)
7+
}
8+
9+
type RemoteConfig struct {
10+
Name string
11+
URL string
12+
Fetch RefSpec
13+
}

config/refspec.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package config
2+
3+
import (
4+
"strings"
5+
6+
"gopkg.in/src-d/go-git.v4/core"
7+
)
8+
9+
const (
10+
refSpecWildcard = "*"
11+
refSpecForce = "+"
12+
refSpecSeparator = ":"
13+
)
14+
15+
// RefSpec is a mapping from local branches to remote references
16+
// The format of the refspec is an optional +, followed by <src>:<dst>, where
17+
// <src> is the pattern for references on the remote side and <dst> is where
18+
// those references will be written locally. The + tells Git to update the
19+
// reference even if it isn’t a fast-forward.
20+
// eg.: "+refs/heads/*:refs/remotes/origin/*"
21+
//
22+
// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec
23+
type RefSpec string
24+
25+
// IsValid validates the RefSpec
26+
func (s RefSpec) IsValid() bool {
27+
spec := string(s)
28+
if strings.Count(spec, refSpecSeparator) != 1 {
29+
return false
30+
}
31+
32+
sep := strings.Index(spec, refSpecSeparator)
33+
if sep == len(spec) {
34+
return false
35+
}
36+
37+
ws := strings.Count(spec[0:sep], refSpecWildcard)
38+
wd := strings.Count(spec[sep+1:len(spec)], refSpecWildcard)
39+
return ws == wd && ws < 2 && wd < 2
40+
}
41+
42+
// IsForceUpdate returns if update is allowed in non fast-forward merges
43+
func (s RefSpec) IsForceUpdate() bool {
44+
if s[0] == refSpecForce[0] {
45+
return true
46+
}
47+
48+
return false
49+
}
50+
51+
// Src return the src side
52+
func (s RefSpec) Src() string {
53+
spec := string(s)
54+
start := strings.Index(spec, refSpecForce) + 1
55+
end := strings.Index(spec, refSpecSeparator)
56+
57+
return spec[start:end]
58+
}
59+
60+
// Match match the given core.ReferenceName against the source
61+
func (s RefSpec) Match(n core.ReferenceName) bool {
62+
if !s.isGlob() {
63+
return s.matchExact(n)
64+
}
65+
66+
return s.matchGlob(n)
67+
}
68+
69+
func (s RefSpec) isGlob() bool {
70+
return strings.Index(string(s), refSpecWildcard) != -1
71+
}
72+
73+
func (s RefSpec) matchExact(n core.ReferenceName) bool {
74+
return s.Src() == n.String()
75+
}
76+
77+
func (s RefSpec) matchGlob(n core.ReferenceName) bool {
78+
src := s.Src()
79+
name := n.String()
80+
wildcard := strings.Index(src, refSpecWildcard)
81+
82+
var prefix, suffix string
83+
prefix = src[0:wildcard]
84+
if len(src) < wildcard {
85+
suffix = src[wildcard+1 : len(suffix)]
86+
}
87+
88+
return len(name) > len(prefix)+len(suffix) &&
89+
strings.HasPrefix(name, prefix) &&
90+
strings.HasSuffix(name, suffix)
91+
}
92+
93+
// Dst returns the destination for the given remote reference
94+
func (s RefSpec) Dst(n core.ReferenceName) core.ReferenceName {
95+
spec := string(s)
96+
start := strings.Index(spec, refSpecSeparator) + 1
97+
dst := spec[start:len(spec)]
98+
src := s.Src()
99+
100+
if !s.isGlob() {
101+
return core.ReferenceName(dst)
102+
}
103+
104+
name := n.String()
105+
ws := strings.Index(src, refSpecWildcard)
106+
wd := strings.Index(dst, refSpecWildcard)
107+
match := name[ws : len(name)-(len(src)-(ws+1))]
108+
109+
return core.ReferenceName(dst[0:wd] + match + dst[wd+1:len(dst)])
110+
}

config/refspec_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package config
2+
3+
import (
4+
"testing"
5+
6+
. "gopkg.in/check.v1"
7+
"gopkg.in/src-d/go-git.v4/core"
8+
)
9+
10+
type RefSpecSuite struct{}
11+
12+
var _ = Suite(&RefSpecSuite{})
13+
14+
func Test(t *testing.T) { TestingT(t) }
15+
16+
func (s *RefSpecSuite) TestRefSpecIsValid(c *C) {
17+
spec := RefSpec("+refs/heads/*:refs/remotes/origin/*")
18+
c.Assert(spec.IsValid(), Equals, true)
19+
20+
spec = RefSpec("refs/heads/*:refs/remotes/origin/")
21+
c.Assert(spec.IsValid(), Equals, false)
22+
23+
spec = RefSpec("refs/heads/master:refs/remotes/origin/master")
24+
c.Assert(spec.IsValid(), Equals, true)
25+
26+
spec = RefSpec("refs/heads/*")
27+
c.Assert(spec.IsValid(), Equals, false)
28+
}
29+
30+
func (s *RefSpecSuite) TestRefSpecIsForceUpdate(c *C) {
31+
spec := RefSpec("+refs/heads/*:refs/remotes/origin/*")
32+
c.Assert(spec.IsForceUpdate(), Equals, true)
33+
34+
spec = RefSpec("refs/heads/*:refs/remotes/origin/*")
35+
c.Assert(spec.IsForceUpdate(), Equals, false)
36+
}
37+
38+
func (s *RefSpecSuite) TestRefSpecSrc(c *C) {
39+
spec := RefSpec("refs/heads/*:refs/remotes/origin/*")
40+
c.Assert(spec.Src(), Equals, "refs/heads/*")
41+
}
42+
43+
func (s *RefSpecSuite) TestRefSpecMatch(c *C) {
44+
spec := RefSpec("refs/heads/master:refs/remotes/origin/master")
45+
c.Assert(spec.Match(core.ReferenceName("refs/heads/foo")), Equals, false)
46+
c.Assert(spec.Match(core.ReferenceName("refs/heads/master")), Equals, true)
47+
}
48+
49+
func (s *RefSpecSuite) TestRefSpecMatchBlob(c *C) {
50+
spec := RefSpec("refs/heads/*:refs/remotes/origin/*")
51+
c.Assert(spec.Match(core.ReferenceName("refs/tag/foo")), Equals, false)
52+
c.Assert(spec.Match(core.ReferenceName("refs/heads/foo")), Equals, true)
53+
}
54+
55+
func (s *RefSpecSuite) TestRefSpecDst(c *C) {
56+
spec := RefSpec("refs/heads/master:refs/remotes/origin/master")
57+
c.Assert(
58+
spec.Dst(core.ReferenceName("refs/heads/master")).String(), Equals,
59+
"refs/remotes/origin/master",
60+
)
61+
}
62+
63+
func (s *RefSpecSuite) TestRefSpecDstBlob(c *C) {
64+
spec := RefSpec("refs/heads/*:refs/remotes/origin/*")
65+
c.Assert(
66+
spec.Dst(core.ReferenceName("refs/heads/foo")).String(), Equals,
67+
"refs/remotes/origin/foo",
68+
)
69+
}

0 commit comments

Comments
 (0)