No, you found one of the longstanding bugs in dir.c:read_directory().
The funny thing is that I just sent out a message pointing out
bogus handling of per-directory exclude files in ls-files last
night. Somehow people have a tendency to encounter the bugs in
the same vicinity independently.
The initial loop in read_directory() to push per-directory
exclusion elements into the stack for directories above the
given base forgets that push() does not make a copy of the path
given as its parameter but stores the pointer to it instead, so
multiple calls to push() need to use separate path buffers.
Here is a tentative patch. I do not think the patch is broken
but I call it tentative because:
- It is ugly -- I never get this "walking path delimited by
slashes" loop right;
- It leaks the path buffer given to push(), but it is inherent
in the design of "push/pop exclude per-directory" API. They
were designed to be called from the recursive directory
walking, and the path buffers are placed on the function call
stack to be reclaimed automatically upon function return;
---
dir.c | 55 ++++++++++++++++++++++++++++++++++---------------------
1 files changed, 34 insertions(+), 21 deletions(-)
diff --git a/dir.c b/dir.c
index fa9f902..d32f437 100644
--- a/dir.c
+++ b/dir.c
@@ -651,38 +651,51 @@ static void free_simplify(struct path_simplify *simplify)
free(simplify);
}
+static int push_excludes(struct dir_struct *dir, const char *base, int len)
+{
+ /*
+ * base is like "a/b/c/" -- cause .gitignore, b/.gitignore and
+ * b/c/.gitignore to be read in this order, as if we recursed
+ * into it.
+ */
+ int stk = -1;
+ int partlen = 0;
+
+ if (!(dir->exclude_per_dir && len))
+ return stk;
+
+ while (1) {
+ char *part = xmalloc(partlen + 1);
+ memcpy(part, base, partlen);
+ part[partlen] = '\0';
+ stk = push_exclude_per_directory(dir, part, partlen);
+
+ if (len <= partlen++)
+ break;
+
+ while (partlen < len && base[partlen] != '/')
+ partlen++;
+ partlen++; /* point at one past the found '/' */
+ }
+ return stk;
+}
+
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
{
struct path_simplify *simplify = create_simplify(pathspec);
+ int stk;
/*
* Make sure to do the per-directory exclude for all the
* directories leading up to our base.
*/
- if (baselen) {
- if (dir->exclude_per_dir) {
- char *p, *pp = xmalloc(baselen+1);
- memcpy(pp, base, baselen+1);
- p = pp;
- while (1) {
- char save = *p;
- *p = 0;
- push_exclude_per_directory(dir, pp, p-pp);
- *p++ = save;
- if (!save)
- break;
- p = strchr(p, '/');
- if (p)
- p++;
- else
- p = pp + baselen;
- }
- free(pp);
- }
- }
+ stk = push_excludes(dir, base, baselen);
read_directory_recursive(dir, path, base, baselen, 0, simplify);
free_simplify(simplify);
+ if (0 <= stk)
+ pop_exclude_per_directory(dir, stk);
+
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html