aboutsummaryrefslogtreecommitdiffstats
path: root/core/reference.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/reference.go')
-rw-r--r--core/reference.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/core/reference.go b/core/reference.go
index bde3ff4..72bfe9d 100644
--- a/core/reference.go
+++ b/core/reference.go
@@ -225,3 +225,107 @@ 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/*/*: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)])
+
+}