aboutsummaryrefslogtreecommitdiffstats
path: root/config/refspec.go
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2016-08-19 17:42:13 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2016-08-19 17:42:13 +0200
commit1d56b98d9b02e20f7feea542c75746eab34fad63 (patch)
tree006e8c3ac5e40353032109a5259bb28c37751996 /config/refspec.go
parentb1d116c59f7656dc8d5ff7294ba8f8a82c51bfd1 (diff)
downloadgo-git-1d56b98d9b02e20f7feea542c75746eab34fad63.tar.gz
Remote.Fetch base on RefSpec, improvement of the responsabilities separation
Diffstat (limited to 'config/refspec.go')
-rw-r--r--config/refspec.go110
1 files changed, 110 insertions, 0 deletions
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)])
+}