fork of go-git with some jj specific features
1package config
2
3import (
4 "errors"
5 "strings"
6
7 "github.com/go-git/go-git/v5/plumbing"
8)
9
10const (
11 refSpecWildcard = "*"
12 refSpecForce = "+"
13 refSpecSeparator = ":"
14)
15
16var (
17 ErrRefSpecMalformedSeparator = errors.New("malformed refspec, separators are wrong")
18 ErrRefSpecMalformedWildcard = errors.New("malformed refspec, mismatched number of wildcards")
19)
20
21// RefSpec is a mapping from local branches to remote references.
22// The format of the refspec is an optional +, followed by <src>:<dst>, where
23// <src> is the pattern for references on the remote side and <dst> is where
24// those references will be written locally. The + tells Git to update the
25// reference even if it isn’t a fast-forward.
26// eg.: "+refs/heads/*:refs/remotes/origin/*"
27//
28// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
29type RefSpec string
30
31// Validate validates the RefSpec
32func (s RefSpec) Validate() error {
33 spec := string(s)
34 if strings.Count(spec, refSpecSeparator) != 1 {
35 return ErrRefSpecMalformedSeparator
36 }
37
38 sep := strings.Index(spec, refSpecSeparator)
39 if sep == len(spec)-1 {
40 return ErrRefSpecMalformedSeparator
41 }
42
43 ws := strings.Count(spec[0:sep], refSpecWildcard)
44 wd := strings.Count(spec[sep+1:], refSpecWildcard)
45 if ws == wd && ws < 2 && wd < 2 {
46 return nil
47 }
48
49 return ErrRefSpecMalformedWildcard
50}
51
52// IsForceUpdate returns if update is allowed in non fast-forward merges.
53func (s RefSpec) IsForceUpdate() bool {
54 return s[0] == refSpecForce[0]
55}
56
57// IsDelete returns true if the refspec indicates a delete (empty src).
58func (s RefSpec) IsDelete() bool {
59 return s[0] == refSpecSeparator[0]
60}
61
62// IsExactSHA1 returns true if the source is a SHA1 hash.
63func (s RefSpec) IsExactSHA1() bool {
64 return plumbing.IsHash(s.Src())
65}
66
67// Src returns the src side.
68func (s RefSpec) Src() string {
69 spec := string(s)
70
71 var start int
72 if s.IsForceUpdate() {
73 start = 1
74 } else {
75 start = 0
76 }
77
78 end := strings.Index(spec, refSpecSeparator)
79 return spec[start:end]
80}
81
82// Match match the given plumbing.ReferenceName against the source.
83func (s RefSpec) Match(n plumbing.ReferenceName) bool {
84 if !s.IsWildcard() {
85 return s.matchExact(n)
86 }
87
88 return s.matchGlob(n)
89}
90
91// IsWildcard returns true if the RefSpec contains a wildcard.
92func (s RefSpec) IsWildcard() bool {
93 return strings.Contains(string(s), refSpecWildcard)
94}
95
96func (s RefSpec) matchExact(n plumbing.ReferenceName) bool {
97 return s.Src() == n.String()
98}
99
100func (s RefSpec) matchGlob(n plumbing.ReferenceName) bool {
101 src := s.Src()
102 name := n.String()
103 wildcard := strings.Index(src, refSpecWildcard)
104
105 var prefix, suffix string
106 prefix = src[0:wildcard]
107 if len(src) > wildcard+1 {
108 suffix = src[wildcard+1:]
109 }
110
111 return len(name) >= len(prefix)+len(suffix) &&
112 strings.HasPrefix(name, prefix) &&
113 strings.HasSuffix(name, suffix)
114}
115
116// Dst returns the destination for the given remote reference.
117func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName {
118 spec := string(s)
119 start := strings.Index(spec, refSpecSeparator) + 1
120 dst := spec[start:]
121 src := s.Src()
122
123 if !s.IsWildcard() {
124 return plumbing.ReferenceName(dst)
125 }
126
127 name := n.String()
128 ws := strings.Index(src, refSpecWildcard)
129 wd := strings.Index(dst, refSpecWildcard)
130 match := name[ws : len(name)-(len(src)-(ws+1))]
131
132 return plumbing.ReferenceName(dst[0:wd] + match + dst[wd+1:])
133}
134
135func (s RefSpec) Reverse() RefSpec {
136 spec := string(s)
137 separator := strings.Index(spec, refSpecSeparator)
138
139 return RefSpec(spec[separator+1:] + refSpecSeparator + spec[:separator])
140}
141
142func (s RefSpec) String() string {
143 return string(s)
144}
145
146// MatchAny returns true if any of the RefSpec match with the given ReferenceName.
147func MatchAny(l []RefSpec, n plumbing.ReferenceName) bool {
148 for _, r := range l {
149 if r.Match(n) {
150 return true
151 }
152 }
153
154 return false
155}