Skip to content

Commit 48ffef9

Browse files
committed
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 <[email protected]>
1 parent 16e2cfa commit 48ffef9

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

dir.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,41 @@ static void free_simplify(struct path_simplify *simplify)
813813
free(simplify);
814814
}
815815

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+
816851
int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
817852
{
818853
struct path_simplify *simplify;
@@ -821,7 +856,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
821856
return dir->nr;
822857

823858
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);
825861
free_simplify(simplify);
826862
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
827863
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);

t/t3001-ls-files-others-exclude.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' '
183183
test_cmp expect actual
184184
'
185185

186-
test_expect_failure 'subdirectory ignore (l1)' '
186+
test_expect_success 'subdirectory ignore (l1)' '
187187
(
188188
cd top/l1 &&
189189
git ls-files -o --exclude-standard

0 commit comments

Comments
 (0)