@matheusbsbs / matheus-bagliano / commits / 48ffef966c
Fork 0

ls-files: fix overeager pathspec optimization

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: $ git ls-files -o --exclude-standard $ git ls-files -o --exclude-standard t/ t/junk $ git ls-files -o --exclude-standard t/junk t/junk $ cd t && git ls-files -o --exclude-standard junk We could argue that you are overriding the ignore file by giving a patchspec that matches or being in that directory, but it is somewhat unexpected. Worse yet, these behave differently: $ git ls-files -o --exclude-standard t/ . $ git ls-files -o --exclude-standard t/ t/junk This patch changes the optimization so that it notices when the common prefix directory that it starts reading from is an ignored one. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jan 8, 2010 at 23:05 UTC 48ffef966c762578eb818c0c54a7e11dd054f5db
2 files changed +38 -2
dir.c
+37 -1
@@ -813,6 +813,41 @@ static void free_simplify(struct path_simplify *simplify)
813 free(simplify);
814 }
815
816 +static int treat_leading_path(struct dir_struct *dir,
817 + const char *path, int len,
818 + const struct path_simplify *simplify)
819 +{
820 + char pathbuf[PATH_MAX];
821 + int baselen, blen;
822 + const char *cp;
823 +
824 + while (len && path[len - 1] == '/')
825 + len--;
826 + if (!len)
827 + return 1;
828 + baselen = 0;
829 + while (1) {
830 + cp = path + baselen + !!baselen;
831 + cp = memchr(cp, '/', path + len - cp);
832 + if (!cp)
833 + baselen = len;
834 + else
835 + baselen = cp - path;
836 + memcpy(pathbuf, path, baselen);
837 + pathbuf[baselen] = '\0';
838 + if (!is_directory(pathbuf))
839 + return 0;
840 + if (simplify_away(pathbuf, baselen, simplify))
841 + return 0;
842 + blen = baselen;
843 + if (treat_one_path(dir, pathbuf, &blen, simplify,
844 + DT_DIR, NULL) == path_ignored)
845 + return 0; /* do not recurse into it */
846 + if (len <= baselen)
847 + return 1; /* finished checking */
848 + }
849 +}
850 +
851 int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
852 {
853 struct path_simplify *simplify;
@@ -821,7 +856,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
856 return dir->nr;
857
858 simplify = create_simplify(pathspec);
824 - read_directory_recursive(dir, path, len, 0, simplify);
859 + if (!len || treat_leading_path(dir, path, len, simplify))
860 + read_directory_recursive(dir, path, len, 0, simplify);
861 free_simplify(simplify);
862 qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
863 qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
t/t3001-ls-files-others-exclude.sh
+1 -1
@@ -183,7 +183,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' '
183 test_cmp expect actual
184 '
185
186 -test_expect_failure 'subdirectory ignore (l1)' '
186 +test_expect_success 'subdirectory ignore (l1)' '
187 (
188 cd top/l1 &&
189 git ls-files -o --exclude-standard