`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(+)
Show changes to 3 files +40 −0
dir.c, t/t2204-add-ignored.sh, t/t3905-stash-include-untracked.sh
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