Given pathspecs that share a common prefix, ls-files optimized its call into recursive directory reader by starting at the common prefix directory. If you have a directory "t" with an untracked file "t/junk" in it, but the top-level .gitignore file told us to ignore "t/", this resulted in an unexpected behaviour: $ git ls-files -o --exclude-standard $ cd t && git ls-files -o --exclude-standard junk Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx> --- Junio C Hamano <gitster@xxxxxxxxx> writes: >> I think a proper fix should be in dir.c::read_directory() where it calls >> read_directory_recursive() without first checking if the directory itself >> should be ignored. read_directory_recursive() itself has a logic to see >> if the dirent it found is worth recursing into and a similar logic should >> be in the toplevel caller. So here it is. dir.c | 37 ++++++++++++++++++++++++++++++++++ t/t3001-ls-files-others-exclude.sh | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 0 deletions(-) diff --git a/dir.c b/dir.c index f9ac454..9316eb6 100644 --- a/dir.c +++ b/dir.c @@ -787,6 +787,40 @@ static void free_simplify(struct path_simplify *simplify) free(simplify); } +static int has_leading_ignored_dir(struct dir_struct *dir, + const char *path_, int len, + const struct path_simplify *simplify) +{ + int at, dtype = DT_DIR; + char path[PATH_MAX]; + + at = 0; + memcpy(path, path_, len); + while (1) { + char *cp; + path[at] = '\0'; + /* + * NOTE! NOTE! NOTE!: we might want to actually lstat(2) + * path[] to make sure it is a directory. + */ + if (excluded(dir, path, &dtype)) + return 1; + if (at < len) { + path[at] = path_[at]; + if (at < len && path[at] == '/') + at++; + } + if (len <= at) + break; + cp = memchr(path + at, '/', len - at); + if (!cp) + at = len; + else + at = cp - path; + } + return 0; +} + int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec) { struct path_simplify *simplify; @@ -795,6 +829,9 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char return dir->nr; simplify = create_simplify(pathspec); + if (has_leading_ignored_dir(dir, path, len, simplify)) + return dir->nr; + read_directory_recursive(dir, path, len, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index c65bca8..9e71260 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -153,4 +153,43 @@ test_expect_success 'negated exclude matches can override previous ones' ' grep "^a.1" output ' +test_expect_success 'subdirectory ignore (setup)' ' + mkdir -p top/l1/l2 && + ( + cd top && + git init && + echo /.gitignore >.gitignore && + echo l1 >>.gitignore && + echo l2 >l1/.gitignore && + >l1/l2/l1 + ) +' + +test_expect_success 'subdirectory ignore (toplevel)' ' + ( + cd top && + git ls-files -o --exclude-standard + ) >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'subdirectory ignore (l1/l2)' ' + ( + cd top/l1/l2 && + git ls-files -o --exclude-standard + ) >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'subdirectory ignore (l1)' ' + ( + cd top/l1 && + git ls-files -o --exclude-standard + ) >actual && + >expect && + test_cmp expect actual +' + test_done -- 1.6.6.209.g52296.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html