dir: also check directories for matching pathspecs

Even if a directory doesn't match a pathspec, it is possible, depending
on the precise pathspecs, that some file underneath it might.  So we
special case and recurse into the directory for such situations.  However,
we previously always added any untracked directory that we recursed into
to the list of untracked paths, regardless of whether the directory
itself matched the pathspec.

For the case of git-clean and a set of pathspecs of "dir/file" and "more",
this caused a problem because we'd end up with dir entries for both of
  "dir"
  "dir/file"
Then correct_untracked_entries() would try to helpfully prune duplicates
for us by removing "dir/file" since it's under "dir", leaving us with
  "dir"
Since the original pathspec only had "dir/file", the only entry left
doesn't match and leaves nothing to be removed.  (Note that if only one
pathspec was specified, e.g. only "dir/file", then the common_prefix_len
optimizations in fill_directory would cause us to bypass this problem,
making it appear in simple tests that we could correctly remove manually
specified pathspecs.)

Fix this by actually checking whether the directory we are about to add
to the list of dir entries actually matches the pathspec; only do this
matching check after we have already returned from recursing into the
directory.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Elijah Newren 2019-09-17 09:34:56 -07:00 committed by Junio C Hamano
parent a5e916c745
commit 404ebceda0
2 changed files with 7 additions and 2 deletions

5
dir.c
View File

@ -1951,6 +1951,11 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
check_only, stop_at_first_file, pathspec); check_only, stop_at_first_file, pathspec);
if (subdir_state > dir_state) if (subdir_state > dir_state)
dir_state = subdir_state; dir_state = subdir_state;
if (!match_pathspec(istate, pathspec, path.buf, path.len,
0 /* prefix */, NULL,
0 /* do NOT special case dirs */))
state = path_none;
} }
if (check_only) { if (check_only) {

View File

@ -699,7 +699,7 @@ test_expect_failure 'git clean handles being told what to clean' '
test_path_is_missing d2/ut test_path_is_missing d2/ut
' '
test_expect_failure 'git clean handles being told what to clean, with -d' ' test_expect_success 'git clean handles being told what to clean, with -d' '
mkdir -p d1 d2 && mkdir -p d1 d2 &&
touch d1/ut d2/ut && touch d1/ut d2/ut &&
git clean -ffd */ut && git clean -ffd */ut &&
@ -715,7 +715,7 @@ test_expect_failure 'git clean works if a glob is passed without -d' '
test_path_is_missing d2/ut test_path_is_missing d2/ut
' '
test_expect_failure 'git clean works if a glob is passed with -d' ' test_expect_success 'git clean works if a glob is passed with -d' '
mkdir -p d1 d2 && mkdir -p d1 d2 &&
touch d1/ut d2/ut && touch d1/ut d2/ut &&
git clean -ffd "*ut" && git clean -ffd "*ut" &&