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
next 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