After some more digging (and familiarizing myself with the behind-the-scenes logic) the issue is that dir.c has this implicit assumption that a directory which contains only untracked and ignored files should itself be considered untracked. While that works fine for use cases where we're asking if a directory should be added to the git database, that decidedly does not make sense when we're asking if a directory can be removed from the working tree. I'm not sure where to proceed from here. I see two ways forward: one, builtin/clean.c can collect ignored files when it calls dir.c:fill_directory(), and then clean -d can prune out directories that contain ignored files; two, path_treatment can learn about untracked directories which contain excluded (ignored) files. On Mon, May 1, 2017 at 8:51 AM, Samuel Lijin <sxlijin@xxxxxxxxx> wrote: > On Sun, Apr 30, 2017 at 8:56 PM, Chris Johnson <chrisjohnson0@xxxxxxxxx> wrote: >> Good assessment/understanding of the issue. git clean -n does not >> report anything as being targeted for removal, and git clean -f >> matches that behavior. I agree with it probably being related >> specifically to the -d flag. >> >> As another experiment I modified .gitignore to ignore /A/B/C instead >> of /A/B/ and the same result occurs (-n reports nothing, -dn reports >> removing A/) >> >> Lastly, I changed .gitignore to just be /A/, and in doing so, clean >> -dn stops reporting that it will remove A/. I’m not exactly sure if >> this last one is surprising or not. > > It doesn't seem so to me, since a trailing slash in .gitignore is an > explicit directive to ignore the entire directory, so when pruning the > tree of files/dirs to ignore, it drops everything in A/ before even > getting to A/B/C. The issue is that ignoring A/B/C shouldn't leave A/ > to be cleaned. > >> Also, and sorry for the noise, but I did a reply-all here, but will a >> reply automatically include the rest of the list? Or was reply-all the >> right move? >> >> On Sun, Apr 30, 2017 at 9:41 PM, Junio C Hamano <gitster@xxxxxxxxx> wrote: >>> Chris Johnson <chrisjohnson0@xxxxxxxxx> writes: >>> >>>> I am a mailing list noob so I’m sorry if this is the wrong format or >>>> the wrong please. >>>> >>>> Here’s the setup for the bug (I will call it a bug but I half expect >>>> somebody to tell me I’m an idiot): >>>> >>>> git init >>>> echo "/A/B/" > .gitignore >>> >>> You tell Git that anything in A/B/ are uninteresting. >>> >>>> git add .gitignore && git commit -m 'Add ignore' >>>> mkdir -p A/B >>>> touch A/B/C >>> >>> And create an uninteresting cruft. >>> >>>> git status >>> >>> And Git does not bug you about it. >>> >>>> git clean -dn >>> >>> This incorrectly reports "Would remove A/" and if you gave 'f' >>> instead of 'n', it does remove A/, A/B, and A/B/C. >>> >>> Despite that "git clean --help" says 'only files unknown to Git are >>> removed' (with an undefined term 'unknown to Git'). What it wants >>> the term mean can be guessed by seeing 'if the -x option is >>> specified, ignored files are also removed'---so 'unknown to Git' >>> does not include what you told .gitignore that they are >>> uninteresting. IOW, Git knows they are not interesting. >>> >>> It looks like a bug in "git clean -d" to me. > > I may be wrong (I'm not very familiar with the codebase), but I don't > think it's a bug in specifically git clean -d. > > Throwing gdb at it, when builtin/clean.c:cmd_clean() calls > dir.c:fill_directory(), A/ gets appended to dir->entries, regardless > of whether or not git clean is called with or without -d. The > difference is that if git clean is called without -d, the loop that > strips out directories strips out the A/ entry. > > When dir.c:fill_directory() is invoked through git status, A/ does > not, however, get appended to dir->entries. As best as I can tell, > this seems to be because git status sets the > DIR_HIDE_EMPTY_DIRECTORIES flag, whereas git clean does not (which > makes sense), but the fact that DIR_HIDE_EMPTY_DIRECTORIES is > responsible for not adding A/ to dir->entries seems to be the issue.