Re: [PATCH] dir: skip excluded directory with nested repo on prefix match
flat view
From: Junio C Hamano <hidden>
Date: 2026-09-22 04:57:46
René Scharfe [off-list ref] writes:
quoted hunk ↗ jump to hunk
+static int dir_match(struct index_state *istate, + const struct pathspec *pathspec, + const char *dirname, int len) +{ + return match_pathspec_with_flags(istate, pathspec, dirname, len, + 0 /* prefix */, + NULL /* seen */, + DO_MATCH_LEADING_PATHSPEC); +}
OK, this is a good helper to extract and reuse.
quoted hunk ↗ jump to hunk
@@ -1996,11 +2006,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, * for matching patterns. */ if (pathspec && !excluded) { - matches_how = match_pathspec_with_flags(istate, pathspec, - dirname, len, - 0 /* prefix */, - NULL /* seen */, - DO_MATCH_LEADING_PATHSPEC); + matches_how = dir_match(istate, pathspec, dirname, len); if (!matches_how) return path_none; }
So, this hunk shows that the treat_directory() function is prepared to be passed a NULL in pathspec. We make sure we do not trigger match_pathspec_with_flags() when pathspec is NULL, and this should extends to dir_match() now. This is a very sensible conditional, as match_pathspec_with_flags() calls do_match_pathspec() as the first thing, which begins with GUARD_PATHSPEC() macro that unconditionally dereferences the pathspec.
quoted hunk ↗ jump to hunk
@@ -2034,8 +2040,13 @@ static enum path_treatment treat_directory(struct dir_struct *dir, strbuf_release(&sb); if (nested_repo) { - if ((dir->flags & DIR_SKIP_NESTED_GIT) || - (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) + if (dir->flags & DIR_SKIP_NESTED_GIT) + return path_none; + if (!matches_how) + matches_how = dir_match(istate, pathspec, + dirname, len); + if (!matches_how || + matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC) return path_none;
Here, we do not know if we have pathspec==NULL. Shouldn't "make sure we have a result from dir_match() and return path_none as appropriate" be done only when pathspec != NULL or something like that, since dir_match() will crash if pathspec is NULL? Taking all together, something along the following line squashed into your patch, perhaps? The newly added test is not essential; it merely is to demonstrate why an extra conditional I added below would help avoid segfaulting. dir.c | 15 +++++++++------ t/t7061-wtstatus-ignore.sh | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-)
diff --git c/dir.c w/dir.c
index 6bda650891..8e858c26d1 100644
--- c/dir.c
+++ w/dir.c@@ -2042,12 +2042,15 @@ static enum path_treatment treat_directory(struct dir_struct *dir, if (nested_repo) { if (dir->flags & DIR_SKIP_NESTED_GIT) return path_none; - if (!matches_how) - matches_how = dir_match(istate, pathspec, - dirname, len); - if (!matches_how || - matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC) - return path_none; + + if (pathspec) { + if (!matches_how) + matches_how = dir_match(istate, pathspec, + dirname, len); + if (!matches_how || + matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC) + return path_none; + } return excluded ? path_excluded : path_untracked; } }
diff --git c/t/t7061-wtstatus-ignore.sh w/t/t7061-wtstatus-ignore.sh
index 182933471f..6bc680312e 100755
--- c/t/t7061-wtstatus-ignore.sh
+++ w/t/t7061-wtstatus-ignore.sh@@ -340,6 +340,31 @@ test_expect_success 'status ignores submodule in excluded directory' ' test_cmp expected actual ' +test_expect_success 'replace directory with untracked nested repo' ' + test_create_repo checkout-nested && + ( + cd checkout-nested && + test_commit base && + + # Branch with tracked file "dir" + git checkout -b branch-file && + echo "tracked file" >dir && + git add dir && + git commit -m "add file dir" && + + # Switch back to base + git checkout -b branch-nested base && + + # Create an untracked directory containing a nested git repo + mkdir -p dir/nested && + git init dir/nested && + + # Attempting checkout invokes verify_clean_subdirectory() + # with pathspec=NULL + test_must_fail git checkout branch-file + ) +' + test_expect_success 'status omits excluded directory with submodule on prefix match' ' git status --porcelain --ignored -u tracke >actual && test_must_be_empty actual