{"thread":{"id":"34805","subject":"[PATCH] revision: add --except option","startedAt":"2013-08-30T05:00:05Z","lastAt":"2013-11-01T21:08:14Z","messageCount":32,"participants":["Felipe Contreras","Junio C Hamano","Johannes Sixt","Duy Nguyen","Michael Haggerty","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"226292","messageId":"1377838805-7693-1-git-send-email-felipe.contreras@gmail.com","threadId":"34805","inReplyTo":null,"subject":"[PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T05:00:05Z","receivedAt":"2013-08-30T05:00:05Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"So that it's possible to remove certain refs from the list without\nremoving the objects that are referenced by other refs.\n\nFor example this repository:\n\n  * 374e8dd (crap) crap\n  * 4cbbf7b (test) two\n  * d025ae0 (HEAD, master) one\n\nWhen using '--branches --except crap':\n\n  * 4cbbf7b (test) two\n  * d025ae0 (HEAD, master) one\n\nBut when using '--branches --not crap' nothing will come out.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Documentation/git-rev-parse.txt        |  6 ++++++\n contrib/completion/git-completion.bash |  2 +-\n revision.c                             | 10 ++++++++++\n revision.h                             |  3 ++-\n t/t6112-rev-list-except.sh             | 35 ++++++++++++++++++++++++++++++++++\n 5 files changed, 54 insertions(+), 2 deletions(-)\n create mode 100755 t/t6112-rev-list-except.sh\n\ndiff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\nindex 2b126c0..fe5cc6b 100644\n--- a/Documentation/git-rev-parse.txt\n+++ b/Documentation/git-rev-parse.txt\n@@ -110,6 +110,12 @@ can be used.\n \tstrip '{caret}' prefix from the object names that already have\n \tone.\n \n+--except::\n+\tSkip the following object names. For example:\n+\t'--branches --except master' will show all the branches, except master.\n+\tThis differs from --not in that --except will still show the object, if\n+\tthey are referenced by another object name.\n+\n --symbolic::\n \tUsually the object names are output in SHA-1 form (with\n \tpossible '{caret}' prefix); this option makes them output in a\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 5da920e..aed8c12 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1386,7 +1386,7 @@ _git_ls_tree ()\n \n # Options that go well for log, shortlog and gitk\n __git_log_common_options=\"\n-\t--not --all\n+\t--not --except --all\n \t--branches --tags --remotes\n \t--first-parent --merges --no-merges\n \t--max-count=\ndiff --git a/revision.c b/revision.c\nindex 84ccc05..375adab 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1984,6 +1984,8 @@ static int handle_revision_pseudo_opt(const char *submodule,\n \t\thandle_reflog(revs, *flags);\n \t} else if (!strcmp(arg, \"--not\")) {\n \t\t*flags ^= UNINTERESTING | BOTTOM;\n+\t} else if (!strcmp(arg, \"--except\")) {\n+\t\t*flags ^= SKIP;\n \t} else if (!strcmp(arg, \"--no-walk\")) {\n \t\trevs->no_walk = REVISION_WALK_NO_WALK_SORTED;\n \t} else if (!prefixcmp(arg, \"--no-walk=\")) {\n@@ -2578,6 +2580,7 @@ int prepare_revision_walk(struct rev_info *revs)\n \tint nr = revs->pending.nr;\n \tstruct object_array_entry *e, *list;\n \tstruct commit_list **next = &revs->commits;\n+\tint i;\n \n \te = list = revs->pending.objects;\n \trevs->pending.nr = 0;\n@@ -2585,12 +2588,19 @@ int prepare_revision_walk(struct rev_info *revs)\n \trevs->pending.objects = NULL;\n \twhile (--nr >= 0) {\n \t\tstruct commit *commit = handle_commit(revs, e->item, e->name);\n+\t\tfor (i = 0; i < revs->cmdline.nr; i++) {\n+\t\t\tstruct rev_cmdline_entry *ce;\n+\t\t\tce = &revs->cmdline.rev[i];\n+\t\t\tif ((ce->flags & SKIP) && !strcmp(ce->name, e->name))\n+\t\t\t\tgoto next;\n+\t\t}\n \t\tif (commit) {\n \t\t\tif (!(commit->object.flags & SEEN)) {\n \t\t\t\tcommit->object.flags |= SEEN;\n \t\t\t\tnext = commit_list_append(commit, next);\n \t\t\t}\n \t\t}\n+next:\n \t\te++;\n \t}\n \tif (!revs->leak_pending)\ndiff --git a/revision.h b/revision.h\nindex 95859ba..89f5037 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -17,7 +17,8 @@\n #define SYMMETRIC_LEFT\t(1u<<8)\n #define PATCHSAME\t(1u<<9)\n #define BOTTOM\t\t(1u<<10)\n-#define ALL_REV_FLAGS\t((1u<<11)-1)\n+#define SKIP\t\t(1u<<11)\n+#define ALL_REV_FLAGS\t((1u<<12)-1)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\ndiff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh\nnew file mode 100755\nindex 0000000..b8f9a61\n--- /dev/null\n+++ b/t/t6112-rev-list-except.sh\n@@ -0,0 +1,35 @@\n+#!/bin/sh\n+\n+test_description='test for rev-list --except'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\techo one > content &&\n+\tgit add content &&\n+\tgit commit -m one &&\n+\tgit checkout -b test master &&\n+\techo two > content &&\n+\tgit commit -a -m two &&\n+\tgit checkout -b merge master &&\n+\tgit merge test\n+'\n+\n+test_expect_success 'rev-list --except' '\n+\n+\tgit rev-list --topo-order --branches --except merge > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --except with extra' '\n+\n+\techo three > content &&\n+\tgit commit -a -m three &&\n+\tgit rev-list --topo-order --branches --except merge > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.8.4-fc\n"},{"id":"226294","messageId":"CAMP44s2=S=6_DAVuiG+0iO+rxQuPqqsL8fEtzGkSxJfaBu3hdQ@mail.gmail.com","threadId":"34805","inReplyTo":"1377838805-7693-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T05:40:25Z","receivedAt":"2013-08-30T05:40:25Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 12:00 AM, Felipe Contreras\n<felipe.contreras@gmail.com> wrote:\n> So that it's possible to remove certain refs from the list without\n> removing the objects that are referenced by other refs.\n>\n> For example this repository:\n>\n>   * 374e8dd (crap) crap\n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n>\n> When using '--branches --except crap':\n>\n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n>\n> But when using '--branches --not crap' nothing will come out.\n\nDoesn't work with certain refs, here's a fix:\n\ndiff --git a/revision.c b/revision.c\nindex 375adab..25564c1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2575,6 +2575,13 @@ void reset_revision_walk(void)\n        clear_object_flags(SEEN | ADDED | SHOWN);\n }\n\n+static int refcmp(const char *a, const char *b)\n+{\n+       a = prettify_refname(a);\n+       b = prettify_refname(b);\n+       return strcmp(a, b);\n+}\n+\n int prepare_revision_walk(struct rev_info *revs)\n {\n        int nr = revs->pending.nr;\n@@ -2591,7 +2598,7 @@ int prepare_revision_walk(struct rev_info *revs)\n                for (i = 0; i < revs->cmdline.nr; i++) {\n                        struct rev_cmdline_entry *ce;\n                        ce = &revs->cmdline.rev[i];\n-                       if ((ce->flags & SKIP) && !strcmp(ce->name, e->name))\n+                       if ((ce->flags & SKIP) && !refcmp(ce->name, e->name))\n                                goto next;\n                }\n                if (commit) {\ndiff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh\nindex b8f9a61..a295f43 100755\n--- a/t/t6112-rev-list-except.sh\n+++ b/t/t6112-rev-list-except.sh\n@@ -32,4 +32,11 @@ test_expect_success 'rev-list --except with extra' '\n        test_cmp expect actual\n '\n\n+test_expect_success 'rev-list --except with full ref' '\n+\n+       git rev-list --topo-order --branches --except refs/heads/merge\n> actual &&\n+       git rev-list --topo-order test > expect &&\n+       test_cmp expect actual\n+'\n+\n test_done\n\n-- \nFelipe Contreras\n"},{"id":"226325","messageId":"7vhae7k7t1.fsf@alter.siamese.dyndns.org","threadId":"34805","inReplyTo":"1377838805-7693-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-08-30T06:32:26Z","receivedAt":"2013-08-30T06:32:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n> So that it's possible to remove certain refs from the list without\n> removing the objects that are referenced by other refs.\n>\n> For example this repository:\n>\n>   * 374e8dd (crap) crap\n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n\nCan we make it more clear that your assumption is \"crap\" is a child\nof \"test\" which is a child of \"master\"?  Without that, the \"nothing\nwill come out\" will not follow.\n\n> When using '--branches --except crap':\n>\n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n>\n> But when using '--branches --not crap' nothing will come out.\n\nIf you have a history where\n\n - branches \"master\" and \"maint\" point at commit A;\n - branch \"next\" points at commit B that is a descendant of A; and\n - there are tags X and Y pointing at commits that are ahead of B\n   or behind A\n\ni.e.\n\n\t----X----A----B----Y\n\nwhat are the desired semantics for these?\n\n (1) --branches --except maint\n\n (2) --all --not --branches --except maint\n\n (3) ^master next --except maint\n\n\"--branches\" wants to include \"master\", \"next\", and \"maint\", and the\n\"--except\" tells us we do not want to take \"maint\" into account, but\nshould that affect what \"master\" wants to do (either include or\nexclude what are reachable from it)?\n\nAs the way we parse the revisions from the command line is to mark\n\"objects\", not \"refs\", as we go, it looks like that the flag SKIP in\nthis patch is placed conceptually at a wrong level.\n\nI agree \"--branches --except maint\" is a good concept, but to\nimplement what this patch wants to do properly, I suspect that the\nrevision parser may need to be extended to be a two-phase process;\nthe first phase will collect list of objects involved in the range\nexpression without marking them with UNINTERESTING mark (that would\nalso involve expanding things like --all or --branches), while\nremembering those given with --except, exclude the \"except\" set from\nthe first set, and then finally marking the objects using the\nremainder, or something like that.\n\n> @@ -2585,12 +2588,19 @@ int prepare_revision_walk(struct rev_info *revs)\n>  \trevs->pending.objects = NULL;\n>  \twhile (--nr >= 0) {\n>  \t\tstruct commit *commit = handle_commit(revs, e->item, e->name);\n> +\t\tfor (i = 0; i < revs->cmdline.nr; i++) {\n> +\t\t\tstruct rev_cmdline_entry *ce;\n\n\"ce\" will have a strong association with \"cache entry\"; avoid using\nthat variable name for anything else to avoid confusion.\n\n> +\t\t\tce = &revs->cmdline.rev[i];\n> +\t\t\tif ((ce->flags & SKIP) && !strcmp(ce->name, e->name))\n> +\t\t\t\tgoto next;\n\nI think this SKIP will not help an object that is already tainted by\nUNINTERESTING; if it is discovered during a traversal from another\nobject that will remain in the rev->commits, the travesal will stop\nthere, even if a ref that is marked with SKIP will \"goto next\" here.\n\n> +\t\t}\n>  \t\tif (commit) {\n>  \t\t\tif (!(commit->object.flags & SEEN)) {\n>  \t\t\t\tcommit->object.flags |= SEEN;\n>  \t\t\t\tnext = commit_list_append(commit, next);\n>  \t\t\t}\n>  \t\t}\n> +next:\n>  \t\te++;\n>  \t}\n>  \tif (!revs->leak_pending)\n\n> diff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh\n> new file mode 100755\n> index 0000000..b8f9a61\n> --- /dev/null\n> +++ b/t/t6112-rev-list-except.sh\n> @@ -0,0 +1,35 @@\n> +#!/bin/sh\n> +\n> +test_description='test for rev-list --except'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\n> +\techo one > content &&\n\nStyle.\n"},{"id":"226327","messageId":"522045A1.3040409@viscovery.net","threadId":"34805","inReplyTo":"1377838805-7693-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2013-08-30T07:11:29Z","receivedAt":"2013-08-30T07:11:29Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 8/30/2013 7:00, schrieb Felipe Contreras:\n> So that it's possible to remove certain refs from the list without\n> removing the objects that are referenced by other refs.\n> \n> For example this repository:\n> \n>   * 374e8dd (crap) crap\n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n> \n> When using '--branches --except crap':\n> \n>   * 4cbbf7b (test) two\n>   * d025ae0 (HEAD, master) one\n> \n> But when using '--branches --not crap' nothing will come out.\n\nI like the idea to be able to exclude refs from listings. My use-case is\nthe following:\n\nTo unclutter 'git branch' output, I rename work-in-progress branches to\nbegin with \"wip/\", ready-to-merge branches do not carry this prefix. To\ninspect unmerged work of the latter kind of branches I would like to\ntype... what?\n\n  gitk --branches --except --branches=wip --not master\n  gitk --branches --not master --except --branches=wip\n\nWould one of these work with your current patch?\n\n-- Hannes\n"},{"id":"226328","messageId":"CAMP44s1y2kvSnF3dKDMr9QtS40PNSW93DWCxFUoL658YkqYeVA@mail.gmail.com","threadId":"34805","inReplyTo":"7vhae7k7t1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T07:17:07Z","receivedAt":"2013-08-30T07:17:07Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 1:32 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Felipe Contreras <felipe.contreras@gmail.com> writes:\n>\n>> So that it's possible to remove certain refs from the list without\n>> removing the objects that are referenced by other refs.\n>>\n>> For example this repository:\n>>\n>>   * 374e8dd (crap) crap\n>>   * 4cbbf7b (test) two\n>>   * d025ae0 (HEAD, master) one\n>\n> Can we make it more clear that your assumption is \"crap\" is a child\n> of \"test\" which is a child of \"master\"?  Without that, the \"nothing\n> will come out\" will not follow.\n>\n>> When using '--branches --except crap':\n>>\n>>   * 4cbbf7b (test) two\n>>   * d025ae0 (HEAD, master) one\n>>\n>> But when using '--branches --not crap' nothing will come out.\n>\n> If you have a history where\n>\n>  - branches \"master\" and \"maint\" point at commit A;\n>  - branch \"next\" points at commit B that is a descendant of A; and\n>  - there are tags X and Y pointing at commits that are ahead of B\n>    or behind A\n>\n> i.e.\n>\n>         ----X----A----B----Y\n>\n> what are the desired semantics for these?\n>\n>  (1) --branches --except maint\n>\n>  (2) --all --not --branches --except maint\n>\n>  (3) ^master next --except maint\n>\n> \"--branches\" wants to include \"master\", \"next\", and \"maint\", and the\n> \"--except\" tells us we do not want to take \"maint\" into account, but\n> should that affect what \"master\" wants to do (either include or\n> exclude what are reachable from it)?\n\nNo, it should not. '--branches --except main' becomes 'master next'.\n\n> As the way we parse the revisions from the command line is to mark\n> \"objects\", not \"refs\", as we go, it looks like that the flag SKIP in\n> this patch is placed conceptually at a wrong level.\n\nrefs are marked as well.\n\n> I agree \"--branches --except maint\" is a good concept, but to\n> implement what this patch wants to do properly, I suspect that the\n> revision parser may need to be extended to be a two-phase process;\n> the first phase will collect list of objects involved in the range\n> expression without marking them with UNINTERESTING mark (that would\n> also involve expanding things like --all or --branches), while\n> remembering those given with --except, exclude the \"except\" set from\n> the first set, and then finally marking the objects using the\n> remainder, or something like that.\n\nThat's not necessary, this patch does the trick.\n\n>> +                     ce = &revs->cmdline.rev[i];\n>> +                     if ((ce->flags & SKIP) && !strcmp(ce->name, e->name))\n>> +                             goto next;\n>\n> I think this SKIP will not help an object that is already tainted by\n> UNINTERESTING; if it is discovered during a traversal from another\n> object that will remain in the rev->commits, the travesal will stop\n> there, even if a ref that is marked with SKIP will \"goto next\" here.\n\nNo, the traversal will continue just fine. At this point we are still\nnot traversing anything, simply adding the heads that will need to be\ntraversed later on to a list. Whether this object has been tainted by\nUNINTERESTED or not is irrelevant.\n\nIf you do 'master ^maint --except master', handle_commit will return\nthree commits:\n\nmaster -> ce->flags will have SKIP, nothing more happens\nmaint -> ce->flags doesn't have SKIP, processing continues, and it's\nadded to the list of commits, the commit has UNINTERESTING, but that\ndoesn't matter\nmaster -> ce->flags will have SKIP, nothing more happens\n\nEssentially it's the same as:\n\nmaint -> it's added to the list of commits, the commit has UNINTERESTING\n\nSo, it's exactly the same as if you had typed '^maint', which is\nexactly what we want.\n\n-- \nFelipe Contreras\n"},{"id":"226330","messageId":"CAMP44s2n8k8ZXYde_+-X3qtiWNAcYKOgqTD0NY-cuuMwmPrhzQ@mail.gmail.com","threadId":"34805","inReplyTo":"522045A1.3040409@viscovery.net","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T07:24:46Z","receivedAt":"2013-08-30T07:24:46Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 2:11 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n> Am 8/30/2013 7:00, schrieb Felipe Contreras:\n>> So that it's possible to remove certain refs from the list without\n>> removing the objects that are referenced by other refs.\n>>\n>> For example this repository:\n>>\n>>   * 374e8dd (crap) crap\n>>   * 4cbbf7b (test) two\n>>   * d025ae0 (HEAD, master) one\n>>\n>> When using '--branches --except crap':\n>>\n>>   * 4cbbf7b (test) two\n>>   * d025ae0 (HEAD, master) one\n>>\n>> But when using '--branches --not crap' nothing will come out.\n>\n> I like the idea to be able to exclude refs from listings. My use-case is\n> the following:\n>\n> To unclutter 'git branch' output, I rename work-in-progress branches to\n> begin with \"wip/\", ready-to-merge branches do not carry this prefix. To\n> inspect unmerged work of the latter kind of branches I would like to\n> type... what?\n>\n>   gitk --branches --except --branches=wip --not master\n>   gitk --branches --not master --except --branches=wip\n>\n> Would one of these work with your current patch?\n\nThe first one should work, but mostly by accident. A proper fix is not\nfar though:\n\ndiff --git a/revision.c b/revision.c\nindex 25564c1..1380cd1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1983,9 +1983,9 @@ static int handle_revision_pseudo_opt(const char\n*submodule,\n        } else if (!strcmp(arg, \"--reflog\")) {\n                handle_reflog(revs, *flags);\n        } else if (!strcmp(arg, \"--not\")) {\n-               *flags ^= UNINTERESTING | BOTTOM;\n+               *flags = UNINTERESTING | BOTTOM;\n        } else if (!strcmp(arg, \"--except\")) {\n-               *flags ^= SKIP;\n+               *flags = SKIP;\n        } else if (!strcmp(arg, \"--no-walk\")) {\n                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;\n        } else if (!prefixcmp(arg, \"--no-walk=\")) {\n\nThen both should work.\n\n-- \nFelipe Contreras\n"},{"id":"226331","messageId":"CAMP44s0P=XF5C8+fU2cJ-Xuq57iqcAn674Upub6N=+iiMpQK0g@mail.gmail.com","threadId":"34805","inReplyTo":"CAPc5daVSqoE74kmsobg7RpMtiL3vzKN+ckAcWEKU_Q_wF8HYuA@mail.gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T07:32:31Z","receivedAt":"2013-08-30T07:32:31Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 2:26 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Pardon terseness, typo and HTML from a tablet.\n>\n>\n> On Aug 30, 2013 12:19 AM, \"Felipe Contreras\" <felipe.contreras@gmail.com>\n> wrote:\n>>\n>> On Fri, Aug 30, 2013 at 1:32 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> > Felipe Contreras <felipe.contreras@gmail.com> writes:\n>> >\n>> >> So that it's possible to remove certain refs from the list without\n>> >> removing the objects that are referenced by other refs.\n>> >>\n>> >> For example this repository:\n>> >>\n>> >>   * 374e8dd (crap) crap\n>> >>   * 4cbbf7b (test) two\n>> >>   * d025ae0 (HEAD, master) one\n>> >\n>> > Can we make it more clear that your assumption is \"crap\" is a child\n>> > of \"test\" which is a child of \"master\"?  Without that, the \"nothing\n>> > will come out\" will not follow.\n>> >\n>> >> When using '--branches --except crap':\n>> >>\n>> >>   * 4cbbf7b (test) two\n>> >>   * d025ae0 (HEAD, master) one\n>> >>\n>> >> But when using '--branches --not crap' nothing will come out.\n>> >\n>> > If you have a history where\n>> >\n>> >  - branches \"master\" and \"maint\" point at commit A;\n>> >  - branch \"next\" points at commit B that is a descendant of A; and\n>> >  - there are tags X and Y pointing at commits that are ahead of B\n>> >    or behind A\n>> >\n>> > i.e.\n>> >\n>> >         ----X----A----B----Y\n>> >\n>> > what are the desired semantics for these?\n>> >\n>> >  (1) --branches --except maint\n>> >\n>> >  (2) --all --not --branches --except maint\n>> >\n>> >  (3) ^master next --except maint\n>> >\n>> > \"--branches\" wants to include \"master\", \"next\", and \"maint\", and the\n>> > \"--except\" tells us we do not want to take \"maint\" into account, but\n>> > should that affect what \"master\" wants to do (either include or\n>> > exclude what are reachable from it)?\n>>\n>> No, it should not. '--branches --except main' becomes 'master next'.\n>>\n>> > As the way we parse the revisions from the command line is to mark\n>> > \"objects\", not \"refs\", as we go, it looks like that the flag SKIP in\n>> > this patch is placed conceptually at a wrong level.\n>>\n>> refs are marked as well.\n>>\n>> > I agree \"--branches --except maint\" is a good concept, but to\n>> > implement what this patch wants to do properly, I suspect that the\n>> > revision parser may need to be extended to be a two-phase process;\n>> > the first phase will collect list of objects involved in the range\n>> > expression without marking them with UNINTERESTING mark (that would\n>> > also involve expanding things like --all or --branches), while\n>> > remembering those given with --except, exclude the \"except\" set from\n>> > the first set, and then finally marking the objects using the\n>> > remainder, or something like that.\n>>\n>> That's not necessary, this patch does the trick.\n>>\n>> >> +                     ce = &revs->cmdline.rev[i];\n>> >> +                     if ((ce->flags & SKIP) && !strcmp(ce->name,\n>> >> e->name))\n>> >> +                             goto next;\n>> >\n>> > I think this SKIP will not help an object that is already tainted by\n>> > UNINTERESTING; if it is discovered during a traversal from another\n>> > object that will remain in the rev->commits, the travesal will stop\n>> > there, even if a ref that is marked with SKIP will \"goto next\" here.\n>>\n>> No, the traversal will continue just fine. At this point we are still\n>> not traversing anything, simply adding the heads that will need to be\n>> traversed later on to a list. Whether this object has been tainted by\n>> UNINTERESTED or not is irrelevant.\n>>\n>> If you do 'master ^maint --except master', handle_commit will return\n>> three commits:\n>\n> Would the same argument apply to\n>\n>   next ^maint --except maint\n>\n> where next gets in the queue, maint in tainted, and skipped?\n\nmaint is not skipped, as it's not the same as ^maint, basically it's\nthe same as:\n\nnext ^maint\n\nI think that's good, as there's absolutely no reason why anybody would\nwant '^maint --except maint' to cancel each other out.\n\n-- \nFelipe Contreras\n"},{"id":"226332","messageId":"5220503F.2080608@viscovery.net","threadId":"34805","inReplyTo":"7vhae7k7t1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] revision: add --except option","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2013-08-30T07:56:47Z","receivedAt":"2013-08-30T07:56:47Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 8/30/2013 8:32, schrieb Junio C Hamano:\n> If you have a history where\n> \n>  - branches \"master\" and \"maint\" point at commit A;\n>  - branch \"next\" points at commit B that is a descendant of A; and\n>  - there are tags X and Y pointing at commits that are ahead of B\n>    or behind A\n> \n> i.e.\n> \n> \t----X----A----B----Y\n> \n> what are the desired semantics for these?\n\nI think the simplest were that --except trumps everything and means\n\"whatever else I say, do as if I did not mention the following\".\n\n>  (1) --branches --except maint\n\n=> master next\n\n>  (2) --all --not --branches --except maint\n\n=> X Y --not master next\n\n>  (3) ^master next --except maint\n\n=> ^master next\n\nWhat should the following mean? Does --not forget that --except was\nearlier on the command line?\n\n(4) Y next --except master next --not --branches\n\nthis => Y --not maint\nor this => Y --not maint master next\n\nWhat about this:\n\n(5) --branches --except ^master\n\nthis => maint next\nor this => maint master next ^master\nor error(\"--except does not allow negated revisions\")\n\n-- Hannes\n"},{"id":"226335","messageId":"5220611C.5080605@viscovery.net","threadId":"34805","inReplyTo":"CAMP44s0P=XF5C8+fU2cJ-Xuq57iqcAn674Upub6N=+iiMpQK0g@mail.gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2013-08-30T09:08:44Z","receivedAt":"2013-08-30T09:08:44Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 8/30/2013 9:32, schrieb Felipe Contreras:\n> On Fri, Aug 30, 2013 at 2:26 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> On Aug 30, 2013 12:19 AM, \"Felipe Contreras\" <felipe.contreras@gmail.com>\n>> Would the same argument apply to\n>>\n>>   next ^maint --except maint\n>>\n>> where next gets in the queue, maint in tainted, and skipped?\n> \n> maint is not skipped, as it's not the same as ^maint, basically it's\n> the same as:\n> \n> next ^maint\n> \n> I think that's good, as there's absolutely no reason why anybody would\n> want '^maint --except maint' to cancel each other out.\n\nBut isn't this basically the same as '--not maint --except maint'? This by\nitself looks strange. But when disguised in the form '--not --branches\n--except maint', it would make sense to mean '--not master next', aka\n'^master ^next'.\n\n-- Hannes\n"},{"id":"226342","messageId":"xmqqeh9b15x6.fsf@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"CAMP44s0P=XF5C8+fU2cJ-Xuq57iqcAn674Upub6N=+iiMpQK0g@mail.gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-08-30T16:48:05Z","receivedAt":"2013-08-30T16:48:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n>>> If you do 'master ^maint --except master', handle_commit will return\n>>> three commits:\n>>\n>> Would the same argument apply to\n>>\n>>   next ^maint --except maint\n>>\n>> where next gets in the queue, maint in tainted, and skipped?\n>\n> maint is not skipped, as it's not the same as ^maint, basically it's\n> the same as:\n>\n> next ^maint\n>\n> I think that's good, as there's absolutely no reason why anybody would\n> want '^maint --except maint' to cancel each other out.\n\nI do not expect anybody to say \"^maint --except maint\", but the\nnegative one can come from expansion of --branches and such.\nEssentially, the semantics the patch implements is \"--except applies\nonly to positive tips\" (I have not thought about its implications on\nleft-right traversals; there may be some impact there as well).\n\nBeing able to exclude only positive ones is better than not being\nable to exclude any, but if we consider j6t's example, I think\napplying except to negative ones is equally useful.\n\nFor example, \"I want to see everything I have reachable from my refs\n(not just branches), but exclude those that are reachable from my\nstable branches (my local branches except those whose names match\nthe pattern 'wip/*')\" is a reasonable thing to ask.  A wip/* branch\nmay be based on the work in a remote tracking branch that you have\nnot merged to your 'maint', 'master' or 'next', and you want to view\nall the commits on the remote tracking branch you haven't merged to\nyour stable branches, including the ones that you already use in\nyour WIP that are not ready.  And the request may be spelled as\n\n\t--all --not --branches --except 'wip/*'\n\nWhich means that the approach taken by the patch to only allow\nexclusion of negative ones makes the idea only 50% useful compared\nto its potential.  And I suspect that \"we can start from 50% which\nis better than 0% and later fill the other 50%\" would not work in\nthis case, without ripping out the \"SKIP on object\" approach and\nredoing the \"--except\" support from scratch, because \"SKIP on\nobject\" fundamentally cannot undo the effects of the negative ones,\nbecause it records the information at a wrong level.\n\nIt may be a good idea to step back a bit and think of this topic as\na way to enhance the --branches option and its friends with only the\ninclusive wildcard semantics.  It lets us include those that match\nthe pattern with \"--branches=wip/*\", but there is no way to say \"oh\nby the way, I do not want those that match this pattern included\nwhen you expand this short-hand\".  We have --branches=pattern that\nis inclusive; perhaps it can be prefixed with --branches=!pattern to\npre-declare \"whatever the next --branches expands to, do not include\nthose that match this pattern\", or something, which would make the\nearlier \"wip\" example to be:\n\n\t--all --not --branches='!wip/*' --branches\n"},{"id":"226361","messageId":"CAMP44s180h8OTXx0v7dp3WKO2=ZBF3h7nXp_CcomQFmFn1SHSw@mail.gmail.com","threadId":"34805","inReplyTo":"xmqqeh9b15x6.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-30T18:37:46Z","receivedAt":"2013-08-30T18:37:46Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 11:48 AM, Junio C Hamano <gitster@pobox.com> wrote:\n\n> Which means that the approach taken by the patch to only allow\n> exclusion of negative ones makes the idea only 50% useful compared\n> to its potential.  And I suspect that \"we can start from 50% which\n> is better than 0% and later fill the other 50%\" would not work in\n> this case, without ripping out the \"SKIP on object\" approach and\n> redoing the \"--except\" support from scratch, because \"SKIP on\n> object\" fundamentally cannot undo the effects of the negative ones,\n> because it records the information at a wrong level.\n\nI think it is not 50%, it is 98%. I think one or two persons might use\nthis secondary feature if ever, and I think waiting for that\nimplementation will delay the feature that 98% of people would use,\nand maybe block it entirely.\n\n-- \nFelipe Contreras\n"},{"id":"226384","messageId":"xmqq1u5aybri.fsf_-_@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"xmqqeh9b15x6.fsf@gitster.dls.corp.google.com","subject":"[PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-08-30T23:55:29Z","receivedAt":"2013-08-30T23:55:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"People often find \"git log --branches\" etc. that includes _all_\nbranches is cumbersome to use when they want to grab most but except\nsome.  The same applies to --tags, --all and --glob.\n\nTeach the revision machinery to remember patterns, and then upon the\nnext such a globbing option, exclude those that match the pattern.\n\nWith this, I can view only my integration branches (e.g. maint,\nmaster, etc.) without topic branches, which are named after two\nletters from primary authors' names, slash and topic name.\n\n    git rev-list --no-walk --exclude=??/* --branches |\n    git name-rev --refs refs/heads/* --stdin\n\nThis one shows things reachable from local and remote branches that\nhave not been merged to the integration branches.\n\n    git log --remotes --branches --not --exclude=??/* --branches\n\nIt may be a bit rough around the edges, in that the pattern to give\nthe exclude option depends on what globbing option follows.  In\nthese examples, the pattern \"??/*\" is used, not \"refs/heads/??/*\",\nbecause the globbing option that follows the -\"-exclude=<pattern>\"\nis \"--branches\".  As each use of globbing option resets previously\nset \"--exclude\", this may not be such a bad thing, though.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n Junio C Hamano <gitster@pobox.com> writes:\n\n > It may be a good idea to step back a bit and think of this topic as\n > a way to enhance the --branches option and its friends with only the\n > inclusive wildcard semantics.  It lets us include those that match\n > the pattern with \"--branches=wip/*\", but there is no way to say \"oh\n > by the way, I do not want those that match this pattern included\n > when you expand this short-hand\".  We have --branches=pattern that\n > is inclusive; perhaps it can be prefixed with --branches=!pattern to\n > pre-declare \"whatever the next --branches expands to, do not include\n > those that match this pattern\", or something, which would make the\n > earlier \"wip\" example to be:\n >\n > \t--all --not --branches='!wip/*' --branches\n\n So here is a quick attempt at that approach, which does not look\n too intrusive. \n\n revision.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--\n revision.h |  3 +++\n 2 files changed, 51 insertions(+), 2 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 84ccc05..3e82874 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1180,11 +1180,28 @@ struct all_refs_cb {\n \tconst char *name_for_errormsg;\n };\n \n+static int ref_excluded(struct rev_info *revs, const char *path)\n+{\n+\tstruct string_list_item *item;\n+\n+\tif (!revs->ref_excludes)\n+\t\treturn 0;\n+\tfor_each_string_list_item(item, revs->ref_excludes) {\n+\t\tif (!fnmatch(item->string, path, 0))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n-\tstruct object *object = get_reference(cb->all_revs, path, sha1,\n-\t\t\t\t\t      cb->all_flags);\n+\tstruct object *object;\n+\n+\tif (ref_excluded(cb->all_revs, path))\n+\t    return 0;\n+\n+\tobject = get_reference(cb->all_revs, path, sha1, cb->all_flags);\n \tadd_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);\n \tadd_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);\n \treturn 0;\n@@ -1197,6 +1214,24 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,\n \tcb->all_flags = flags;\n }\n \n+static void clear_ref_exclusion(struct rev_info *revs)\n+{\n+\tif (revs->ref_excludes) {\n+\t\tstring_list_clear(revs->ref_excludes, 0);\n+\t\tfree(revs->ref_excludes);\n+\t}\n+\trevs->ref_excludes = NULL;\n+}\n+\n+static void add_ref_exclusion(struct rev_info *revs, const char *exclude)\n+{\n+\tif (!revs->ref_excludes) {\n+\t\trevs->ref_excludes = xcalloc(1, sizeof(*revs->ref_excludes));\n+\t\trevs->ref_excludes->strdup_strings = 1;\n+\t}\n+\tstring_list_append(revs->ref_excludes, exclude);\n+}\n+\n static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,\n \t\tint (*for_each)(const char *, each_ref_fn, void *))\n {\n@@ -1953,33 +1988,44 @@ static int handle_revision_pseudo_opt(const char *submodule,\n \tif (!strcmp(arg, \"--all\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_ref_submodule);\n \t\thandle_refs(submodule, revs, *flags, head_ref_submodule);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!strcmp(arg, \"--branches\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!strcmp(arg, \"--bisect\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);\n \t\thandle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);\n \t\trevs->bisect = 1;\n \t} else if (!strcmp(arg, \"--tags\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!strcmp(arg, \"--remotes\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);\n+\t\tclear_ref_exclusion(revs);\n \t} else if ((argcount = parse_long_opt(\"glob\", argv, &optarg))) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref(handle_one_ref, optarg, &cb);\n+\t\tclear_ref_exclusion(revs);\n+\t\treturn argcount;\n+\t} else if ((argcount = parse_long_opt(\"exclude\", argv, &optarg))) {\n+\t\tadd_ref_exclusion(revs, optarg);\n \t\treturn argcount;\n \t} else if (!prefixcmp(arg, \"--branches=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 11, \"refs/heads/\", &cb);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!prefixcmp(arg, \"--tags=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 7, \"refs/tags/\", &cb);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!prefixcmp(arg, \"--remotes=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 10, \"refs/remotes/\", &cb);\n+\t\tclear_ref_exclusion(revs);\n \t} else if (!strcmp(arg, \"--reflog\")) {\n \t\thandle_reflog(revs, *flags);\n \t} else if (!strcmp(arg, \"--not\")) {\ndiff --git a/revision.h b/revision.h\nindex 95859ba..b4dc56c 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -59,6 +59,9 @@ struct rev_info {\n \t/* The end-points specified by the end user */\n \tstruct rev_cmdline_info cmdline;\n \n+\t/* excluding from --branches, --refs, etc. expansion */\n+\tstruct string_list *ref_excludes;\n+\n \t/* Basic information */\n \tconst char *prefix;\n \tconst char *def;\n-- \n1.8.4-358-g02cbb92\n"},{"id":"226386","messageId":"CACsJy8DOx8Q5kLizfeP2AgDaD8+EDsQL9xtOGphrmgG=dAabcQ@mail.gmail.com","threadId":"34805","inReplyTo":"xmqq1u5aybri.fsf_-_@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-08-31T00:22:45Z","receivedAt":"2013-08-31T00:22:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Aug 31, 2013 at 6:55 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> +static int ref_excluded(struct rev_info *revs, const char *path)\n> +{\n> +       struct string_list_item *item;\n> +\n> +       if (!revs->ref_excludes)\n> +               return 0;\n> +       for_each_string_list_item(item, revs->ref_excludes) {\n> +               if (!fnmatch(item->string, path, 0))\n> +                       return 1;\n> +       }\n> +       return 0;\n> +}\n\nIf you pursue this, please use wildmatch instead so it supports \"foo/**\".\n-- \nDuy\n"},{"id":"226387","messageId":"xmqqwqn2wvlz.fsf@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"CACsJy8DOx8Q5kLizfeP2AgDaD8+EDsQL9xtOGphrmgG=dAabcQ@mail.gmail.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-08-31T00:29:44Z","receivedAt":"2013-08-31T00:29:44Z","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> On Sat, Aug 31, 2013 at 6:55 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +static int ref_excluded(struct rev_info *revs, const char *path)\n>> +{\n>> +       struct string_list_item *item;\n>> +\n>> +       if (!revs->ref_excludes)\n>> +               return 0;\n>> +       for_each_string_list_item(item, revs->ref_excludes) {\n>> +               if (!fnmatch(item->string, path, 0))\n>> +                       return 1;\n>> +       }\n>> +       return 0;\n>> +}\n>\n> If you pursue this, please use wildmatch instead so it supports \"foo/**\".\n\nThe thought crossed my mind and I think we should match what the\nexisting --glob=<pattern> option does.  A cursory look in\nrefs.c::filter_refs() used by refs.c::for_each_glob_ref_in() tells\nme that we are using fnmatch without FNM_PATHNAME, so that is what\nthe above part does.\n"},{"id":"226444","messageId":"CAMP44s0D98tggTjQsMn+-03KgSsbrh3nxYfLofpC1gfnJpEPyw@mail.gmail.com","threadId":"34805","inReplyTo":"5220503F.2080608@viscovery.net","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-31T19:27:08Z","receivedAt":"2013-08-31T19:27:08Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 2:56 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n> Am 8/30/2013 8:32, schrieb Junio C Hamano:\n>> If you have a history where\n>>\n>>  - branches \"master\" and \"maint\" point at commit A;\n>>  - branch \"next\" points at commit B that is a descendant of A; and\n>>  - there are tags X and Y pointing at commits that are ahead of B\n>>    or behind A\n>>\n>> i.e.\n>>\n>>       ----X----A----B----Y\n>>\n>> what are the desired semantics for these?\n>\n> I think the simplest were that --except trumps everything and means\n> \"whatever else I say, do as if I did not mention the following\".\n\nActually, my patch is almost there, I attach the necessary changed\nbelow to make everything work. I've added debug prints to show what\nit's actually doing:\n\n>>  (1) --branches --except maint\n>\n> => master next\n\n=> master next\n\n>>  (2) --all --not --branches --except maint\n>\n> => X Y --not master next\n\n=> ^master ^next X Y HEAD\n\n>>  (3) ^master next --except maint\n>\n> => ^master next\n\n=> ^master next\n\n> (4) Y next --except master next --not --branches\n>\n> this => Y --not maint\n> or this => Y --not maint master next\n\n=> Y\n\nRemember that maint (or rather ^maint) is after --except.\n\n> (5) --branches --except ^master\n>\n> this => maint next\n> or this => maint master next ^master\n> or error(\"--except does not allow negated revisions\")\n\n=> maint next\n\nHere's the diff:\n\n--- a/revision.c\n+++ b/revision.c\n@@ -2578,7 +2578,11 @@ void reset_revision_walk(void)\n static int refcmp(const char *a, const char *b)\n {\n        a = prettify_refname(a);\n+       if (*a == '^')\n+               a++;\n        b = prettify_refname(b);\n+       if (*b == '^')\n+               b++;\n        return strcmp(a, b);\n }\n\n@@ -2594,13 +2598,14 @@ int prepare_revision_walk(struct rev_info *revs)\n        revs->pending.alloc = 0;\n        revs->pending.objects = NULL;\n        while (--nr >= 0) {\n-               struct commit *commit = handle_commit(revs, e->item, e->name);\n+               struct commit *commit;\n                for (i = 0; i < revs->cmdline.nr; i++) {\n                        struct rev_cmdline_entry *ce;\n                        ce = &revs->cmdline.rev[i];\n                        if ((ce->flags & SKIP) && !refcmp(ce->name, e->name))\n                                goto next;\n                }\n+               commit = handle_commit(revs, e->item, e->name);\n                if (commit) {\n                        if (!(commit->object.flags & SEEN)) {\n                                commit->object.flags |= SEEN;\n\n-- \nFelipe Contreras\n"},{"id":"226445","messageId":"CAMP44s1hhoPbeBsmN8NL_VtCz3bO-jg1sPP7hovL1kPBhbrXFQ@mail.gmail.com","threadId":"34805","inReplyTo":"xmqq1u5aybri.fsf_-_@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-08-31T19:33:52Z","receivedAt":"2013-08-31T19:33:52Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Fri, Aug 30, 2013 at 6:55 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> People often find \"git log --branches\" etc. that includes _all_\n> branches is cumbersome to use when they want to grab most but except\n> some.  The same applies to --tags, --all and --glob.\n\nThis idea looks very familiar, from the wording of this commit message\nit seems you came with the idea out of nowhere. Did you?\n\n> Teach the revision machinery to remember patterns, and then upon the\n> next such a globbing option, exclude those that match the pattern.\n>\n> With this, I can view only my integration branches (e.g. maint,\n> master, etc.) without topic branches, which are named after two\n> letters from primary authors' names, slash and topic name.\n>\n>     git rev-list --no-walk --exclude=??/* --branches |\n>     git name-rev --refs refs/heads/* --stdin\n\nMy patch does the trick with:\n\n--branches --except --glob='heads/??/*'\n\n-- \nFelipe Contreras\n"},{"id":"226504","messageId":"52242F61.3090404@viscovery.net","threadId":"34805","inReplyTo":"CAMP44s0D98tggTjQsMn+-03KgSsbrh3nxYfLofpC1gfnJpEPyw@mail.gmail.com","subject":"Re: [PATCH] revision: add --except option","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2013-09-02T06:25:37Z","receivedAt":"2013-09-02T06:25:37Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 8/31/2013 21:27, schrieb Felipe Contreras:\n> On Fri, Aug 30, 2013 at 2:56 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n>> Am 8/30/2013 8:32, schrieb Junio C Hamano:\n>>> If you have a history where\n>>>\n>>>  - branches \"master\" and \"maint\" point at commit A;\n>>>  - branch \"next\" points at commit B that is a descendant of A; and\n>>>  - there are tags X and Y pointing at commits that are ahead of B\n>>>    or behind A\n>>>\n>>> i.e.\n>>>\n>>>       ----X----A----B----Y\n>>>\n>>> what are the desired semantics for these?\n>>\n>> I think the simplest were that --except trumps everything and means\n>> \"whatever else I say, do as if I did not mention the following\".\n> \n> Actually, my patch is almost there, I attach the necessary changed\n> below to make everything work. I've added debug prints to show what\n> it's actually doing:\n> \n>>>  (1) --branches --except maint\n>>\n>> => master next\n> \n> => master next\n> \n>>>  (2) --all --not --branches --except maint\n>>\n>> => X Y --not master next\n> \n> => ^master ^next X Y HEAD\n> \n>>>  (3) ^master next --except maint\n>>\n>> => ^master next\n> \n> => ^master next\n> \n>> (4) Y next --except master next --not --branches\n>>\n>> this => Y --not maint\n>> or this => Y --not maint master next\n> \n> => Y\n> \n> Remember that maint (or rather ^maint) is after --except.\n\nSure, but why is it not in the result? maint is not even mentioned under\n--except. Confused...\n\nAh, are you treating the union of master, next, and --branches as --except\nand ignore --not?\n\n-- Hannes\n"},{"id":"226515","messageId":"CAMP44s3eUmA9pVfZFbXHNvDiYwp9jttSTb6DBr8JrgTfhfD+_Q@mail.gmail.com","threadId":"34805","inReplyTo":"52242F61.3090404@viscovery.net","subject":"Re: [PATCH] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-02T06:48:37Z","receivedAt":"2013-09-02T06:48:37Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Mon, Sep 2, 2013 at 1:25 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n> Am 8/31/2013 21:27, schrieb Felipe Contreras:\n>> On Fri, Aug 30, 2013 at 2:56 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:\n>>> Am 8/30/2013 8:32, schrieb Junio C Hamano:\n>>>> If you have a history where\n>>>>\n>>>>  - branches \"master\" and \"maint\" point at commit A;\n>>>>  - branch \"next\" points at commit B that is a descendant of A; and\n>>>>  - there are tags X and Y pointing at commits that are ahead of B\n>>>>    or behind A\n>>>>\n>>>> i.e.\n>>>>\n>>>>       ----X----A----B----Y\n>>>>\n>>>> what are the desired semantics for these?\n>>>\n>>> I think the simplest were that --except trumps everything and means\n>>> \"whatever else I say, do as if I did not mention the following\".\n>>\n>> Actually, my patch is almost there, I attach the necessary changed\n>> below to make everything work. I've added debug prints to show what\n>> it's actually doing:\n>>\n>>>>  (1) --branches --except maint\n>>>\n>>> => master next\n>>\n>> => master next\n>>\n>>>>  (2) --all --not --branches --except maint\n>>>\n>>> => X Y --not master next\n>>\n>> => ^master ^next X Y HEAD\n>>\n>>>>  (3) ^master next --except maint\n>>>\n>>> => ^master next\n>>\n>> => ^master next\n>>\n>>> (4) Y next --except master next --not --branches\n>>>\n>>> this => Y --not maint\n>>> or this => Y --not maint master next\n>>\n>> => Y\n>>\n>> Remember that maint (or rather ^maint) is after --except.\n>\n> Sure, but why is it not in the result? maint is not even mentioned under\n> --except. Confused...\n\nIt is mentioned under --except, by --branches.\n\n> Ah, are you treating the union of master, next, and --branches as --except\n> and ignore --not?\n\nThese are the same:\n\nY next --except master next --not --branches\nY next --except master next --not master next maint\n\nIf you add more positive branches:\n\nThey get removed anyway by --except:\n\nY next master maint --except master next --not master next maint\nY master maint --except master --not master maint\nY maint --except --not maint\nY\n\n-- \nFelipe Contreras\n"},{"id":"226552","messageId":"5224F0EE.1080205@kdbg.org","threadId":"34805","inReplyTo":"xmqq1u5aybri.fsf_-_@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2013-09-02T20:11:26Z","receivedAt":"2013-09-02T20:11:26Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 31.08.2013 01:55, schrieb Junio C Hamano:\n> People often find \"git log --branches\" etc. that includes _all_\n> branches is cumbersome to use when they want to grab most but except\n> some.  The same applies to --tags, --all and --glob.\n> \n> Teach the revision machinery to remember patterns, and then upon the\n> next such a globbing option, exclude those that match the pattern.\n> \n> With this, I can view only my integration branches (e.g. maint,\n> master, etc.) without topic branches, which are named after two\n> letters from primary authors' names, slash and topic name.\n> \n>     git rev-list --no-walk --exclude=??/* --branches |\n>     git name-rev --refs refs/heads/* --stdin\n> \n> This one shows things reachable from local and remote branches that\n> have not been merged to the integration branches.\n> \n>     git log --remotes --branches --not --exclude=??/* --branches\n> \n> It may be a bit rough around the edges, in that the pattern to give\n> the exclude option depends on what globbing option follows.  In\n> these examples, the pattern \"??/*\" is used, not \"refs/heads/??/*\",\n> because the globbing option that follows the -\"-exclude=<pattern>\"\n> is \"--branches\".  As each use of globbing option resets previously\n> set \"--exclude\", this may not be such a bad thing, though.\n\nI argued \"--except should trump everything\" earlier, but the case\ninvolving --not\n\n  --branches --except maint --not master\n\nto mean the same as\n\n  --branches --except maint master\n\njust does not make sense.\n\nAn alternative would be that --not would divide the command line\narguments into ranges within which one --except would subtract\nsubsequent refs from earlier globbing arguments in the same range.\nThat's not simpler to explain than your current proposal.\n\nSo I like the relative simplicity of this approach. Here is a bit of\ndocumentation.\n\n--- 8< ---\nSubject: [PATCH] document --exclude option\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n Documentation/rev-list-options.txt | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 5bdfb42..650c252 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -174,6 +174,21 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).\n \tis automatically prepended if missing. If pattern lacks '?', '{asterisk}',\n \tor '[', '/{asterisk}' at the end is implied.\n \n+--exclude=<glob-pattern>::\n+\n+\tDo not include refs matching '<glob-pattern>' that the next `--all`,\n+\t`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n+\tconsider. Repetitions of this option accumulate exclusion patterns\n+\tup to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n+\t`--glob` option (other options or arguments do not clear\n+\taccumlated patterns).\n++\n+The patterns given should not begin with `refs/heads`, `refs/tags`, or\n+`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n+restrictively, and they must begin with `refs/` when applied to `--glob`\n+or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n+explicitly.\n+\n --ignore-missing::\n \n \tUpon seeing an invalid object name in the input, pretend as if\n-- \n1.8.4.33.gd68f7e8\n"},{"id":"226570","messageId":"CAMP44s2+cG2kb=o9OTT=8=2pm+xCwZEJE5dNkmyKx6P=03sX-Q@mail.gmail.com","threadId":"34805","inReplyTo":"5224F0EE.1080205@kdbg.org","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-02T23:09:50Z","receivedAt":"2013-09-02T23:09:50Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Mon, Sep 2, 2013 at 3:11 PM, Johannes Sixt <j6t@kdbg.org> wrote:\n> Am 31.08.2013 01:55, schrieb Junio C Hamano:\n>> People often find \"git log --branches\" etc. that includes _all_\n>> branches is cumbersome to use when they want to grab most but except\n>> some.  The same applies to --tags, --all and --glob.\n>>\n>> Teach the revision machinery to remember patterns, and then upon the\n>> next such a globbing option, exclude those that match the pattern.\n>>\n>> With this, I can view only my integration branches (e.g. maint,\n>> master, etc.) without topic branches, which are named after two\n>> letters from primary authors' names, slash and topic name.\n>>\n>>     git rev-list --no-walk --exclude=??/* --branches |\n>>     git name-rev --refs refs/heads/* --stdin\n>>\n>> This one shows things reachable from local and remote branches that\n>> have not been merged to the integration branches.\n>>\n>>     git log --remotes --branches --not --exclude=??/* --branches\n>>\n>> It may be a bit rough around the edges, in that the pattern to give\n>> the exclude option depends on what globbing option follows.  In\n>> these examples, the pattern \"??/*\" is used, not \"refs/heads/??/*\",\n>> because the globbing option that follows the -\"-exclude=<pattern>\"\n>> is \"--branches\".  As each use of globbing option resets previously\n>> set \"--exclude\", this may not be such a bad thing, though.\n>\n> I argued \"--except should trump everything\" earlier, but the case\n> involving --not\n>\n>   --branches --except maint --not master\n>\n> to mean the same as\n>\n>   --branches --except maint master\n>\n> just does not make sense.\n\nNo, but this could make sense:\n\n--branches ^master --except maint --not master\n==\n--branches --except maint\n\n> An alternative would be that --not would divide the command line\n> arguments into ranges within which one --except would subtract\n> subsequent refs from earlier globbing arguments in the same range.\n> That's not simpler to explain than your current proposal.\n\nSomething like that can be easily done in my approach:\n\n--- a/revision.c\n+++ b/revision.c\n@@ -1984,6 +1984,7 @@ static int handle_revision_pseudo_opt(const char\n*submodule,\n                handle_reflog(revs, *flags);\n        } else if (!strcmp(arg, \"--not\")) {\n                *flags ^= UNINTERESTING | BOTTOM;\n+               *flags &= ~SKIP;\n        } else if (!strcmp(arg, \"--except\")) {\n                *flags |= SKIP;\n        } else if (!strcmp(arg, \"--no-walk\")) {\n@@ -2628,7 +2629,8 @@ int prepare_revision_walk(struct rev_info *revs)\n                for (i = 0; i < revs->cmdline.nr; i++) {\n                        struct rev_cmdline_entry *ce;\n                        ce = &revs->cmdline.rev[i];\n-                       if ((ce->flags & SKIP) && !refcmp(ce->name, e->name)) {\n+                       if ((ce->flags & SKIP) && !refcmp(ce->name, e->name) &&\n+                                       ((ce->flags & UNINTERESTING)\n== (e->item->flags & UNINTERESTING))) {\n                                e->item->flags =\nrecalculate_flag(revs, e->item->sha1, ce->name);\n                                goto next;\n                        }\ndiff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh\nindex a40a641..441e1da 100755\n--- a/t/t6112-rev-list-except.sh\n+++ b/t/t6112-rev-list-except.sh\n@@ -57,4 +57,21 @@ test_expect_success 'rev-list --except and --not\nwith proper flags' '\n        test_cmp expect actual\n '\n\n+test_expect_success 'rev-list --not ranges' '\n+\n+       git rev-list --topo-order test --not master --except master\ntest > actual &&\n+       git rev-list --topo-order test > expect &&\n+       test_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list multiple --not ranges' '\n+\n+       git checkout -b extra test &&\n+       echo five > content &&\n+       git commit -a -m five &&\n+       git rev-list --topo-order test --not master --except master\ntest --not extra > actual &&\n+       git rev-list --topo-order test extra > expect &&\n+       test_cmp expect actual\n+'\n+\n\n-- \nFelipe Contreras\n"},{"id":"226574","messageId":"52256014.70203@alum.mit.edu","threadId":"34805","inReplyTo":"5224F0EE.1080205@kdbg.org","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2013-09-03T04:05:40Z","receivedAt":"2013-09-03T04:05:40Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 09/02/2013 10:11 PM, Johannes Sixt wrote:\n> Am 31.08.2013 01:55, schrieb Junio C Hamano:\n>> People often find \"git log --branches\" etc. that includes _all_\n>> branches is cumbersome to use when they want to grab most but except\n>> some.  The same applies to --tags, --all and --glob.\n>>\n>> Teach the revision machinery to remember patterns, and then upon the\n>> next such a globbing option, exclude those that match the pattern.\n>>\n>> With this, I can view only my integration branches (e.g. maint,\n>> master, etc.) without topic branches, which are named after two\n>> letters from primary authors' names, slash and topic name.\n>>\n>>     git rev-list --no-walk --exclude=??/* --branches |\n>>     git name-rev --refs refs/heads/* --stdin\n>>\n>> This one shows things reachable from local and remote branches that\n>> have not been merged to the integration branches.\n>>\n>>     git log --remotes --branches --not --exclude=??/* --branches\n>>\n>> It may be a bit rough around the edges, in that the pattern to give\n>> the exclude option depends on what globbing option follows.  In\n>> these examples, the pattern \"??/*\" is used, not \"refs/heads/??/*\",\n>> because the globbing option that follows the -\"-exclude=<pattern>\"\n>> is \"--branches\".  As each use of globbing option resets previously\n>> set \"--exclude\", this may not be such a bad thing, though.\n> \n> I argued \"--except should trump everything\" earlier, but the case\n> involving --not\n> \n>   --branches --except maint --not master\n> \n> to mean the same as\n> \n>   --branches --except maint master\n> \n> just does not make sense.\n> \n> An alternative would be that --not would divide the command line\n> arguments into ranges within which one --except would subtract\n> subsequent refs from earlier globbing arguments in the same range.\n> That's not simpler to explain than your current proposal.\n> \n> So I like the relative simplicity of this approach. Here is a bit of\n> documentation.\n> \n> --- 8< ---\n> Subject: [PATCH] document --exclude option\n> \n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  Documentation/rev-list-options.txt | 15 +++++++++++++++\n>  1 file changed, 15 insertions(+)\n> \n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index 5bdfb42..650c252 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -174,6 +174,21 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).\n>  \tis automatically prepended if missing. If pattern lacks '?', '{asterisk}',\n>  \tor '[', '/{asterisk}' at the end is implied.\n>  \n> +--exclude=<glob-pattern>::\n> +\n> +\tDo not include refs matching '<glob-pattern>' that the next `--all`,\n> +\t`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n> +\tconsider. Repetitions of this option accumulate exclusion patterns\n> +\tup to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n> +\t`--glob` option (other options or arguments do not clear\n> +\taccumlated patterns).\n> ++\n> +The patterns given should not begin with `refs/heads`, `refs/tags`, or\n> +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n> +restrictively, and they must begin with `refs/` when applied to `--glob`\n\ns/restrictively/respectively/\n\n> +or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n> +explicitly.\n> +\n>  --ignore-missing::\n>  \n>  \tUpon seeing an invalid object name in the input, pretend as if\n> \n\nIt seems to me that this is growing into a language for expressing\nboolean expressions without allowing terms to be combined in the full\ngenerality that, say, a real programming language would allow.  Maybe\ninstead of trying to decide on the \"perfect\" grouping and precedence\nrules, it would be clearer to allow the user to specify them.  I almost\nhate to suggest it, but have you considered making the expression\n\"syntax\" a little bit more flexible by allowing parentheses, à la\nfind(1), or something analogous?:\n\n'(' --tags --except='v[0-9]*' ')' -o '(' --branches --except='mh/*' ')'\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"226626","messageId":"xmqq8uzdvrh1.fsf@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"CAMP44s1hhoPbeBsmN8NL_VtCz3bO-jg1sPP7hovL1kPBhbrXFQ@mail.gmail.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-03T15:45:46Z","receivedAt":"2013-09-03T15:45:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n> On Fri, Aug 30, 2013 at 6:55 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> People often find \"git log --branches\" etc. that includes _all_\n>> branches is cumbersome to use when they want to grab most but except\n>> some.  The same applies to --tags, --all and --glob.\n>\n> This idea looks very familiar, from the wording of this commit message\n> it seems you came with the idea out of nowhere. Did you?\n\nAs the comment after three-dash quotes, the inspiration came from\nthis suggestion:\n\n > It may be a good idea to step back a bit and think of this topic as\n > a way to enhance the --branches option and its friends with only the\n > inclusive wildcard semantics.\n\nwhich is not anything new.  It takes from J6t's\n\n      To unclutter 'git branch' output, I rename work-in-progress branches\n      to begin with \"wip/\", ready-to-merge branches do not carry this\n      prefix. To inspect unmerged work of the latter kind of branches I\n      would like to type... what?\n\nBut the thing is, that is nothing new, either.\n\nPretty much ever since we added --branches --tags and later --glob,\n\n (1) traversing from \"almost all but minus some branches\", and\n (2) stopping traversal at \"almost all but minus some branches\"\n\nwere what people sometimes wanted to have (which is pretty much what\nthe first paragraph of the proposed commit message says) using\n\"negative glob\". Looking the phrase in the list archive finds for\nexample $gmane/159770 from 2010, but I would not be surprised if you\nfind older message wishing the same.\n"},{"id":"226628","messageId":"xmqq4na1vqng.fsf@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"5224F0EE.1080205@kdbg.org","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-09-03T16:03:31Z","receivedAt":"2013-09-03T16:03:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> Am 31.08.2013 01:55, schrieb Junio C Hamano:\n>> People often find \"git log --branches\" etc. that includes _all_\n>> branches is cumbersome to use when they want to grab most but except\n>> some.  The same applies to --tags, --all and --glob.\n>> \n>> Teach the revision machinery to remember patterns, and then upon the\n>> next such a globbing option, exclude those that match the pattern.\n>> \n>> With this, I can view only my integration branches (e.g. maint,\n>> master, etc.) without topic branches, which are named after two\n>> letters from primary authors' names, slash and topic name.\n>> \n>>     git rev-list --no-walk --exclude=??/* --branches |\n>>     git name-rev --refs refs/heads/* --stdin\n>> \n>> This one shows things reachable from local and remote branches that\n>> have not been merged to the integration branches.\n>> \n>>     git log --remotes --branches --not --exclude=??/* --branches\n>> \n>> It may be a bit rough around the edges, in that the pattern to give\n>> the exclude option depends on what globbing option follows.  In\n>> these examples, the pattern \"??/*\" is used, not \"refs/heads/??/*\",\n>> because the globbing option that follows the -\"-exclude=<pattern>\"\n>> is \"--branches\".  As each use of globbing option resets previously\n>> set \"--exclude\", this may not be such a bad thing, though.\n>\n> I argued \"--except should trump everything\" earlier, but the case\n> involving --not\n>\n>   --branches --except maint --not master\n>\n> to mean the same as\n>\n>   --branches --except maint master\n>\n> just does not make sense.\n\nI'll have to mull the above two over to firmly grasp what you mean\nby \"'except' trumping 'not'\" before deciding if you are agreeing or\ndisagreeing with the approach of taking it as a \"taming wildcard\"\nissue...\n\n> An alternative would be that --not would divide the command line\n> arguments into ranges within which one --except would subtract\n> subsequent refs from earlier globbing arguments in the same range.\n> That's not simpler to explain than your current proposal.\n>\n> So I like the relative simplicity of this approach. Here is a bit of\n> documentation.\n\nOh, thanks for helping.  An example or two may also help, and using\nyour original \"I have branches and wip/\" situation would be good.\n\n        git log --exclude='wip/*' --branches::\n\n                Show history of local branches whose names do not\n                match pattern `wip/*`.\n\n\tgit log --remotes --not --exclude='wip/*' --branches::\n\n\t\tShow history of other people's work that are not\n\t\tmerged to local branches whose names do not match\n\t\tpattern `wip/*`.\n\nor something like that, perhaps?\n\n> --- 8< ---\n> Subject: [PATCH] document --exclude option\n>\n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  Documentation/rev-list-options.txt | 15 +++++++++++++++\n>  1 file changed, 15 insertions(+)\n>\n> diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\n> index 5bdfb42..650c252 100644\n> --- a/Documentation/rev-list-options.txt\n> +++ b/Documentation/rev-list-options.txt\n> @@ -174,6 +174,21 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).\n>  \tis automatically prepended if missing. If pattern lacks '?', '{asterisk}',\n>  \tor '[', '/{asterisk}' at the end is implied.\n>  \n> +--exclude=<glob-pattern>::\n> +\n> +\tDo not include refs matching '<glob-pattern>' that the next `--all`,\n> +\t`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n> +\tconsider. Repetitions of this option accumulate exclusion patterns\n> +\tup to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n> +\t`--glob` option (other options or arguments do not clear\n> +\taccumlated patterns).\n> ++\n> +The patterns given should not begin with `refs/heads`, `refs/tags`, or\n> +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n> +restrictively, and they must begin with `refs/` when applied to `--glob`\n> +or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n> +explicitly.\n> +\n>  --ignore-missing::\n>  \n>  \tUpon seeing an invalid object name in the input, pretend as if\n"},{"id":"226665","messageId":"52264070.3080909@kdbg.org","threadId":"34805","inReplyTo":"xmqq4na1vqng.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2013-09-03T20:02:56Z","receivedAt":"2013-09-03T20:02:56Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 03.09.2013 18:03, schrieb Junio C Hamano:\n> Johannes Sixt <j6t@kdbg.org> writes:\n>> So I like the relative simplicity of this approach. Here is a bit of\n>> documentation.\n> \n> Oh, thanks for helping.  An example or two may also help, and using\n> your original \"I have branches and wip/\" situation would be good.\n> \n>         git log --exclude='wip/*' --branches::\n> \n>                 Show history of local branches whose names do not\n>                 match pattern `wip/*`.\n> \n> \tgit log --remotes --not --exclude='wip/*' --branches::\n> \n> \t\tShow history of other people's work that are not\n> \t\tmerged to local branches whose names do not match\n> \t\tpattern `wip/*`.\n> \n> or something like that, perhaps?\n\nAgreed and noted.\n\nThe new option must also be integrated in rev-parse. That will take a\nbit more work.\n\nI'll pick up the topic when I find the time to do so.\n\n-- Hannes\n"},{"id":"226684","messageId":"CAMP44s12Huoeuxw-pD7Y-C5FeKD=-4BOvsA8vpLzaySZfi1k1Q@mail.gmail.com","threadId":"34805","inReplyTo":"xmqq8uzdvrh1.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH] revision: introduce --exclude=<glob> to tame wildcards","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-03T22:03:47Z","receivedAt":"2013-09-03T22:03:47Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Tue, Sep 3, 2013 at 10:45 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Felipe Contreras <felipe.contreras@gmail.com> writes:\n>\n>> On Fri, Aug 30, 2013 at 6:55 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> People often find \"git log --branches\" etc. that includes _all_\n>>> branches is cumbersome to use when they want to grab most but except\n>>> some.  The same applies to --tags, --all and --glob.\n>>\n>> This idea looks very familiar, from the wording of this commit message\n>> it seems you came with the idea out of nowhere. Did you?\n>\n> As the comment after three-dash quotes, the inspiration came from\n> this suggestion:\n>\n>  > It may be a good idea to step back a bit and think of this topic as\n>  > a way to enhance the --branches option and its friends with only the\n>  > inclusive wildcard semantics.\n>\n> which is not anything new.  It takes from J6t's\n>\n>       To unclutter 'git branch' output, I rename work-in-progress branches\n>       to begin with \"wip/\", ready-to-merge branches do not carry this\n>       prefix. To inspect unmerged work of the latter kind of branches I\n>       would like to type... what?\n\nYeah, and where did that come from?\n\n> But the thing is, that is nothing new, either.\n>\n> Pretty much ever since we added --branches --tags and later --glob,\n>\n>  (1) traversing from \"almost all but minus some branches\", and\n>  (2) stopping traversal at \"almost all but minus some branches\"\n>\n> were what people sometimes wanted to have (which is pretty much what\n> the first paragraph of the proposed commit message says) using\n> \"negative glob\". Looking the phrase in the list archive finds for\n> example $gmane/159770 from 2010, but I would not be surprised if you\n> find older message wishing the same.\n\nIt is very peculiar that your patch suddenly appeared after I sent a\npatch for --except, and nobody has sent a similar patch, which is not\nwhat you would expect if \"this was nothing new\".\n\n-- \nFelipe Contreras\n"},{"id":"230011","messageId":"1383334455-18623-1-git-send-email-gitster@pobox.com","threadId":"34805","inReplyTo":"52264070.3080909@kdbg.org","subject":"[PATCH 0/5] ref glob exclusion follow-up","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-01T19:34:12Z","receivedAt":"2013-11-01T19:34:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"In $gmane/234730, J6t pointed out that \"rev-list --exclude\" needs a\nmatching updates to the globbing option \"rev-parse\" supports.\n\nHere is a follow-up to do just that. They are meant to be applied on\ntop of the two patch series that has been cooking in 'pu'.\n\n - $gmane/233484: e7b432c5 (revision: introduce --exclude=<glob> to\n   tame wildcards, 2013-08-30)\n    \n - $gmane/233656 by J6t: 5c5d4cdb (document --exclude option,\n  2013-09-02) came from\n\nThe second one [4/5] in this follow-up series should probably be\nsquashed into [1/5] e7b432c5.  We may even want to squash these five\npatches further down to two patches, one for rev-list and the other\nfor rev-parse.\n\nJunio C Hamano (3):\n  rev-list --exclude: tests\n  rev-list --exclude: export add/clear-ref-exclusion and ref-excluded\n    API\n  rev-parse: introduce --exclude=<glob> to tame wildcards\n\n Documentation/git-rev-parse.txt | 14 +++++++++++\n builtin/rev-parse.c             | 17 +++++++++++++\n revision.c                      | 46 +++++++++++++++++------------------\n revision.h                      |  5 ++++\n t/t6018-rev-list-glob.sh        | 54 +++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 113 insertions(+), 23 deletions(-)\n\n-- \n1.8.5-rc0-281-g8951339\n"},{"id":"230013","messageId":"1383334455-18623-2-git-send-email-gitster@pobox.com","threadId":"34805","inReplyTo":"1383334455-18623-1-git-send-email-gitster@pobox.com","subject":"[PATCH 3/5] rev-list --exclude: tests","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-01T19:34:13Z","receivedAt":"2013-11-01T19:34:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Add tests for the --exclude=<glob> feature.\n\nA few tests are added for cases where use of globbing and\n\"--exclude\" results in no positive revisions:\n\n * \"--exclude=<glob>\" before \"--all\" etc. resulted in no results;\n\n * \"--stdin\" is used but no input was given;\n\n * \"--all\" etc. is used but no matching refs are found.\n\nCurrently, we fail such a request with the same error message we\nwould give to a command line that does not specify any positive\nrevision (e.g. \"git rev-list<ENTER>\").\n\nWe may want to treat these cases differently and not error out, but\nthe logic to detect that would be common to all of them, so I'd\nleave it outside this topic for now, and stop at adding these tests\nas food-for-thought.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t6018-rev-list-glob.sh | 42 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 42 insertions(+)\n\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex f00cebf..77ef323 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -231,6 +231,48 @@ test_expect_success 'rev-list --remotes=foo' '\n \n '\n \n+test_expect_success 'rev-list --exclude with --branches' '\n+\tcompare rev-list \"--exclude=*/* --branches\" \"master someref subspace-x\"\n+'\n+\n+test_expect_success 'rev-list --exclude with --all' '\n+\tcompare rev-list \"--exclude=refs/remotes/* --all\" \"--branches --tags\"\n+'\n+\n+test_expect_success 'rev-list accumulates multiple --exclude' '\n+\tcompare rev-list \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n+'\n+\n+\n+# \"git rev-list<ENTER>\" is likely to be a bug in the calling script and may\n+# deserve an error message, but do cases where set of refs programatically\n+# given using globbing and/or --stdin need to fail with the same error, or\n+# are we better off reporting a success with no output?  The following few\n+# tests document the current behaviour to remind us that we might want to\n+# think about this issue.\n+\n+test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' '\n+\t>expect &&\n+\tgit rev-list --stdin <expect >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' '\n+\t>expect &&\n+\tgit rev-list --exclude=* --all >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' '\n+\t(\n+\t\ttest_create_repo empty &&\n+\t\tcd empty &&\n+\t\t>expect &&\n+\t\tgit rev-list --all >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'shortlog accepts --glob/--tags/--remotes' '\n \n \tcompare shortlog \"subspace/one subspace/two\" --branches=subspace &&\n-- \n1.8.5-rc0-281-g8951339\n"},{"id":"230012","messageId":"1383334455-18623-3-git-send-email-gitster@pobox.com","threadId":"34805","inReplyTo":"1383334455-18623-1-git-send-email-gitster@pobox.com","subject":"[PATCH 4/5] rev-list --exclude: export add/clear-ref-exclusion and ref-excluded API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-01T19:34:14Z","receivedAt":"2013-11-01T19:34:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"... while updating their function signature.  To be squashed into\nthe initial patch to rev-list.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n revision.c | 46 +++++++++++++++++++++++-----------------------\n revision.h |  5 +++++\n 2 files changed, 28 insertions(+), 23 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 3e82874..ddc71b9 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1180,13 +1180,13 @@ struct all_refs_cb {\n \tconst char *name_for_errormsg;\n };\n \n-static int ref_excluded(struct rev_info *revs, const char *path)\n+int ref_excluded(struct string_list *ref_excludes, const char *path)\n {\n \tstruct string_list_item *item;\n \n-\tif (!revs->ref_excludes)\n+\tif (!ref_excludes)\n \t\treturn 0;\n-\tfor_each_string_list_item(item, revs->ref_excludes) {\n+\tfor_each_string_list_item(item, ref_excludes) {\n \t\tif (!fnmatch(item->string, path, 0))\n \t\t\treturn 1;\n \t}\n@@ -1198,7 +1198,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,\n \tstruct all_refs_cb *cb = cb_data;\n \tstruct object *object;\n \n-\tif (ref_excluded(cb->all_revs, path))\n+\tif (ref_excluded(cb->all_revs->ref_excludes, path))\n \t    return 0;\n \n \tobject = get_reference(cb->all_revs, path, sha1, cb->all_flags);\n@@ -1214,22 +1214,22 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,\n \tcb->all_flags = flags;\n }\n \n-static void clear_ref_exclusion(struct rev_info *revs)\n+void clear_ref_exclusion(struct string_list **ref_excludes_p)\n {\n-\tif (revs->ref_excludes) {\n-\t\tstring_list_clear(revs->ref_excludes, 0);\n-\t\tfree(revs->ref_excludes);\n+\tif (*ref_excludes_p) {\n+\t\tstring_list_clear(*ref_excludes_p, 0);\n+\t\tfree(*ref_excludes_p);\n \t}\n-\trevs->ref_excludes = NULL;\n+\t*ref_excludes_p = NULL;\n }\n \n-static void add_ref_exclusion(struct rev_info *revs, const char *exclude)\n+void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)\n {\n-\tif (!revs->ref_excludes) {\n-\t\trevs->ref_excludes = xcalloc(1, sizeof(*revs->ref_excludes));\n-\t\trevs->ref_excludes->strdup_strings = 1;\n+\tif (!*ref_excludes_p) {\n+\t\t*ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));\n+\t\t(*ref_excludes_p)->strdup_strings = 1;\n \t}\n-\tstring_list_append(revs->ref_excludes, exclude);\n+\tstring_list_append(*ref_excludes_p, exclude);\n }\n \n static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,\n@@ -1988,44 +1988,44 @@ static int handle_revision_pseudo_opt(const char *submodule,\n \tif (!strcmp(arg, \"--all\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_ref_submodule);\n \t\thandle_refs(submodule, revs, *flags, head_ref_submodule);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!strcmp(arg, \"--branches\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!strcmp(arg, \"--bisect\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);\n \t\thandle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);\n \t\trevs->bisect = 1;\n \t} else if (!strcmp(arg, \"--tags\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!strcmp(arg, \"--remotes\")) {\n \t\thandle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if ((argcount = parse_long_opt(\"glob\", argv, &optarg))) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref(handle_one_ref, optarg, &cb);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t\treturn argcount;\n \t} else if ((argcount = parse_long_opt(\"exclude\", argv, &optarg))) {\n-\t\tadd_ref_exclusion(revs, optarg);\n+\t\tadd_ref_exclusion(&revs->ref_excludes, optarg);\n \t\treturn argcount;\n \t} else if (!prefixcmp(arg, \"--branches=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 11, \"refs/heads/\", &cb);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!prefixcmp(arg, \"--tags=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 7, \"refs/tags/\", &cb);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!prefixcmp(arg, \"--remotes=\")) {\n \t\tstruct all_refs_cb cb;\n \t\tinit_all_refs_cb(&cb, revs, *flags);\n \t\tfor_each_glob_ref_in(handle_one_ref, arg + 10, \"refs/remotes/\", &cb);\n-\t\tclear_ref_exclusion(revs);\n+\t\tclear_ref_exclusion(&revs->ref_excludes);\n \t} else if (!strcmp(arg, \"--reflog\")) {\n \t\thandle_reflog(revs, *flags);\n \t} else if (!strcmp(arg, \"--not\")) {\ndiff --git a/revision.h b/revision.h\nindex b4dc56c..c67c46d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -192,6 +192,11 @@ struct rev_info {\n \tstruct decoration line_log_data;\n };\n \n+extern int ref_excluded(struct string_list *, const char *path);\n+void clear_ref_exclusion(struct string_list **);\n+void add_ref_exclusion(struct string_list **, const char *exclude);\n+\n+\n #define REV_TREE_SAME\t\t0\n #define REV_TREE_NEW\t\t1\t/* Only new files */\n #define REV_TREE_OLD\t\t2\t/* Only files removed */\n-- \n1.8.5-rc0-281-g8951339\n"},{"id":"230014","messageId":"1383334455-18623-4-git-send-email-gitster@pobox.com","threadId":"34805","inReplyTo":"1383334455-18623-1-git-send-email-gitster@pobox.com","subject":"[PATCH 5/5] rev-parse: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-01T19:34:15Z","receivedAt":"2013-11-01T19:34:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Teach \"rev-parse\" the same \"I'm going to glob, but omit the ones\nthat match these patterns\" feature as \"rev-list\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-rev-parse.txt | 14 ++++++++++++++\n builtin/rev-parse.c             | 17 +++++++++++++++++\n t/t6018-rev-list-glob.sh        | 12 ++++++++++++\n 3 files changed, 43 insertions(+)\n\ndiff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\nindex 2b126c0..d4639a2 100644\n--- a/Documentation/git-rev-parse.txt\n+++ b/Documentation/git-rev-parse.txt\n@@ -155,6 +155,20 @@ shown.  If the pattern does not contain a globbing character (`?`,\n \tcharacter (`?`, `*`, or `[`), it is turned into a prefix\n \tmatch by appending `/*`.\n \n+--exclude=<glob-pattern>::\n+\tDo not include refs matching '<glob-pattern>' that the next `--all`,\n+\t`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n+\tconsider. Repetitions of this option accumulate exclusion patterns\n+\tup to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n+\t`--glob` option (other options or arguments do not clear\n+\taccumlated patterns).\n++\n+The patterns given should not begin with `refs/heads`, `refs/tags`, or\n+`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n+restrictively, and they must begin with `refs/` when applied to `--glob`\n+or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n+explicitly.\n+\n --show-toplevel::\n \tShow the absolute path of the top-level directory.\n \ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex de894c7..f52f804 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -9,6 +9,8 @@\n #include \"quote.h\"\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n \n #define DO_REVS\t\t1\n #define DO_NOREV\t2\n@@ -30,6 +32,8 @@ static int abbrev_ref;\n static int abbrev_ref_strict;\n static int output_sq;\n \n+static struct string_list *ref_excludes;\n+\n /*\n  * Some arguments are relevant \"revision\" arguments,\n  * others are about output format or other details.\n@@ -185,6 +189,8 @@ static int show_default(void)\n \n static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n+\tif (ref_excluded(ref_excludes, refname))\n+\t\treturn 0;\n \tshow_rev(NORMAL, sha1, refname);\n \treturn 0;\n }\n@@ -633,32 +639,43 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\tif (!prefixcmp(arg, \"--branches=\")) {\n \t\t\t\tfor_each_glob_ref_in(show_reference, arg + 11,\n \t\t\t\t\t\"refs/heads/\", NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--branches\")) {\n \t\t\t\tfor_each_branch_ref(show_reference, NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--tags=\")) {\n \t\t\t\tfor_each_glob_ref_in(show_reference, arg + 7,\n \t\t\t\t\t\"refs/tags/\", NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--tags\")) {\n \t\t\t\tfor_each_tag_ref(show_reference, NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--glob=\")) {\n \t\t\t\tfor_each_glob_ref(show_reference, arg + 7, NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--remotes=\")) {\n \t\t\t\tfor_each_glob_ref_in(show_reference, arg + 10,\n \t\t\t\t\t\"refs/remotes/\", NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--remotes\")) {\n \t\t\t\tfor_each_remote_ref(show_reference, NULL);\n+\t\t\t\tclear_ref_exclusion(&ref_excludes);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!prefixcmp(arg, \"--exclude=\")) {\n+\t\t\t\tadd_ref_exclusion(&ref_excludes, arg + 10);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-toplevel\")) {\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 77ef323..d00f7db 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -129,6 +129,18 @@ test_expect_success 'rev-parse --remotes=foo' '\n \n '\n \n+test_expect_success 'rev-parse --exclude with --branches' '\n+\tcompare rev-parse \"--exclude=*/* --branches\" \"master someref subspace-x\"\n+'\n+\n+test_expect_success 'rev-parse --exclude with --all' '\n+\tcompare rev-parse \"--exclude=refs/remotes/* --all\" \"--branches --tags\"\n+'\n+\n+test_expect_success 'rev-parse accumulates multiple --exclude' '\n+\tcompare rev-parse \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n+'\n+\n test_expect_success 'rev-list --glob=refs/heads/subspace/*' '\n \n \tcompare rev-list \"subspace/one subspace/two\" \"--glob=refs/heads/subspace/*\"\n-- \n1.8.5-rc0-281-g8951339\n"},{"id":"230015","messageId":"CAPig+cQf+FEiZiLfHUsH2XH9KcpCqmJP7xVPok-Ww9fuTX_Wxw@mail.gmail.com","threadId":"34805","inReplyTo":"1383334455-18623-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 5/5] rev-parse: introduce --exclude=<glob> to tame wildcards","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-11-01T19:43:45Z","receivedAt":"2013-11-01T19:43:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Nov 1, 2013 at 3:34 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Teach \"rev-parse\" the same \"I'm going to glob, but omit the ones\n> that match these patterns\" feature as \"rev-list\".\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/git-rev-parse.txt | 14 ++++++++++++++\n>  builtin/rev-parse.c             | 17 +++++++++++++++++\n>  t/t6018-rev-list-glob.sh        | 12 ++++++++++++\n>  3 files changed, 43 insertions(+)\n>\n> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\n> index 2b126c0..d4639a2 100644\n> --- a/Documentation/git-rev-parse.txt\n> +++ b/Documentation/git-rev-parse.txt\n> @@ -155,6 +155,20 @@ shown.  If the pattern does not contain a globbing character (`?`,\n>         character (`?`, `*`, or `[`), it is turned into a prefix\n>         match by appending `/*`.\n>\n> +--exclude=<glob-pattern>::\n> +       Do not include refs matching '<glob-pattern>' that the next `--all`,\n> +       `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n> +       consider. Repetitions of this option accumulate exclusion patterns\n> +       up to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n> +       `--glob` option (other options or arguments do not clear\n> +       accumlated patterns).\n> ++\n> +The patterns given should not begin with `refs/heads`, `refs/tags`, or\n> +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n> +restrictively, and they must begin with `refs/` when applied to `--glob`\n\nDid you mean s/restrictively/respectively/ ?\n\n> +or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n> +explicitly.\n"},{"id":"230016","messageId":"xmqqy55728nl.fsf@gitster.dls.corp.google.com","threadId":"34805","inReplyTo":"CAPig+cQf+FEiZiLfHUsH2XH9KcpCqmJP7xVPok-Ww9fuTX_Wxw@mail.gmail.com","subject":"Re: [PATCH 5/5] rev-parse: introduce --exclude=<glob> to tame wildcards","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-11-01T20:01:50Z","receivedAt":"2013-11-01T20:01:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Fri, Nov 1, 2013 at 3:34 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Teach \"rev-parse\" the same \"I'm going to glob, but omit the ones\n>> that match these patterns\" feature as \"rev-list\".\n>>\n>> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n>> ---\n>>  Documentation/git-rev-parse.txt | 14 ++++++++++++++\n>>  builtin/rev-parse.c             | 17 +++++++++++++++++\n>>  t/t6018-rev-list-glob.sh        | 12 ++++++++++++\n>>  3 files changed, 43 insertions(+)\n>>\n>> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\n>> index 2b126c0..d4639a2 100644\n>> --- a/Documentation/git-rev-parse.txt\n>> +++ b/Documentation/git-rev-parse.txt\n>> @@ -155,6 +155,20 @@ shown.  If the pattern does not contain a globbing character (`?`,\n>>         character (`?`, `*`, or `[`), it is turned into a prefix\n>>         match by appending `/*`.\n>>\n>> +--exclude=<glob-pattern>::\n>> +       Do not include refs matching '<glob-pattern>' that the next `--all`,\n>> +       `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise\n>> +       consider. Repetitions of this option accumulate exclusion patterns\n>> +       up to the next `--all`, `--branches`, `--tags`, `--remotes`, or\n>> +       `--glob` option (other options or arguments do not clear\n>> +       accumlated patterns).\n>> ++\n>> +The patterns given should not begin with `refs/heads`, `refs/tags`, or\n>> +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,\n>> +restrictively, and they must begin with `refs/` when applied to `--glob`\n>\n> Did you mean s/restrictively/respectively/ ?\n\nI guess so; it was \"cut&paste without thinking\" from [Patch 2/5] ;-).\n\n>\n>> +or `--all`. If a trailing '/{asterisk}' is intended, it must be given\n>> +explicitly.\n"},{"id":"230017","messageId":"5274183E.5090202@kdbg.org","threadId":"34805","inReplyTo":"1383334455-18623-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 0/5] ref glob exclusion follow-up","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2013-11-01T21:08:14Z","receivedAt":"2013-11-01T21:08:14Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 01.11.2013 20:34, schrieb Junio C Hamano:\n> In $gmane/234730, J6t pointed out that \"rev-list --exclude\" needs a\n> matching updates to the globbing option \"rev-parse\" supports.\n> \n> Here is a follow-up to do just that. They are meant to be applied on\n> top of the two patch series that has been cooking in 'pu'.\n\nThanks a lot! I took a stab at the task the other day. I intended to\nweave the exclusion matching into the do_for_each_* functions in refs.c.\nBut I quickly found that I would take me much more time than I was able\nto spend.\n\n-- Hannes\n"}]}