aboutsummaryrefslogtreecommitdiffstats
path: root/config
diff options
context:
space:
mode:
Diffstat (limited to 'config')
-rw-r--r--config/config.go13
-rw-r--r--config/refspec.go110
-rw-r--r--config/refspec_test.go69
3 files changed, 192 insertions, 0 deletions
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",
+ )
+}