Skip to content

Commit

Permalink
ls-files: fix overeager pathspec optimization
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
gitster committed Jan 9, 2010
1 parent 16e2cfa commit 48ffef9
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
38 changes: 37 additions & 1 deletion dir.c
Expand Up @@ -813,6 +813,41 @@ static void free_simplify(struct path_simplify *simplify)
free(simplify);
}

static int treat_leading_path(struct dir_struct *dir,
const char *path, int len,
const struct path_simplify *simplify)
{
char pathbuf[PATH_MAX];
int baselen, blen;
const char *cp;

while (len && path[len - 1] == '/')
len--;
if (!len)
return 1;
baselen = 0;
while (1) {
cp = path + baselen + !!baselen;
cp = memchr(cp, '/', path + len - cp);
if (!cp)
baselen = len;
else
baselen = cp - path;
memcpy(pathbuf, path, baselen);
pathbuf[baselen] = '\0';
if (!is_directory(pathbuf))
return 0;
if (simplify_away(pathbuf, baselen, simplify))
return 0;
blen = baselen;
if (treat_one_path(dir, pathbuf, &blen, simplify,
DT_DIR, NULL) == path_ignored)
return 0; /* do not recurse into it */
if (len <= baselen)
return 1; /* finished checking */
}
}

int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
{
struct path_simplify *simplify;
Expand All @@ -821,7 +856,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
return dir->nr;

simplify = create_simplify(pathspec);
read_directory_recursive(dir, path, len, 0, simplify);
if (!len || treat_leading_path(dir, path, len, simplify))
read_directory_recursive(dir, path, len, 0, simplify);
free_simplify(simplify);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
Expand Down
2 changes: 1 addition & 1 deletion t/t3001-ls-files-others-exclude.sh
Expand Up @@ -183,7 +183,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' '
test_cmp expect actual
'

test_expect_failure 'subdirectory ignore (l1)' '
test_expect_success 'subdirectory ignore (l1)' '
(
cd top/l1 &&
git ls-files -o --exclude-standard
Expand Down

0 comments on commit 48ffef9

Please sign in to comment.