summaryrefslogtreecommitdiff
path: root/attr.c
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2013-01-15 20:35:24 +0700
committerJunio C Hamano <gitster@pobox.com>2013-01-15 08:17:23 -0800
commit711536bd4ba791adfd506583927a8f6c8f821e24 (patch)
tree6df22b89a9a0a733c218f1f5e43dc97185fa50d1 /attr.c
parent94bc671a1f2e8610de475c2494d2763355a99f65 (diff)
downloadgit-711536bd4ba791adfd506583927a8f6c8f821e24.tar.gz
attr: fix off-by-one directory component length calculation
94bc671 (Add directory pattern matching to attributes - 2012-12-08) uses find_basename() to calculate the length of directory part in prepare_attr_stack. This function expects the directory without the trailing slash (as "origin" field in match_attr struct is without the trailing slash). find_basename() includes the trailing slash and confuses push/pop algorithm. Consider path = "abc/def" and the push down code: while (1) { len = strlen(attr_stack->origin); if (dirlen <= len) break; cp = memchr(path + len + 1, '/', dirlen - len - 1); if (!cp) cp = path + dirlen; dirlen is 4, not 3, without this patch. So when attr_stack->origin is "abc", it'll miss the exit condition because 4 <= 3 is wrong. It'll then try to push "abc/" down the attr stack (because "cp" would be NULL). So we have both "abc" and "abc/" in the stack. Next time when "abc/ghi" is checked, "abc/" is popped out because of the off-by-one dirlen, only to be pushed back in again by the above code. This repeats for all files in the same directory. Which means at least one failed open syscall per file, or more if .gitattributes exists. This is the perf result with 10 runs on git.git: Test 94bc671^ 94bc671 HEAD ---------------------------------------------------------------------------------------------------------- 7810.1: grep worktree, cheap regex 0.02(0.01+0.04) 0.05(0.03+0.05) +150.0% 0.02(0.01+0.04) +0.0% 7810.2: grep worktree, expensive regex 0.25(0.94+0.01) 0.26(0.94+0.02) +4.0% 0.25(0.93+0.02) +0.0% 7810.3: grep --cached, cheap regex 0.11(0.10+0.00) 0.12(0.10+0.02) +9.1% 0.10(0.10+0.00) -9.1% 7810.4: grep --cached, expensive regex 0.61(0.60+0.01) 0.62(0.61+0.01) +1.6% 0.61(0.60+0.00) +0.0% Reported-by: Ross Lagerwall <rosslagerwall@gmail.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'attr.c')
-rw-r--r--attr.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/attr.c b/attr.c
index 466c93fa50..bb9a470c85 100644
--- a/attr.c
+++ b/attr.c
@@ -584,6 +584,13 @@ static void prepare_attr_stack(const char *path)
dirlen = find_basename(path) - path;
/*
+ * find_basename() includes the trailing slash, but we do
+ * _not_ want it.
+ */
+ if (dirlen)
+ dirlen--;
+
+ /*
* At the bottom of the attribute stack is the built-in
* set of attribute definitions, followed by the contents
* of $(prefix)/etc/gitattributes and a file specified by