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

[PATCH] fix git add :!x exiting with error when x is in .gitignore

From
RFRemy D. Farley <one-d-wide@protonmail.com>
Date
Feb 4, 2026, 13:30 UTC
Message-ID
<20260204132747.1564157-1-one-d-wide@protonmail.com>

`git add :!x .`, which is also executed as part of `git stash :!x`, seems to treat pathspec with and without exclude magic the same, exiting with error when "x" exists and is in gitignore.

Git-add manpage doesn't specify that exclude pathspecs should be treated anyhow differently from normal ones, which seems like a bug. Two inconsistencies I noticed: `git add :!ignored .` succeeds when "ignored" file doesn't exist, and `git add :!ignored/x .` succeeds even when "ignored/x" file exists.

This commit makes makes `git add :!x` not error on x being excluded path.
| $ sh repro.sh
| [...]
| + echo x >.gitignore
| + echo x >x
| + git stash --include-untracked -- ':!x'
| Saved working directory and index state WIP on main: c8a842d Init
| The following paths are ignored by one of your .gitignore files:
| x
| hint: Use -f if you really want to add them.
| hint: Disable this message with "git config set advice.addIgnoredFile false"
| + echo exited with code 1
| exited with code 1
| # repro.sh
| rm -rf repro; mkdir repro; cd repro
| trap 'echo exited with code $?' EXIT
| set -euo pipefail -o xtrace
|
| git init
| git commit -m Init --allow-empty
|
| # Commenting out either of the following lines makes git add/stash below succeed
| echo x >.gitignore
| echo x >x
|
| # Git add . is executed as part of git stash, as can be seen using strace -ffeexecve:
| git add -- ":!x" . # fails
| # git stash --include-untracked -- ":!x" # fails

--- I'm not sure who else to cc, last commit touching this code is 2ec87741 from 10 year ago, being a mere refactoring. I think this bug was simply overlooked when introducing PATHSPEC_EXCLUDE.

Thanks to Tian Yuchen for looking at my earlier submission (and noticing
an awkwardly stupid bug there).
---
 dir.c                              |  3 +++
 t/t2204-add-ignored.sh             | 14 ++++++++++++++
 t/t3905-stash-include-untracked.sh | 23 +++++++++++++++++++++++
 3 files changed, 40 insertions(+)
diff --git a/dir.c b/dir.c
index b00821f294..ed6b99e337 100644
--- a/dir.c
+++ b/dir.c
@@ -2280,6 +2280,9 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
 		const struct pathspec_item *item = &pathspec->items[i];
 		int len = item->nowildcard_len;
 
+		if (item->magic & PATHSPEC_EXCLUDE)
+			continue;
+
 		if (len == pathlen &&
 		    !ps_strncmp(item, item->match, path, pathlen))
 			return 1;
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
index 31eb233df5..76c53fbfde 100755
--- a/t/t2204-add-ignored.sh
+++ b/t/t2204-add-ignored.sh
@@ -47,6 +47,20 @@ do
 	test_expect_success "complaints for ignored $i with unignored file output" '
 		test_grep -e "Use -f if" err
 	'
+
+	test_expect_success "no complaints for unignored file with ignored :!$i" '
+		rm -f .git/index &&
+		git add file ":!$i" &&
+		git ls-files file "$i" >out &&
+		test -s out
+	'
+
+	test_expect_success "complaints for ignored $i with ignored :!ign" '
+		rm -f .git/index &&
+		test_must_fail git add "$i" :!ign 2>err &&
+		git ls-files "$i" ign >out &&
+		test_must_be_empty out
+	'
 done
 
 for i in sub sub/*
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 7704709054..028ff3efc0 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -206,6 +206,29 @@ test_expect_success 'stash push --include-untracked with pathspec' '
 	test_path_is_file foo
 '
 
+test_expect_success 'stash push --include-untracked with :!pathspec' '
+	>foo &&
+	>bar &&
+	git stash push --include-untracked -- :!bar &&
+	test_path_is_file bar &&
+	test_path_is_missing foo &&
+	git stash pop &&
+	test_path_is_file bar &&
+	test_path_is_file foo
+'
+
+test_expect_success 'stash push --include-untracked with :!pathspec in .gitignore' '
+	echo ignored > .gitignore &&
+	>foo &&
+	>ignored &&
+	git stash push --include-untracked -- :!ignored &&
+	test_path_is_file ignored &&
+	test_path_is_missing foo &&
+	git stash pop &&
+	test_path_is_file ignored &&
+	test_path_is_file foo
+'
+
 test_expect_success 'stash push with $IFS character' '
 	>"foo bar" &&
 	>foo &&
-- 
2.51.2
Next: Junio C Hamano
Message 1 of 6 in “fix git add :!x exiting with error when x is in .gitignore”
  1. fix git add :!x exiting with error when x is in .gitignoreRemy D. Farley, Feb 4, 2026
  2. Junio C HamanoFeb 4, 2026
  3. Tian YuchenFeb 4, 2026
  4. Junio C HamanoFeb 4, 2026
  5. fix git add :!x exiting with error when x is in .gitignoreRemy D. Farley, Feb 4, 2026
  6. Junio C HamanoFeb 4, 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.