{"thread":{"id":"44905","subject":"[PATCH v3 4/5] describe: teach --match to accept multiple patterns","startedAt":"2017-01-18T00:09:41Z","lastAt":"2017-01-19T18:32:41Z","messageCount":22,"participants":["Jacob Keller","Junio C Hamano","Philip Oakley","Johannes Schindelin"],"isPatch":true,"patchVersion":3,"patchTotal":5},"messages":[{"id":"309597","messageId":"20170118000930.5431-5-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"[PATCH v3 4/5] describe: teach --match to accept multiple patterns","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:29Z","receivedAt":"2017-01-18T00:09:41Z","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.403.g196674b8396b\n\n"},{"id":"309598","messageId":"20170118000930.5431-6-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"[PATCH v3 5/5] describe: teach describe negative pattern matches","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:30Z","receivedAt":"2017-01-18T00:09: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-describe the `--exclude` 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*\" --exclude=\"*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 t/t6120-describe.sh            |  8 ++++++++\n 3 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt\nindex 7ad41e2f6ade..21a43b78924a 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+--exclude <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 excluded. Use `--no-exclude` 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..6769446e1f57 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 exclude_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 exclude patterns, first exclude any tag which match\n+\t * any of the exclude pattern.\n+\t */\n+\tif (exclude_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, &exclude_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, \"exclude\", &exclude_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, &exclude_patterns)\n+\t\t\t\targv_array_pushf(&args, \"--exclude=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 9e5db9b87a1f..167491fd5b0d 100755\n--- a/t/t6120-describe.sh\n+++ b/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 --exclude' '\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=\"?\" --exclude=\"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-- \n2.11.0.403.g196674b8396b\n\n"},{"id":"309599","messageId":"20170118000930.5431-1-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":null,"subject":"[PATCH v3 0/5] extend git-describe pattern matching","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:25Z","receivedAt":"2017-01-18T00:09: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\n** v3 fixes a minor typo in one of the test cases, so please ignore v2\n   I left the interdiff as between v1 and v3 instead of v2 **\n\nTeach git describe and git name-rev the ability to match multiple\npatterns inclusively. Additionally, teach these commands to also accept\nnegative patterns to exclude any refs which match.\n\nThe pattern lists for positive and negative patterns are inclusive. This\nmeans that for the positive patterns, a reference will be considered as\nlong as it matches at least one of the match patterns. It need not match\nall given patterns. Additionally for negative patterns, we will not\nconsider any ref which matches any negative pattern, even if it matches\none of the positive patterns.\n\nTogether this allows the ability to express far more sets of tags than a\nsingle match pattern alone. It does not provide quite the same depth as\nwould teaching full regexp but it is simpler and easy enough to\nunderstand.\n\n- v2\n* use --exclude instead of --discard\n* use modern style in tests\n\n- v3\n* fix broken test (sorry for the thrash!)\n\nI chose *not* to implement the suggestion of ordered values for exclude\nand match, since this is not how the current implementation of\ngit-describe worked, and it didn't really make sense to me what was\nbeing requested. I looked at the interface for git-log, and it appears\nthat the command accepts multiple invocations of --branches, --remotes,\nand similar. I do not believe these need to be identical interfaces. I\nwelcome feedback on this decision, but I am not convinced yet that the\nordered arguments are worth the trouble.\n\ndiff --git c/Documentation/git-describe.txt w/Documentation/git-describe.txt\nindex a89bbde207b2..21a43b78924a 100644\n--- c/Documentation/git-describe.txt\n+++ w/Documentation/git-describe.txt\n@@ -88,12 +88,12 @@ OPTIONS\n\tpatterns will be considered. Use `--no-match` to clear and reset the\n\tlist of patterns.\n \n---discard <pattern>::\n+--exclude <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+\tof the patterns will be excluded. Use `--no-exclude` to clear and\n\treset the list of patterns.\n \n --always::\ndiff --git c/Documentation/git-name-rev.txt w/Documentation/git-name-rev.txt\nindex 9b46e5ea9aae..301b4a8d55e6 100644\n--- c/Documentation/git-name-rev.txt\n+++ w/Documentation/git-name-rev.txt\n@@ -30,11 +30,11 @@ 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+--exclude=<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+\tname. If given multiple times, exclude refs that match any of the given\n+\tshell patterns. Use `--no-exclude` to clear the list of exclude\n\tpatterns.\n \n --all::\ndiff --git c/Documentation/technical/api-parse-options.txt w/Documentation/technical/api-parse-options.txt\nindex 15e876e4c804..6914f54f5f44 100644\n--- c/Documentation/technical/api-parse-options.txt\n+++ w/Documentation/technical/api-parse-options.txt\n@@ -169,9 +169,9 @@ There are some macros to easily define options:\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+\tIntroduce an option with string argument.\n+\tThe string argument is stored as an element in `&list` which must be a\n+\tstruct string_list. Reset the list using `--no-option`.\n \n `OPT_INTEGER(short, long, &int_var, description)`::\n\tIntroduce an option with integer argument.\ndiff --git c/builtin/describe.c w/builtin/describe.c\nindex c09288ee6321..6769446e1f57 100644\n--- c/builtin/describe.c\n+++ w/builtin/describe.c\n@@ -29,7 +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 struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;\n static int always;\n static const char *dirty;\n \n@@ -131,16 +131,16 @@ 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 * If we're given exclude patterns, first exclude any tag which match\n+\t * any of the exclude pattern.\n\t */\n-\tif (discard_patterns.nr) {\n+\tif (exclude_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\tfor_each_string_list_item(item, &exclude_patterns) {\n\t\t\tif (!wildmatch(item->string, path + 10, 0, NULL))\n\t\t\t\treturn 0;\n\t\t}\n@@ -438,7 +438,7 @@ 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\tOPT_STRING_LIST(0, \"exclude\", &exclude_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@@ -477,8 +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\tfor_each_string_list_item(item, &exclude_patterns)\n+\t\t\t\targv_array_pushf(&args, \"--exclude=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 86479c17a7c9..da4a0d7c0fdf 100644\n--- c/builtin/name-rev.c\n+++ w/builtin/name-rev.c\n@@ -109,7 +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+\tstruct string_list exclude_filters;\n };\n \n static struct tip_table {\n@@ -151,10 +151,10 @@ 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+\tif (data->exclude_filters.nr) {\n\t\tstruct string_list_item *item;\n \n-\t\tfor_each_string_list_item(item, &data->discard_filters) {\n+\t\tfor_each_string_list_item(item, &data->exclude_filters) {\n\t\t\tif (subpath_matches(path, item->string) >= 0)\n\t\t\t\treturn 0;\n\t\t}\n@@ -339,7 +339,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)\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\tOPT_STRING_LIST(0, \"exclude\", &data.exclude_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\")),\ndiff --git c/t/t6007-rev-list-cherry-pick-file.sh w/t/t6007-rev-list-cherry-pick-file.sh\nindex 8a4c35f6ffee..83838d0da208 100755\n--- c/t/t6007-rev-list-cherry-pick-file.sh\n+++ w/t/t6007-rev-list-cherry-pick-file.sh\n@@ -100,42 +100,43 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' '\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 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+\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 rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&\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-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+\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+<tags/F\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+test_expect_success 'name-rev --exclude excludes matched patterns' '\n+\tgit rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&\n+\tgit rev-list --left-right --cherry-pick F...E -- bar >actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/*\" --exclude=\"*E\" \\\n+\t\t<actual >actual.named &&\n\ttest_cmp actual.named expect\n '\n \n+test_expect_success 'name-rev --no-refs clears the refs list' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar >expect &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/F\" --refs=\"*tags/E\" --no-refs --refs=\"*tags/G\" \\\n+\t\t<expect >actual &&\n+\ttest_cmp actual expect\n+'\n+\n cat >expect <<EOF\n +tags/F\n =tags/D\ndiff --git c/t/t6120-describe.sh w/t/t6120-describe.sh\nindex 4e4a9f2e5305..167491fd5b0d 100755\n--- c/t/t6120-describe.sh\n+++ w/t/t6120-describe.sh\n@@ -218,11 +218,11 @@ test_expect_success 'describe --contains and --match' '\n\ttest_cmp expect actual\n '\n \n-test_expect_success 'describe --discard' '\n+test_expect_success 'describe --exclude' '\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+\tgit describe --contains --match=\"?\" --exclude=\"A\" $tagged_commit >actual &&\n\ttest_cmp expect actual\n '\n \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 exclude 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          | 38 +++++++++++++++++++\n t/t6120-describe.sh                           | 27 ++++++++++++++\n 7 files changed, 177 insertions(+), 21 deletions(-)\n\n-- \n2.11.0.403.g196674b8396b\n\n"},{"id":"309600","messageId":"20170118000930.5431-4-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"[PATCH v3 3/5] name-rev: add support to exclude refs by pattern match","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:28Z","receivedAt":"2017-01-18T00:09: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\nExtend name-rev further to support matching refs by adding `--exclude`\npatterns. These patterns will limit the scope of refs by excluding any\nref that matches at least one exclude pattern. Checking the exclude 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 t/t6007-rev-list-cherry-pick-file.sh | 12 ++++++++++++\n 3 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt\nindex 7433627db12d..301b4a8d55e6 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+--exclude=<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, exclude refs that match any of the given\n+\tshell patterns. Use `--no-exclude` to clear the list of exclude\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..da4a0d7c0fdf 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 exclude_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->exclude_filters.nr) {\n+\t\tstruct string_list_item *item;\n+\n+\t\tfor_each_string_list_item(item, &data->exclude_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, \"exclude\", &data.exclude_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 a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh\nindex d9827a6389a3..29597451967d 100755\n--- a/t/t6007-rev-list-cherry-pick-file.sh\n+++ b/t/t6007-rev-list-cherry-pick-file.sh\n@@ -118,6 +118,18 @@ test_expect_success 'name-rev --refs excludes non-matched patterns' '\n \ttest_cmp actual.named expect\n '\n \n+cat >expect <<EOF\n+<tags/F\n+EOF\n+\n+test_expect_success 'name-rev --exclude excludes matched patterns' '\n+\tgit rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&\n+\tgit rev-list --left-right --cherry-pick F...E -- bar >actual &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/*\" --exclude=\"*E\" \\\n+\t\t<actual >actual.named &&\n+\ttest_cmp actual.named expect\n+'\n+\n test_expect_success 'name-rev --no-refs clears the refs list' '\n \tgit rev-list --left-right --cherry-pick F...E -- bar >expect &&\n \tgit name-rev --stdin --name-only --refs=\"*tags/F\" --refs=\"*tags/E\" --no-refs --refs=\"*tags/G\" \\\n-- \n2.11.0.403.g196674b8396b\n\n"},{"id":"309601","messageId":"20170118000930.5431-3-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"[PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:27Z","receivedAt":"2017-01-18T00:09:53Z","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. The tests do use\ndynamically generated output as part of the expected output because the\nexpected output is the raw commit-id and it seemed like a bad idea to\nhardcode that into a tests expected output.\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 | 26 +++++++++++++++++++++++\n 3 files changed, 58 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..d9827a6389a3 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,32 @@ 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+EOF\n+\n+test_expect_success 'name-rev --refs excludes non-matched patterns' '\n+\tgit rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&\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+test_expect_success 'name-rev --no-refs clears the refs list' '\n+\tgit rev-list --left-right --cherry-pick F...E -- bar >expect &&\n+\tgit name-rev --stdin --name-only --refs=\"*tags/F\" --refs=\"*tags/E\" --no-refs --refs=\"*tags/G\" \\\n+\t\t<expect >actual &&\n+\ttest_cmp actual expect\n+'\n+\n cat >expect <<EOF\n +tags/F\n =tags/D\n-- \n2.11.0.403.g196674b8396b\n\n"},{"id":"309602","messageId":"20170118000930.5431-2-jacob.e.keller@intel.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"[PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Jacob Keller","fromEmail":"jacob.e.keller@intel.com","sentAt":"2017-01-18T00:09:26Z","receivedAt":"2017-01-18T00:09: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\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..6914f54f5f44 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 string argument.\n+\tThe string argument is stored as an element in `&list` which must be a\n+\tstruct string_list. Reset the list using `--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.403.g196674b8396b\n\n"},{"id":"309652","messageId":"xmqq37gg9moc.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"20170118000930.5431-2-jacob.e.keller@intel.com","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T19:45:55Z","receivedAt":"2017-01-18T19:46:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\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 b/Documentation/technical/api-parse-options.txt\n> index 27bd701c0d68..6914f54f5f44 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 string argument.\n> +\tThe string argument is stored as an element in `&list` which must be a\n> +\tstruct string_list. Reset the list using `--no-option`.\n> +\n\nI do not know if it is clear enough that 'option' in the last\nsentence is a placeholder.  I then wondered if spelling it as\n`--no-<long>` would make it a bit clearer, but that is ugly.\n\nThe \"Reset the list\" is an instruction to the end-users who interact\nwith a program written by readers of this document using\nOPT_STRING_LIST(), and it feels a bit out of place.  Perhaps\n\n\tEnd users can reset the list by negating the option,\n\ti.e. passing \"--no-<long>\", on the command line.\n\nI dunno.\n\nAnyway, thanks for adding a missing doc here.\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"},{"id":"309653","messageId":"xmqqy3y8878k.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"20170118000930.5431-3-jacob.e.keller@intel.com","subject":"Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T20:04:43Z","receivedAt":"2017-01-18T20:04:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\n> From: Jacob Keller <jacob.keller@gmail.com>\n>\n> Teach git name-rev to take a string list of patterns from --refs instead\n> of only a single pattern. The list of patterns will be matched\n> inclusively, such that a ref only needs to match one pattern to be\n> included. If a ref will only be excluded if it does not match any of the\n> patterns.\n\nI think \"If a\" in the last sentence should be \"A\".\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\nUnlike 1/5, this is facing the end-users, and the last sentence is\nvery appropriate.\n\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\nI agree that we cannot short-cut on the first match to make sure\nthat the outcome is stable, but I wondered what should be shown when\nwe do have multiple matches.  Say I gave\n\n    --refs=\"v*\" --refs=\"refs/tags/v1.*\"\n\nand refs/tags/v1.0 matched.  The above code would say we can\nabbreviate.\n\nWhat is the reason behind this design decision?  Is it because it is\nclear that the user shows her willingness to accept more compact\nform by having --refs=\"v*\" that would allow shortening?  If that is\nthe case, I think I agree with the reasoning.  But we probably want\nto write it down somewhere, because another reasoning, which may\nalso be valid, would call for an opposite behaviour (i.e. the more\nspecific --refs=\"refs/tags/v1.*\" also matched, so let's show that\nfact by not shortening).\n\n"},{"id":"309654","messageId":"254445096AD0412287CBB994E8BCA043@PhilipOakley","threadId":"44905","inReplyTo":"xmqq37gg9moc.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":null,"receivedAt":"2017-01-18T20:16:48Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Junio C Hamano\" <gitster@pobox.com>\nv3 1/5] doc: add documentation for OPT_STRING_LIST\n\n\n> Jacob Keller <jacob.e.keller@intel.com> writes:\n>\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..6914f54f5f44 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>> + Introduce an option with string argument.\n>> + The string argument is stored as an element in `&list` which must be a\n>> + struct string_list. Reset the list using `--no-option`.\n>> +\n>\n> I do not know if it is clear enough that 'option' in the last\n> sentence is a placeholder.  I then wondered if spelling it as\n> `--no-<long>` would make it a bit clearer, but that is ugly.\n\nBikeshedding:: `--no-<option>` maybe, i.e. just surround the option word \nwith the angle brackets to indicate it is to be replaced by the real \noption's name.\n\n>\n> The \"Reset the list\" is an instruction to the end-users who interact\n> with a program written by readers of this document using\n> OPT_STRING_LIST(), and it feels a bit out of place.  Perhaps\n>\n> End users can reset the list by negating the option,\n> i.e. passing \"--no-<long>\", on the command line.\n>\n> I dunno.\n>\n> Anyway, thanks for adding a missing doc here.\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--\n\nPhilip \n\n"},{"id":"309656","messageId":"xmqqtw8w86xc.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"20170118000930.5431-4-jacob.e.keller@intel.com","subject":"Re: [PATCH v3 3/5] name-rev: add support to exclude refs by pattern match","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T20:11:27Z","receivedAt":"2017-01-18T20:16:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\n> From: Jacob Keller <jacob.keller@gmail.com>\n>\n> Extend name-rev further to support matching refs by adding `--exclude`\n> patterns. These patterns will limit the scope of refs by excluding any\n> ref that matches at least one exclude pattern. Checking the exclude refs\n> shall happen first, before checking the include --refs patterns.\n\nI do not think we should have this \"exclude first and then include\"\nwritten down here, as it is an irrelevant implementation detail.\nThe desired end result is that only refs that match at least one\ninclude and none of the exclude survive.  You could implement it by\nfirst checking with include and then further narrowing that set by\nfiltering those that match exclude (I am not saying that \"include\nfirst then exclude\" is better---I am saying that it is far less\nimportant than \"at least one include and none of the exclude\" to\nmention the order of application).\n\n> +--exclude=<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, exclude refs that match any of the given\n> +\tshell patterns. Use `--no-exclude` to clear the list of exclude\n> +\tpatterns.\n\nPerhaps insert\n\n    When used together with --refs, only those that match at least\n    one of the --refs patterns and none of the --exclude patterns\n    are used.\n\nbefore \"Use `--no-exclude` to clear\"?\n\n"},{"id":"309657","messageId":"xmqqpojk86nh.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"20170118000930.5431-6-jacob.e.keller@intel.com","subject":"Re: [PATCH v3 5/5] describe: teach describe negative pattern matches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T20:17:22Z","receivedAt":"2017-01-18T20:17:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\n> From: Jacob Keller <jacob.keller@gmail.com>\n>\n> Teach git-describe the `--exclude` option which will allow specifying\n> a glob pattern of tags to ignore. This can be combined with the\n> `--match` patterns to enable more flexibility in determining which tags\n> to consider.\n>\n> For example, suppose you wish to find the first official release tag\n> that contains a certain commit. If we assume that official release tags\n> are of the form \"v*\" and pre-release candidates include \"*rc*\" in their\n> name, we can now find the first tag that introduces commit abcdef via:\n>\n>   git describe --contains --match=\"v*\" --exclude=\"*rc*\"\n>\n> Add documentation and tests for this change.\n>\n> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>\n> ---\n\nThe above is much better than 3/5 with a concrete example (compared\nto the vague \"certain kinds of references\").  It also does not have\nthe \"we check this first and then that\" ;-).\n\n> diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt\n> index 7ad41e2f6ade..21a43b78924a 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> +--exclude <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 excluded. Use `--no-exclude` to clear and\n> +\treset the list of patterns.\n> +\n\nSimilar to 3/5, perhaps we want to say something about interaction\nbetween this one and --match?\n\n"},{"id":"309658","messageId":"xmqqlgu886lf.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"20170118000930.5431-1-jacob.e.keller@intel.com","subject":"Re: [PATCH v3 0/5] extend git-describe pattern matching","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T20:18:36Z","receivedAt":"2017-01-18T20:18:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.e.keller@intel.com> writes:\n\n> From: Jacob Keller <jacob.keller@gmail.com>\n>\n> ** v3 fixes a minor typo in one of the test cases, so please ignore v2\n>    I left the interdiff as between v1 and v3 instead of v2 **\n\nVery much appreciated.\n\nI just finished reading this round through and didn't have any major\nissues.  I sent comments on the way the feature is explained to the\nend users and history readers, though.\n\nThanks.\n"},{"id":"309666","messageId":"CA+P7+xqM58tQ3=jkX3q4YkB_Jz=u6bf2FC1-f738-xCCGP3brg@mail.gmail.com","threadId":"44905","inReplyTo":"xmqqlgu886lf.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 0/5] extend git-describe pattern matching","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-18T21:06:09Z","receivedAt":"2017-01-18T21:06:56Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 18, 2017 at 12:18 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.e.keller@intel.com> writes:\n>\n>> From: Jacob Keller <jacob.keller@gmail.com>\n>>\n>> ** v3 fixes a minor typo in one of the test cases, so please ignore v2\n>>    I left the interdiff as between v1 and v3 instead of v2 **\n>\n> Very much appreciated.\n>\n> I just finished reading this round through and didn't have any major\n> issues.  I sent comments on the way the feature is explained to the\n> end users and history readers, though.\n>\n> Thanks.\n\nOk. I am currently taking a look at the exclude_list that was\nmentioned, and I will see if it makes sense to go that route. If we\ndo, it would open up a way to make the git-describe logic similar to\ngit-log (which is probably good), and if we want to do this, it's best\nto do it now before we add any additional arguments (ie: before we\ncreate a conflicting user interface).\n\nThanks,\nJake\n"},{"id":"309668","messageId":"CA+P7+xrv35w0RYNEZ88K_EWC57A4utBQTibtw6+TJBCtzA9Ybw@mail.gmail.com","threadId":"44905","inReplyTo":"xmqq37gg9moc.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-18T21:10:36Z","receivedAt":"2017-01-18T21:11:10Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 18, 2017 at 11:45 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> I do not know if it is clear enough that 'option' in the last\n> sentence is a placeholder.  I then wondered if spelling it as\n> `--no-<long>` would make it a bit clearer, but that is ugly.\n>\n\nTo be fair, this is exactly how the rest of the doc spells these\nthings, so I would rather be consistent with the doc as is, and a\nfuture patch could clean this up. See OPT_SET_INT, for an example of\n`--no-option`.\n\n\n> The \"Reset the list\" is an instruction to the end-users who interact\n> with a program written by readers of this document using\n> OPT_STRING_LIST(), and it feels a bit out of place.  Perhaps\n>\n>         End users can reset the list by negating the option,\n>         i.e. passing \"--no-<long>\", on the command line.\n>\n> I dunno.\n\nMaybe we can rephrase this \"The list is reset via `--no-option`\"?\n\nThanks,\nJake\n"},{"id":"309674","messageId":"CA+P7+xpMAVq8K41cDZy5FTiRTHoWWd3yOSmLoj4ucAvCPoNa0g@mail.gmail.com","threadId":"44905","inReplyTo":"xmqqy3y8878k.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-18T21:12:37Z","receivedAt":"2017-01-18T21:22:37Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 18, 2017 at 12:04 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.e.keller@intel.com> writes:\n>\n>> From: Jacob Keller <jacob.keller@gmail.com>\n>>\n>> Teach git name-rev to take a string list of patterns from --refs instead\n>> of only a single pattern. The list of patterns will be matched\n>> inclusively, such that a ref only needs to match one pattern to be\n>> included. If a ref will only be excluded if it does not match any of the\n>> patterns.\n>\n> I think \"If a\" in the last sentence should be \"A\".\n\nYou are correct, that is a typo.\n\n>\n>>  --refs=<pattern>::\n>>       Only use refs whose names match a given shell pattern.  The pattern\n>> -     can be one of branch name, tag name or fully qualified ref name.\n>> +     can be one of branch name, tag name or fully qualified ref name. If\n>> +     given multiple times, use refs whose names match any of the given shell\n>> +     patterns. Use `--no-refs` to clear any previous ref patterns given.\n>\n> Unlike 1/5, this is facing the end-users, and the last sentence is\n> very appropriate.\n\nYes.\n\n>\n>> +     if (data->ref_filters.nr) {\n>> +             struct string_list_item *item;\n>> +             int matched = 0;\n>> +\n>> +             /* See if any of the patterns match. */\n>> +             for_each_string_list_item(item, &data->ref_filters) {\n>> +                     /*\n>> +                      * We want to check every pattern even if we already\n>> +                      * found a match, just in case one of the later\n>> +                      * patterns could abbreviate the output.\n>> +                      */\n>> +                     switch (subpath_matches(path, item->string)) {\n>> +                     case -1: /* did not match */\n>> +                             break;\n>> +                     case 0: /* matched fully */\n>> +                             matched = 1;\n>> +                             break;\n>> +                     default: /* matched subpath */\n>> +                             matched = 1;\n>> +                             can_abbreviate_output = 1;\n>> +                             break;\n>> +                     }\n>>               }\n>\n> I agree that we cannot short-cut on the first match to make sure\n> that the outcome is stable, but I wondered what should be shown when\n> we do have multiple matches.  Say I gave\n>\n>     --refs=\"v*\" --refs=\"refs/tags/v1.*\"\n>\n> and refs/tags/v1.0 matched.  The above code would say we can\n> abbreviate.\n>\n> What is the reason behind this design decision?  Is it because it is\n> clear that the user shows her willingness to accept more compact\n> form by having --refs=\"v*\" that would allow shortening?  If that is\n> the case, I think I agree with the reasoning.  But we probably want\n> to write it down somewhere, because another reasoning, which may\n> also be valid, would call for an opposite behaviour (i.e. the more\n> specific --refs=\"refs/tags/v1.*\" also matched, so let's show that\n> fact by not shortening).\n>\n\nI'm not sure which reasoning makes most sense. Any other opinions?\n\nThanks,\nJake\n"},{"id":"309678","messageId":"CA+P7+xo4-45je995LLoyh-LbGTTf3EZUVW-UV+Dd=Wg0EGRvVA@mail.gmail.com","threadId":"44905","inReplyTo":"xmqqtw8w86xc.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 3/5] name-rev: add support to exclude refs by pattern match","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-18T21:13:30Z","receivedAt":"2017-01-18T21:40:50Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 18, 2017 at 12:11 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.e.keller@intel.com> writes:\n>\n>> From: Jacob Keller <jacob.keller@gmail.com>\n>>\n>> Extend name-rev further to support matching refs by adding `--exclude`\n>> patterns. These patterns will limit the scope of refs by excluding any\n>> ref that matches at least one exclude pattern. Checking the exclude refs\n>> shall happen first, before checking the include --refs patterns.\n>\n> I do not think we should have this \"exclude first and then include\"\n> written down here, as it is an irrelevant implementation detail.\n> The desired end result is that only refs that match at least one\n> include and none of the exclude survive.  You could implement it by\n> first checking with include and then further narrowing that set by\n> filtering those that match exclude (I am not saying that \"include\n> first then exclude\" is better---I am saying that it is far less\n> important than \"at least one include and none of the exclude\" to\n> mention the order of application).\n>\n>> +--exclude=<pattern>::\n>> +     Do not use any ref whose name matches a given shell pattern. The\n>> +     pattern can be one of branch name, tag name or fully qualified ref\n>> +     name. If given multiple times, exclude refs that match any of the given\n>> +     shell patterns. Use `--no-exclude` to clear the list of exclude\n>> +     patterns.\n>\n> Perhaps insert\n>\n>     When used together with --refs, only those that match at least\n>     one of the --refs patterns and none of the --exclude patterns\n>     are used.\n>\n> before \"Use `--no-exclude` to clear\"?\n>\n\nYes this makes sense. I'm still looking at whether the alternative\nimplementation suggested based on the git-log style would make more\nsense or not, but if we keep this as is, the added text you gave is\nimportant.\n\nThanks,\nJake\n"},{"id":"309681","messageId":"xmqqr3403x1r.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"254445096AD0412287CBB994E8BCA043@PhilipOakley","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T20:58:24Z","receivedAt":"2017-01-18T21:52:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n>>> +`OPT_STRING_LIST(short, long, &list, arg_str, description)`::\n>>> + Introduce an option with string argument.\n>>> + The string argument is stored as an element in `&list` which must be a\n>>> + struct string_list. Reset the list using `--no-option`.\n>>> +\n>>\n>> I do not know if it is clear enough that 'option' in the last\n>> sentence is a placeholder.  I then wondered if spelling it as\n>> `--no-<long>` would make it a bit clearer, but that is ugly.\n>\n> Bikeshedding:: `--no-<option>` maybe, i.e. just surround the option\n> word with the angle brackets to indicate it is to be replaced by the\n> real option's name.\n\nYeah, I bikeshedded that myself, and rejected it because there is no\n<option> mentioned anywhere in the enumeration header.\n"},{"id":"309682","messageId":"xmqqy3y82fs8.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"CA+P7+xo4-45je995LLoyh-LbGTTf3EZUVW-UV+Dd=Wg0EGRvVA@mail.gmail.com","subject":"Re: [PATCH v3 3/5] name-rev: add support to exclude refs by pattern match","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T21:56:39Z","receivedAt":"2017-01-18T21:57:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> Yes this makes sense. I'm still looking at whether the alternative\n> implementation suggested based on the git-log style would make more\n> sense or not, but if we keep this as is, the added text you gave is\n> important.\n\nI actually think it is a red-herring that \"git log\" honors \"orders\";\nit does, but that is not a result of carefully considering the\ndesired behaviour.  It instead is a historical wart that came from\nthe fact that \"--branches\" and friends uses for_each_glob_ref_in()\nthat takes the top-level hierarchy paths like \"refs/heads/\" and the\nimplementation of \"--exclude\" piggybacked into the function in a\nlazy way.  \n\nIf exclusion were done independently (e.g. in a way similar to what\nyou did in this series using subpath match), we wouldn't have had\nthe \"the user must give exclude patterns first that would affect the\nnext inclusion pattern, at which point the exclude patterns are\ncleared and the user needs to start over\", which is an end-user\nexperience that is clunky.\n\n"},{"id":"309688","messageId":"CA+P7+xo3bS0EfXaVCZbd6NwbBA8vP5HiZNpAh_4v7S_p2jm+Zg@mail.gmail.com","threadId":"44905","inReplyTo":"xmqqy3y82fs8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 3/5] name-rev: add support to exclude refs by pattern match","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-18T22:31:03Z","receivedAt":"2017-01-18T22:41:13Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Jan 18, 2017 at 1:56 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>> Yes this makes sense. I'm still looking at whether the alternative\n>> implementation suggested based on the git-log style would make more\n>> sense or not, but if we keep this as is, the added text you gave is\n>> important.\n>\n> I actually think it is a red-herring that \"git log\" honors \"orders\";\n> it does, but that is not a result of carefully considering the\n> desired behaviour.  It instead is a historical wart that came from\n> the fact that \"--branches\" and friends uses for_each_glob_ref_in()\n> that takes the top-level hierarchy paths like \"refs/heads/\" and the\n> implementation of \"--exclude\" piggybacked into the function in a\n> lazy way.\n>\n> If exclusion were done independently (e.g. in a way similar to what\n> you did in this series using subpath match), we wouldn't have had\n> the \"the user must give exclude patterns first that would affect the\n> next inclusion pattern, at which point the exclude patterns are\n> cleared and the user needs to start over\", which is an end-user\n> experience that is clunky.\n>\n\nHowever, it is useful that exclude patterns only apply to specific\nmatch parameters? That is the advantage of the other implementation.\n\nI think I agree that it's not really worth the complexity, as it\nrequires a much more complex explanation of how the parameters\ninteract, and in general doesn't provide that much more\nexpressiveness, since at least for \"git describe\" by definition it\neither finds the tag as a match or not. Sure you could say \"include\nall tags matching x but only if they don't match y\" and include all\ntags matching z even if they match y\" using that mechanism, but I\nthink that makes the entire thing needlessly more complicated than \"we\nuse a tag if it matches any match and doesn't match any exclude\".\n\nThanks,\nJake\n"},{"id":"309689","messageId":"xmqqpojk2dnu.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"CA+P7+xpMAVq8K41cDZy5FTiRTHoWWd3yOSmLoj4ucAvCPoNa0g@mail.gmail.com","subject":"Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-18T22:42:29Z","receivedAt":"2017-01-18T22:43:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> On Wed, Jan 18, 2017 at 12:04 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n>> I agree that we cannot short-cut on the first match to make sure\n>> that the outcome is stable, but I wondered what should be shown when\n>> we do have multiple matches.  Say I gave\n>>\n>>     --refs=\"v*\" --refs=\"refs/tags/v1.*\"\n>>\n>> and refs/tags/v1.0 matched.  The above code would say we can\n>> abbreviate.\n>>\n>> What is the reason behind this design decision?  Is it because it is\n>> clear that the user shows her willingness to accept more compact\n>> form by having --refs=\"v*\" that would allow shortening?  If that is\n>> the case, I think I agree with the reasoning.  But we probably want\n>> to write it down somewhere, because another reasoning, which may\n>> also be valid, would call for an opposite behaviour (i.e. the more\n>> specific --refs=\"refs/tags/v1.*\" also matched, so let's show that\n>> fact by not shortening).\n>\n> I'm not sure which reasoning makes most sense. Any other opinions?\n\nFWIW, I do think that the design decision to declare that it can be\nabbreviated if the ref matches at least one short pattern makes\nsense, and I am guessing (because you didn't answer when asked what\n_your_ reasoning behind the code was) that you are in agreement.  I\njust want it to be spelled out probably as in-code comment, so that\npeople who later come to this part of the code know why it was\ndesigned that way.  And they can disagree and change it if the end\nresult is better---I just want to make sure that they can understand\nwhat they are disagreeing when it happens, as opposed to them\nscratching their head saying \"we do not know why it was chosen to be\ndone this way, let's make a random change to make it behave\ndifferently\".\n"},{"id":"309738","messageId":"alpine.DEB.2.20.1701191749340.3469@virtualbox","threadId":"44905","inReplyTo":"xmqqr3403x1r.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-19T16:55:22Z","receivedAt":"2017-01-19T16:56:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 18 Jan 2017, Junio C Hamano wrote:\n\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n> \n> >>> +`OPT_STRING_LIST(short, long, &list, arg_str, description)`::\n> >>> + Introduce an option with string argument.\n> >>> + The string argument is stored as an element in `&list` which must be a\n> >>> + struct string_list. Reset the list using `--no-option`.\n> >>> +\n> >>\n> >> I do not know if it is clear enough that 'option' in the last\n> >> sentence is a placeholder.  I then wondered if spelling it as\n> >> `--no-<long>` would make it a bit clearer, but that is ugly.\n> >\n> > Bikeshedding:: `--no-<option>` maybe, i.e. just surround the option\n> > word with the angle brackets to indicate it is to be replaced by the\n> > real option's name.\n> \n> Yeah, I bikeshedded that myself, and rejected it because there is no\n> <option> mentioned anywhere in the enumeration header.\n\nAs I pointed out in a previous review round: the surrounding test uses\n--no-option (I agree that it is tedious to go back to the original code\nfor review, rather than a mere patch review that lacks context, but I\nsuspected that Jake did not come up with that `--no-option` himself), so\nby our own recommendation (imitate the surrounding, existing code/text)\nJake did exactly the right thing:\n\n$ git grep -e --no-option upstream/master -- Documentation/technical/api-parse-options.txt\nupstream/master:Documentation/technical/api-parse-options.txt:\t`--option` and set to zero with `--no-option`.\nupstream/master:Documentation/technical/api-parse-options.txt:\t(even if initially negative), and `--no-option` resets it to\nupstream/master:Documentation/technical/api-parse-options.txt:\tzero. To determine if `--option` or `--no-option` was encountered at\nupstream/master:Documentation/technical/api-parse-options.txt:\t`--no-option` was seen.\nupstream/master:Documentation/technical/api-parse-options.txt:\treset to zero with `--no-option`.\n\nCiao,\nJohannes\n\n"},{"id":"309749","messageId":"xmqqh94v2apv.fsf@gitster.mtv.corp.google.com","threadId":"44905","inReplyTo":"CA+P7+xrv35w0RYNEZ88K_EWC57A4utBQTibtw6+TJBCtzA9Ybw@mail.gmail.com","subject":"Re: [PATCH v3 1/5] doc: add documentation for OPT_STRING_LIST","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-19T17:58:20Z","receivedAt":"2017-01-19T18:32:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> On Wed, Jan 18, 2017 at 11:45 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> I do not know if it is clear enough that 'option' in the last\n>> sentence is a placeholder.  I then wondered if spelling it as\n>> `--no-<long>` would make it a bit clearer, but that is ugly.\n>\n> To be fair, this is exactly how the rest of the doc spells these\n> things, so I would rather be consistent with the doc as is, and a\n> future patch could clean this up. See OPT_SET_INT, for an example of\n> `--no-option`.\n\nAh, OK, then I have no issues with it.\n\n> Maybe we can rephrase this \"The list is reset via `--no-option`\"?\n\nI think I saw that in your latest interdiff and I think it is good.\n\nThanks.\n\n"}]}