{"thread":{"id":"64251","subject":"git clean deletes negated pathspec if directory causing data loss","startedAt":"2025-10-05T11:27:29Z","lastAt":"2025-10-30T17:04:54Z","messageCount":5,"participants":["Devste Devste","Johannes Schindelin","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"527934","messageId":"CANM0SV0R7OYJKeBAs5-WY2Wqp7VxhKv5=LQ4cuDd6MaQwNqGNw@mail.gmail.com","threadId":"64251","inReplyTo":null,"subject":"git clean deletes negated pathspec if directory causing data loss","fromName":"Devste Devste","fromEmail":"devstemail@gmail.com","sentAt":"2025-10-05T11:27:15Z","receivedAt":"2025-10-05T11:27:29Z","isPatch":false,"sender":{"key":"devstemail@gmail.com","avatar":null},"body":"I am using:\ngit version 2.51.0.windows.1\n\nRun:\necho .idea/dictionaries >> .gitignore\nmkdir -p .idea/dictionaries\ntouch .idea/dictionaries/foo.xml\ngit clean -f -f -d -X --dry-run -- ':!/.idea/dictionaries' ':*.rej' ':/*/*.log'\n\nOutputs:\nWould remove .idea/dictionaries/\n\nNo matter how you specify the pathspec (':!.idea',...) it always wants\nto delete the .idea/dictionaries directory, even though it does not\ncontain any .rej or .log files and is explicitly set to excluded\n"},{"id":"527936","messageId":"CANM0SV3iRF=jVAFj=+rrgOC_nXEJ=_v1bUG88n3T9+PQD=a2-A@mail.gmail.com","threadId":"64251","inReplyTo":"CANM0SV0R7OYJKeBAs5-WY2Wqp7VxhKv5=LQ4cuDd6MaQwNqGNw@mail.gmail.com","subject":"Re: git clean deletes negated pathspec if directory causing data loss","fromName":"Devste Devste","fromEmail":"devstemail@gmail.com","sentAt":"2025-10-05T11:33:23Z","receivedAt":"2025-10-05T11:33:36Z","isPatch":false,"sender":{"key":"devstemail@gmail.com","avatar":null},"body":"Just to clarify: it's not about the negation not working, but clean\nignores the pathspec for ignored directories completely. I only want\nto delete .rej and .log files, but it will also delete gitignored\ndirectories (that may or may not contain any .rej or .log files)\n\nOn Sun, 5 Oct 2025 at 13:27, Devste Devste <devstemail@gmail.com> wrote:\n>\n> I am using:\n> git version 2.51.0.windows.1\n>\n> Run:\n> echo .idea/dictionaries >> .gitignore\n> mkdir -p .idea/dictionaries\n> touch .idea/dictionaries/foo.xml\n> git clean -f -f -d -X --dry-run -- ':!/.idea/dictionaries' ':*.rej' ':/*/*.log'\n>\n> Outputs:\n> Would remove .idea/dictionaries/\n>\n> No matter how you specify the pathspec (':!.idea',...) it always wants\n> to delete the .idea/dictionaries directory, even though it does not\n> contain any .rej or .log files and is explicitly set to excluded\n"},{"id":"529887","messageId":"CANM0SV3qgqjWeAu+NvfPxeDLgo6-eRWKmi9xkpUtN6w7LJh7oQ@mail.gmail.com","threadId":"64251","inReplyTo":"CANM0SV3iRF=jVAFj=+rrgOC_nXEJ=_v1bUG88n3T9+PQD=a2-A@mail.gmail.com","subject":"Re: git clean deletes negated pathspec if directory causing data loss","fromName":"Devste Devste","fromEmail":"devstemail@gmail.com","sentAt":"2025-10-29T20:00:54Z","receivedAt":"2025-10-29T20:01:09Z","isPatch":false,"sender":{"key":"devstemail@gmail.com","avatar":null},"body":"Anyone?\n\nOn Sun, 5 Oct 2025 at 13:33, Devste Devste <devstemail@gmail.com> wrote:\n>\n> Just to clarify: it's not about the negation not working, but clean\n> ignores the pathspec for ignored directories completely. I only want\n> to delete .rej and .log files, but it will also delete gitignored\n> directories (that may or may not contain any .rej or .log files)\n>\n> On Sun, 5 Oct 2025 at 13:27, Devste Devste <devstemail@gmail.com> wrote:\n> >\n> > I am using:\n> > git version 2.51.0.windows.1\n> >\n> > Run:\n> > echo .idea/dictionaries >> .gitignore\n> > mkdir -p .idea/dictionaries\n> > touch .idea/dictionaries/foo.xml\n> > git clean -f -f -d -X --dry-run -- ':!/.idea/dictionaries' ':*.rej' ':/*/*.log'\n> >\n> > Outputs:\n> > Would remove .idea/dictionaries/\n> >\n> > No matter how you specify the pathspec (':!.idea',...) it always wants\n> > to delete the .idea/dictionaries directory, even though it does not\n> > contain any .rej or .log files and is explicitly set to excluded\n"},{"id":"529923","messageId":"a77f4e6f-86a3-ea15-feb5-4d08636ffd94@gmx.de","threadId":"64251","inReplyTo":"CANM0SV3qgqjWeAu+NvfPxeDLgo6-eRWKmi9xkpUtN6w7LJh7oQ@mail.gmail.com","subject":"Re: git clean deletes negated pathspec if directory causing data loss","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-10-30T08:49:41Z","receivedAt":"2025-10-30T08:49:44Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Devste,\n\nOn Wed, 29 Oct 2025, Devste Devste wrote:\n\n> Anyone?\n> \n> On Sun, 5 Oct 2025 at 13:33, Devste Devste <devstemail@gmail.com> wrote:\n> >\n> > Just to clarify: it's not about the negation not working, but clean\n> > ignores the pathspec for ignored directories completely. I only want\n> > to delete .rej and .log files, but it will also delete gitignored\n> > directories (that may or may not contain any .rej or .log files)\n> >\n> > On Sun, 5 Oct 2025 at 13:27, Devste Devste <devstemail@gmail.com> wrote:\n> > >\n> > > I am using:\n> > > git version 2.51.0.windows.1\n> > >\n> > > Run:\n> > > echo .idea/dictionaries >> .gitignore\n> > > mkdir -p .idea/dictionaries\n> > > touch .idea/dictionaries/foo.xml\n> > > git clean -f -f -d -X --dry-run -- ':!/.idea/dictionaries' ':*.rej' ':/*/*.log'\n> > >\n> > > Outputs:\n> > > Would remove .idea/dictionaries/\n> > >\n> > > No matter how you specify the pathspec (':!.idea',...) it always wants\n> > > to delete the .idea/dictionaries directory, even though it does not\n> > > contain any .rej or .log files and is explicitly set to excluded\n\nI can reproduce, both on Windows and on Linux. (Note that I prefer the\n`:(exclude).idea/directories` form because it is more descriptive and it\nalso does not run afoul of Bash's special handling of the exclamation\npoint).\n\nUnfortunately, the code in question is quite convoluted, and the intention\nis also not quite clear. The main problem seems to be to agree on what\n`-X` should mean in conjunction with `:(exclude)`.\n\nOne interpretation (which I assume is yours): When `-X` implicitly adds\nitems to be removed, `:(exclude)` should be able to remove them again.\n\nThe interpretation of the authors of the logic I see in the source code,\nthough, seem to treat the `-X` as a completely separate mechanism that\noverrides whatever `:(exclude)` may be specified on the command-line.\n\nHonestly, I am unsure how to resolve this, especially given that the\noverall architecture of `dir.c` (which contains the business logic of\nexclusions specified e.g. by `.gitignore) seems to have grown so\norganically as to result in a complex, hard-to-reason-about state.\n\nIn case the Git maintainer (whose call it is, ultimately, on which side to\nland regarding above-mentioned options) sides with your interpretation,\nhere is a patch to add a regression test demonstrating your use case:\n\n-- snipsnap --\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 6f16f389319..6bc7c42a572 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -362,6 +362,14 @@ test_expect_success 'git clean -d -X with ignored tracked directory' '\n \n '\n \n+test_expect_failure 'git clean -d -X with :(exclude)' '\n+\ttest_when_finished \"rm -rf build\" &&\n+\tmkdir -p build &&\n+\ttouch build/lib.so &&\n+\tgit clean -d -X -- \":(exclude)build\" &&\n+\ttest_path_is_file build/lib.sh\n+'\n+\n test_expect_success 'clean.requireForce defaults to true' '\n \n \tgit config --unset clean.requireForce &&\n"},{"id":"529977","messageId":"xmqqms585nik.fsf@gitster.g","threadId":"64251","inReplyTo":"a77f4e6f-86a3-ea15-feb5-4d08636ffd94@gmx.de","subject":"Re: git clean deletes negated pathspec if directory causing data loss","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-10-30T17:04:51Z","receivedAt":"2025-10-30T17:04:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> In case the Git maintainer (whose call it is, ultimately, on which side to\n> land regarding above-mentioned options) sides with your interpretation,\n> here is a patch to add a regression test demonstrating your use case:\n\nThanks for an opinion.\n\nAs \"git clean -X\" is not really \"my thing\", not in the sense that I\ndid not write it (I didn't, except for code clean-ups) but in the\nsense that I see no good use case for the option and I rarely use it\nmyself, as long as its behaviour can be \"explained\", like you just\ndid, I do not have preference myself, and no strong motivation to\nadvocate changing it, risking potential disruption on existing\nusers.\n"}]}