Skip to content

Commit 777b420

Browse files
newrengitster
authored andcommitted
dir: synchronize treat_leading_path() and read_directory_recursive()
Our optimization to avoid calling into read_directory_recursive() when all pathspecs have a common leading directory mean that we need to match the logic that read_directory_recursive() would use if we had just called it from the root. Since it does more than call treat_path() we need to copy that same logic. Alternatively, we could try to change treat_path to return path_recurse for an untracked directory under the given special circumstances that this logic checks for, but a simple switch results in many test failures such as 'git clean -d' not wiping out untracked but empty directories. To work around that, we'd need the caller of treat_path to check for path_recurse and sometimes special case it into path_untracked. In other words, we'd still have extra logic in both places. Needing to duplicate logic like this means it is guaranteed someone will eventually need to make further changes and forget to update both locations. It is tempting to just nuke the leading_directory special casing to avoid such bugs and simplify the code, but unpack_trees' verify_clean_subdirectory() also calls read_directory() and does so with a non-empty leading path, so I'm hesitant to try to restructure further. Add obnoxious warnings to treat_leading_path() and read_directory_recursive() to try to warn people of such problems. Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b9670c1 commit 777b420

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

dir.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,6 +1990,15 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
19901990
struct untracked_cache_dir *untracked, int check_only,
19911991
int stop_at_first_file, const struct pathspec *pathspec)
19921992
{
1993+
/*
1994+
* WARNING WARNING WARNING:
1995+
*
1996+
* Any updates to the traversal logic here may need corresponding
1997+
* updates in treat_leading_path(). See the commit message for the
1998+
* commit adding this warning as well as the commit preceding it
1999+
* for details.
2000+
*/
2001+
19932002
struct cached_dir cdir;
19942003
enum path_treatment state, subdir_state, dir_state = path_none;
19952004
struct strbuf path = STRBUF_INIT;
@@ -2101,6 +2110,15 @@ static int treat_leading_path(struct dir_struct *dir,
21012110
const char *path, int len,
21022111
const struct pathspec *pathspec)
21032112
{
2113+
/*
2114+
* WARNING WARNING WARNING:
2115+
*
2116+
* Any updates to the traversal logic here may need corresponding
2117+
* updates in treat_leading_path(). See the commit message for the
2118+
* commit adding this warning as well as the commit preceding it
2119+
* for details.
2120+
*/
2121+
21042122
struct strbuf sb = STRBUF_INIT;
21052123
int prevlen, baselen;
21062124
const char *cp;
@@ -2166,6 +2184,18 @@ static int treat_leading_path(struct dir_struct *dir,
21662184
de->d_name[baselen-prevlen] = '\0';
21672185
state = treat_path(dir, NULL, &cdir, istate, &sb, prevlen,
21682186
pathspec);
2187+
if (state == path_untracked &&
2188+
get_dtype(cdir.de, istate, sb.buf, sb.len) == DT_DIR &&
2189+
(dir->flags & DIR_SHOW_IGNORED_TOO ||
2190+
do_match_pathspec(istate, pathspec, sb.buf, sb.len,
2191+
baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) {
2192+
add_path_to_appropriate_result_list(dir, NULL, &cdir,
2193+
istate,
2194+
&sb, baselen,
2195+
pathspec, state);
2196+
state = path_recurse;
2197+
}
2198+
21692199
if (state != path_recurse)
21702200
break; /* do not recurse into it */
21712201
if (len <= baselen)

t/t3011-common-prefixes-and-directory-traversal.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ test_expect_success 'git ls-files -o consistent between one or two dirs' '
195195

196196
# ls-files doesn't have a way to request showing both untracked and ignored
197197
# files at the same time, so use `git status --ignored`
198-
test_expect_failure 'git status --ignored shows same files under dir with or without pathspec' '
198+
test_expect_success 'git status --ignored shows same files under dir with or without pathspec' '
199199
cat <<-EOF >expect &&
200200
?? an_untracked_dir/
201201
!! an_untracked_dir/ignored

t/t7061-wtstatus-ignore.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ cat >expected <<\EOF
4747
!! untracked/ignored
4848
EOF
4949

50-
test_expect_failure 'status of untracked directory with --ignored works with or without prefix' '
50+
test_expect_success 'status of untracked directory with --ignored works with or without prefix' '
5151
git status --porcelain --ignored >tmp &&
5252
grep untracked/ tmp >actual &&
5353
rm tmp &&

0 commit comments

Comments
 (0)