@@ -3,6 +3,7 @@ package plumbing
3
3
import (
4
4
"errors"
5
5
"fmt"
6
+ "regexp"
6
7
"strings"
7
8
)
8
9
@@ -29,6 +30,9 @@ var RefRevParseRules = []string{
29
30
30
31
var (
31
32
ErrReferenceNotFound = errors .New ("reference not found" )
33
+
34
+ // ErrInvalidReferenceName is returned when a reference name is invalid.
35
+ ErrInvalidReferenceName = errors .New ("invalid reference name" )
32
36
)
33
37
34
38
// ReferenceType reference type's
@@ -124,6 +128,91 @@ func (r ReferenceName) Short() string {
124
128
return res
125
129
}
126
130
131
+ var (
132
+ ctrlSeqs = regexp .MustCompile (`[\000-\037\177]` )
133
+ )
134
+
135
+ // Validate validates a reference name.
136
+ // This follows the git-check-ref-format rules.
137
+ // See https://git-scm.com/docs/git-check-ref-format
138
+ //
139
+ // It is important to note that this function does not check if the reference
140
+ // exists in the repository.
141
+ // It only checks if the reference name is valid.
142
+ // This functions does not support the --refspec-pattern, --normalize, and
143
+ // --allow-onelevel options.
144
+ //
145
+ // Git imposes the following rules on how references are named:
146
+ //
147
+ // 1. They can include slash / for hierarchical (directory) grouping, but no
148
+ // slash-separated component can begin with a dot . or end with the
149
+ // sequence .lock.
150
+ // 2. They must contain at least one /. This enforces the presence of a
151
+ // category like heads/, tags/ etc. but the actual names are not
152
+ // restricted. If the --allow-onelevel option is used, this rule is
153
+ // waived.
154
+ // 3. They cannot have two consecutive dots .. anywhere.
155
+ // 4. They cannot have ASCII control characters (i.e. bytes whose values are
156
+ // lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon :
157
+ // anywhere.
158
+ // 5. They cannot have question-mark ?, asterisk *, or open bracket [
159
+ // anywhere. See the --refspec-pattern option below for an exception to this
160
+ // rule.
161
+ // 6. They cannot begin or end with a slash / or contain multiple consecutive
162
+ // slashes (see the --normalize option below for an exception to this rule).
163
+ // 7. They cannot end with a dot ..
164
+ // 8. They cannot contain a sequence @{.
165
+ // 9. They cannot be the single character @.
166
+ // 10. They cannot contain a \.
167
+ func (r ReferenceName ) Validate () error {
168
+ s := string (r )
169
+ if len (s ) == 0 {
170
+ return ErrInvalidReferenceName
171
+ }
172
+
173
+ // HEAD is a special case
174
+ if r == HEAD {
175
+ return nil
176
+ }
177
+
178
+ // rule 7
179
+ if strings .HasSuffix (s , "." ) {
180
+ return ErrInvalidReferenceName
181
+ }
182
+
183
+ // rule 2
184
+ parts := strings .Split (s , "/" )
185
+ if len (parts ) < 2 {
186
+ return ErrInvalidReferenceName
187
+ }
188
+
189
+ isBranch := r .IsBranch ()
190
+ isTag := r .IsTag ()
191
+ for _ , part := range parts {
192
+ // rule 6
193
+ if len (part ) == 0 {
194
+ return ErrInvalidReferenceName
195
+ }
196
+
197
+ if strings .HasPrefix (part , "." ) || // rule 1
198
+ strings .Contains (part , ".." ) || // rule 3
199
+ ctrlSeqs .MatchString (part ) || // rule 4
200
+ strings .ContainsAny (part , "~^:?*[ \t \n " ) || // rule 4 & 5
201
+ strings .Contains (part , "@{" ) || // rule 8
202
+ part == "@" || // rule 9
203
+ strings .Contains (part , "\\ " ) || // rule 10
204
+ strings .HasSuffix (part , ".lock" ) { // rule 1
205
+ return ErrInvalidReferenceName
206
+ }
207
+
208
+ if (isBranch || isTag ) && strings .HasPrefix (part , "-" ) { // branches & tags can't start with -
209
+ return ErrInvalidReferenceName
210
+ }
211
+ }
212
+
213
+ return nil
214
+ }
215
+
127
216
const (
128
217
HEAD ReferenceName = "HEAD"
129
218
Master ReferenceName = "refs/heads/master"
0 commit comments