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

[RFC PATCH 3/6] dir.c: teach parse_path_pattern() precious files

From
Mirko Faina <mroik@delayed.space>
Date
Oct 8, 2026, 12:06 UTC
Message-ID
<677a1af4c6bacf129450e3c0c150d27bf65baecf.1791460418.git.mroik@delayed.space>
In-Reply-To
<cover.1791460418.git.mroik@delayed.space>

parse_path_pattern() knows only to recognize patterns that are either directories or normal files. It also knows that there are negated patterns, but it doesn't know about patterns that indicate precious files.

Teach about precious files to parse_path_pattern(). We teach it also to detect the invalid patterns starting with '!$' which would mean a negated precious file, that we don't allow. This is reported back to the caller so we change its signature to return an int. We also teach the callers parse_attr_line() and add_pattern() to handle such errors.

Signed-off-by: Mirko Faina <mroik@delayed.space>
---
 attr.c            |  8 ++++++--
 attr.h            |  1 +
 dir.c             | 26 +++++++++++++++++++++++---
 dir.h             |  2 +-
 t/t7508-status.sh |  9 +++++++++
 5 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/attr.c b/attr.c
index 0e63f1b6de..5c60ca6b23 100644
--- a/attr.c
+++ b/attr.c
@@ -382,10 +382,14 @@ struct match_attr *parse_attr_line(const char *line, const char *src,
 		char *p = (char *)&(res->state[num_attr]);
 		memcpy(p, name, namelen);
 		res->u.pat.pattern = p;
-		parse_path_pattern(&res->u.pat.pattern,
+		if (parse_path_pattern(&res->u.pat.pattern,
 				      &res->u.pat.patternlen,
 				      &res->u.pat.flags,
-				      &res->u.pat.nowildcardlen);
+				      &res->u.pat.nowildcardlen)) {
+			warning(_("pattern '%s' is invalid, skipping"),
+				  (char *)&res->u.pat.pattern);
+			goto fail_return;
+		}
 		if (res->u.pat.flags & PATTERN_FLAG_NEGATIVE) {
 			warning(_("Negative patterns are ignored in git attributes\n"
 				  "Use '\\!' for literal leading exclamation."));
diff --git a/attr.h b/attr.h
index c083d47df5..d00939732b 100644
--- a/attr.h
+++ b/attr.h
@@ -255,6 +255,7 @@ enum pattern_flags {
 	PATTERN_FLAG_ENDSWITH = 4,
 	PATTERN_FLAG_MUSTBEDIR = 8,
 	PATTERN_FLAG_NEGATIVE = 16,
+	PATTERN_FLAG_PRECIOUS = 32,
 };
 
 struct pattern {
diff --git a/dir.c b/dir.c
index c6342c882a..c6f1bed429 100644
--- a/dir.c
+++ b/dir.c
@@ -700,7 +700,10 @@ int no_wildcard(const char *string)
 	return string[simple_length(string)] == '\0';
 }
 
-void parse_path_pattern(const char **pattern,
+/*
+ * Returns 1 if the pattern is problematic, 0 otherwise
+ */
+int parse_path_pattern(const char **pattern,
 			   int *patternlen,
 			   enum pattern_flags *flags,
 			   int *nowildcardlen)
@@ -709,7 +712,12 @@ void parse_path_pattern(const char **pattern,
 	size_t i, len;
 
 	*flags = 0;
-	if (*p == '!') {
+	if (simple_length(p) >= 2 && p[0] == '!' && p[1] == '$') {
+		return 1;
+	} else if (*p == '$') {
+		*flags |= PATTERN_FLAG_PRECIOUS;
+		p++;
+	} else if (*p == '!') {
 		*flags |= PATTERN_FLAG_NEGATIVE;
 		p++;
 	}
@@ -736,6 +744,7 @@ void parse_path_pattern(const char **pattern,
 		*flags |= PATTERN_FLAG_ENDSWITH;
 	*pattern = p;
 	*patternlen = len;
+	return 0;
 }
 
 int pl_hashmap_cmp(const void *cmp_data UNUSED,
@@ -975,6 +984,14 @@ int hashmap_contains_parent(struct hashmap *map,
 	return 0;
 }
 
+/*
+ * Parses the pattern for its type and sets flags accordingly, then adds it to
+ * the pattern list. If the pattern is invalid the function returns early with a
+ * warning.
+ *
+ * The only problematic patterns at the moment are the one starting with '!$'
+ * which are negated precious-files.
+ */
 void add_pattern(const char *string, const char *base,
 		 int baselen, struct pattern_list *pl, int srcpos)
 {
@@ -983,7 +1000,10 @@ void add_pattern(const char *string, const char *base,
 	enum pattern_flags flags;
 	int nowildcardlen;
 
-	parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
+	if (parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen)) {
+		warning(_("pattern '%s' is problematic, skipping"), string);
+		return;
+	}
 	FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
 	pattern->patternlen = patternlen;
 	pattern->nowildcardlen = nowildcardlen;
diff --git a/dir.h b/dir.h
index 210ee8a98d..5cda2cdba7 100644
--- a/dir.h
+++ b/dir.h
@@ -465,7 +465,7 @@ int add_patterns_from_blob_to_list(struct object_id *oid,
 int add_patterns_from_buffer(char *buf, size_t size,
 			     const char *base, int baselen,
 			     struct pattern_list *pl);
-void parse_path_pattern(const char **string, int *patternlen, enum pattern_flags *flags, int *nowildcardlen);
+int parse_path_pattern(const char **string, int *patternlen, enum pattern_flags *flags, int *nowildcardlen);
 void add_pattern(const char *string, const char *base,
 		 int baselen, struct pattern_list *pl, int srcpos);
 void clear_pattern_list(struct pattern_list *pl);
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 0fd7c79911..aa251c6abd 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -301,6 +301,15 @@ EOF
 	test_cmp expect output
 '
 
+test_expect_success 'parse invalid pattern' "
+	test_when_finished rm actual gitignore_backup &&
+	test_when_finished cp gitignore_backup .gitignore &&
+	cp .gitignore gitignore_backup &&
+	echo '!$/ciao' >> .gitignore &&
+	git status >actual 2>&1 &&
+	test_grep \"warning: pattern '!$/ciao' is problematic, skipping\" actual
+"
+
 test_expect_success 'status with gitignore (nothing untracked)' '
 	{
 		echo ".gitignore" &&
-- 
2.56.0
Previous: Mirko FainaNext: Mirko Faina
Message 4 of 8 in “Introduce precious files”
  1. 0/6 Introduce precious filesMirko Faina, Oct 8, 2026
  2. 1/6 precious-files.txt: new document proposing new precious file typeMirko Faina, Oct 8, 2026
  3. 2/6 dir.h: replace pattern macros with enum in attr.hMirko Faina, Oct 8, 2026
  4. 3/6 dir.c: teach parse_path_pattern() precious filesMirko Faina, Oct 8, 2026
  5. 4/6 dir.c: teach add_pattern() reject precious patternMirko Faina, Oct 8, 2026
  6. 5/6 unpack-trees: teach check_ok_to_remove() preciousMirko Faina, Oct 8, 2026
  7. 6/6 builtin/ls-files.c: support for precious filesMirko Faina, Oct 8, 2026
  8. Mirko FainaOct 8, 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.