{"thread":{"id":"44724","subject":"[PATCH v2 0/5] extend describe and name-rev pattern matching","startedAt":"2016-12-17T01:24:39Z","lastAt":"2016-12-18T12:20:54Z","messageCount":8,"participants":["Jacob Keller","Philip Oakley"],"isPatch":true,"patchVersion":2,"patchTotal":5},"messages":[{"id":"307940","messageId":"20161217012431.29548-1-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":null,"subject":"[PATCH v2 0/5] extend describe and name-rev pattern matching","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:26Z","receivedAt":"2016-12-17T01:24:39Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nThis series adds support for extending describe and name-rev pattern\nmatching to allow (a) taking multiple patterns and (b) negative patterns\nwhich discard instead of keep. These changes increase the flexibility of\nthe describe mechanism so that searching for specific tag formats can be\ndone more easily.\n\nChanges since v1\n* add new patches for negative pattern matching\n* tweak the documentation slightly\n\n-- interdiff since v1 --\ndiff --git c/Documentation/git-describe.txt w/Documentation/git-describe.txt\nindex c85f2811ce28..a89bbde207b2 100644\n--- c/Documentation/git-describe.txt\n+++ w/Documentation/git-describe.txt\n@@ -83,11 +83,18 @@ OPTIONS\n --match <pattern>::\n\tOnly consider tags matching the given `glob(7)` pattern,\n\texcluding the \"refs/tags/\" prefix.  This can be used to avoid\n-\tleaking private tags from the repository, or to shrink the scope of\n-\tsearched tags to avoid -rc tags or similar. If given multiple a list\n-\tof patterns will be accumulated, and tags matching any of the patterns\n-\twill be considered. Use `--no-match` to clear and reset the list of\n-\tpatterns.\n+\tleaking private tags from the repository. If given multiple times, a\n+\tlist of patterns will be accumulated, and tags matching any of the\n+\tpatterns will be considered. Use `--no-match` to clear and reset the\n+\tlist of patterns.\n+\n+--discard <pattern>::\n+\tDo not consider tags matching the given `glob(7)` pattern, excluding\n+\tthe \"refs/tags/\" prefix. This can be used to narrow the tag space and\n+\tfind only tags matching some meaningful criteria. If given multiple\n+\ttimes, a list of patterns will be accumulated and tags matching any\n+\tof the patterns will be discarded. Use `--no-discard` to clear and\n+\treset the list of patterns.\n \n --always::\n\tShow uniquely abbreviated commit object as fallback.\ndiff --git c/Documentation/git-name-rev.txt w/Documentation/git-name-rev.txt\nindex 7433627db12d..9b46e5ea9aae 100644\n--- c/Documentation/git-name-rev.txt\n+++ w/Documentation/git-name-rev.txt\n@@ -30,6 +30,13 @@ OPTIONS\n\tgiven multiple times, use refs whose names match any of the given shell\n\tpatterns. Use `--no-refs` to clear any previous ref patterns given.\n \n+--discard=<pattern>::\n+\tDo not use any ref whose name matches a given shell pattern. The\n+\tpattern can be one of branch name, tag name or fully qualified ref\n+\tname. If given multiple times, discard refs that match any of the given\n+\tshell patterns. Use `--no-discards` to clear the list of discard\n+\tpatterns.\n+\n --all::\n\tList all commits reachable from all refs\n \ndiff --git c/builtin/describe.c w/builtin/describe.c\nindex e3ceab65e273..c09288ee6321 100644\n--- c/builtin/describe.c\n+++ w/builtin/describe.c\n@@ -29,6 +29,7 @@ static int max_candidates = 10;\n static struct hashmap names;\n static int have_util;\n static struct string_list patterns = STRING_LIST_INIT_NODUP;\n+static struct string_list discard_patterns = STRING_LIST_INIT_NODUP;\n static int always;\n static const char *dirty;\n \n@@ -130,6 +131,22 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi\n\t\treturn 0;\n \n\t/*\n+\t * If we're given discard patterns, first discard any tag which match\n+\t * any of the discard pattern.\n+\t */\n+\tif (discard_patterns.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tif (!is_tag)\n+\t\t\treturn 0;\n+\n+\t\tfor_each_string_list_item(item, &discard_patterns) {\n+\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL))\n+\t\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/*\n\t * If we're given patterns, accept only tags which match at least one\n\t * pattern.\n\t */\n@@ -140,9 +157,8 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi\n\t\t\treturn 0;\n \n\t\tfor_each_string_list_item(item, &patterns) {\n-\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL)) {\n+\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL))\n\t\t\t\tbreak;\n-\t\t\t}\n \n\t\t\t/* If we get here, no pattern matched. */\n\t\t\treturn 0;\n@@ -422,6 +438,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n\t\t\t    N_(\"consider <n> most recent tags (default: 10)\")),\n\t\tOPT_STRING_LIST(0, \"match\", &patterns, N_(\"pattern\"),\n\t\t\t   N_(\"only consider tags matching <pattern>\")),\n+\t\tOPT_STRING_LIST(0, \"discard\", &discard_patterns, N_(\"pattern\"),\n+\t\t\t   N_(\"do not consider tags matching <pattern>\")),\n\t\tOPT_BOOL(0, \"always\",        &always,\n\t\t\tN_(\"show abbreviated commit object as fallback\")),\n\t\t{OPTION_STRING, 0, \"dirty\",  &dirty, N_(\"mark\"),\n@@ -459,6 +477,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n\t\t\targv_array_push(&args, \"--tags\");\n\t\t\tfor_each_string_list_item(item, &patterns)\n\t\t\t\targv_array_pushf(&args, \"--refs=refs/tags/%s\", item->string);\n+\t\t\tfor_each_string_list_item(item, &discard_patterns)\n+\t\t\t\targv_array_pushf(&args, \"--discard=refs/tags/%s\", item->string);\n\t\t}\n\t\tif (argc)\n\t\t\targv_array_pushv(&args, argv);\ndiff --git c/builtin/name-rev.c w/builtin/name-rev.c\nindex 000a2a700ed3..86479c17a7c9 100644\n--- c/builtin/name-rev.c\n+++ w/builtin/name-rev.c\n@@ -109,6 +109,7 @@ struct name_ref_data {\n\tint tags_only;\n\tint name_only;\n\tstruct string_list ref_filters;\n+\tstruct string_list discard_filters;\n };\n \n static struct tip_table {\n@@ -150,6 +151,15 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo\n\tif (data->tags_only && !starts_with(path, \"refs/tags/\"))\n\t\treturn 0;\n \n+\tif (data->discard_filters.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tfor_each_string_list_item(item, &data->discard_filters) {\n+\t\t\tif (subpath_matches(path, item->string) >= 0)\n+\t\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n\tif (data->ref_filters.nr) {\n\t\tstruct string_list_item *item;\n\t\tint matched = 0;\n@@ -323,12 +333,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n {\n\tstruct object_array revs = OBJECT_ARRAY_INIT;\n\tint all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;\n-\tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP };\n+\tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };\n\tstruct option opts[] = {\n\t\tOPT_BOOL(0, \"name-only\", &data.name_only, N_(\"print only names (no SHA-1)\")),\n\t\tOPT_BOOL(0, \"tags\", &data.tags_only, N_(\"only use tags to name the commits\")),\n\t\tOPT_STRING_LIST(0, \"refs\", &data.ref_filters, N_(\"pattern\"),\n\t\t\t\t   N_(\"only use refs matching <pattern>\")),\n+\t\tOPT_STRING_LIST(0, \"discard\", &data.discard_filters, N_(\"pattern\"),\n+\t\t\t\t   N_(\"ignore refs matching <pattern>\")),\n\t\tOPT_GROUP(\"\"),\n\t\tOPT_BOOL(0, \"all\", &all, N_(\"list all commits reachable from all refs\")),\n\t\tOPT_BOOL(0, \"stdin\", &transform_stdin, N_(\"read from stdin\")),\ndiff --git c/t/t6007-rev-list-cherry-pick-file.sh w/t/t6007-rev-list-cherry-pick-file.sh\nindex d072ec43b016..8a4c35f6ffee 100755\n--- c/t/t6007-rev-list-cherry-pick-file.sh\n+++ w/t/t6007-rev-list-cherry-pick-file.sh\n@@ -118,6 +118,13 @@ test_expect_success 'name-rev --refs excludes non-matched patterns' '\n\ttest_cmp actual.named expect\n '\n \n+test_expect_success 'name-rev --discard excludes matched patterns' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar > actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/*\" --discard=\"*E\" \\\n+\t\t< actual > actual.named &&\n+\ttest_cmp actual.named expect\n+'\n+\n cat >expect <<EOF\n $(git rev-list --left-right --cherry-pick F...E -- bar)\n EOF\ndiff --git c/t/t6120-describe.sh w/t/t6120-describe.sh\nindex 9e5db9b87a1f..4e4a9f2e5305 100755\n--- c/t/t6120-describe.sh\n+++ w/t/t6120-describe.sh\n@@ -218,6 +218,14 @@ test_expect_success 'describe --contains and --match' '\n\ttest_cmp expect actual\n '\n \n+test_expect_success 'describe --discard' '\n+\techo \"c~1\" >expect &&\n+\ttagged_commit=$(git rev-parse \"refs/tags/A^0\") &&\n+\ttest_must_fail git describe --contains --match=\"B\" $tagged_commit &&\n+\tgit describe --contains --match=\"?\" --discard=\"A\" $tagged_commit >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'describe --contains and --no-match' '\n\techo \"A^0\" >expect &&\n\ttagged_commit=$(git rev-parse \"refs/tags/A^0\") &&\n\nJacob Keller (5):\n  doc: add documentation for OPT_STRING_LIST\n  name-rev: extend --refs to accept multiple patterns\n  name-rev: add support to discard refs by pattern match\n  describe: teach --match to accept multiple patterns\n  describe: teach describe negative pattern matches\n\n Documentation/git-describe.txt                | 13 ++++++-\n Documentation/git-name-rev.txt                | 11 +++++-\n Documentation/technical/api-parse-options.txt |  5 +++\n builtin/describe.c                            | 51 ++++++++++++++++++++++----\n builtin/name-rev.c                            | 53 +++++++++++++++++++++------\n t/t6007-rev-list-cherry-pick-file.sh          | 30 +++++++++++++++\n t/t6120-describe.sh                           | 19 ++++++++++\n 7 files changed, 161 insertions(+), 21 deletions(-)\n\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307941","messageId":"20161217012431.29548-3-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":"20161217012431.29548-1-jacob.e.keller@intel.com","subject":"[PATCH v2 2/5] name-rev: extend --refs to accept multiple patterns","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:28Z","receivedAt":"2016-12-17T01:24:47Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nTeach git name-rev to take a string list of patterns from --refs instead\nof only a single pattern. The list of patterns will be matched\ninclusively, such that a ref only needs to match one pattern to be\nincluded. If a ref will only be excluded if it does not match any of the\npatterns.\n\nAdd tests and documentation for this change.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/git-name-rev.txt       |  4 +++-\n builtin/name-rev.c                   | 41 +++++++++++++++++++++++++-----------\n t/t6007-rev-list-cherry-pick-file.sh | 30 ++++++++++++++++++++++++++\n 3 files changed, 62 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt\nindex ca28fb8e2a07..7433627db12d 100644\n--- a/Documentation/git-name-rev.txt\n+++ b/Documentation/git-name-rev.txt\n@@ -26,7 +26,9 @@ OPTIONS\n \n --refs=<pattern>::\n \tOnly use refs whose names match a given shell pattern.  The pattern\n-\tcan be one of branch name, tag name or fully qualified ref name.\n+\tcan be one of branch name, tag name or fully qualified ref name. If\n+\tgiven multiple times, use refs whose names match any of the given shell\n+\tpatterns. Use `--no-refs` to clear any previous ref patterns given.\n \n --all::\n \tList all commits reachable from all refs\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex cd89d48b65e8..000a2a700ed3 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -108,7 +108,7 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)\n struct name_ref_data {\n \tint tags_only;\n \tint name_only;\n-\tconst char *ref_filter;\n+\tstruct string_list ref_filters;\n };\n \n static struct tip_table {\n@@ -150,16 +150,33 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo\n \tif (data->tags_only && !starts_with(path, \"refs/tags/\"))\n \t\treturn 0;\n \n-\tif (data->ref_filter) {\n-\t\tswitch (subpath_matches(path, data->ref_filter)) {\n-\t\tcase -1: /* did not match */\n-\t\t\treturn 0;\n-\t\tcase 0:  /* matched fully */\n-\t\t\tbreak;\n-\t\tdefault: /* matched subpath */\n-\t\t\tcan_abbreviate_output = 1;\n-\t\t\tbreak;\n+\tif (data->ref_filters.nr) {\n+\t\tstruct string_list_item *item;\n+\t\tint matched = 0;\n+\n+\t\t/* See if any of the patterns match. */\n+\t\tfor_each_string_list_item(item, &data->ref_filters) {\n+\t\t\t/*\n+\t\t\t * We want to check every pattern even if we already\n+\t\t\t * found a match, just in case one of the later\n+\t\t\t * patterns could abbreviate the output.\n+\t\t\t */\n+\t\t\tswitch (subpath_matches(path, item->string)) {\n+\t\t\tcase -1: /* did not match */\n+\t\t\t\tbreak;\n+\t\t\tcase 0: /* matched fully */\n+\t\t\t\tmatched = 1;\n+\t\t\t\tbreak;\n+\t\t\tdefault: /* matched subpath */\n+\t\t\t\tmatched = 1;\n+\t\t\t\tcan_abbreviate_output = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n \t\t}\n+\n+\t\t/* If none of the patterns matched, stop now */\n+\t\tif (!matched)\n+\t\t\treturn 0;\n \t}\n \n \tadd_to_tip_table(oid->hash, path, can_abbreviate_output);\n@@ -306,11 +323,11 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_array revs = OBJECT_ARRAY_INIT;\n \tint all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;\n-\tstruct name_ref_data data = { 0, 0, NULL };\n+\tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP };\n \tstruct option opts[] = {\n \t\tOPT_BOOL(0, \"name-only\", &data.name_only, N_(\"print only names (no SHA-1)\")),\n \t\tOPT_BOOL(0, \"tags\", &data.tags_only, N_(\"only use tags to name the commits\")),\n-\t\tOPT_STRING(0, \"refs\", &data.ref_filter, N_(\"pattern\"),\n+\t\tOPT_STRING_LIST(0, \"refs\", &data.ref_filters, N_(\"pattern\"),\n \t\t\t\t   N_(\"only use refs matching <pattern>\")),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_BOOL(0, \"all\", &all, N_(\"list all commits reachable from all refs\")),\ndiff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh\nindex 1408b608eb03..d072ec43b016 100755\n--- a/t/t6007-rev-list-cherry-pick-file.sh\n+++ b/t/t6007-rev-list-cherry-pick-file.sh\n@@ -99,6 +99,36 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' '\n \ttest_cmp actual.named expect\n '\n \n+test_expect_success 'name-rev multiple --refs combine inclusive' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar > actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/F\" --refs=\"*tags/E\" \\\n+\t\t< actual > actual.named &&\n+\ttest_cmp actual.named expect\n+'\n+\n+cat >expect <<EOF\n+<tags/F\n+$(git rev-list --left-right --right-only --cherry-pick F...E -- bar)\n+EOF\n+\n+test_expect_success 'name-rev --refs excludes non-matched patterns' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar > actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/F\" \\\n+\t\t< actual > actual.named &&\n+\ttest_cmp actual.named expect\n+'\n+\n+cat >expect <<EOF\n+$(git rev-list --left-right --cherry-pick F...E -- bar)\n+EOF\n+\n+test_expect_success 'name-rev --no-refs clears the refs list' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar > actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/F\" --refs=\"*tags/E\" --no-refs --refs=\"*tags/G\" \\\n+\t\t< actual > actual.named &&\n+\ttest_cmp actual.named expect\n+'\n+\n cat >expect <<EOF\n +tags/F\n =tags/D\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307942","messageId":"20161217012431.29548-4-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":"20161217012431.29548-1-jacob.e.keller@intel.com","subject":"[PATCH v2 3/5] name-rev: add support to discard refs by pattern match","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:29Z","receivedAt":"2016-12-17T01:24:50Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nExtend name-rev further to support matching refs by adding `--discard`\npatterns. These patterns will limit the scope of refs by discarding any\nref that matches at least one discard pattern. Checking the discard refs\nshall happen first, before checking the include --refs patterns. This\nwill allow more flexibility to matching certain kinds of references.\n\nAdd tests and update Documentation for this change.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/git-name-rev.txt |  7 +++++++\n builtin/name-rev.c             | 14 +++++++++++++-\n 2 files changed, 20 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt\nindex 7433627db12d..9b46e5ea9aae 100644\n--- a/Documentation/git-name-rev.txt\n+++ b/Documentation/git-name-rev.txt\n@@ -30,6 +30,13 @@ OPTIONS\n \tgiven multiple times, use refs whose names match any of the given shell\n \tpatterns. Use `--no-refs` to clear any previous ref patterns given.\n \n+--discard=<pattern>::\n+\tDo not use any ref whose name matches a given shell pattern. The\n+\tpattern can be one of branch name, tag name or fully qualified ref\n+\tname. If given multiple times, discard refs that match any of the given\n+\tshell patterns. Use `--no-discards` to clear the list of discard\n+\tpatterns.\n+\n --all::\n \tList all commits reachable from all refs\n \ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 000a2a700ed3..86479c17a7c9 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -109,6 +109,7 @@ struct name_ref_data {\n \tint tags_only;\n \tint name_only;\n \tstruct string_list ref_filters;\n+\tstruct string_list discard_filters;\n };\n \n static struct tip_table {\n@@ -150,6 +151,15 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo\n \tif (data->tags_only && !starts_with(path, \"refs/tags/\"))\n \t\treturn 0;\n \n+\tif (data->discard_filters.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tfor_each_string_list_item(item, &data->discard_filters) {\n+\t\t\tif (subpath_matches(path, item->string) >= 0)\n+\t\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n \tif (data->ref_filters.nr) {\n \t\tstruct string_list_item *item;\n \t\tint matched = 0;\n@@ -323,12 +333,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_array revs = OBJECT_ARRAY_INIT;\n \tint all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;\n-\tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP };\n+\tstruct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };\n \tstruct option opts[] = {\n \t\tOPT_BOOL(0, \"name-only\", &data.name_only, N_(\"print only names (no SHA-1)\")),\n \t\tOPT_BOOL(0, \"tags\", &data.tags_only, N_(\"only use tags to name the commits\")),\n \t\tOPT_STRING_LIST(0, \"refs\", &data.ref_filters, N_(\"pattern\"),\n \t\t\t\t   N_(\"only use refs matching <pattern>\")),\n+\t\tOPT_STRING_LIST(0, \"discard\", &data.discard_filters, N_(\"pattern\"),\n+\t\t\t\t   N_(\"ignore refs matching <pattern>\")),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_BOOL(0, \"all\", &all, N_(\"list all commits reachable from all refs\")),\n \t\tOPT_BOOL(0, \"stdin\", &transform_stdin, N_(\"read from stdin\")),\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307943","messageId":"20161217012431.29548-2-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":"20161217012431.29548-1-jacob.e.keller@intel.com","subject":"[PATCH v2 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:27Z","receivedAt":"2016-12-17T01:24:52Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nCommit c8ba16391655 (\"parse-options: add OPT_STRING_LIST helper\",\n2011-06-09) added the OPT_STRING_LIST as a way to accumulate a repeated\nlist of strings. However, this was not documented in the\napi-parse-options documentation. Add documentation now so that future\ndevelopers may learn of its existence.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/technical/api-parse-options.txt | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt\nindex 27bd701c0d68..92791740aa64 100644\n--- a/Documentation/technical/api-parse-options.txt\n+++ b/Documentation/technical/api-parse-options.txt\n@@ -168,6 +168,11 @@ There are some macros to easily define options:\n \tIntroduce an option with string argument.\n \tThe string argument is put into `str_var`.\n \n+`OPT_STRING_LIST(short long, &list, arg_str, description)`::\n+\tIntroduce an option with a string argument. Repeated invocations\n+\taccumulate into a list of strings. Reset and clear the list with\n+\t`--no-option`.\n+\n `OPT_INTEGER(short, long, &int_var, description)`::\n \tIntroduce an option with integer argument.\n \tThe integer is put into `int_var`.\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307944","messageId":"20161217012431.29548-5-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":"20161217012431.29548-1-jacob.e.keller@intel.com","subject":"[PATCH v2 4/5] describe: teach --match to accept multiple patterns","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:30Z","receivedAt":"2016-12-17T01:24:55Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nTeach `--match` to be accepted multiple times, accumulating a list of\npatterns to match into a string list. Each pattern is inclusive, such\nthat a tag need only match one of the provided patterns to be\nconsidered for matching.\n\nThis extension is useful as it enables more flexibility in what tags\nmatch, and may avoid the need to run the describe command multiple\ntimes to get the same result.\n\nAdd tests and update the documentation for this change.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/git-describe.txt |  5 ++++-\n builtin/describe.c             | 30 +++++++++++++++++++++++-------\n t/t6120-describe.sh            | 19 +++++++++++++++++++\n 3 files changed, 46 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt\nindex e4ac448ff565..7ad41e2f6ade 100644\n--- a/Documentation/git-describe.txt\n+++ b/Documentation/git-describe.txt\n@@ -83,7 +83,10 @@ OPTIONS\n --match <pattern>::\n \tOnly consider tags matching the given `glob(7)` pattern,\n \texcluding the \"refs/tags/\" prefix.  This can be used to avoid\n-\tleaking private tags from the repository.\n+\tleaking private tags from the repository. If given multiple times, a\n+\tlist of patterns will be accumulated, and tags matching any of the\n+\tpatterns will be considered. Use `--no-match` to clear and reset the\n+\tlist of patterns.\n \n --always::\n \tShow uniquely abbreviated commit object as fallback.\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 01490a157efc..5cc9e9abe798 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -28,7 +28,7 @@ static int abbrev = -1; /* unspecified */\n static int max_candidates = 10;\n static struct hashmap names;\n static int have_util;\n-static const char *pattern;\n+static struct string_list patterns = STRING_LIST_INIT_NODUP;\n static int always;\n static const char *dirty;\n \n@@ -129,9 +129,24 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi\n \tif (!all && !is_tag)\n \t\treturn 0;\n \n-\t/* Accept only tags that match the pattern, if given */\n-\tif (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL)))\n-\t\treturn 0;\n+\t/*\n+\t * If we're given patterns, accept only tags which match at least one\n+\t * pattern.\n+\t */\n+\tif (patterns.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tif (!is_tag)\n+\t\t\treturn 0;\n+\n+\t\tfor_each_string_list_item(item, &patterns) {\n+\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL))\n+\t\t\t\tbreak;\n+\n+\t\t\t/* If we get here, no pattern matched. */\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n \n \t/* Is it annotated? */\n \tif (!peel_ref(path, peeled.hash)) {\n@@ -404,7 +419,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"only output exact matches\"), 0),\n \t\tOPT_INTEGER(0, \"candidates\", &max_candidates,\n \t\t\t    N_(\"consider <n> most recent tags (default: 10)\")),\n-\t\tOPT_STRING(0, \"match\",       &pattern, N_(\"pattern\"),\n+\t\tOPT_STRING_LIST(0, \"match\", &patterns, N_(\"pattern\"),\n \t\t\t   N_(\"only consider tags matching <pattern>\")),\n \t\tOPT_BOOL(0, \"always\",        &always,\n \t\t\tN_(\"show abbreviated commit object as fallback\")),\n@@ -430,6 +445,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--long is incompatible with --abbrev=0\"));\n \n \tif (contains) {\n+\t\tstruct string_list_item *item;\n \t\tstruct argv_array args;\n \n \t\targv_array_init(&args);\n@@ -440,8 +456,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\targv_array_push(&args, \"--always\");\n \t\tif (!all) {\n \t\t\targv_array_push(&args, \"--tags\");\n-\t\t\tif (pattern)\n-\t\t\t\targv_array_pushf(&args, \"--refs=refs/tags/%s\", pattern);\n+\t\t\tfor_each_string_list_item(item, &patterns)\n+\t\t\t\targv_array_pushf(&args, \"--refs=refs/tags/%s\", item->string);\n \t\t}\n \t\tif (argc)\n \t\t\targv_array_pushv(&args, argv);\ndiff --git a/t/t6120-describe.sh b/t/t6120-describe.sh\nindex 85f269411cb3..9e5db9b87a1f 100755\n--- a/t/t6120-describe.sh\n+++ b/t/t6120-describe.sh\n@@ -182,6 +182,10 @@ check_describe \"test2-lightweight-*\" --tags --match=\"test2-*\"\n \n check_describe \"test2-lightweight-*\" --long --tags --match=\"test2-*\" HEAD^\n \n+check_describe \"test1-lightweight-*\" --long --tags --match=\"test1-*\" --match=\"test2-*\" HEAD^\n+\n+check_describe \"test2-lightweight-*\" --long --tags --match=\"test1-*\" --no-match --match=\"test2-*\" HEAD^\n+\n test_expect_success 'name-rev with exact tags' '\n \techo A >expect &&\n \ttag_object=$(git rev-parse refs/tags/A) &&\n@@ -206,4 +210,19 @@ test_expect_success 'describe --contains with the exact tags' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'describe --contains and --match' '\n+\techo \"A^0\" >expect &&\n+\ttagged_commit=$(git rev-parse \"refs/tags/A^0\") &&\n+\ttest_must_fail git describe --contains --match=\"B\" $tagged_commit &&\n+\tgit describe --contains --match=\"B\" --match=\"A\" $tagged_commit >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'describe --contains and --no-match' '\n+\techo \"A^0\" >expect &&\n+\ttagged_commit=$(git rev-parse \"refs/tags/A^0\") &&\n+\tgit describe --contains --match=\"B\" --no-match $tagged_commit >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307945","messageId":"20161217012431.29548-6-jacob.e.keller@intel.com","threadId":"44724","inReplyTo":"20161217012431.29548-1-jacob.e.keller@intel.com","subject":"[PATCH v2 5/5] describe: teach describe negative pattern matches","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2016-12-17T01:24:31Z","receivedAt":"2016-12-17T01:24:56Z","isPatch":true,"sender":{"key":"jacob.e.keller@intel.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"From: Jacob Keller <jacob.keller@gmail.com>\n\nTeach git-describe the `--discard` option which will allow specifying\na glob pattern of tags to ignore. This can be combined with the\n`--match` patterns to enable more flexibility in determining which tags\nto consider.\n\nFor example, suppose you wish to find the first official release tag\nthat contains a certain commit. If we assume that official release tags\nare of the form \"v*\" and pre-release candidates include \"*rc*\" in their\nname, we can now find the first tag that introduces commit abcdef via:\n\n  git describe --contains --match=\"v*\" --discard=\"*rc*\"\n\nAdd documentation and tests for this change.\n\nSigned-off-by: Jacob Keller <jacob.keller@gmail.com>\n---\n Documentation/git-describe.txt |  8 ++++++++\n builtin/describe.c             | 21 +++++++++++++++++++++\n 2 files changed, 29 insertions(+)\n\ndiff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt\nindex 7ad41e2f6ade..a89bbde207b2 100644\n--- a/Documentation/git-describe.txt\n+++ b/Documentation/git-describe.txt\n@@ -88,6 +88,14 @@ OPTIONS\n \tpatterns will be considered. Use `--no-match` to clear and reset the\n \tlist of patterns.\n \n+--discard <pattern>::\n+\tDo not consider tags matching the given `glob(7)` pattern, excluding\n+\tthe \"refs/tags/\" prefix. This can be used to narrow the tag space and\n+\tfind only tags matching some meaningful criteria. If given multiple\n+\ttimes, a list of patterns will be accumulated and tags matching any\n+\tof the patterns will be discarded. Use `--no-discard` to clear and\n+\treset the list of patterns.\n+\n --always::\n \tShow uniquely abbreviated commit object as fallback.\n \ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 5cc9e9abe798..c09288ee6321 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -29,6 +29,7 @@ static int max_candidates = 10;\n static struct hashmap names;\n static int have_util;\n static struct string_list patterns = STRING_LIST_INIT_NODUP;\n+static struct string_list discard_patterns = STRING_LIST_INIT_NODUP;\n static int always;\n static const char *dirty;\n \n@@ -130,6 +131,22 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi\n \t\treturn 0;\n \n \t/*\n+\t * If we're given discard patterns, first discard any tag which match\n+\t * any of the discard pattern.\n+\t */\n+\tif (discard_patterns.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tif (!is_tag)\n+\t\t\treturn 0;\n+\n+\t\tfor_each_string_list_item(item, &discard_patterns) {\n+\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL))\n+\t\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/*\n \t * If we're given patterns, accept only tags which match at least one\n \t * pattern.\n \t */\n@@ -421,6 +438,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"consider <n> most recent tags (default: 10)\")),\n \t\tOPT_STRING_LIST(0, \"match\", &patterns, N_(\"pattern\"),\n \t\t\t   N_(\"only consider tags matching <pattern>\")),\n+\t\tOPT_STRING_LIST(0, \"discard\", &discard_patterns, N_(\"pattern\"),\n+\t\t\t   N_(\"do not consider tags matching <pattern>\")),\n \t\tOPT_BOOL(0, \"always\",        &always,\n \t\t\tN_(\"show abbreviated commit object as fallback\")),\n \t\t{OPTION_STRING, 0, \"dirty\",  &dirty, N_(\"mark\"),\n@@ -458,6 +477,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\targv_array_push(&args, \"--tags\");\n \t\t\tfor_each_string_list_item(item, &patterns)\n \t\t\t\targv_array_pushf(&args, \"--refs=refs/tags/%s\", item->string);\n+\t\t\tfor_each_string_list_item(item, &discard_patterns)\n+\t\t\t\targv_array_pushf(&args, \"--discard=refs/tags/%s\", item->string);\n \t\t}\n \t\tif (argc)\n \t\t\targv_array_pushv(&args, argv);\n-- \n2.11.0.rc2.152.g4d04e67\n\n"},{"id":"307954","messageId":"67572777448E4DCE967BA079110A3487@PhilipOakley","threadId":"44724","inReplyTo":"20161217012431.29548-2-jacob.e.keller@intel.com","subject":"Re: [PATCH v2 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":null,"receivedAt":"2016-12-17T11:56:28Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Jacob Keller\" <jacob.e.keller@intel.com>\n> From: Jacob Keller <jacob.keller@gmail.com>\n>\n> Commit c8ba16391655 (\"parse-options: add OPT_STRING_LIST helper\",\n> 2011-06-09) added the OPT_STRING_LIST as a way to accumulate a repeated\n> list of strings. However, this was not documented in the\n> api-parse-options documentation. Add documentation now so that future\n> developers may learn of its existence.\n>\n> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>\n> ---\n> Documentation/technical/api-parse-options.txt | 5 +++++\n> 1 file changed, 5 insertions(+)\n>\n> diff --git a/Documentation/technical/api-parse-options.txt \n> b/Documentation/technical/api-parse-options.txt\n> index 27bd701c0d68..92791740aa64 100644\n> --- a/Documentation/technical/api-parse-options.txt\n> +++ b/Documentation/technical/api-parse-options.txt\n> @@ -168,6 +168,11 @@ There are some macros to easily define options:\n>  Introduce an option with string argument.\n>  The string argument is put into `str_var`.\n>\n> +`OPT_STRING_LIST(short long, &list, arg_str, description)`::\n\nshould there be an extra comma between 'short long' in a similar manner to \nthe OPT_INTEGER argument list below?\n\n\n> + Introduce an option with a string argument. Repeated invocations\n> + accumulate into a list of strings. Reset and clear the list with\n> + `--no-option`.\n> +\n> `OPT_INTEGER(short, long, &int_var, description)`::\n>  Introduce an option with integer argument.\n>  The integer is put into `int_var`.\n> -- \n> 2.11.0.rc2.152.g4d04e67\n>\n> \n\n"},{"id":"307989","messageId":"E9C90A8A-8D05-4357-BCEF-AAFAE16225CC@gmail.com","threadId":"44724","inReplyTo":"67572777448E4DCE967BA079110A3487@PhilipOakley","subject":"Re: [PATCH v2 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-12-18T12:19:38Z","receivedAt":"2016-12-18T12:20:54Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On December 17, 2016 3:56:19 AM PST, Philip Oakley <philipoakley@iee.org> wrote:\n>From: \"Jacob Keller\" <jacob.e.keller@intel.com>\n>> From: Jacob Keller <jacob.keller@gmail.com>\n>>\n>> Commit c8ba16391655 (\"parse-options: add OPT_STRING_LIST helper\",\n>> 2011-06-09) added the OPT_STRING_LIST as a way to accumulate a\n>repeated\n>> list of strings. However, this was not documented in the\n>> api-parse-options documentation. Add documentation now so that future\n>> developers may learn of its existence.\n>>\n>> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>\n>> ---\n>> Documentation/technical/api-parse-options.txt | 5 +++++\n>> 1 file changed, 5 insertions(+)\n>>\n>> diff --git a/Documentation/technical/api-parse-options.txt \n>> b/Documentation/technical/api-parse-options.txt\n>> index 27bd701c0d68..92791740aa64 100644\n>> --- a/Documentation/technical/api-parse-options.txt\n>> +++ b/Documentation/technical/api-parse-options.txt\n>> @@ -168,6 +168,11 @@ There are some macros to easily define options:\n>>  Introduce an option with string argument.\n>>  The string argument is put into `str_var`.\n>>\n>> +`OPT_STRING_LIST(short long, &list, arg_str, description)`::\n>\n>should there be an extra comma between 'short long' in a similar manner\n>to \n>the OPT_INTEGER argument list below?\n>\n>\n\nYou are indeed correct sir. I will fix this up.\n\nThanks,\nJake\n\n"}]}