{"thread":{"id":"34938","subject":"RFC: git bisect should accept \"paths-to-be-excluded\"","startedAt":"2013-09-16T12:39:06Z","lastAt":"2013-11-21T18:43:24Z","messageCount":16,"participants":["Toralf Förster","Christian Couder","Matthieu Moy","Duy Nguyen","Junio C Hamano","Piotr Krukowiecki","Nguyễn Thái Ngọc Duy"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"227709","messageId":"5236FBEA.80909@gmx.de","threadId":"34938","inReplyTo":null,"subject":"RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Toralf Förster","fromEmail":"toralf.foerster@gmx.de","sentAt":"2013-09-16T12:39:06Z","receivedAt":"2013-09-16T12:39:06Z","isPatch":false,"sender":{"key":"toralf.foerster@gmx.de","avatar":null},"body":"I'm bisecting a linux kernel issue and want to ignore all commits just\ntouching something in ./drives/staging.\n\nCurrently the only way would be to specify all dir/subdir combination\nunder ./linux except that particular directory, right ?\n\n-- \nMfG/Sincerely\nToralf Förster\npgp finger print: 7B1A 07F4 EC82 0F90 D4C2 8936 872A E508 7DB6 9DA3\n"},{"id":"227731","messageId":"CAP8UFD0qC3UM3Dgt2dhpcBHt34yZ3HwNO6y7Z=EBtyRYpyc+Bw@mail.gmail.com","threadId":"34938","inReplyTo":"5236FBEA.80909@gmx.de","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2013-09-17T07:26:06Z","receivedAt":"2013-09-17T07:26:06Z","isPatch":false,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Hi,\n\nOn Mon, Sep 16, 2013 at 2:39 PM, Toralf Förster <toralf.foerster@gmx.de> wrote:\n> I'm bisecting a linux kernel issue and want to ignore all commits just\n> touching something in ./drives/staging.\n>\n> Currently the only way would be to specify all dir/subdir combination\n> under ./linux except that particular directory, right ?\n\nYeah, you are right, currently the only way would be to specify all\ndir/subdir combination\nunder ./linux except the particular directory you want to exclude.\n\nIt might indeed be useful to have a way to exclude some directories or files.\n\nIn practice though, as git bisect is a kind of binary search, if what\nyou want to exclude is exclusively touched by half the commits, it\nwill only add one more bisection step if you don't exclude it.\n\nBest regards,\nChristian.\n"},{"id":"227732","messageId":"vpqvc1z6eoo.fsf@anie.imag.fr","threadId":"34938","inReplyTo":"CAP8UFD0qC3UM3Dgt2dhpcBHt34yZ3HwNO6y7Z=EBtyRYpyc+Bw@mail.gmail.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-09-17T08:21:59Z","receivedAt":"2013-09-17T08:21:59Z","isPatch":false,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> In practice though, as git bisect is a kind of binary search, if what\n> you want to exclude is exclusively touched by half the commits, it\n> will only add one more bisection step if you don't exclude it.\n\nActually, I think the same remark would apply to any other Git command\nthat deal with a set of revisions. If you want to review code with \"git\nlog -p\", but you don't care about a subdirectory, you may want a \"git\nlog -p --ignore-dir foo/\" or so, too.\n\nAnd then, the \"it's logarithmic\" argument doesn't work anymore ;-).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"227738","messageId":"CAP8UFD1u9hPFcbftpacDFdp27Jmp0YLGbpHPP12uEtjzEmnPQA@mail.gmail.com","threadId":"34938","inReplyTo":"vpqvc1z6eoo.fsf@anie.imag.fr","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2013-09-17T09:03:45Z","receivedAt":"2013-09-17T09:03:45Z","isPatch":false,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Christian Couder <christian.couder@gmail.com> writes:\n>\n>> In practice though, as git bisect is a kind of binary search, if what\n>> you want to exclude is exclusively touched by half the commits, it\n>> will only add one more bisection step if you don't exclude it.\n>\n> Actually, I think the same remark would apply to any other Git command\n> that deal with a set of revisions. If you want to review code with \"git\n> log -p\", but you don't care about a subdirectory, you may want a \"git\n> log -p --ignore-dir foo/\" or so, too.\n\nYeah, and there was a patch series about that 2 years ago:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/182830/\n\n> And then, the \"it's logarithmic\" argument doesn't work anymore ;-).\n\nSure.\n\nBest regards,\nChristian.\n"},{"id":"227746","messageId":"CACsJy8AEoUUat-1smJ1BmDuDBLseWf8oZ+EJyuadSLncb1UMSw@mail.gmail.com","threadId":"34938","inReplyTo":"CAP8UFD1u9hPFcbftpacDFdp27Jmp0YLGbpHPP12uEtjzEmnPQA@mail.gmail.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-09-17T11:45:00Z","receivedAt":"2013-09-17T11:45:00Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 17, 2013 at 4:03 PM, Christian Couder\n<christian.couder@gmail.com> wrote:\n> On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Christian Couder <christian.couder@gmail.com> writes:\n>>\n>>> In practice though, as git bisect is a kind of binary search, if what\n>>> you want to exclude is exclusively touched by half the commits, it\n>>> will only add one more bisection step if you don't exclude it.\n>>\n>> Actually, I think the same remark would apply to any other Git command\n>> that deal with a set of revisions. If you want to review code with \"git\n>> log -p\", but you don't care about a subdirectory, you may want a \"git\n>> log -p --ignore-dir foo/\" or so, too.\n>\n> Yeah, and there was a patch series about that 2 years ago:\n>\n> http://thread.gmane.org/gmane.comp.version-control.git/182830/\n\nAnd that's just one of the few attempts if I remember correctly. I\nguess it's time revisit it. A few things to sort out before we get to\nthe implementation:\n\nSupport flat or nested negation (i.e.include A, ignore A/B, but\ninclude A/B/C..). Nested thing complicates things so I'm towards the\nflat exclusion (exclude B means all inside B, no buts nor excepts) and\nprobably cover most use cases\n\nInteraction with \"git grep --depth\"\n\nSyntax. I guess --ignore (or --exclude) is more intuitive than\n\":(exclude)something\" but then it might collide with existing options\n(I did not check if --ignore or --exclude is used anywhere though).\nThe latter also enables combining with other filters, such as\ncase-insensitive matching..\n-- \nDuy\n"},{"id":"227755","messageId":"523881E9.9040204@gmx.de","threadId":"34938","inReplyTo":"CAP8UFD0qC3UM3Dgt2dhpcBHt34yZ3HwNO6y7Z=EBtyRYpyc+Bw@mail.gmail.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Toralf Förster","fromEmail":"toralf.foerster@gmx.de","sentAt":"2013-09-17T16:23:05Z","receivedAt":"2013-09-17T16:23:05Z","isPatch":false,"sender":{"key":"toralf.foerster@gmx.de","avatar":null},"body":"On 09/17/2013 09:26 AM, Christian Couder wrote:\n> Hi,\n> \n> On Mon, Sep 16, 2013 at 2:39 PM, Toralf Förster <toralf.foerster@gmx.de> wrote:\n>> I'm bisecting a linux kernel issue and want to ignore all commits just\n>> touching something in ./drives/staging.\n>>\n>> Currently the only way would be to specify all dir/subdir combination\n>> under ./linux except that particular directory, right ?\n> \n> Yeah, you are right, currently the only way would be to specify all\n> dir/subdir combination\n> under ./linux except the particular directory you want to exclude.\n> \n> It might indeed be useful to have a way to exclude some directories or files.\n\nGreat to hear\n\n> In practice though, as git bisect is a kind of binary search, if what\n> you want to exclude is exclusively touched by half the commits, it\n> will only add one more bisection step if you don't exclude it.\n\nUnfortunately not. Linus pulls from Greg's staging tree usually once in\na merge window. It is not uncommon to have hundreds of commits in that\nmerge. If now (by accident) the merge point is marked as \"BAD\" and the\nbase is \"GOOD\", then git bisect falls into that trap and wastes about\nld(few hundreds) steps - and this happened here for me and each bisect\nstep took hours ...\n\n\n> Best regards,\n> Christian.\n> \n\n\n-- \nMfG/Sincerely\nToralf Förster\npgp finger print: 7B1A 07F4 EC82 0F90 D4C2 8936 872A E508 7DB6 9DA3\n"},{"id":"227761","messageId":"xmqqsix3z8ie.fsf@gitster.dls.corp.google.com","threadId":"34938","inReplyTo":"CACsJy8AEoUUat-1smJ1BmDuDBLseWf8oZ+EJyuadSLncb1UMSw@mail.gmail.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-17T17:02:33Z","receivedAt":"2013-09-17T17:02:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Tue, Sep 17, 2013 at 4:03 PM, Christian Couder\n> <christian.couder@gmail.com> wrote:\n>> On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy\n>> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>>> Christian Couder <christian.couder@gmail.com> writes:\n>>>\n>>>> In practice though, as git bisect is a kind of binary search, if what\n>>>> you want to exclude is exclusively touched by half the commits, it\n>>>> will only add one more bisection step if you don't exclude it.\n>>>\n>>> Actually, I think the same remark would apply to any other Git command\n>>> that deal with a set of revisions. If you want to review code with \"git\n>>> log -p\", but you don't care about a subdirectory, you may want a \"git\n>>> log -p --ignore-dir foo/\" or so, too.\n>>\n>> Yeah, and there was a patch series about that 2 years ago:\n>>\n>> http://thread.gmane.org/gmane.comp.version-control.git/182830/\n>\n> And that's just one of the few attempts if I remember correctly. I\n> guess it's time revisit it. A few things to sort out before we get to\n> the implementation:\n>\n> Support flat or nested negation (i.e.include A, ignore A/B, but\n> include A/B/C..). Nested thing complicates things so I'm towards the\n> flat exclusion (exclude B means all inside B, no buts nor excepts) and\n> probably cover most use cases\n\nYeah, it is easy to say that\n\n\tgit log -- A ':(exclude)A/B' A/B/C\n\nhas two positive (A, A/B/C) and one negative (A/B), and then the\nmost specific one A/B/C matches a path A/B/C/D and hence A/B/C/D is\nincluded.\n\nBut to actually _design_ it, there are ambiguities that makes\nunderstanding and explaining the semantics, especially given\npathspecs can have wildcards, icase matches, etc.  For example, is\n\":(exclude,icase)A/B/?\"  more specific than \"A/?/C\" or less?\n\nSo I tend to agree that we should aim for an easier to explain, if\nless capable, approach.\n\n> Interaction with \"git grep --depth\"\n\nI am not sure how that affects anything.  Conceptually, isn't\n\"--depth\" an independent axis to filter out paths that have too many\ncomponents after given positive pathspec elements?  E.g. given\n\n\tgit grep --depth=2 pattern -- A B/C\n\nwe will grab paths from two levels starting at A and B/C (so A/1/2\nand B/C/1/2 may hit but not A/1/2/3 nor B/C/1/2/3).  Shouldn't\nnegative pathspecs just filter that depth filtering, i.e. if you\nhave \":(exclude)*/1/*\", even though both \"A/1/2\" and \"A/a/b\" may\npass the --depth=2 filter, the former is excluded while the latter\nis not.\n\n> Syntax. I guess --ignore (or --exclude) is more intuitive than\n> \":(exclude)something\" but then it might collide with existing options\n> (I did not check if --ignore or --exclude is used anywhere though).\n> The latter also enables combining with other filters, such as\n> case-insensitive matching..\n\nI do not think it is an option to do this with any mechanism other\nthan negative pathspecs.\n"},{"id":"227770","messageId":"1496b663-6b6c-45a2-95d1-cbe634b0d160@email.android.com","threadId":"34938","inReplyTo":"xmqqsix3z8ie.fsf@gitster.dls.corp.google.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Piotr Krukowiecki","fromEmail":"piotr.krukowiecki@gmail.com","sentAt":"2013-09-17T18:12:10Z","receivedAt":"2013-09-17T18:12:10Z","isPatch":false,"sender":{"key":"piotr.krukowiecki@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3259959?v=4"},"body":"Junio C Hamano <gitster@pobox.com> napisał:\n>Yeah, it is easy to say that\n>\n>\tgit log -- A ':(exclude)A/B' A/B/C\n>\n>has two positive (A, A/B/C) and one negative (A/B), and then the\n>most specific one A/B/C matches a path A/B/C/D and hence A/B/C/D is\n>included.\n>\n>But to actually _design_ it, there are ambiguities that makes\n>understanding and explaining the semantics, especially given\n>pathspecs can have wildcards, icase matches, etc.  For example, is\n>\":(exclude,icase)A/B/?\"  more specific than \"A/?/C\" or less?\n\n What about simply iterating over options in order in which they are specified and the last option that matches specifies the result? \n\n-- \nPiotr Krukowiecki \n"},{"id":"227773","messageId":"xmqqpps7xoax.fsf@gitster.dls.corp.google.com","threadId":"34938","inReplyTo":"1496b663-6b6c-45a2-95d1-cbe634b0d160@email.android.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-17T19:04:22Z","receivedAt":"2013-09-17T19:04:22Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:\n\n>  What about simply iterating over options in order in which they\n>  are specified and the last option that matches specifies the\n>  result?\n\nBut isn't it very inconsistent from the way normal pathspec works?\n\"git log -- A B\" and \"git log -- B A\" would give the same result.\n"},{"id":"227779","messageId":"CAA01Csp6tjKJ9LqX+9qcJL4t3kfFJCagjZQ=QwddvscPori9Ow@mail.gmail.com","threadId":"34938","inReplyTo":"xmqqpps7xoax.fsf@gitster.dls.corp.google.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Piotr Krukowiecki","fromEmail":"piotr.krukowiecki@gmail.com","sentAt":"2013-09-17T19:41:50Z","receivedAt":"2013-09-17T19:41:50Z","isPatch":false,"sender":{"key":"piotr.krukowiecki@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3259959?v=4"},"body":"On Tue, Sep 17, 2013 at 9:04 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:\n>\n>>  What about simply iterating over options in order in which they\n>>  are specified and the last option that matches specifies the\n>>  result?\n>\n> But isn't it very inconsistent from the way normal pathspec works?\n> \"git log -- A B\" and \"git log -- B A\" would give the same result.\n\nBoth are include-type filters. \"--include A --include B\" will give the\nsame result as \"--include B --include A\" too.\n\nAre there existing include/exclude filters where order does not\nmatter? For example gitattributes(5) says \"When more than one pattern\nmatches the path, a later line overrides an earlier line.\"\n\nIgnoring (possible) inconsistency thing, I think they are easy to\nunderstand and use.\n\n\n-- \nPiotr Krukowiecki\n"},{"id":"227797","messageId":"xmqqwqmfw4z8.fsf@gitster.dls.corp.google.com","threadId":"34938","inReplyTo":"CAA01Csp6tjKJ9LqX+9qcJL4t3kfFJCagjZQ=QwddvscPori9Ow@mail.gmail.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-17T20:47:07Z","receivedAt":"2013-09-17T20:47:07Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:\n\n> Ignoring (possible) inconsistency thing, I think they are easy to\n> understand and use.\n\nProbably you are right (in the sense that I do not offhand think of\na confusing and ambiguous set of positive and negative pathspecs;\nothers may find holes in my/our thinking).\n\nI am not sure if it will fit well to the current \"struct pathspec\"\ndesign, though.  We could start from \"when there is any negative\npathspec, disable the 'optimize away the common leading prefix'\nthing\", I guess.\n"},{"id":"227811","messageId":"CACsJy8CwtiJPLoFxts2NANH+i0ZoXcWSyS2qZC_zOx=WME2FkQ@mail.gmail.com","threadId":"34938","inReplyTo":"xmqqsix3z8ie.fsf@gitster.dls.corp.google.com","subject":"Re: RFC: git bisect should accept \"paths-to-be-excluded\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-09-18T02:22:42Z","receivedAt":"2013-09-18T02:22:42Z","isPatch":false,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Sep 18, 2013 at 12:02 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Interaction with \"git grep --depth\"\n>\n> I am not sure how that affects anything.  Conceptually, isn't\n> \"--depth\" an independent axis to filter out paths that have too many\n> components after given positive pathspec elements?  E.g. given\n>\n>         git grep --depth=2 pattern -- A B/C\n>\n> we will grab paths from two levels starting at A and B/C (so A/1/2\n> and B/C/1/2 may hit but not A/1/2/3 nor B/C/1/2/3).  Shouldn't\n> negative pathspecs just filter that depth filtering, i.e. if you\n> have \":(exclude)*/1/*\", even though both \"A/1/2\" and \"A/a/b\" may\n> pass the --depth=2 filter, the former is excluded while the latter\n> is not.\n\nImplementation details leaked into the design thoughts. I was worried\nthat the qsort() in pathspec() might make it incompatible with the\n:(exclude). Or I was thinking that --depth should be part of this new\nfilter.. Never mind.\n\n>> Syntax. I guess --ignore (or --exclude) is more intuitive than\n>> \":(exclude)something\" but then it might collide with existing options\n>> (I did not check if --ignore or --exclude is used anywhere though).\n>> The latter also enables combining with other filters, such as\n>> case-insensitive matching..\n>\n> I do not think it is an option to do this with any mechanism other\n> than negative pathspecs.\n\nUnder the hood, a new pathspec magic must be introduced (else we can't\npass them from \"git add -u\" to git-add--interactive then some other\ncommands that take pathspec). So --exclude would be transformed to the\npathspec magic, similar to \"git grep --depth\". But we could add that\nlater if :(exclude)something is too long to type.\n-- \nDuy\n"},{"id":"230826","messageId":"1384911691-11664-1-git-send-email-pclouds@gmail.com","threadId":"34938","inReplyTo":"xmqqsix3z8ie.fsf@gitster.dls.corp.google.com","subject":"[PATCH] Support pathspec magic :(exclude) and its short form :-","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-11-20T01:41:31Z","receivedAt":"2013-11-20T01:41:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n This is yet another stab at the negative pathspec thing. It's not\n ready yet (there are a few XXXs) but I could use some feedback\n regarding the interface, or the behavior. It looks better this time\n now that pathspec magic is supported (or maybe I'm just biased).\n\n For :(glob) or :(icase) you're more likely to enable it for all\n pathspec, i.e. --glob-pathspecs. But I expect :(exclude) to be typed\n more often (it does not make sense to add --exclude-pathspecs to\n exclude everything), which is why I add the short form for it.\n\n We don't have many options that say \"negative\" in short form.\n Either '!', '-' or '~'. '!' is already used for bash history expansion.\n ~ looks more like $HOME expansion. Which left me '-'.\n\n Documentation/glossary-content.txt |  5 ++++\n builtin/add.c                      |  5 +++-\n dir.c                              | 50 +++++++++++++++++++++++++++++++-----\n pathspec.c                         |  9 ++++++-\n pathspec.h                         |  4 ++-\n tree-walk.c                        | 52 +++++++++++++++++++++++++++++++++++---\n 6 files changed, 112 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt\nindex e470661..f7d7d8c 100644\n--- a/Documentation/glossary-content.txt\n+++ b/Documentation/glossary-content.txt\n@@ -377,6 +377,11 @@ full pathname may have special meaning:\n  - Other consecutive asterisks are considered invalid.\n +\n Glob magic is incompatible with literal magic.\n+\n+exclude `-`;;\n+\tAfter a path matches any non-exclude pathspec, it will be run\n+\tthrough all exclude pathspec. If it matches, the path is\n+\tignored.\n --\n +\n Currently only the slash `/` is recognized as the \"magic signature\",\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 226f758..0df73ae 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -540,10 +540,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t\t       PATHSPEC_FROMTOP |\n \t\t\t       PATHSPEC_LITERAL |\n \t\t\t       PATHSPEC_GLOB |\n-\t\t\t       PATHSPEC_ICASE);\n+\t\t\t       PATHSPEC_ICASE |\n+\t\t\t       PATHSPEC_EXCLUDE);\n \n \t\tfor (i = 0; i < pathspec.nr; i++) {\n \t\t\tconst char *path = pathspec.items[i].match;\n+\t\t\tif (pathspec.items[i].magic & PATHSPEC_EXCLUDE)\n+\t\t\t\tcontinue;\n \t\t\tif (!seen[i] &&\n \t\t\t    ((pathspec.items[i].magic &\n \t\t\t      (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||\ndiff --git a/dir.c b/dir.c\nindex 23b6de4..e2df82f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)\n \t\t       PATHSPEC_MAXDEPTH |\n \t\t       PATHSPEC_LITERAL |\n \t\t       PATHSPEC_GLOB |\n-\t\t       PATHSPEC_ICASE);\n+\t\t       PATHSPEC_ICASE |\n+\t\t       PATHSPEC_EXCLUDE);\n \n \tfor (n = 0; n < pathspec->nr; n++) {\n \t\tsize_t i = 0, len = 0, item_len;\n+\t\tif (pathspec->items[n].magic & PATHSPEC_EXCLUDE)\n+\t\t\tcontinue;\n \t\tif (pathspec->items[n].magic & PATHSPEC_ICASE)\n \t\t\titem_len = pathspec->items[n].prefix;\n \t\telse\n@@ -279,9 +282,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,\n  * pathspec did not match any names, which could indicate that the\n  * user mistyped the nth pathspec.\n  */\n-int match_pathspec_depth(const struct pathspec *ps,\n-\t\t\t const char *name, int namelen,\n-\t\t\t int prefix, char *seen)\n+static int match_pathspec_depth_1(const struct pathspec *ps,\n+\t\t\t\t  const char *name, int namelen,\n+\t\t\t\t  int prefix, char *seen,\n+\t\t\t\t  int exclude)\n {\n \tint i, retval = 0;\n \n@@ -290,7 +294,8 @@ int match_pathspec_depth(const struct pathspec *ps,\n \t\t       PATHSPEC_MAXDEPTH |\n \t\t       PATHSPEC_LITERAL |\n \t\t       PATHSPEC_GLOB |\n-\t\t       PATHSPEC_ICASE);\n+\t\t       PATHSPEC_ICASE |\n+\t\t       PATHSPEC_EXCLUDE);\n \n \tif (!ps->nr) {\n \t\tif (!ps->recursive ||\n@@ -309,6 +314,11 @@ int match_pathspec_depth(const struct pathspec *ps,\n \n \tfor (i = ps->nr - 1; i >= 0; i--) {\n \t\tint how;\n+\n+\t\tif ((!exclude &&   ps->items[i].magic & PATHSPEC_EXCLUDE) ||\n+\t\t    ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))\n+\t\t\tcontinue;\n+\n \t\tif (seen && seen[i] == MATCHED_EXACTLY)\n \t\t\tcontinue;\n \t\thow = match_pathspec_item(ps->items+i, prefix, name, namelen);\n@@ -327,6 +337,16 @@ int match_pathspec_depth(const struct pathspec *ps,\n \t\tif (how) {\n \t\t\tif (retval < how)\n \t\t\t\tretval = how;\n+\t\t\t/*\n+\t\t\t * seen[i] is used for detecting unused\n+\t\t\t * pathspec. For excluded pathspec, it's less\n+\t\t\t * obvious if seen[] should be set if the\n+\t\t\t * pathspec matches.\n+\t\t\t *\n+\t\t\t * XXX: perhaps we should also set seen[] for\n+\t\t\t * exclude patterns and stop e.g. \"git add\"\n+\t\t\t * from complaining?\n+\t\t\t */\n \t\t\tif (seen && seen[i] < how)\n \t\t\t\tseen[i] = how;\n \t\t}\n@@ -334,6 +354,18 @@ int match_pathspec_depth(const struct pathspec *ps,\n \treturn retval;\n }\n \n+int match_pathspec_depth(const struct pathspec *ps,\n+\t\t\t const char *name, int namelen,\n+\t\t\t int prefix, char *seen)\n+{\n+\tint positive, negative;\n+\tpositive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);\n+\tif (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)\n+\t\treturn positive;\n+\tnegative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);\n+\treturn negative ? 0 : positive;\n+}\n+\n /*\n  * Return the length of the \"simple\" part of a path match limiter.\n  */\n@@ -1375,11 +1407,17 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru\n \t\t\t       PATHSPEC_MAXDEPTH |\n \t\t\t       PATHSPEC_LITERAL |\n \t\t\t       PATHSPEC_GLOB |\n-\t\t\t       PATHSPEC_ICASE);\n+\t\t\t       PATHSPEC_ICASE |\n+\t\t\t       PATHSPEC_EXCLUDE);\n \n \tif (has_symlink_leading_path(path, len))\n \t\treturn dir->nr;\n \n+\t/*\n+\t * XXX: exclude patterns are treated like positive ones in\n+\t * create_simplify! This is not wrong, but may make path\n+\t * filtering less efficient.\n+\t */\n \tsimplify = create_simplify(pathspec ? pathspec->_raw : NULL);\n \tif (!len || treat_leading_path(dir, path, len, simplify))\n \t\tread_directory_recursive(dir, path, len, 0, simplify);\ndiff --git a/pathspec.c b/pathspec.c\nindex 4cf2bd3..a021959 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -71,6 +71,7 @@ static struct pathspec_magic {\n \t{ PATHSPEC_LITERAL,   0, \"literal\" },\n \t{ PATHSPEC_GLOB,   '\\0', \"glob\" },\n \t{ PATHSPEC_ICASE,  '\\0', \"icase\" },\n+\t{ PATHSPEC_EXCLUDE, '-', \"exclude\" },\n };\n \n /*\n@@ -355,7 +356,7 @@ void parse_pathspec(struct pathspec *pathspec,\n {\n \tstruct pathspec_item *item;\n \tconst char *entry = argv ? *argv : NULL;\n-\tint i, n, prefixlen;\n+\tint i, n, prefixlen, nr_exclude = 0;\n \n \tmemset(pathspec, 0, sizeof(*pathspec));\n \n@@ -412,6 +413,8 @@ void parse_pathspec(struct pathspec *pathspec,\n \t\tif ((flags & PATHSPEC_LITERAL_PATH) &&\n \t\t    !(magic_mask & PATHSPEC_LITERAL))\n \t\t\titem[i].magic |= PATHSPEC_LITERAL;\n+\t\tif (item[i].magic & PATHSPEC_EXCLUDE)\n+\t\t\tnr_exclude++;\n \t\tif (item[i].magic & magic_mask)\n \t\t\tunsupported_magic(entry,\n \t\t\t\t\t  item[i].magic & magic_mask,\n@@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,\n \t\tpathspec->magic |= item[i].magic;\n \t}\n \n+\tif (nr_exclude == n)\n+\t\tdie(_(\"There is nothing to exclude from by :(exclude) patterns.\\n\"\n+\t\t      \"Perhaps you forgot to add either ':/' or '.' ?\"));\n+\n \n \tif (pathspec->magic & PATHSPEC_MAXDEPTH) {\n \t\tif (flags & PATHSPEC_KEEP_ORDER)\ndiff --git a/pathspec.h b/pathspec.h\nindex a75e924..0c11262 100644\n--- a/pathspec.h\n+++ b/pathspec.h\n@@ -7,12 +7,14 @@\n #define PATHSPEC_LITERAL\t(1<<2)\n #define PATHSPEC_GLOB\t\t(1<<3)\n #define PATHSPEC_ICASE\t\t(1<<4)\n+#define PATHSPEC_EXCLUDE\t(1<<5)\n #define PATHSPEC_ALL_MAGIC\t  \\\n \t(PATHSPEC_FROMTOP\t| \\\n \t PATHSPEC_MAXDEPTH\t| \\\n \t PATHSPEC_LITERAL\t| \\\n \t PATHSPEC_GLOB\t\t| \\\n-\t PATHSPEC_ICASE)\n+\t PATHSPEC_ICASE\t\t| \\\n+\t PATHSPEC_EXCLUDE)\n \n #define PATHSPEC_ONESTAR 1\t/* the pathspec pattern satisfies GFNM_ONESTAR */\n \ndiff --git a/tree-walk.c b/tree-walk.c\nindex 5ece8c3..9011f87 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -662,9 +662,10 @@ static int match_wildcard_base(const struct pathspec_item *item,\n  * Pre-condition: either baselen == base_offset (i.e. empty path)\n  * or base[baselen-1] == '/' (i.e. with trailing slash).\n  */\n-enum interesting tree_entry_interesting(const struct name_entry *entry,\n-\t\t\t\t\tstruct strbuf *base, int base_offset,\n-\t\t\t\t\tconst struct pathspec *ps)\n+static enum interesting tree_entry_interesting_1(const struct name_entry *entry,\n+\t\t\t\t\t\t struct strbuf *base, int base_offset,\n+\t\t\t\t\t\t const struct pathspec *ps,\n+\t\t\t\t\t\t int exclude)\n {\n \tint i;\n \tint pathlen, baselen = base->len - base_offset;\n@@ -676,7 +677,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,\n \t\t       PATHSPEC_MAXDEPTH |\n \t\t       PATHSPEC_LITERAL |\n \t\t       PATHSPEC_GLOB |\n-\t\t       PATHSPEC_ICASE);\n+\t\t       PATHSPEC_ICASE |\n+\t\t       PATHSPEC_EXCLUDE);\n \n \tif (!ps->nr) {\n \t\tif (!ps->recursive ||\n@@ -697,6 +699,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,\n \t\tconst char *base_str = base->buf + base_offset;\n \t\tint matchlen = item->len, matched = 0;\n \n+\t\tif ((!exclude &&   item->magic & PATHSPEC_EXCLUDE) ||\n+\t\t    ( exclude && !(item->magic & PATHSPEC_EXCLUDE)))\n+\t\t\tcontinue;\n+\n \t\tif (baselen >= matchlen) {\n \t\t\t/* If it doesn't match, move along... */\n \t\t\tif (!match_dir_prefix(item, base_str, match, matchlen))\n@@ -782,3 +788,41 @@ match_wildcards:\n \t}\n \treturn never_interesting; /* No matches */\n }\n+\n+enum interesting tree_entry_interesting(const struct name_entry *entry,\n+\t\t\t\t\tstruct strbuf *base, int base_offset,\n+\t\t\t\t\tconst struct pathspec *ps)\n+{\n+\tenum interesting positive, negative;\n+\tpositive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);\n+\n+\t/*\n+\t *   #  | positive | negative | result\n+\t * -----+----------+----------+-------\n+\t * 1..4 |   -1     |    *     |  -1\n+\t * 5..8 |    0     |    *     |   0\n+\t *   9  |    1     |   -1     |   1\n+\t *  10  |    1     |    0     |   1\n+\t *  11  |    1     |    1     |   0\n+\t *  12  |    1     |    2     |   0\n+\t *  13  |    2     |   -1     |   2\n+\t *  14  |    2     |    0     |   2\n+\t *  15  |    2     |    1     |   0\n+\t *  16  |    2     |    2     |  -1\n+\t */\n+\n+\tif (!(ps->magic & PATHSPEC_EXCLUDE) ||\n+\t    positive <= entry_not_interesting) /* #1..#8 */\n+\t\treturn positive;\n+\n+\tnegative = tree_entry_interesting_1(entry, base, base_offset, ps, 1);\n+\n+\tif (negative <= entry_not_interesting)\t /* #9, #10, #13, #14 */\n+\t\treturn positive;\n+\tif ((positive == entry_interesting &&\n+\t     negative >= entry_interesting) || /* #11, #12 */\n+\t    (positive == all_entries_interesting &&\n+\t     negative == entry_interesting)) /* #15 */\n+\t\treturn entry_not_interesting;\n+\treturn all_entries_not_interesting; /* #16 */\n+}\n-- \n1.8.2.82.gc24b958\n"},{"id":"230871","messageId":"xmqqhab663ef.fsf@gitster.dls.corp.google.com","threadId":"34938","inReplyTo":"1384911691-11664-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH] Support pathspec magic :(exclude) and its short form :-","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-20T23:48:24Z","receivedAt":"2013-11-20T23:48:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  This is yet another stab at the negative pathspec thing. It's not\n>  ready yet (there are a few XXXs) but I could use some feedback\n>  regarding the interface, or the behavior. It looks better this time\n>  now that pathspec magic is supported (or maybe I'm just biased).\n>\n>  For :(glob) or :(icase) you're more likely to enable it for all\n>  pathspec, i.e. --glob-pathspecs. But I expect :(exclude) to be typed\n>  more often (it does not make sense to add --exclude-pathspecs to\n>  exclude everything), which is why I add the short form for it.\n>\n>  We don't have many options that say \"negative\" in short form.\n>  Either '!', '-' or '~'. '!' is already used for bash history expansion.\n>  ~ looks more like $HOME expansion. Which left me '-'.\n\nI agree with your decision to reject ~, but \"!not-this-pattern\" is\nvery much consistent with the patterns used in .gitignore (and the\n\"--exclude <pattern>\" option), so avoiding \"!\" and introducing an\ninconsistent \"-\" only to appease bash leaves somewhat a funny taste\nin my mouth.\n\n>  Documentation/glossary-content.txt |  5 ++++\n>  builtin/add.c                      |  5 +++-\n>  dir.c                              | 50 +++++++++++++++++++++++++++++++-----\n>  pathspec.c                         |  9 ++++++-\n>  pathspec.h                         |  4 ++-\n>  tree-walk.c                        | 52 +++++++++++++++++++++++++++++++++++---\n>  6 files changed, 112 insertions(+), 13 deletions(-)\n>\n> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt\n> index e470661..f7d7d8c 100644\n> --- a/Documentation/glossary-content.txt\n> +++ b/Documentation/glossary-content.txt\n> @@ -377,6 +377,11 @@ full pathname may have special meaning:\n>   - Other consecutive asterisks are considered invalid.\n>  +\n>  Glob magic is incompatible with literal magic.\n> +\n> +exclude `-`;;\n> +\tAfter a path matches any non-exclude pathspec, it will be run\n> +\tthrough all exclude pathspec. If it matches, the path is\n> +\tignored.\n>  --\n>  +\n>  Currently only the slash `/` is recognized as the \"magic signature\",\n\nNo longer, no?  \"magic signature\" is a non-alphanumeric that follows\nthe ':' introducer, as opposed to \"magic words\" that are in \":(...)\".\n\n> diff --git a/builtin/add.c b/builtin/add.c\n> index 226f758..0df73ae 100644\n> --- a/builtin/add.c\n> +++ b/builtin/add.c\n> @@ -540,10 +540,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n>  \t\t\t       PATHSPEC_FROMTOP |\n>  \t\t\t       PATHSPEC_LITERAL |\n>  \t\t\t       PATHSPEC_GLOB |\n> -\t\t\t       PATHSPEC_ICASE);\n> +\t\t\t       PATHSPEC_ICASE |\n> +\t\t\t       PATHSPEC_EXCLUDE);\n>  \n>  \t\tfor (i = 0; i < pathspec.nr; i++) {\n>  \t\t\tconst char *path = pathspec.items[i].match;\n> +\t\t\tif (pathspec.items[i].magic & PATHSPEC_EXCLUDE)\n> +\t\t\t\tcontinue;\n>  \t\t\tif (!seen[i] &&\n>  \t\t\t    ((pathspec.items[i].magic &\n>  \t\t\t      (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||\n\nSo \"git add ':(exclude)junk/' '*.c'\" to add all .c files except for\nthe ones in the 'junk/' directory may find that ':(exclude)junk/'\nmatched nothing (because there is no .c file in there), and that is\nnot an error.  It makes sense to me.\n\n> diff --git a/dir.c b/dir.c\n> index 23b6de4..e2df82f 100644\n> --- a/dir.c\n> +++ b/dir.c\n> @@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)\n>  \t\t       PATHSPEC_MAXDEPTH |\n>  \t\t       PATHSPEC_LITERAL |\n>  \t\t       PATHSPEC_GLOB |\n> -\t\t       PATHSPEC_ICASE);\n> +\t\t       PATHSPEC_ICASE |\n> +\t\t       PATHSPEC_EXCLUDE);\n>  \n>  \tfor (n = 0; n < pathspec->nr; n++) {\n>  \t\tsize_t i = 0, len = 0, item_len;\n> +\t\tif (pathspec->items[n].magic & PATHSPEC_EXCLUDE)\n> +\t\t\tcontinue;\n>  \t\tif (pathspec->items[n].magic & PATHSPEC_ICASE)\n>  \t\t\titem_len = pathspec->items[n].prefix;\n>  \t\telse\n\nLikewise.  Exclusion does not participate in the early culling with\nthe common prefix.\n\n> @@ -1375,11 +1407,17 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru\n>  \t\t\t       PATHSPEC_MAXDEPTH |\n>  \t\t\t       PATHSPEC_LITERAL |\n>  \t\t\t       PATHSPEC_GLOB |\n> -\t\t\t       PATHSPEC_ICASE);\n> +\t\t\t       PATHSPEC_ICASE |\n> +\t\t\t       PATHSPEC_EXCLUDE);\n>  \n>  \tif (has_symlink_leading_path(path, len))\n>  \t\treturn dir->nr;\n>  \n> +\t/*\n> +\t * XXX: exclude patterns are treated like positive ones in\n> +\t * create_simplify! This is not wrong, but may make path\n> +\t * filtering less efficient.\n> +\t */\n\nTrue, but \"git add ':(exclude)a/b/c' a/b\" would not suffer.  And\nthose who do \"git add ':(exclude)a/b' a/b/c\" deserve it, no ;-)?\n\n> @@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,\n>  \t\tpathspec->magic |= item[i].magic;\n>  \t}\n>  \n> +\tif (nr_exclude == n)\n> +\t\tdie(_(\"There is nothing to exclude from by :(exclude) patterns.\\n\"\n> +\t\t      \"Perhaps you forgot to add either ':/' or '.' ?\"));\n\n;-).\n\n> +enum interesting tree_entry_interesting(const struct name_entry *entry,\n> +\t\t\t\t\tstruct strbuf *base, int base_offset,\n> +\t\t\t\t\tconst struct pathspec *ps)\n> +{\n> +\tenum interesting positive, negative;\n> +\tpositive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);\n> +\n> +\t/*\n> +\t *   #  | positive | negative | result\n> +\t * -----+----------+----------+-------\n> +\t * 1..4 |   -1     |    *     |  -1\n> +\t * 5..8 |    0     |    *     |   0\n> +\t *   9  |    1     |   -1     |   1\n> +\t *  10  |    1     |    0     |   1\n> +\t *  11  |    1     |    1     |   0\n> +\t *  12  |    1     |    2     |   0\n> +\t *  13  |    2     |   -1     |   2\n> +\t *  14  |    2     |    0     |   2\n> +\t *  15  |    2     |    1     |   0\n> +\t *  16  |    2     |    2     |  -1\n> +\t */\n\nNot sure what this case-table means...\n\n> +\tif (!(ps->magic & PATHSPEC_EXCLUDE) ||\n> +\t    positive <= entry_not_interesting) /* #1..#8 */\n> +\t\treturn positive;\n> +\n> +\tnegative = tree_entry_interesting_1(entry, base, base_offset, ps, 1);\n> +\n> +\tif (negative <= entry_not_interesting)\t /* #9, #10, #13, #14 */\n> +\t\treturn positive;\n> +\tif ((positive == entry_interesting &&\n> +\t     negative >= entry_interesting) || /* #11, #12 */\n> +\t    (positive == all_entries_interesting &&\n> +\t     negative == entry_interesting)) /* #15 */\n> +\t\treturn entry_not_interesting;\n> +\treturn all_entries_not_interesting; /* #16 */\n> +}\n"},{"id":"230872","messageId":"CACsJy8AeL+EVZme3BPocXDfRqPpqKDA8nCuwx3buiS66L7G4fA@mail.gmail.com","threadId":"34938","inReplyTo":"xmqqhab663ef.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] Support pathspec magic :(exclude) and its short form :-","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-11-21T02:10:50Z","receivedAt":"2013-11-21T02:10:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Nov 21, 2013 at 6:48 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>  We don't have many options that say \"negative\" in short form.\n>>  Either '!', '-' or '~'. '!' is already used for bash history expansion.\n>>  ~ looks more like $HOME expansion. Which left me '-'.\n>\n> I agree with your decision to reject ~, but \"!not-this-pattern\" is\n> very much consistent with the patterns used in .gitignore (and the\n> \"--exclude <pattern>\" option), so avoiding \"!\" and introducing an\n> inconsistent \"-\" only to appease bash leaves somewhat a funny taste\n> in my mouth.\n\nThe thing about '!'  is it's history expansion in bash and I suspect\nnot many people are aware of it. So \"git log -- :!something\" may\nrecall the last command that has \"something\" in it, which is confusing\nfor those new people and may potentially be dangerous (multiple\ncommand in one line, separated by semicolon). Compared to \":git log --\n(exclude)somethign\" the worst that could happen is a syntax error\nmessage from bash.\n\nOther than that I'm fine with '!' being the shortcut.\n\nBtw I'm thinking of extending pathspec magic syntax a bit to allow\npath completion. Right now the user has to write\n\ngit log -- :-Documentation\n\nwhich does not play well with path completion. I'm thinking of accepting\n\ngit log -- :- Documentation\n\nIn other words, if there's no path (or pattern) component after the\nmagic, then the next argument must contain the path. This enables path\ncompletion and I haven't seen any drawbacks yet..\n\n>> @@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,\n>>               pathspec->magic |= item[i].magic;\n>>       }\n>>\n>> +     if (nr_exclude == n)\n>> +             die(_(\"There is nothing to exclude from by :(exclude) patterns.\\n\"\n>> +                   \"Perhaps you forgot to add either ':/' or '.' ?\"));\n>\n> ;-).\n\nHey it was originally not there, then I made a mistake of typing \"git\nlog -- :-po\" and wondered why it shows nothing. Intuitively, if \"git\nlog\" shows every path, then \"git log -- :-po\" should show every path\nexcept 'po' and the user should not be required to type \"git log -- :/\n:-po\". parse_pathspec() can do that, but it's more work and I'm lazy\nso I push that back to the user until they scream :)\n\n>> +enum interesting tree_entry_interesting(const struct name_entry *entry,\n>> +                                     struct strbuf *base, int base_offset,\n>> +                                     const struct pathspec *ps)\n>> +{\n>> +     enum interesting positive, negative;\n>> +     positive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);\n>> +\n>> +     /*\n>> +      *   #  | positive | negative | result\n>> +      * -----+----------+----------+-------\n>> +      * 1..4 |   -1     |    *     |  -1\n>> +      * 5..8 |    0     |    *     |   0\n>> +      *   9  |    1     |   -1     |   1\n>> +      *  10  |    1     |    0     |   1\n>> +      *  11  |    1     |    1     |   0\n>> +      *  12  |    1     |    2     |   0\n>> +      *  13  |    2     |   -1     |   2\n>> +      *  14  |    2     |    0     |   2\n>> +      *  15  |    2     |    1     |   0\n>> +      *  16  |    2     |    2     |  -1\n>> +      */\n>\n> Not sure what this case-table means...\n\nSorry, because tree_entry_interesting_1() returns more than \"match or\nnot\", we need to combine the result from positive pathspec with the\nnegative one to correctly handle all_not_interesting and\nall_interesting. This table sums it up. I'll add more explanation in\nthe next patch.\n-- \nDuy\n"},{"id":"230895","messageId":"xmqqpppt4mur.fsf@gitster.dls.corp.google.com","threadId":"34938","inReplyTo":"CACsJy8AeL+EVZme3BPocXDfRqPpqKDA8nCuwx3buiS66L7G4fA@mail.gmail.com","subject":"Re: [PATCH] Support pathspec magic :(exclude) and its short form :-","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-21T18:43:24Z","receivedAt":"2013-11-21T18:43:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> Btw I'm thinking of extending pathspec magic syntax a bit to allow\n> path completion. Right now the user has to write\n>\n> git log -- :-Documentation\n>\n> which does not play well with path completion. I'm thinking of accepting\n>\n> git log -- :- Documentation\n\nPlease don't.  That does not help our users, but actively harm them.\nThey have to stop and wonder why a single pathspec is spelled as two\ntokens on the command line of some other people.\n\nDoing that stupidity only to help those who polish the tool (namely,\n\"bash completion\") to be lazy is doubly wrong (in the meantime, the\nusers can type your second variant and then edit the result).\n\nFor the same reason why I do not think rewriting\n\n\techo \"hello, world!\"\n\nto\n\n\techo \"hello, world-\"\n\nonly to work around a pitfall of a particular tool (namely \"bash\")\nmakes any sense, I do not think it makes sense to make _our_ tool\ninconsistent by using \"!excluded\" in the files (and --exclude) and\n\"-not this pattern\" only here.\n\n>>> +     if (nr_exclude == n)\n>>> +             die(_(\"There is nothing to exclude from by :(exclude) patterns.\\n\"\n>>> +                   \"Perhaps you forgot to add either ':/' or '.' ?\"));\n>>\n>> ;-).\n>\n> Hey it was originally not there,...\n\nI am not objecting. I noticed it and was commending on it as \"a nice\ntouch\" ;-)\n\n>>> +     /*\n>>> +      *   #  | positive | negative | result\n>>> +      * -----+----------+----------+-------\n>>> +      * 1..4 |   -1     |    *     |  -1\n>>> +      * 5..8 |    0     |    *     |   0\n>>> +      *   9  |    1     |   -1     |   1\n>>> +      *  10  |    1     |    0     |   1\n>>> +      *  11  |    1     |    1     |   0\n>>> +      *  12  |    1     |    2     |   0\n>>> +      *  13  |    2     |   -1     |   2\n>>> +      *  14  |    2     |    0     |   2\n>>> +      *  15  |    2     |    1     |   0\n>>> +      *  16  |    2     |    2     |  -1\n>>> +      */\n>>\n>> Not sure what this case-table means...\n>\n> Sorry, because tree_entry_interesting_1() returns more than \"match\n> or not\", we need to combine the result from positive pathspec with\n> the negative one to correctly handle all_not_interesting and\n> all_interesting. This table sums it up. I'll add more explanation\n> in the next patch.\n\nI managed to have guessed what the three columns on the right meant;\nI was wondering about the meaning of the \"#\" column and where it is\ndefined/explained.\n"}]}