Subject: Re: [RFH] "git clean" deletes excluded files in untracked directories

From: Junio C Hamano <[email-masked]>
Date: Tue, 22 Jul 2014 15:53:59 -0700

Junio C Hamano <[email-masked]> writes:

> Jeff King <[email-masked]> writes:
>
>> If you have an untracked directory that contains excluded files, like
>> this:
>>
>>   mkdir foo
>>   echo content >foo/one
>>   echo content >foo/two
>>   echo "foo/one" >.gitignore
>>
>> then "git clean -d" will notice that "foo" is untracked and recursively
>> delete it and its contents, including the ignored "foo/one".
>
> Hmph, starting from an empty repository and doing the above four
> commands, and "git clean" without "-d" does not bother removing
> either foo/one or foo/two.  Is this correct?
>
> It gets worse.  Following the above four commands and then these two:
>
>     >foo/three
>     git add foo/two
>
> and "git clean" (with or without "-d") suddenly notices that
> "foo/three" is expendable, but not foo/one nor foo/two, which sounds
> about right.
>
> Honestly, as I do not use "git clean" myself, I do not know what the
> "right" behaviour is for that command.  Anything it does seems just
> arbitrary and wrong to me ;-)

Having said that...

Let's play my usual "I do not know what I am doing because I do not
use it myself, but instead of leaving the mess other people created
that may harm users, let's try to clean it up", and the first step
for that is to clarify what is wrong about it, and what, if
anything, could be a more sensible behaviour.

"git clean" with or without "-d" in my two-command added to your
four-command example notices .gitignore and foo/three are to be
cleaned, and further with "-x" makes foo/one eligible for
removal.  I think these are all sensible.

"git clean" without "-d" in your four-command example says
".gitignore is untracked and can be cleaned", but is "foo/two" that
is not tracked fundamentally different from ".gitignore"?

The only difference I can see is one is at the top-level while
the other is not.  What bad things could happen if we remove
"foo/two" with or without "-d", making "-d" a no-op?

I know "-d" talks about "untracked directory", but notice that with
your four-command example, there is no "git add"; there is no
difference between the top-level and "foo/" in tracked-ness, so it
is a meaningless distinction.  I could be pursuaded to buy a
proposal to make "-d" almost no-op except that a directory that
becomes empty after running "git clean [-x]" is rmdir(2)'ed and
without "-d" the empty directory is kept.

Would such a pair of changes bring some sanity to the command?

No matter what we do, I suspect that people expect various
combinations of options to "git clean" has some parallel to
"ls-files -o" with various combinations of options, to allow them to
list paths that will be cleaned with "ls-files", store them away,
and then run "clean", or something, so any "fix" would involve
updating "ls-files" in a way consistent with the updated behaviour
of "clean".