git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] dir: skip excluded directory with nested repo on prefix match

From
Junio C Hamano <gitster@pobox.com>
Date
Sep 22, 2026, 04:57 UTC
Message-ID
<xmqqjyodj320.fsf@gitster.g>
In-Reply-To
<be53c379-b0e1-4242-8504-e96c2c49d294@web.de>
René Scharfe <l.s.r@web.de> writes:
Show 9 quoted lines
> +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.
Show 13 quoted lines
> @@ -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.

Show 14 quoted lines
> @@ -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
Previous: René ScharfeNext: René Scharfe
Message 4 of 6 in “[BUG] 'git status --ignored' <pathspec> matches partial names”
  1. Sean WhittonSep 14, 2026
  2. René ScharfeSep 15, 2026
  3. dir: skip excluded directory with nested repo on prefix matchRené Scharfe, Sep 18, 2026
  4. Junio C HamanoSep 22, 2026
  5. René ScharfeSep 26, 2026
  6. dir: skip excluded directory with nested repo on prefix matchRené Scharfe, Sep 26, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.