Skip to content

Commit ae7dd1a

Browse files
committed
Merge branch 'dt/refs-check-refname-component-optim'
* dt/refs-check-refname-component-optim: refs.c: optimize check_refname_component()
2 parents c651ccc + dde8a90 commit ae7dd1a

File tree

2 files changed

+44
-29
lines changed

2 files changed

+44
-29
lines changed

refs.c

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,29 @@
66
#include "string-list.h"
77

88
/*
9-
* Make sure "ref" is something reasonable to have under ".git/refs/";
10-
* We do not like it if:
9+
* How to handle various characters in refnames:
10+
* 0: An acceptable character for refs
11+
* 1: End-of-component
12+
* 2: ., look for a preceding . to reject .. in refs
13+
* 3: {, look for a preceding @ to reject @{ in refs
14+
* 4: A bad character: ASCII control characters, "~", "^", ":" or SP
15+
*/
16+
static unsigned char refname_disposition[256] = {
17+
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
18+
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
19+
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
20+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
21+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
23+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
25+
};
26+
27+
/*
28+
* Try to read one refname component from the front of refname.
29+
* Return the length of the component found, or -1 if the component is
30+
* not legal. It is legal if it is something reasonable to have under
31+
* ".git/refs/"; We do not like it if:
1132
*
1233
* - any path component of it begins with ".", or
1334
* - it has double dots "..", or
@@ -16,41 +37,31 @@
1637
* - it ends with ".lock"
1738
* - it contains a "\" (backslash)
1839
*/
19-
20-
/* Return true iff ch is not allowed in reference names. */
21-
static inline int bad_ref_char(int ch)
22-
{
23-
if (((unsigned) ch) <= ' ' || ch == 0x7f ||
24-
ch == '~' || ch == '^' || ch == ':' || ch == '\\')
25-
return 1;
26-
/* 2.13 Pattern Matching Notation */
27-
if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
28-
return 1;
29-
return 0;
30-
}
31-
32-
/*
33-
* Try to read one refname component from the front of refname. Return
34-
* the length of the component found, or -1 if the component is not
35-
* legal.
36-
*/
3740
static int check_refname_component(const char *refname, int flags)
3841
{
3942
const char *cp;
4043
char last = '\0';
4144

4245
for (cp = refname; ; cp++) {
43-
char ch = *cp;
44-
if (ch == '\0' || ch == '/')
46+
int ch = *cp & 255;
47+
unsigned char disp = refname_disposition[ch];
48+
switch (disp) {
49+
case 1:
50+
goto out;
51+
case 2:
52+
if (last == '.')
53+
return -1; /* Refname contains "..". */
54+
break;
55+
case 3:
56+
if (last == '@')
57+
return -1; /* Refname contains "@{". */
4558
break;
46-
if (bad_ref_char(ch))
47-
return -1; /* Illegal character in refname. */
48-
if (last == '.' && ch == '.')
49-
return -1; /* Refname contains "..". */
50-
if (last == '@' && ch == '{')
51-
return -1; /* Refname contains "@{". */
59+
case 4:
60+
return -1;
61+
}
5262
last = ch;
5363
}
64+
out:
5465
if (cp == refname)
5566
return 0; /* Component has zero length. */
5667
if (refname[0] == '.') {

t/t5511-refspec.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ test_description='refspec parsing'
55
. ./test-lib.sh
66

77
test_refspec () {
8-
98
kind=$1 refspec=$2 expect=$3
109
git config remote.frotz.url "." &&
1110
git config --remove-section remote.frotz &&
@@ -84,4 +83,9 @@ test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
8483
test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
8584
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
8685

86+
good=$(printf '\303\204')
87+
test_refspec fetch "refs/heads/${good}"
88+
bad=$(printf '\011tab')
89+
test_refspec fetch "refs/heads/${bad}" invalid
90+
8791
test_done

0 commit comments

Comments
 (0)