refs.c: optimize check_refname_component()
In a repository with many refs, check_refname_component can be a major contributor to the runtime of some git commands. One such command is git rev-parse HEAD Timings for one particular repo, with about 60k refs, almost all packed, are: Old: 35 ms New: 29 ms Many other commands which read refs are also sped up. Signed-off-by: David Turner <dturner@twitter.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
David Turner committed
Jun 3, 2014 at 23:38 UTC
dde8a902c774268628015705a7767f61af18b865
2 files changed
+44
-29
refs.c
+39
-28
@@ -6,8 +6,29 @@
6
#include "string-list.h"
7
8
/*
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:
32
*
33
* - any path component of it begins with ".", or
34
* - it has double dots "..", or
@@ -16,41 +37,31 @@
37
* - it ends with ".lock"
38
* - it contains a "\" (backslash)
39
*/
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
- */
40
static int check_refname_component(const char *refname, int flags)
41
{
42
const char *cp;
43
char last = '\0';
44
45
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 "@{". */
58
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
+ }
62
last = ch;
63
}
64
+out:
65
if (cp == refname)
66
return 0; /* Component has zero length. */
67
if (refname[0] == '.') {
t/t5511-refspec.sh
+5
-1
@@ -5,7 +5,6 @@ test_description='refspec parsing'
5
. ./test-lib.sh
6
7
test_refspec () {
8
-
8
kind=$1 refspec=$2 expect=$3
9
git config remote.frotz.url "." &&
10
git config --remove-section remote.frotz &&
@@ -84,4 +83,9 @@ test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
83
test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
84
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
85
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
+
91
test_done