public inbox for git@vger.kernel.org 
 help / color / mirror / Atom feed
From: "Remy D. Farley" <one-d-wide@protonmail•com>
To: git@vger•kernel.org
Cc: Junio C Hamano <gitster@pobox•com>,
	Tian Yuchen <a3205153416@gmail•com>,
	"Remy D. Farley" <one-d-wide@protonmail•com>
Subject: [PATCH] fix git add :!x exiting with error when x is in .gitignore
Date: Wed, 04 Feb 2026 13:30:38 +0000	[thread overview]
Message-ID: <20260204132747.1564157-1-one-d-wide@protonmail.com> (raw)

`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



             reply	other threads:[~2026-02-04 13:30 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-04 13:30 Remy D. Farley [this message]
2026-02-04 16:48 ` [PATCH] fix git add :!x exiting with error when x is in .gitignore Junio C Hamano
2026-02-04 17:53   ` Tian Yuchen
2026-02-04 18:47     ` Junio C Hamano
2026-02-04 20:11       ` Remy D. Farley
2026-02-04 20:47         ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260204132747.1564157-1-one-d-wide@protonmail.com \
    --to=one-d-wide@protonmail$(echo .)com \
    --cc=a3205153416@gmail$(echo .)com \
    --cc=git@vger$(echo .)kernel.org \
    --cc=gitster@pobox$(echo .)com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox