I have a curious case with git clean -X
. I want to delete all ignored files in a clone, except the .vs
folder (which is ignored but should not be deleted).
This works with git clean -Xnd -e !.vs
- except for those files inside .vs
that are also ignored by a second ignore rule.
What that means is that for a file not to be deleted, I need to exclude every matching ignore rule. Is that intended?
I would have argued that excluding a single matching ignore rule should be sufficient.
Here's example commands to reproduce the problem
rem Setup repository
mkdir repo
cd repo
mkdir .vs
touch .vs\.suo
git init
git status
rem Ignore existing files
>.gitignore echo .vs
git add .gitignore
git commit -m "Initial .gitignore"
git status
rem Clean
git clean -Xnd -e !.vs
rem Add overlapping ignore
>>.gitignore echo .suo
git add .gitignore
git commit -m "Updated .gitignore"
rem Clean
git clean -Xnd -e !.vs
The final command prints
Would remove .vs/
Which is not what I want.
Edit: I know I could do git clean -Xnd -e !.vs -e !.suo
, but what if I have some .suo
file outside the .vs
folder? I would like that one to be removed, and git clean -Xnd -e !.vs -e !.suo
does not do that.
I would have argued that excluding a single matching ignore rule should be sufficient.
And it is, but directory matches don't match everything inside, they achieve their effect by shutting off matching, Git doesn't even check inside the directory. Your -e !.vs
did exclude the directory match, it did shut it off.
To see this, add e.g. *.suo
and .[sS][uU][oO]
to your .gitignore
, and -e !.suo
to your testcase command line. The existing -e !.vs
turns on matching inside .vs
, the -e !.suo
override matches the contained .suo
, and that is enough.
If you want to forcibly not ignore anything anywhere within an entire directory regardless of any rules, you can -e !.vs -e !.vs/**
, and since the embedded /
anchors the match at that directory if there was .vs
anywhere but the toplevel you'd want -e !**/.vs/**
too to reenable the "find anywhere" a bare name gets.