@@ -63,39 +63,71 @@ static unsigned char refname_disposition[256] = {
6363 * not legal. It is legal if it is something reasonable to have under
6464 * ".git/refs/"; We do not like it if:
6565 *
66- * - any path component of it begins with ".", or
66+ * - it begins with ".", or
6767 * - it has double dots "..", or
6868 * - it has ASCII control characters, or
6969 * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
7070 * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
7171 * - it ends with a "/", or
7272 * - it ends with ".lock", or
7373 * - 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.
7478 */
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 )
7681{
7782 const char * cp ;
7883 char last = '\0' ;
84+ size_t component_start = 0 ; /* garbage - not a reasonable initial value */
85+
86+ if (sanitized )
87+ component_start = sanitized -> len ;
7988
8089 for (cp = refname ; ; cp ++ ) {
8190 int ch = * cp & 255 ;
8291 unsigned char disp = refname_disposition [ch ];
92+
93+ if (sanitized && disp != 1 )
94+ strbuf_addch (sanitized , ch );
95+
8396 switch (disp ) {
8497 case 1 :
8598 goto out ;
8699 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+ }
89107 break ;
90108 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+ }
93115 break ;
94116 case 4 :
95- return -1 ;
117+ /* forbidden char */
118+ if (sanitized )
119+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
120+ else
121+ return -1 ;
122+ break ;
96123 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+ }
99131
100132 /*
101133 * Unset the pattern flag so that we only accept
@@ -109,26 +141,48 @@ static int check_refname_component(const char *refname, int *flags)
109141out :
110142 if (cp == refname )
111143 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+ }
114151 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+ }
117160 return cp - refname ;
118161}
119162
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 )
121165{
122166 int component_len , component_count = 0 ;
123167
124- if (!strcmp (refname , "@" ))
168+ if (!strcmp (refname , "@" )) {
125169 /* Refname is a single character '@'. */
126- return -1 ;
170+ if (sanitized )
171+ strbuf_addch (sanitized , '-' );
172+ else
173+ return -1 ;
174+ }
127175
128176 while (1 ) {
177+ if (sanitized && sanitized -> len )
178+ strbuf_complete (sanitized , '/' );
179+
129180 /* 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 )
132186 return -1 ;
133187
134188 component_count ++ ;
@@ -138,13 +192,29 @@ int check_refname_format(const char *refname, int flags)
138192 refname += component_len + 1 ;
139193 }
140194
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+ }
143202 if (!(flags & REFNAME_ALLOW_ONELEVEL ) && component_count < 2 )
144203 return -1 ; /* Refname has only one component. */
145204 return 0 ;
146205}
147206
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+
148218int refname_is_safe (const char * refname )
149219{
150220 const char * rest ;
0 commit comments