Thread (6 messages) 6 messages, 3 authors, 10d ago

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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help