From: Junio C Hamano Date: Tue, 22 Sep 2026 04:57:43 GMT Subject: Re: [PATCH] dir: skip excluded directory with nested repo on prefix match Message-ID: In-Reply-To: René Scharfe writes: > +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. > @@ -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. > @@ -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