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

[PATCH] attr: always treat the leading exclamation mark as literal

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Mar 2, 2013, 04:18 UTC
Message-ID
<1362197913-313-1-git-send-email-pclouds@gmail.com>
In-Reply-To
<a1679159dbe67c34675bbe016e131260310db189.1362168366.git.trast@student.ethz.ch>

8b1bd02 (Make !pattern in .gitattributes non-fatal - 2013-03-01) raises the fact that even though '!' should have been a negative pattern indicator, people have been misusing for plain '!' and old Git versions accept that. This makes the leading '!' a de facto literal. We can't ever make it negative indicator again because we can't distinguish the literal '!' in existing repos and the negative '!' in future repos.

Acknowledge it in document and code that the leading '!' is forever literal.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Perhaps this on top of Thomas' patch? I see no way else that we can
 change the meaning of '!' again if such patterns are already stored
 in repositories.
 Documentation/gitattributes.txt | 3 ++-
 attr.c                          | 8 ++------
 dir.c                           | 7 ++++---
 dir.h                           | 6 +++++-
 t/t0003-attributes.sh           | 5 ++---
 5 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 2698f63..02bff9d 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -56,7 +56,8 @@ When more than one pattern matches the path, a later line
 overrides an earlier line.  This overriding is done per
 attribute.  The rules how the pattern matches paths are the
 same as in `.gitignore` files; see linkgit:gitignore[5].
-Unlike `.gitignore`, negative patterns are forbidden.
+Unlike `.gitignore`, the leading `!` is treated as a literal
+exclamation, not an indicator of negative patterns.
 
 When deciding what attributes are assigned to a path, git
 consults `$GIT_DIR/info/attributes` file (which has the highest
diff --git a/attr.c b/attr.c
index d181d98..838ec21 100644
--- a/attr.c
+++ b/attr.c
@@ -254,12 +254,8 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
 		parse_exclude_pattern(&res->u.pat.pattern,
 				      &res->u.pat.patternlen,
 				      &res->u.pat.flags,
-				      &res->u.pat.nowildcardlen);
-		if (res->u.pat.flags & EXC_FLAG_NEGATIVE) {
-			warning(_("Negative patterns are ignored in git attributes\n"
-				  "Use '\\!' for literal leading exclamation."));
-			return NULL;
-		}
+				      &res->u.pat.nowildcardlen,
+				      1);
 	}
 	res->is_macro = is_macro;
 	res->num_attr = num_attr;
diff --git a/dir.c b/dir.c
index a473ca2..609ab99 100644
--- a/dir.c
+++ b/dir.c
@@ -311,13 +311,14 @@ static int no_wildcard(const char *string)
 void parse_exclude_pattern(const char **pattern,
 			   int *patternlen,
 			   int *flags,
-			   int *nowildcardlen)
+			   int *nowildcardlen,
+			   int gitattributes)
 {
 	const char *p = *pattern;
 	size_t i, len;
 
 	*flags = 0;
-	if (*p == '!') {
+	if (!gitattributes && *p == '!') {
 		*flags |= EXC_FLAG_NEGATIVE;
 		p++;
 	}
@@ -354,7 +355,7 @@ void add_exclude(const char *string, const char *base,
 	int flags;
 	int nowildcardlen;
 
-	parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
+	parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen, 0);
 	if (flags & EXC_FLAG_MUSTBEDIR) {
 		char *s;
 		x = xmalloc(sizeof(*x) + patternlen + 1);
diff --git a/dir.h b/dir.h
index f5c89e3..3aeb519 100644
--- a/dir.h
+++ b/dir.h
@@ -107,7 +107,11 @@ extern int path_excluded(struct path_exclude_check *, const char *, int namelen,
 extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
 					  char **buf_p, struct exclude_list *which, int check_index);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
-extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen);
+extern void parse_exclude_pattern(const char **string,
+				  int *patternlen,
+				  int *flags,
+				  int *nowildcardlen,
+				  int gitattributes);
 extern void add_exclude(const char *string, const char *base,
 			int baselen, struct exclude_list *which);
 extern void free_excludes(struct exclude_list *el);
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 1035a14..7ca283f 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -197,9 +197,8 @@ test_expect_success 'root subdir attribute test' '
 '
 
 test_expect_success 'negative patterns' '
-	echo "!f test=bar" >.gitattributes &&
-	git check-attr test -- '"'"'!f'"'"' 2>errors &&
-	test_i18ngrep "Negative patterns are ignored" errors
+	echo "!f test=foo" >.gitattributes &&
+	attr_check "!f" foo
 '
 
 test_expect_success 'patterns starting with exclamation' '
-- 
1.8.1.2.536.gf441e6d
Previous: Junio C Hamano
Message 8 of 8 in “Make !pattern in .gitattributes non-fatal”
  1. Make !pattern in .gitattributes non-fatalThomas Rast, Mar 1, 2013
  2. Junio C HamanoMar 1, 2013
  3. Junio C HamanoMar 1, 2013
  4. Thomas RastMar 1, 2013
  5. Junio C HamanoMar 1, 2013
  6. Duy NguyenMar 2, 2013
  7. Junio C HamanoMar 3, 2013
  8. attr: always treat the leading exclamation mark as literalNguyễn Thái Ngọc Duy, Mar 2, 2013

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.