@@ -63,39 +63,71 @@ static unsigned char refname_disposition[256] = {
63
63
* not legal. It is legal if it is something reasonable to have under
64
64
* ".git/refs/"; We do not like it if:
65
65
*
66
- * - any path component of it begins with ".", or
66
+ * - it begins with ".", or
67
67
* - it has double dots "..", or
68
68
* - it has ASCII control characters, or
69
69
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
70
70
* - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
71
71
* - it ends with a "/", or
72
72
* - it ends with ".lock", or
73
73
* - it contains a "@{" portion
74
+ *
75
+ * When sanitized is not NULL, instead of rejecting the input refname
76
+ * as an error, try to come up with a usable replacement for the input
77
+ * refname in it.
74
78
*/
75
- static int check_refname_component (const char * refname , int * flags )
79
+ static int check_refname_component (const char * refname , int * flags ,
80
+ struct strbuf * sanitized )
76
81
{
77
82
const char * cp ;
78
83
char last = '\0' ;
84
+ size_t component_start = 0 ; /* garbage - not a reasonable initial value */
85
+
86
+ if (sanitized )
87
+ component_start = sanitized -> len ;
79
88
80
89
for (cp = refname ; ; cp ++ ) {
81
90
int ch = * cp & 255 ;
82
91
unsigned char disp = refname_disposition [ch ];
92
+
93
+ if (sanitized && disp != 1 )
94
+ strbuf_addch (sanitized , ch );
95
+
83
96
switch (disp ) {
84
97
case 1 :
85
98
goto out ;
86
99
case 2 :
87
- if (last == '.' )
88
- return -1 ; /* Refname contains "..". */
100
+ if (last == '.' ) { /* Refname contains "..". */
101
+ if (sanitized )
102
+ /* collapse ".." to single "." */
103
+ strbuf_setlen (sanitized , sanitized -> len - 1 );
104
+ else
105
+ return -1 ;
106
+ }
89
107
break ;
90
108
case 3 :
91
- if (last == '@' )
92
- return -1 ; /* Refname contains "@{". */
109
+ if (last == '@' ) { /* Refname contains "@{". */
110
+ if (sanitized )
111
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
112
+ else
113
+ return -1 ;
114
+ }
93
115
break ;
94
116
case 4 :
95
- return -1 ;
117
+ /* forbidden char */
118
+ if (sanitized )
119
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
120
+ else
121
+ return -1 ;
122
+ break ;
96
123
case 5 :
97
- if (!(* flags & REFNAME_REFSPEC_PATTERN ))
98
- return -1 ; /* refspec can't be a pattern */
124
+ if (!(* flags & REFNAME_REFSPEC_PATTERN )) {
125
+ /* refspec can't be a pattern */
126
+ if (sanitized )
127
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
128
+ else
129
+ return -1 ;
130
+ }
99
131
100
132
/*
101
133
* Unset the pattern flag so that we only accept
@@ -109,26 +141,48 @@ static int check_refname_component(const char *refname, int *flags)
109
141
out :
110
142
if (cp == refname )
111
143
return 0 ; /* Component has zero length. */
112
- if (refname [0 ] == '.' )
113
- return -1 ; /* Component starts with '.'. */
144
+
145
+ if (refname [0 ] == '.' ) { /* Component starts with '.'. */
146
+ if (sanitized )
147
+ sanitized -> buf [component_start ] = '-' ;
148
+ else
149
+ return -1 ;
150
+ }
114
151
if (cp - refname >= LOCK_SUFFIX_LEN &&
115
- !memcmp (cp - LOCK_SUFFIX_LEN , LOCK_SUFFIX , LOCK_SUFFIX_LEN ))
116
- return -1 ; /* Refname ends with ".lock". */
152
+ !memcmp (cp - LOCK_SUFFIX_LEN , LOCK_SUFFIX , LOCK_SUFFIX_LEN )) {
153
+ if (!sanitized )
154
+ return -1 ;
155
+ /* Refname ends with ".lock". */
156
+ while (strbuf_strip_suffix (sanitized , LOCK_SUFFIX )) {
157
+ /* try again in case we have .lock.lock */
158
+ }
159
+ }
117
160
return cp - refname ;
118
161
}
119
162
120
- int check_refname_format (const char * refname , int flags )
163
+ static int check_or_sanitize_refname (const char * refname , int flags ,
164
+ struct strbuf * sanitized )
121
165
{
122
166
int component_len , component_count = 0 ;
123
167
124
- if (!strcmp (refname , "@" ))
168
+ if (!strcmp (refname , "@" )) {
125
169
/* Refname is a single character '@'. */
126
- return -1 ;
170
+ if (sanitized )
171
+ strbuf_addch (sanitized , '-' );
172
+ else
173
+ return -1 ;
174
+ }
127
175
128
176
while (1 ) {
177
+ if (sanitized && sanitized -> len )
178
+ strbuf_complete (sanitized , '/' );
179
+
129
180
/* We are at the start of a path component. */
130
- component_len = check_refname_component (refname , & flags );
131
- if (component_len <= 0 )
181
+ component_len = check_refname_component (refname , & flags ,
182
+ sanitized );
183
+ if (sanitized && component_len == 0 )
184
+ ; /* OK, omit empty component */
185
+ else if (component_len <= 0 )
132
186
return -1 ;
133
187
134
188
component_count ++ ;
@@ -138,13 +192,29 @@ int check_refname_format(const char *refname, int flags)
138
192
refname += component_len + 1 ;
139
193
}
140
194
141
- if (refname [component_len - 1 ] == '.' )
142
- return -1 ; /* Refname ends with '.'. */
195
+ if (refname [component_len - 1 ] == '.' ) {
196
+ /* Refname ends with '.'. */
197
+ if (sanitized )
198
+ ; /* omit ending dot */
199
+ else
200
+ return -1 ;
201
+ }
143
202
if (!(flags & REFNAME_ALLOW_ONELEVEL ) && component_count < 2 )
144
203
return -1 ; /* Refname has only one component. */
145
204
return 0 ;
146
205
}
147
206
207
+ int check_refname_format (const char * refname , int flags )
208
+ {
209
+ return check_or_sanitize_refname (refname , flags , NULL );
210
+ }
211
+
212
+ void sanitize_refname_component (const char * refname , struct strbuf * out )
213
+ {
214
+ if (check_or_sanitize_refname (refname , REFNAME_ALLOW_ONELEVEL , out ))
215
+ BUG ("sanitizing refname '%s' check returned error" , refname );
216
+ }
217
+
148
218
int refname_is_safe (const char * refname )
149
219
{
150
220
const char * rest ;
0 commit comments