{"thread":{"id":"48180","subject":"[PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","startedAt":"2018-03-28T23:47:48Z","lastAt":"2018-04-19T03:01:53Z","messageCount":60,"participants":["Taylor Blau","Junio C Hamano","Jeff King","René Scharfe","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"343300","messageId":"20180328234719.595-1-me@ttaylorr.com","threadId":"48180","inReplyTo":null,"subject":"[PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-03-28T23:47:19Z","receivedAt":"2018-03-28T23:47:48Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to add extend this functionality with\n`--color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n`--int`, `--bool`, and etc. This allows the aforementioned other patch\nto add `--color` (in the non-traditional sense) via `--type=color`,\ninstead of `--color`.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 66 +++++++++++++++++++-----------------\n builtin/config.c             | 25 ++++++++++++++\n t/t1300-repo-config.sh       | 21 ++++++++++++\n 3 files changed, 80 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..a4a5ffb41 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type] --add name value\n+'git config' [<file-option>] [--type] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+A type specifier may be given as an argument to `--type` to make 'git config'\n+ensure that the variable(s) are of the given type and convert the value to the\n+canonical form. If no type specifier is passed, no checks or transformations are\n+performed on the value.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,34 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type [type]::\n+  'git config' will ensure that any input output is valid under the given type\n+  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n+  form.\n++\n+Valid `[type]`'s include:\n++\n+- 'bool': canonicalize  values as either \"true\" or \"false\".\n+- 'int': canonicalize  values as simple decimla numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative ate-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd62..ea7923493 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -26,6 +26,7 @@ static char term = '\\n';\n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n static int actions, types;\n+static char *type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -84,6 +85,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_STRING('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\")),\n \tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n@@ -493,6 +495,21 @@ static char *default_user_config(void)\n \treturn strbuf_detach(&buf, NULL);\n }\n \n+static int type_name_to_specifier(char *name)\n+{\n+\tif (!(strcmp(name, \"bool\")))\n+\t\treturn TYPE_BOOL;\n+\telse if (!(strcmp(name, \"int\")))\n+\t\treturn TYPE_INT;\n+\telse if (!(strcmp(name, \"bool-or-int\")))\n+\t\treturn TYPE_BOOL_OR_INT;\n+\telse if (!(strcmp(name, \"path\")))\n+\t\treturn TYPE_PATH;\n+\telse if (!(strcmp(name, \"expiry-date\")))\n+\t\treturn TYPE_EXPIRY_DATE;\n+\tdie(_(\"unexpected --type argument, %s\"), name);\n+}\n+\n int cmd_config(int argc, const char **argv, const char *prefix)\n {\n \tint nongit = !startup_info->have_repository;\n@@ -601,6 +618,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n+\tif (type) {\n+\t\tif (types != 0) {\n+\t\t\terror(\"usage of --type is ambiguous\");\n+\t\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n+\t\t}\n+\t\ttypes = type_name_to_specifier(type);\n+\t}\n+\n \tif (actions & PAGING_ACTIONS)\n \t\tsetup_auto_pager(\"config\", 1);\n \ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fd..12dc94bd2 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,25 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+foo = true\n+EOF\n+\n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unexpected --type argument\" error\n+'\n+\n+test_expect_success '--type does not allow for ambiguity' '\n+\ttest_must_fail git config --type=bool --bool core.foo 2>error &&\n+\ttest_i18ngrep \"usage of --type is ambiguous\" error\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343367","messageId":"xmqqh8oy63gk.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-29T20:18:03Z","receivedAt":"2018-03-29T20:18:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n> `git config` has long allowed the ability for callers to provide a 'type\n> specifier', which instructs `git config` to (1) ensure that incoming\n> values are satisfiable under that type, and (2) that outgoing values are\n> canonicalized under that type.\n>\n> In another series, we propose to add extend this functionality with\n> `--color` and `--default` to replace `--get-color`.\n>\n> However, we traditionally use `--color` to mean \"colorize this output\",\n> instead of \"this value should be treated as a color\".\n>\n> Currently, `git config` does not support this kind of colorization, but\n> we should be careful to avoid inhabiting this option too soon, so that\n> `git config` can support `--color` (in the traditional sense) in the\n> future, if that is desired.\n\nOK.\n\n> In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> `--int`, `--bool`, and etc. This allows the aforementioned other patch\n> to add `--color` (in the non-traditional sense) via `--type=color`,\n> instead of `--color`.\n\nOK, it's not just \"we prefer\" but we add this new \"--type\" thing, so\nthat we do not have to worry about having to make --color and other\n\"types\" into double-dash options.  Makes good sense, I would guess.\n\n"},{"id":"343377","messageId":"20180329221122.GL2939@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-29T22:11:22Z","receivedAt":"2018-03-29T22:11:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 28, 2018 at 04:47:19PM -0700, Taylor Blau wrote:\n\n> `git config` has long allowed the ability for callers to provide a 'type\n> specifier', which instructs `git config` to (1) ensure that incoming\n> values are satisfiable under that type, and (2) that outgoing values are\n> canonicalized under that type.\n> \n> In another series, we propose to add extend this functionality with\n> `--color` and `--default` to replace `--get-color`.\n> \n> However, we traditionally use `--color` to mean \"colorize this output\",\n> instead of \"this value should be treated as a color\".\n> \n> Currently, `git config` does not support this kind of colorization, but\n> we should be careful to avoid inhabiting this option too soon, so that\n> `git config` can support `--color` (in the traditional sense) in the\n> future, if that is desired.\n> \n> In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> `--int`, `--bool`, and etc. This allows the aforementioned other patch\n> to add `--color` (in the non-traditional sense) via `--type=color`,\n> instead of `--color`.\n\nMakes sense. I agree with promoting --type as the correct way going\nforward, since it will grow new types, whereas we can stop adding\n\"--foo\" aliases for \"--type=foo\".\n\n> +Valid `[type]`'s include:\n> ++\n> +- 'bool': canonicalize  values as either \"true\" or \"false\".\n> +- 'int': canonicalize  values as simple decimla numbers. An optional suffix of\n> +  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n> +  1073741824 prior to output.\n> +- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n> +  above.\n> +- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n> +  `~user` to the home directory for the specified user. This specifier has no\n> +  effect when setting the value (but you can use `git config section.variable\n> +  ~/` from the command line to let your shell do the expansion.)\n> +- 'expiry-date': canonicalize by converting from a fixed or relative ate-string\n> +  to a timestamp. This specifier has no effect when setting the value.\n> ++\n\nYay. It's nice to have this in only one place now.\n\ns/ate-string/d&/ :)\n\n> +static int type_name_to_specifier(char *name)\n> +{\n> +\tif (!(strcmp(name, \"bool\")))\n> +\t\treturn TYPE_BOOL;\n\nWe'd usually drop the extra level of parentheses, and just write:\n\n  if (!strcmp(name, \"bool\"))\n\n> @@ -601,6 +618,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n>  \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n>  \t}\n>  \n> +\tif (type) {\n> +\t\tif (types != 0) {\n> +\t\t\terror(\"usage of --type is ambiguous\");\n> +\t\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n> +\t\t}\n> +\t\ttypes = type_name_to_specifier(type);\n> +\t}\n\nThis error message left me scratching my head for a minute. Ambiguous\nhow? I think this is covering the case of:\n\n  git config --int --type=bool\n\nSo maybe \"--type cannot be used with other type options\" or something?\n\nLet's take a step back, though. As part of this, should we convert the\nparsing of type options to last-one-wins? The fact that they are all\nOPT_BIT() is quite silly, since you cannot have more than one bit set.\nSo if you do:\n\n  git config --int --bool\n\nyou get an error. Whereas normal behavior for most options would be for\n--bool to override --int. And that is what happens with:\n\n  git config --type=int --type=bool\n\nI don't think there are any backwards compatibility issues to deal with\nhere; we'd be changing a case which is now always an error.\n\nAnd then after that, you truly can make (and document, if we want) that\n\"--int\" is a true synonym for \"--type=int\".\n\nI think it would be pretty simple. One of:\n\n  - convert OPT_BIT(\"bool\") into OPT_CALLBACK(\"bool\") and just assign\n    \"bool\" to the \"type\" string, which will then later get parsed into\n    TYPE_BOOL.\n\nor\n\n  - convert OPT_BIT(\"bool\") into OPT_SET_INT(\"bool\") to set TYPE_BOOL\n    directly. Convert OPT_STRING(\"type\") into OPT_CALLBACK(), and have\n    it assign the result of type_name_to_specifier() directly.\n\nI'd probably do the latter, but would be fine with either (and I'd make\nthe OPT_SET_INT thing its own preparatory patch).\n\nIf you really want to go all-out, I think the ACTION flags could use the\nsame cleanup. We treat them as bitflags, and then issue an error when\nyou set more than one, which is just silly.\n\n-Peff\n"},{"id":"343386","messageId":"20180330052719.GA6628@syl.local","threadId":"48180","inReplyTo":"20180329221122.GL2939@sigill.intra.peff.net","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-03-30T05:27:19Z","receivedAt":"2018-03-30T05:27:27Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Mar 29, 2018 at 06:11:22PM -0400, Jeff King wrote:\n> > +Valid `[type]`'s include:\n> > ++\n> > +- 'bool': canonicalize  values as either \"true\" or \"false\".\n> > +- 'int': canonicalize  values as simple decimla numbers. An optional suffix of\n> > +  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n> > +  1073741824 prior to output.\n> > +- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n> > +  above.\n> > +- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n> > +  `~user` to the home directory for the specified user. This specifier has no\n> > +  effect when setting the value (but you can use `git config section.variable\n> > +  ~/` from the command line to let your shell do the expansion.)\n> > +- 'expiry-date': canonicalize by converting from a fixed or relative ate-string\n> > +  to a timestamp. This specifier has no effect when setting the value.\n> > ++\n>\n> Yay. It's nice to have this in only one place now.\n\nThanks! Agreed :-).\n\n> s/ate-string/d&/ :)\n\nAck.\n\nMy excuse for this is that I have started using iTerm2.app with Vim in\nmy terminal using the new graphics acceleration options, and have had\ntrouble getting Vim to render the underline for misspelled words\nconsistently.\n\n> > +static int type_name_to_specifier(char *name)\n> > +{\n> > +\tif (!(strcmp(name, \"bool\")))\n> > +\t\treturn TYPE_BOOL;\n>\n> We'd usually drop the extra level of parentheses, and just write:\n>\n>   if (!strcmp(name, \"bool\"))\n\nSounds good; I have adjusted this in the appropriate location in v2.\n\n> > @@ -601,6 +618,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n> >  \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n> >  \t}\n> >\n> > +\tif (type) {\n> > +\t\tif (types != 0) {\n> > +\t\t\terror(\"usage of --type is ambiguous\");\n> > +\t\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n> > +\t\t}\n> > +\t\ttypes = type_name_to_specifier(type);\n> > +\t}\n>\n> This error message left me scratching my head for a minute. Ambiguous\n> how? I think this is covering the case of:\n>\n>   git config --int --type=bool\n>\n> So maybe \"--type cannot be used with other type options\" or something?\n>\n> Let's take a step back, though. As part of this, should we convert the\n> parsing of type options to last-one-wins? The fact that they are all\n> OPT_BIT() is quite silly, since you cannot have more than one bit set.\n> So if you do:\n>\n>   git config --int --bool\n>\n> you get an error. Whereas normal behavior for most options would be for\n> --bool to override --int. And that is what happens with:\n>\n>   git config --type=int --type=bool\n>\n> I don't think there are any backwards compatibility issues to deal with\n> here; we'd be changing a case which is now always an error.\n\nAgreed.\n\n> And then after that, you truly can make (and document, if we want) that\n> \"--int\" is a true synonym for \"--type=int\".\n\nGreat point; for me this is the primary motivating factor for making\nthis change. In addition to simplifying our work when we check:\n\n  if (types != 0 && type_str) { ... }\n\nit would be nice not to have to compare the two in order to ensure that\nthey are the same, continuing on if so, and causing an error if not.\n\n> I think it would be pretty simple. One of:\n>\n>   - convert OPT_BIT(\"bool\") into OPT_CALLBACK(\"bool\") and just assign\n>     \"bool\" to the \"type\" string, which will then later get parsed into\n>     TYPE_BOOL.\n>\n> or\n>\n>   - convert OPT_BIT(\"bool\") into OPT_SET_INT(\"bool\") to set TYPE_BOOL\n>\n>     directly. Convert OPT_STRING(\"type\") into OPT_CALLBACK(), and have\n>     it assign the result of type_name_to_specifier() directly.\n>\n> I'd probably do the latter, but would be fine with either (and I'd make\n> the OPT_SET_INT thing its own preparatory patch).\n\nI adopted your advice and used the later, converting each `OPT_BIT` into\nan `OPT_SET_INT`, and adding a callback for `--type`, which does _not_\ncomplain if `type` is already non-zero.\n\n> If you really want to go all-out, I think the ACTION flags could use the\n> same cleanup. We treat them as bitflags, and then issue an error when\n> you set more than one, which is just silly.\n\nAgreed, and I think that this is a good candidate for a future patch.\nThoughts? :-).\n\n\nThanks,\nTaylor\n"},{"id":"343387","messageId":"20180330052830.57251-1-me@ttaylorr.com","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v2 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-03-30T05:28:29Z","receivedAt":"2018-03-30T05:28:42Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nWhere--although the legacy and modern type specifier (`--int`, and\n`--type`, respectively) do not conflict--we would store the value of\n`--type=` in a `char *` and the `--int` as a bit in the `int` bitset.\n\nIn the above, when error-checking `if (types != && type_str)`, we do not\ncheck additionally whether or not these types are the same, and simply\ncomplain immediately. This change makes `--int` and `--type=int` a true\nsynonym of each other, and removes the need for additional complexity,\nas above in the conditional.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 39 +++++++++++++++++----------------------\n t/t1300-repo-config.sh | 11 +++++++++++\n 2 files changed, 28 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd62..fd7b36c41 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fd..aa45b9267 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 > expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343388","messageId":"20180330052830.57251-2-me@ttaylorr.com","threadId":"48180","inReplyTo":"20180330052830.57251-1-me@ttaylorr.com","subject":"[PATCH v2 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-03-30T05:28:30Z","receivedAt":"2018-03-30T05:28:44Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to add extend this functionality with\n`--color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n`--int`, `--bool`, and etc. This allows the aforementioned other patch\nto add `--color` (in the non-traditional sense) via `--type=color`,\ninstead of `--color`.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 66 +++++++++++++++++++-----------------\n builtin/config.c             | 23 +++++++++++++\n t/t1300-repo-config.sh       | 18 ++++++++++\n 3 files changed, 75 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..9956b03f7 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type] --add name value\n+'git config' [<file-option>] [--type] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+A type specifier may be given as an argument to `--type` to make 'git config'\n+ensure that the variable(s) are of the given type and convert the value to the\n+canonical form. If no type specifier is passed, no checks or transformations are\n+performed on the value.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,34 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type [type]::\n+  'git config' will ensure that any input output is valid under the given type\n+  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n+  form.\n++\n+Valid `[type]`'s include:\n++\n+- 'bool': canonicalize  values as either \"true\" or \"false\".\n+- 'int': canonicalize  values as simple decimla numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex fd7b36c41..8833f928a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,28 @@ static int show_origin;\n #define TYPE_PATH (1<<3)\n #define TYPE_EXPIRY_DATE (1<<4)\n \n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse {\n+\t\tdie(_(\"unexpected --type argument, %s\"), arg);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +106,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex aa45b9267..8c5210fdf 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n \n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unexpected --type argument\" error\n+'\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 > expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343389","messageId":"9e25f8fa-6518-5c15-f249-c874dbe1761c@web.de","threadId":"48180","inReplyTo":"20180330052830.57251-2-me@ttaylorr.com","subject":"Re: [PATCH v2 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2018-03-30T06:17:56Z","receivedAt":"2018-03-30T06:18:19Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Am 30.03.2018 um 07:28 schrieb Taylor Blau:\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> index e09ed5d7d..9956b03f7 100644\n> --- a/Documentation/git-config.txt\n> +++ b/Documentation/git-config.txt\n> @@ -160,30 +158,34 @@ See also <<FILES>>.\n>   --list::\n>   \tList all variables set in config file, along with their values.\n>   \n> ---bool::\n> -\t'git config' will ensure that the output is \"true\" or \"false\"\n> +--type [type]::\n> +  'git config' will ensure that any input output is valid under the given type\n> +  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n> +  form.\n> ++\n> +Valid `[type]`'s include:\n> ++\n> +- 'bool': canonicalize  values as either \"true\" or \"false\".\n> +- 'int': canonicalize  values as simple decimla numbers. An optional suffix of\n\ns/ize  val/ize val/\ns/decimla/decimal/\n\nRené\n"},{"id":"343420","messageId":"20180330134148.GC29568@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180330052830.57251-1-me@ttaylorr.com","subject":"Re: [PATCH v2 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-30T13:41:48Z","receivedAt":"2018-03-30T13:41:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Mar 30, 2018 at 01:28:29AM -0400, Taylor Blau wrote:\n\n> Internally, we represent `git config`'s type specifiers as a bitset\n> using OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\n> allows for the representation of multiple type specifiers in the `int\n> types` field, but this multi-representation is left unused.\n> \n> In fact, `git config` will not accept multiple type specifiers at a\n> time, as indicated by:\n> \n>   $ git config --int --bool some.section\n>   error: only one type at a time.\n> \n> This patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\n> specifier, so that the above command would instead be valid, and a\n> synonym of:\n> \n>   $ git config --bool some.section\n> \n> This change is motivated by two urges: (1) it does not make sense to\n> represent a singular type specifier internally as a bitset, only to\n> complain when there are multiple bits in the set. `OPT_SET_INT` is more\n> well-suited to this task than `OPT_BIT` is. (2) a future patch will\n> introduce `--type=<type>`, and we would like not to complain in the\n> following situation:\n> \n>   $ git config --int --type=int\n\nI think you could just end here, since...\n\n> Where--although the legacy and modern type specifier (`--int`, and\n> `--type`, respectively) do not conflict--we would store the value of\n> `--type=` in a `char *` and the `--int` as a bit in the `int` bitset.\n> \n> In the above, when error-checking `if (types != && type_str)`, we do not\n> check additionally whether or not these types are the same, and simply\n> complain immediately. This change makes `--int` and `--type=int` a true\n> synonym of each other, and removes the need for additional complexity,\n> as above in the conditional.\n\nNone of this type_str stuff exists yet, and you've sufficiently\nmotivated the change.\n\n> Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> ---\n>  builtin/config.c       | 39 +++++++++++++++++----------------------\n>  t/t1300-repo-config.sh | 11 +++++++++++\n>  2 files changed, 28 insertions(+), 22 deletions(-)\n\nThe patch mostly looks good. We probably want to also rewrite the TYPE_*\n#defines to be sequential, since somebody may scratch their head\nwondering why we use one bit per type when we do not treat them as a\nbitfield.\n\n> diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> index 4f8e6f5fd..aa45b9267 100755\n> --- a/t/t1300-repo-config.sh\n> +++ b/t/t1300-repo-config.sh\n> @@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n>  \ttest_expect_code 128 nongit git config --local foo.bar\n>  '\n>  \n> +cat >.git/config <<-\\EOF &&\n> +[core]\n> +number = 10\n> +EOF\n> +\n> +test_expect_success 'later legacy specifiers are given precedence' '\n> +\tgit config --bool --int core.number >actual &&\n> +\techo 10 > expect &&\n> +\ttest_cmp expect actual\n> +'\n\nMinor style nit: we prefer \">expect\" with no space. Though t1300 is\ncertainly a cornucopia of style violations already. I could buy it under\nthe guise of matching existing style if there wasn't a counter-example\ndirectly above. :)\n\nWe also prefer to put that \"cat >.git/config\" inside a test block,\nthough I'm pretty sure that _is_ following existing style in the test.\n\n-Peff\n"},{"id":"343421","messageId":"20180330134828.GD29568@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180330052830.57251-2-me@ttaylorr.com","subject":"Re: [PATCH v2 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-30T13:48:29Z","receivedAt":"2018-03-30T13:48:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Mar 30, 2018 at 01:28:30AM -0400, Taylor Blau wrote:\n\n> +static int option_parse_type(const struct option *opt, const char *arg,\n> +\t\t\t     int unset)\n> +{\n> +\tint *type = opt->value;\n> +\n> +\tif (!strcmp(arg, \"bool\"))\n> +\t\t*type = TYPE_BOOL;\n> +\telse if (!strcmp(arg, \"int\"))\n> +\t\t*type = TYPE_INT;\n> +\telse if (!strcmp(arg, \"bool-or-int\"))\n> +\t\t*type = TYPE_BOOL_OR_INT;\n> +\telse if (!strcmp(arg, \"path\"))\n> +\t\t*type = TYPE_PATH;\n> +\telse if (!strcmp(arg, \"expiry-date\"))\n> +\t\t*type = TYPE_EXPIRY_DATE;\n> +\telse {\n> +\t\tdie(_(\"unexpected --type argument, %s\"), arg);\n> +\t\treturn 1;\n> +\t}\n> +\treturn 0;\n> +}\n\nYou need to handle \"unset\" here, which will trigger when somebody does:\n\n  git config --no-type\n\nIn which case you'd probably want to reset it to \"0\".\n\n> @@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n>  \ttest_cmp expect actual\n>  '\n>  \n> +test_expect_success '--type allows valid type specifiers' '\n> +\techo \"true\" >expect &&\n> +\tgit config --type=bool core.foo >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--type rejects unknown specifiers' '\n> +\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n> +\ttest_i18ngrep \"unexpected --type argument\" error\n> +'\n> +\n> +test_expect_success 'later legacy specifiers are given precedence' '\n> +\tgit config --bool --int core.number >actual &&\n> +\techo 10 > expect &&\n> +\ttest_cmp expect actual\n> +'\n\nI think there's some rebasing funkiness with this last test, which\nalready exists from patch 1.\n\nOther than those two minor issues and the typos that René noticed, this\nlooks good to me.\n\n-Peff\n"},{"id":"343424","messageId":"20180330135315.GE29568@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180330052719.GA6628@syl.local","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-30T13:53:15Z","receivedAt":"2018-03-30T13:53:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Mar 30, 2018 at 01:27:19AM -0400, Taylor Blau wrote:\n\n> > If you really want to go all-out, I think the ACTION flags could use the\n> > same cleanup. We treat them as bitflags, and then issue an error when\n> > you set more than one, which is just silly.\n> \n> Agreed, and I think that this is a good candidate for a future patch.\n> Thoughts? :-).\n\nI actually worked this up for fun, though I had second thoughts while\nwriting the commit message.\n\nBesides the cleanup, my primary motivation was following last-one-wins\nrules as we often do elsewhere. But actually, last-one-wins applies only\nto a _single_ option, not necessarily unrelated ones. Many other\nmulti-action commands actually have a series of separate boolean flags,\nand then complain when more than one of the flags is set.\n\nSo maybe it's not such a good idea for the actions (I do still think\nit's the right path for the types).\n\nFor reference, here's the patch I wrote:\n\n builtin/config.c | 137 +++++++++++++++++++++++++----------------------\n 1 file changed, 72 insertions(+), 65 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd628..5581f48ac8 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,35 +25,36 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int types;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n static int show_origin;\n \n-#define ACTION_GET (1<<0)\n-#define ACTION_GET_ALL (1<<1)\n-#define ACTION_GET_REGEXP (1<<2)\n-#define ACTION_REPLACE_ALL (1<<3)\n-#define ACTION_ADD (1<<4)\n-#define ACTION_UNSET (1<<5)\n-#define ACTION_UNSET_ALL (1<<6)\n-#define ACTION_RENAME_SECTION (1<<7)\n-#define ACTION_REMOVE_SECTION (1<<8)\n-#define ACTION_LIST (1<<9)\n-#define ACTION_EDIT (1<<10)\n-#define ACTION_SET (1<<11)\n-#define ACTION_SET_ALL (1<<12)\n-#define ACTION_GET_COLOR (1<<13)\n-#define ACTION_GET_COLORBOOL (1<<14)\n-#define ACTION_GET_URLMATCH (1<<15)\n-\n+enum config_action {\n+\tACTION_NONE = 0,\n+\tACTION_GET,\n+\tACTION_GET_ALL,\n+\tACTION_GET_REGEXP,\n+\tACTION_REPLACE_ALL,\n+\tACTION_ADD,\n+\tACTION_UNSET,\n+\tACTION_UNSET_ALL,\n+\tACTION_RENAME_SECTION,\n+\tACTION_REMOVE_SECTION,\n+\tACTION_LIST,\n+\tACTION_EDIT,\n+\tACTION_SET,\n+\tACTION_SET_ALL,\n+\tACTION_GET_COLOR,\n+\tACTION_GET_COLORBOOL,\n+\tACTION_GET_URLMATCH,\n+};\n /*\n- * The actions \"ACTION_LIST | ACTION_GET_*\" which may produce more than\n- * one line of output and which should therefore be paged.\n+ * This must be an int and not an enum because we pass it by address\n+ * to OPT_SETINT.\n  */\n-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n-\t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n+static int action;\n \n #define TYPE_BOOL (1<<0)\n #define TYPE_INT (1<<1)\n@@ -69,20 +70,20 @@ static struct option builtin_config_options[] = {\n \tOPT_STRING('f', \"file\", &given_config_source.file, N_(\"file\"), N_(\"use given config file\")),\n \tOPT_STRING(0, \"blob\", &given_config_source.blob, N_(\"blob-id\"), N_(\"read config from given blob object\")),\n \tOPT_GROUP(N_(\"Action\")),\n-\tOPT_BIT(0, \"get\", &actions, N_(\"get value: name [value-regex]\"), ACTION_GET),\n-\tOPT_BIT(0, \"get-all\", &actions, N_(\"get all values: key [value-regex]\"), ACTION_GET_ALL),\n-\tOPT_BIT(0, \"get-regexp\", &actions, N_(\"get values for regexp: name-regex [value-regex]\"), ACTION_GET_REGEXP),\n-\tOPT_BIT(0, \"get-urlmatch\", &actions, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n-\tOPT_BIT(0, \"replace-all\", &actions, N_(\"replace all matching variables: name value [value_regex]\"), ACTION_REPLACE_ALL),\n-\tOPT_BIT(0, \"add\", &actions, N_(\"add a new variable: name value\"), ACTION_ADD),\n-\tOPT_BIT(0, \"unset\", &actions, N_(\"remove a variable: name [value-regex]\"), ACTION_UNSET),\n-\tOPT_BIT(0, \"unset-all\", &actions, N_(\"remove all matches: name [value-regex]\"), ACTION_UNSET_ALL),\n-\tOPT_BIT(0, \"rename-section\", &actions, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n-\tOPT_BIT(0, \"remove-section\", &actions, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n-\tOPT_BIT('l', \"list\", &actions, N_(\"list all\"), ACTION_LIST),\n-\tOPT_BIT('e', \"edit\", &actions, N_(\"open an editor\"), ACTION_EDIT),\n-\tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n-\tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n+\tOPT_SET_INT(0, \"get\", &action, N_(\"get value: name [value-regex]\"), ACTION_GET),\n+\tOPT_SET_INT(0, \"get-all\", &action, N_(\"get all values: key [value-regex]\"), ACTION_GET_ALL),\n+\tOPT_SET_INT(0, \"get-regexp\", &action, N_(\"get values for regexp: name-regex [value-regex]\"), ACTION_GET_REGEXP),\n+\tOPT_SET_INT(0, \"get-urlmatch\", &action, N_(\"get value specific for the URL: section[.var] URL\"), ACTION_GET_URLMATCH),\n+\tOPT_SET_INT(0, \"replace-all\", &action, N_(\"replace all matching variables: name value [value_regex]\"), ACTION_REPLACE_ALL),\n+\tOPT_SET_INT(0, \"add\", &action, N_(\"add a new variable: name value\"), ACTION_ADD),\n+\tOPT_SET_INT(0, \"unset\", &action, N_(\"remove a variable: name [value-regex]\"), ACTION_UNSET),\n+\tOPT_SET_INT(0, \"unset-all\", &action, N_(\"remove all matches: name [value-regex]\"), ACTION_UNSET_ALL),\n+\tOPT_SET_INT(0, \"rename-section\", &action, N_(\"rename section: old-name new-name\"), ACTION_RENAME_SECTION),\n+\tOPT_SET_INT(0, \"remove-section\", &action, N_(\"remove a section: name\"), ACTION_REMOVE_SECTION),\n+\tOPT_SET_INT('l', \"list\", &action, N_(\"list all\"), ACTION_LIST),\n+\tOPT_SET_INT('e', \"edit\", &action, N_(\"open an editor\"), ACTION_EDIT),\n+\tOPT_SET_INT(0, \"get-color\", &action, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n+\tOPT_SET_INT(0, \"get-colorbool\", &action, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n \tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n@@ -571,40 +572,46 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif (types &&\n+\t    (action == ACTION_GET_COLOR ||\n+\t     action == ACTION_GET_COLORBOOL)) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n-\tif (HAS_MULTI_BITS(actions)) {\n-\t\terror(\"only one action at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\tif (actions == 0)\n+\tif (action == ACTION_NONE) {\n \t\tswitch (argc) {\n-\t\tcase 1: actions = ACTION_GET; break;\n-\t\tcase 2: actions = ACTION_SET; break;\n-\t\tcase 3: actions = ACTION_SET_ALL; break;\n+\t\tcase 1: action = ACTION_GET; break;\n+\t\tcase 2: action = ACTION_SET; break;\n+\t\tcase 3: action = ACTION_SET_ALL; break;\n \t\tdefault:\n \t\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t\t}\n+\t}\n \tif (omit_values &&\n-\t    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {\n+\t    !(action == ACTION_LIST ||\n+\t      action == ACTION_GET_REGEXP)) {\n \t\terror(\"--name-only is only applicable to --list or --get-regexp\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n-\tif (show_origin && !(actions &\n-\t\t(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {\n+\tif (show_origin &&\n+\t    !(action == ACTION_GET ||\n+\t      action == ACTION_GET_ALL ||\n+\t      action == ACTION_GET_REGEXP ||\n+\t      action == ACTION_LIST)) {\n \t\terror(\"--show-origin is only applicable to --get, --get-all, \"\n \t\t\t  \"--get-regexp, and --list.\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\n \n-\tif (actions & PAGING_ACTIONS)\n+\tif (action == ACTION_LIST ||\n+\t    action == ACTION_GET_ALL ||\n+\t    action == ACTION_GET_REGEXP ||\n+\t    action == ACTION_GET_URLMATCH)\n \t\tsetup_auto_pager(\"config\", 1);\n \n-\tif (actions == ACTION_LIST) {\n+\tif (action == ACTION_LIST) {\n \t\tcheck_argc(argc, 0, 0);\n \t\tif (config_with_options(show_all_config, NULL,\n \t\t\t\t\t&given_config_source,\n@@ -616,7 +623,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t\tdie(\"error processing config file(s)\");\n \t\t}\n \t}\n-\telse if (actions == ACTION_EDIT) {\n+\telse if (action == ACTION_EDIT) {\n \t\tchar *config_file;\n \n \t\tcheck_argc(argc, 0, 0);\n@@ -644,7 +651,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tlaunch_editor(config_file, NULL, NULL);\n \t\tfree(config_file);\n \t}\n-\telse if (actions == ACTION_SET) {\n+\telse if (action == ACTION_SET) {\n \t\tint ret;\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n@@ -656,7 +663,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t\"       Use a regexp, --add or --replace-all to change %s.\"), argv[0]);\n \t\treturn ret;\n \t}\n-\telse if (actions == ACTION_SET_ALL) {\n+\telse if (action == ACTION_SET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n@@ -664,7 +671,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value, argv[2], 0);\n \t}\n-\telse if (actions == ACTION_ADD) {\n+\telse if (action == ACTION_ADD) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n@@ -673,7 +680,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t\t\t      argv[0], value,\n \t\t\t\t\t\t\t      CONFIG_REGEX_NONE, 0);\n \t}\n-\telse if (actions == ACTION_REPLACE_ALL) {\n+\telse if (action == ACTION_REPLACE_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 3);\n \t\tvalue = normalize_value(argv[0], argv[1]);\n@@ -681,27 +688,27 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], value, argv[2], 1);\n \t}\n-\telse if (actions == ACTION_GET) {\n+\telse if (action == ACTION_GET) {\n \t\tcheck_argc(argc, 1, 2);\n \t\treturn get_value(argv[0], argv[1]);\n \t}\n-\telse if (actions == ACTION_GET_ALL) {\n+\telse if (action == ACTION_GET_ALL) {\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n \t\treturn get_value(argv[0], argv[1]);\n \t}\n-\telse if (actions == ACTION_GET_REGEXP) {\n+\telse if (action == ACTION_GET_REGEXP) {\n \t\tshow_keys = 1;\n \t\tuse_key_regexp = 1;\n \t\tdo_all = 1;\n \t\tcheck_argc(argc, 1, 2);\n \t\treturn get_value(argv[0], argv[1]);\n \t}\n-\telse if (actions == ACTION_GET_URLMATCH) {\n+\telse if (action == ACTION_GET_URLMATCH) {\n \t\tcheck_argc(argc, 2, 2);\n \t\treturn get_urlmatch(argv[0], argv[1]);\n \t}\n-\telse if (actions == ACTION_UNSET) {\n+\telse if (action == ACTION_UNSET) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n@@ -711,13 +718,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\t\treturn git_config_set_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t     argv[0], NULL);\n \t}\n-\telse if (actions == ACTION_UNSET_ALL) {\n+\telse if (action == ACTION_UNSET_ALL) {\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 2);\n \t\treturn git_config_set_multivar_in_file_gently(given_config_source.file,\n \t\t\t\t\t\t\t      argv[0], NULL, argv[1], 1);\n \t}\n-\telse if (actions == ACTION_RENAME_SECTION) {\n+\telse if (action == ACTION_RENAME_SECTION) {\n \t\tint ret;\n \t\tcheck_write();\n \t\tcheck_argc(argc, 2, 2);\n@@ -728,7 +735,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tif (ret == 0)\n \t\t\tdie(\"No such section!\");\n \t}\n-\telse if (actions == ACTION_REMOVE_SECTION) {\n+\telse if (action == ACTION_REMOVE_SECTION) {\n \t\tint ret;\n \t\tcheck_write();\n \t\tcheck_argc(argc, 1, 1);\n@@ -739,11 +746,11 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tif (ret == 0)\n \t\t\tdie(\"No such section!\");\n \t}\n-\telse if (actions == ACTION_GET_COLOR) {\n+\telse if (action == ACTION_GET_COLOR) {\n \t\tcheck_argc(argc, 1, 2);\n \t\tget_color(argv[0], argv[1]);\n \t}\n-\telse if (actions == ACTION_GET_COLORBOOL) {\n+\telse if (action == ACTION_GET_COLORBOOL) {\n \t\tcheck_argc(argc, 1, 2);\n \t\tif (argc == 2)\n \t\t\tcolor_stdout_is_tty = git_config_bool(\"command line\", argv[1]);\n-- \n2.17.0.rc2.594.gdb94a0ce02\n\n"},{"id":"343432","messageId":"xmqqmuyp4kqi.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180330135315.GE29568@sigill.intra.peff.net","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-30T16:00:05Z","receivedAt":"2018-03-30T16:00:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> ... But actually, last-one-wins applies only\n> to a _single_ option, not necessarily unrelated ones. Many other\n> multi-action commands actually have a series of separate boolean flags,\n> and then complain when more than one of the flags is set.\n>\n> So maybe it's not such a good idea for the actions (I do still think\n> it's the right path for the types).\n\nIf this were using command verbs (e.g. \"git config get foo.bar\") as\nopposed to command options (e.g. \"git config --get foo.bar\"), it\nwouldn't ahve allowed multiple command verbs from the command line,\nand last-one-wins would not have made much sense because there is no\nway to trigger it as a desirable \"feature\".\n\nJust like the topic of the discussion unifies --int/--bool/etc. into\na single --type={int,bool,...}, perhaps the existing command options\n--get/--list/etc. can be taken as if they were a mistaken historical\nway to spell --action={get,list,...}.  I of course am not recommending\nto add a new \"--action\" option.  I am suggesting it as a thought-aid\nto see if actions are all that different from value type options.\n\nI agree that a-bit-per-type that is checked with HAS_MULTI_BITS()\nfor error at the end does not make much sense.  I also think what\nyou did in this patch for actions is a good clean-up for the above\nreason.\n"},{"id":"343445","messageId":"20180330182713.GA30349@sigill.intra.peff.net","threadId":"48180","inReplyTo":"xmqqmuyp4kqi.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-03-30T18:27:13Z","receivedAt":"2018-03-30T18:27:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Mar 30, 2018 at 09:00:05AM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > ... But actually, last-one-wins applies only\n> > to a _single_ option, not necessarily unrelated ones. Many other\n> > multi-action commands actually have a series of separate boolean flags,\n> > and then complain when more than one of the flags is set.\n> >\n> > So maybe it's not such a good idea for the actions (I do still think\n> > it's the right path for the types).\n> \n> If this were using command verbs (e.g. \"git config get foo.bar\") as\n> opposed to command options (e.g. \"git config --get foo.bar\"), it\n> wouldn't ahve allowed multiple command verbs from the command line,\n> and last-one-wins would not have made much sense because there is no\n> way to trigger it as a desirable \"feature\".\n> \n> Just like the topic of the discussion unifies --int/--bool/etc. into\n> a single --type={int,bool,...}, perhaps the existing command options\n> --get/--list/etc. can be taken as if they were a mistaken historical\n> way to spell --action={get,list,...}.  I of course am not recommending\n> to add a new \"--action\" option.  I am suggesting it as a thought-aid\n> to see if actions are all that different from value type options.\n> \n> I agree that a-bit-per-type that is checked with HAS_MULTI_BITS()\n> for error at the end does not make much sense.  I also think what\n> you did in this patch for actions is a good clean-up for the above\n> reason.\n\nI agree the code internally is nicer after the patch. If we throw away\nthe argument of \"last-one-wins is more consistent with other parts of\ngit\" (because I don't really think that it is for this type of option),\nI could possibly buy this as a code cleanup. But it does have a\nuser-visible impact, which makes the question: are we _OK_ with\nswitching to last-one-wins?\n\n-Peff\n"},{"id":"343778","messageId":"20180404060743.39278-1-me@ttaylorr.com","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v3 0/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-04T06:07:41Z","receivedAt":"2018-04-04T06:08:10Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nHere is a v3 of a series to (1) treat type specifiers in `git config` as\nsingulars rather than a collection of bits, and (2) to prefer --type=<t> over\n--<t>.\n\nI have made the following changes since v2:\n\n  - Fix some misspellings in Documentation/git-config.txt (cc: René).\n  - Handle --no-type correctly (cc: Peff).\n  - No longer prefer (1 << x) style for enumerating type specifiers (cc: Peff).\n  - Fix awkward rebase (cc: Peff).\n\nThanks as always for your review :-).\n\n\nThanks,\nTaylor\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: prefer `--type=bool` over `--bool`, etc.\n\n Documentation/git-config.txt | 66 ++++++++++++++++---------------\n builtin/config.c             | 77 +++++++++++++++++++++++-------------\n t/t1300-repo-config.sh       | 29 ++++++++++++++\n 3 files changed, 113 insertions(+), 59 deletions(-)\n\n--\n2.16.2.440.gc6284da4f\n\n"},{"id":"343779","messageId":"20180404060743.39278-2-me@ttaylorr.com","threadId":"48180","inReplyTo":"20180404060743.39278-1-me@ttaylorr.com","subject":"[PATCH v3 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-04T06:07:42Z","receivedAt":"2018-04-04T06:08:21Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd62..92fb8d56b 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n \n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n \n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fd..24de37d54 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343780","messageId":"20180404060743.39278-3-me@ttaylorr.com","threadId":"48180","inReplyTo":"20180404060743.39278-1-me@ttaylorr.com","subject":"[PATCH v3 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-04T06:07:43Z","receivedAt":"2018-04-04T06:08:31Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to add extend this functionality with\n`--color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n`--int`, `--bool`, and etc. This allows the aforementioned other patch\nto add `--color` (in the non-traditional sense) via `--type=color`,\ninstead of `--color`.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 66 +++++++++++++++++++-----------------\n builtin/config.c             | 28 +++++++++++++++\n t/t1300-repo-config.sh       | 18 ++++++++++\n 3 files changed, 80 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..869999c9b 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type] --add name value\n+'git config' [<file-option>] [--type] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+A type specifier may be given as an argument to `--type` to make 'git config'\n+ensure that the variable(s) are of the given type and convert the value to the\n+canonical form. If no type specifier is passed, no checks or transformations are\n+performed on the value.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,34 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type [type]::\n+  'git config' will ensure that any input output is valid under the given type\n+  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n+  form.\n++\n+Valid `[type]`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b..942fcf4bd 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,33 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (unset) {\n+\t\t*type = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse {\n+\t\tdie(_(\"unexpected --type argument, %s\"), arg);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +111,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d54..5f9a83725 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n \n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" > expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unexpected --type argument\" error\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343781","messageId":"CAPig+cR4uFiC_gFsb2e9JR6SdP-wUQVz-E0MjRHR=vNHd+hvhA@mail.gmail.com","threadId":"48180","inReplyTo":"20180404060743.39278-3-me@ttaylorr.com","subject":"Re: [PATCH v3 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-04T07:27:48Z","receivedAt":"2018-04-04T07:27:54Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 4, 2018 at 2:07 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> `git config` has long allowed the ability for callers to provide a 'type\n> specifier', which instructs `git config` to (1) ensure that incoming\n> values are satisfiable under that type, and (2) that outgoing values are\n> canonicalized under that type.\n>\n> In another series, we propose to add extend this functionality with\n> `--color` and `--default` to replace `--get-color`.\n>\n> However, we traditionally use `--color` to mean \"colorize this output\",\n> instead of \"this value should be treated as a color\".\n>\n> Currently, `git config` does not support this kind of colorization, but\n> we should be careful to avoid inhabiting this option too soon, so that\n> `git config` can support `--color` (in the traditional sense) in the\n> future, if that is desired.\n>\n> In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> `--int`, `--bool`, and etc. This allows the aforementioned other patch\n> to add `--color` (in the non-traditional sense) via `--type=color`,\n> instead of `--color`.\n\nI always find this last sentence confusing since it's not clear to\nwhich ilk of \"--color\" option you refer. Perhaps say instead something\nlike:\n\n    This normalization will allow the aforementioned upcoming patch to\n    support querying a color value with a default via \"--type=color\n    --default=...\".\n\n> Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> ---\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> @@ -160,30 +158,34 @@ See also <<FILES>>.\n> +--type [type]::\n> +  'git config' will ensure that any input output is valid under the given type\n> +  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n> +  form.\n\nDo the brackets in \"[type]\" mean that the argument is optional? If so,\nwhat does 'type' default to when not specified? The documentation\nshould discuss this.\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> @@ -61,6 +61,33 @@ static int show_origin;\n> +static int option_parse_type(const struct option *opt, const char *arg,\n> +                            int unset)\n> +{\n> +       [...]\n> +       if (!strcmp(arg, \"bool\"))\n> +               *type = TYPE_BOOL;\n> +       else if (!strcmp(arg, \"int\"))\n> +               *type = TYPE_INT;\n> +       else if (!strcmp(arg, \"bool-or-int\"))\n> +               *type = TYPE_BOOL_OR_INT;\n> +       else if (!strcmp(arg, \"path\"))\n> +               *type = TYPE_PATH;\n> +       else if (!strcmp(arg, \"expiry-date\"))\n> +               *type = TYPE_EXPIRY_DATE;\n> +       else {\n> +               die(_(\"unexpected --type argument, %s\"), arg);\n\n\"unexpected\" doesn't seem like the best word choice since an argument\nto --type _is_ expected. Perhaps you mean \"unrecognized\"?\n\n> +               return 1;\n> +       }\n> +       return 0;\n> +}\n"},{"id":"343783","messageId":"CAPig+cRNSYz_guBS9yNEXdAbfG+FWtvnzwsWRu0DRe0N_VkiOQ@mail.gmail.com","threadId":"48180","inReplyTo":"20180404060743.39278-2-me@ttaylorr.com","subject":"Re: [PATCH v3 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-04T07:57:12Z","receivedAt":"2018-04-04T07:57:18Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 4, 2018 at 2:07 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> [...]\n> In fact, `git config` will not accept multiple type specifiers at a\n> time, as indicated by:\n>\n>   $ git config --int --bool some.section\n>   error: only one type at a time.\n>\n> This patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\n> specifier, so that the above command would instead be valid, and a\n> synonym of:\n>\n>   $ git config --bool some.section\n>\n> This change is motivated by two urges: (1) it does not make sense to\n> represent a singular type specifier internally as a bitset, only to\n> complain when there are multiple bits in the set. `OPT_SET_INT` is more\n> well-suited to this task than `OPT_BIT` is. (2) a future patch will\n> introduce `--type=<type>`, and we would like not to complain in the\n> following situation:\n>\n>   $ git config --int --type=int\n\nI can understand \"last wins\" for related options such as \"--verbose\n--quiet\" or \"--verbose=1 --verbose=2\", but I have a lot of trouble\nbuying the \"last wins\" argument in this context where there is no\nsemantic relationship between, say, --bool and --expiry-date.\nSpecifying conflicting options together is likely to be unintentional,\nthus reporting it as an error seems sensible, so I'm not convinced\nthat patch's removal of that error is a good idea.\n\nI understand that you're doing this to avoid complaining about \"--int\n--type=int\", but exactly how that case is supported should be an\nimplementation detail; it doesn't need to bleed into the UI as an\nunnecessary and not-well-justified loosening of an age-old\nrestriction. There are other ways to support \"--int --type=int\"\nwithout \"last wins\". Also, do we really need to support \"--int\n--type=int\"? Is that an expected use-case?\n"},{"id":"343827","messageId":"20180405014758.GA4671@syl.local","threadId":"48180","inReplyTo":"CAPig+cR4uFiC_gFsb2e9JR6SdP-wUQVz-E0MjRHR=vNHd+hvhA@mail.gmail.com","subject":"Re: [PATCH v3 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T01:47:58Z","receivedAt":"2018-04-05T01:48:06Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Apr 04, 2018 at 03:27:48AM -0400, Eric Sunshine wrote:\n> On Wed, Apr 4, 2018 at 2:07 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> > In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> > `--int`, `--bool`, and etc. This allows the aforementioned other patch\n> > to add `--color` (in the non-traditional sense) via `--type=color`,\n> > instead of `--color`.\n>\n> I always find this last sentence confusing since it's not clear to\n> which ilk of \"--color\" option you refer. Perhaps say instead something\n> like:\n>\n>     This normalization will allow the aforementioned upcoming patch to\n>     support querying a color value with a default via \"--type=color\n>     --default=...\".\n\nI agree that this is much clearer. I have amended this patch to include\nyour wording here.\n\n> > Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> > ---\n> > diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> > @@ -160,30 +158,34 @@ See also <<FILES>>.\n> > +--type [type]::\n> > +  'git config' will ensure that any input output is valid under the given type\n> > +  constraint(s), and will canonicalize outgoing values in `[type]`'s canonical\n> > +  form.\n>\n> Do the brackets in \"[type]\" mean that the argument is optional? If so,\n> what does 'type' default to when not specified? The documentation\n> should discuss this.\n\nThis is my mistake; I was unaware of the semantic difference between '[]'\nand '<>'. If my understanding is correct that '<>' means \"required\" and\n'[]' means \"optional\", than this is a misspelling of \"<type>\".\n\nI have addressed this during the re-roll.\n\n> > diff --git a/builtin/config.c b/builtin/config.c\n> > @@ -61,6 +61,33 @@ static int show_origin;\n> > +static int option_parse_type(const struct option *opt, const char *arg,\n> > +                            int unset)\n> > +{\n> > +       [...]\n> > +       if (!strcmp(arg, \"bool\"))\n> > +               *type = TYPE_BOOL;\n> > +       else if (!strcmp(arg, \"int\"))\n> > +               *type = TYPE_INT;\n> > +       else if (!strcmp(arg, \"bool-or-int\"))\n> > +               *type = TYPE_BOOL_OR_INT;\n> > +       else if (!strcmp(arg, \"path\"))\n> > +               *type = TYPE_PATH;\n> > +       else if (!strcmp(arg, \"expiry-date\"))\n> > +               *type = TYPE_EXPIRY_DATE;\n> > +       else {\n> > +               die(_(\"unexpected --type argument, %s\"), arg);\n>\n> \"unexpected\" doesn't seem like the best word choice since an argument\n> to --type _is_ expected. Perhaps you mean \"unrecognized\"?\n\nSure; I think \"unrecognized\" is a much better fit. I have updated this\nin the re-roll.\n\nThanks,\nTaylor\n"},{"id":"343828","messageId":"20180405015304.GB4671@syl.local","threadId":"48180","inReplyTo":"CAPig+cRNSYz_guBS9yNEXdAbfG+FWtvnzwsWRu0DRe0N_VkiOQ@mail.gmail.com","subject":"Re: [PATCH v3 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T01:53:04Z","receivedAt":"2018-04-05T01:53:10Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Apr 04, 2018 at 03:57:12AM -0400, Eric Sunshine wrote:\n> On Wed, Apr 4, 2018 at 2:07 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> > [...]\n> > In fact, `git config` will not accept multiple type specifiers at a\n> > time, as indicated by:\n> >\n> >   $ git config --int --bool some.section\n> >   error: only one type at a time.\n> >\n> > This patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\n> > specifier, so that the above command would instead be valid, and a\n> > synonym of:\n> >\n> >   $ git config --bool some.section\n> >\n> > This change is motivated by two urges: (1) it does not make sense to\n> > represent a singular type specifier internally as a bitset, only to\n> > complain when there are multiple bits in the set. `OPT_SET_INT` is more\n> > well-suited to this task than `OPT_BIT` is. (2) a future patch will\n> > introduce `--type=<type>`, and we would like not to complain in the\n> > following situation:\n> >\n> >   $ git config --int --type=int\n>\n> I can understand \"last wins\" for related options such as \"--verbose\n> --quiet\" or \"--verbose=1 --verbose=2\", but I have a lot of trouble\n> buying the \"last wins\" argument in this context where there is no\n> semantic relationship between, say, --bool and --expiry-date.\n> Specifying conflicting options together is likely to be unintentional,\n> thus reporting it as an error seems sensible, so I'm not convinced\n> that patch's removal of that error is a good idea.\n>\n> I understand that you're doing this to avoid complaining about \"--int\n> --type=int\", but exactly how that case is supported should be an\n> implementation detail; it doesn't need to bleed into the UI as an\n> unnecessary and not-well-justified loosening of an age-old\n> restriction. There are other ways to support \"--int --type=int\"\n> without \"last wins\". Also, do we really need to support \"--int\n> --type=int\"? Is that an expected use-case?\n\nThis is a fair concern, though I view the problem slightly differently.\nI see this change as being motivated by the fact that we are adding\n\"--type\", not removing an age-old restriction.\n\nPeff's motivation for this--as I understand it--is that \"--type=int\"\nshould be a _true_ synonym for \"--int\". Adopting the old-style\n\"OPT_SET_BIT\" for this purpose makes \"--type=int\" and \"--int\" _mostly_ a\nsynonym for one another, except that \"--type=bool --type=int\" will not\ncomplain, whereas \"--bool --int\" would.\n\nLoosening this restriction, in my view, brings us closer (1) to the new\nsemantics of multiple \"--type\"'s, and (2) to the existing semantics of\n\"--verbose=1 --verbose=2\" as you mentioned above.\n\nI would like to hear Peff's take on this as well, since he suggested the\npatch originally, and would likely provide the clearest insight into\nthis.\n\n\nThanks,\nTaylor\n"},{"id":"343829","messageId":"20180405020034.GA8879@syl.local","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v4 0/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T02:00:34Z","receivedAt":"2018-04-05T02:00:43Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nI have attached a fourth re-roll of my series to introduce\n\"--type=<type>\" in \"git config\", and prefer it to \"--<type>\".\n\nIn particular, since the last update, I have changed the following:\n\n  - Clearer wording in the second patch per Eric's suggestion.\n\n  - Stopped spelling the required argument to \"--type=\" as \"[type]\", and\n    instead as \"<type>\" (cc: Eric).\n\n  - Changed \"unexpected\" to \"unrecognized\" in the fatal message when we\n    don't know how to interpret the argument to \"--type\".\n\nThanks,\nTaylor\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: prefer `--type=bool` over `--bool`, etc.\n\n Documentation/git-config.txt | 66 ++++++++++++++++---------------\n builtin/config.c             | 77 +++++++++++++++++++++++-------------\n t/t1300-repo-config.sh       | 29 ++++++++++++++\n 3 files changed, 113 insertions(+), 59 deletions(-)\n\n--\n2.16.2.440.gc6284da4f\n"},{"id":"343830","messageId":"20180405020037.GB8879@syl.local","threadId":"48180","inReplyTo":"cover.1522893363.git.me@ttaylorr.com","subject":"[PATCH v4 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T02:00:37Z","receivedAt":"2018-04-05T02:00:46Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd62..92fb8d56b 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n \n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n \n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fd..24de37d54 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n\n"},{"id":"343831","messageId":"20180405020238.GD8879@syl.local","threadId":"48180","inReplyTo":"cover.1522893363.git.me@ttaylorr.com","subject":"[PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T02:02:38Z","receivedAt":"2018-04-05T02:02:45Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to add extend this functionality with\n`--color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n`--int`, `--bool`, and etc. This allows the aforementioned upcoming\npatch to support querying a color value with a default via `--type=color\n--default=....`\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 66 +++++++++++++++++++-----------------\n builtin/config.c             | 28 +++++++++++++++\n t/t1300-repo-config.sh       | 18 ++++++++++\n 3 files changed, 80 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..c7e56e3fd 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type] --add name value\n+'git config' [<file-option>] [--type] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+A type specifier may be given as an argument to `--type` to make 'git config'\n+ensure that the variable(s) are of the given type and convert the value to the\n+canonical form. If no type specifier is passed, no checks or transformations are\n+performed on the value.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,34 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input output is valid under the given type\n+  constraint(s), and will canonicalize outgoing values in `<type>`'s canonical\n+  form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b..6e1495eac 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,33 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (unset) {\n+\t\t*type = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse {\n+\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +111,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d54..b25ab7b9e 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n \n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" > expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n"},{"id":"343832","messageId":"20180405020040.GC8879@syl.local","threadId":"48180","inReplyTo":"cover.1522893363.git.me@ttaylorr.com","subject":"[PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T02:00:40Z","receivedAt":"2018-04-05T02:06:57Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to add extend this functionality with\n`--color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n`--int`, `--bool`, and etc. This allows the aforementioned upcoming\npatch to support querying a color value with a default via `--type=color\n--default=....`\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 66 +++++++++++++++++++-----------------\n builtin/config.c             | 28 +++++++++++++++\n t/t1300-repo-config.sh       | 18 ++++++++++\n 3 files changed, 80 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..c7e56e3fd 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type] --add name value\n+'git config' [<file-option>] [--type] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+A type specifier may be given as an argument to `--type` to make 'git config'\n+ensure that the variable(s) are of the given type and convert the value to the\n+canonical form. If no type specifier is passed, no checks or transformations are\n+performed on the value.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,34 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input output is valid under the given type\n+  constraint(s), and will canonicalize outgoing values in `<type>`'s canonical\n+  form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b..6e1495eac 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,33 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (unset) {\n+\t\t*type = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse {\n+\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +111,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d54..b25ab7b9e 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n \n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" > expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n-- \n2.16.2.440.gc6284da4f\n"},{"id":"343900","messageId":"20180405215136.GA29117@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180405015304.GB4671@syl.local","subject":"Re: [PATCH v3 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-04-05T21:51:36Z","receivedAt":"2018-04-05T21:51:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 04, 2018 at 06:53:04PM -0700, Taylor Blau wrote:\n\n> > I understand that you're doing this to avoid complaining about \"--int\n> > --type=int\", but exactly how that case is supported should be an\n> > implementation detail; it doesn't need to bleed into the UI as an\n> > unnecessary and not-well-justified loosening of an age-old\n> > restriction. There are other ways to support \"--int --type=int\"\n> > without \"last wins\". Also, do we really need to support \"--int\n> > --type=int\"? Is that an expected use-case?\n> \n> This is a fair concern, though I view the problem slightly differently.\n> I see this change as being motivated by the fact that we are adding\n> \"--type\", not removing an age-old restriction.\n> \n> Peff's motivation for this--as I understand it--is that \"--type=int\"\n> should be a _true_ synonym for \"--int\". Adopting the old-style\n> \"OPT_SET_BIT\" for this purpose makes \"--type=int\" and \"--int\" _mostly_ a\n> synonym for one another, except that \"--type=bool --type=int\" will not\n> complain, whereas \"--bool --int\" would.\n> \n> Loosening this restriction, in my view, brings us closer (1) to the new\n> semantics of multiple \"--type\"'s, and (2) to the existing semantics of\n> \"--verbose=1 --verbose=2\" as you mentioned above.\n> \n> I would like to hear Peff's take on this as well, since he suggested the\n> patch originally, and would likely provide the clearest insight into\n> this.\n\nI think you've captured it fairly well.  The options _are_ semantically\nlinked, in that they are all mutually-exclusive types. Obviously we\ncould continue to flag errors, and even catch \"--type=int --type=bool\"\nin the same way if we really wanted to (by using a custom parse-options\ncallback).\n\nSo I think the primary value here is in the code cleanup. Even without\nthe new \"--type\" option, avoiding the bitset makes the code clearer\n(IMHO).\n\nI do agree that a user saying \"--int --bool\" is almost certainly an\nerror, and they'd be just as happy to see an error message as to get the\nlast-one-wins behavior. But I also doubt that it comes up very much\neither way.\n\n-Peff\n"},{"id":"343901","messageId":"20180405215759.GB29117@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180405020034.GA8879@syl.local","subject":"Re: [PATCH v4 0/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-04-05T21:58:00Z","receivedAt":"2018-04-05T21:58:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 04, 2018 at 07:00:34PM -0700, Taylor Blau wrote:\n\n> I have attached a fourth re-roll of my series to introduce\n> \"--type=<type>\" in \"git config\", and prefer it to \"--<type>\".\n> \n> In particular, since the last update, I have changed the following:\n> \n>   - Clearer wording in the second patch per Eric's suggestion.\n> \n>   - Stopped spelling the required argument to \"--type=\" as \"[type]\", and\n>     instead as \"<type>\" (cc: Eric).\n> \n>   - Changed \"unexpected\" to \"unrecognized\" in the fatal message when we\n>     don't know how to interpret the argument to \"--type\".\n\nThis iteration looks good to me, assuming that last-one-wins is still\nthe direction we want to go. I'm open to the notion that the cleanup is\nnot worth the change in behavior. It is IMHO, but obviously it's\nsomewhat subjective.\n\n-Peff\n"},{"id":"343902","messageId":"20180405221201.GA29929@sigill.intra.peff.net","threadId":"48180","inReplyTo":"20180405020238.GD8879@syl.local","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-04-05T22:12:02Z","receivedAt":"2018-04-05T22:12:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 04, 2018 at 07:02:38PM -0700, Taylor Blau wrote:\n\n> +test_expect_success '--no-type unsets type specifiers' '\n> +\techo \"10\" > expect &&\n> +\tgit config --type=bool --no-type core.number >actual &&\n> +\ttest_cmp expect actual\n> +'\n\nActually, one minor nit (not worth a re-roll, but Junio may want to mark\nit up): drop the space in \"> expect\".\n\n-Peff\n"},{"id":"343903","messageId":"20180405221500.GA87758@syl.local","threadId":"48180","inReplyTo":"20180405221201.GA29929@sigill.intra.peff.net","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T22:15:00Z","receivedAt":"2018-04-05T22:15:07Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 05, 2018 at 06:12:02PM -0400, Jeff King wrote:\n> On Wed, Apr 04, 2018 at 07:02:38PM -0700, Taylor Blau wrote:\n>\n> > +test_expect_success '--no-type unsets type specifiers' '\n> > +\techo \"10\" > expect &&\n> > +\tgit config --type=bool --no-type core.number >actual &&\n> > +\ttest_cmp expect actual\n> > +'\n>\n> Actually, one minor nit (not worth a re-roll, but Junio may want to mark\n> it up): drop the space in \"> expect\".\n\nAck; I thought I picked this one up. I am happy to re-roll it, but maybe\nit makes to amend while queueing.\n\nThanks,\nTaylor\n"},{"id":"343904","messageId":"20180405221535.GB87758@syl.local","threadId":"48180","inReplyTo":"20180405215759.GB29117@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-05T22:15:35Z","receivedAt":"2018-04-05T22:15:41Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 05, 2018 at 05:58:00PM -0400, Jeff King wrote:\n> On Wed, Apr 04, 2018 at 07:00:34PM -0700, Taylor Blau wrote:\n>\n> > I have attached a fourth re-roll of my series to introduce\n> > \"--type=<type>\" in \"git config\", and prefer it to \"--<type>\".\n> >\n> > In particular, since the last update, I have changed the following:\n> >\n> >   - Clearer wording in the second patch per Eric's suggestion.\n> >\n> >   - Stopped spelling the required argument to \"--type=\" as \"[type]\", and\n> >     instead as \"<type>\" (cc: Eric).\n> >\n> >   - Changed \"unexpected\" to \"unrecognized\" in the fatal message when we\n> >     don't know how to interpret the argument to \"--type\".\n>\n> This iteration looks good to me, assuming that last-one-wins is still\n> the direction we want to go. I'm open to the notion that the cleanup is\n> not worth the change in behavior. It is IMHO, but obviously it's\n> somewhat subjective.\n\nI am too, unless people on this thread have strong feelings otherwise.\n\nThanks,\nTaylor\n"},{"id":"343905","messageId":"CAPig+cSr744Y293qvgLG8jLHdNsGypkHU6QUQ-AcOyk=-JAbDw@mail.gmail.com","threadId":"48180","inReplyTo":"20180405020040.GC8879@syl.local","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-05T22:29:18Z","receivedAt":"2018-04-05T22:29:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 4, 2018 at 10:00 PM, Taylor Blau <me@ttaylorr.com> wrote:\n> [...]\n> In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> `--int`, `--bool`, and etc. This allows the aforementioned upcoming\n> patch to support querying a color value with a default via `--type=color\n> --default=....`\n>\n> Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> ---\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> @@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n> +A type specifier may be given as an argument to `--type` to make 'git config'\n\nIn [1], you said that the argument to --type is required, so use of\n\"may be given\" here is ambiguous; it makes it sound as if the argument\nis optional. Perhaps rewrite something like:\n\n    The --type option requests `git config` to ...\n\nNot necessarily worth a re-roll, though. (But if you do need to\nre-roll for some reason, it might make sense to combine this series\nwith the --default series to make it slightly easier to review them\ntogether -- since the one depends upon the other -- and probably ease\nthe burden on Junio slightly.)\n\n> +ensure that the variable(s) are of the given type and convert the value to the\n> +canonical form. If no type specifier is passed, no checks or transformations are\n> +performed on the value.\n> @@ -160,30 +158,34 @@ See also <<FILES>>.\n>  --list::\n>         List all variables set in config file, along with their values.\n>\n> ---bool::\n> -       'git config' will ensure that the output is \"true\" or \"false\"\n> +--type <type>::\n> +  'git config' will ensure that any input output is valid under the given type\n> +  constraint(s), and will canonicalize outgoing values in `<type>`'s canonical\n> +  form.\n\nIn response to my question[2] about whether the typesetting \"[type]\"\nmeant that it was optional, you responded[1] that it was not, thus\ncorrectly changed the typesetting to \"<type>\". However...\n\n> diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> @@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n> +test_expect_success '--no-type unsets type specifiers' '\n> +       echo \"10\" > expect &&\n> +       git config --type=bool --no-type core.number >actual &&\n> +       test_cmp expect actual\n> +'\n\nWhat does --no-type mean and why is it being tested? If this is an\nexplicitly supported user-facing option, should it be documented? If\nit's not meant to be user-facing, then why are we enforcing its\npresence and behavior via a test?\n\n[1]: https://public-inbox.org/git/20180405014758.GA4671@syl.local/\n[2]: https://public-inbox.org/git/CAPig+cR4uFiC_gFsb2e9JR6SdP-wUQVz-E0MjRHR=vNHd+hvhA@mail.gmail.com/\n"},{"id":"343910","messageId":"20180405224051.GF29117@sigill.intra.peff.net","threadId":"48180","inReplyTo":"CAPig+cSr744Y293qvgLG8jLHdNsGypkHU6QUQ-AcOyk=-JAbDw@mail.gmail.com","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-04-05T22:40:51Z","receivedAt":"2018-04-05T22:40:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Apr 05, 2018 at 06:29:18PM -0400, Eric Sunshine wrote:\n\n> > +ensure that the variable(s) are of the given type and convert the value to the\n> > +canonical form. If no type specifier is passed, no checks or transformations are\n> > +performed on the value.\n> > @@ -160,30 +158,34 @@ See also <<FILES>>.\n> >  --list::\n> >         List all variables set in config file, along with their values.\n> >\n> > ---bool::\n> > -       'git config' will ensure that the output is \"true\" or \"false\"\n> > +--type <type>::\n> > +  'git config' will ensure that any input output is valid under the given type\n> > +  constraint(s), and will canonicalize outgoing values in `<type>`'s canonical\n> > +  form.\n> \n> In response to my question[2] about whether the typesetting \"[type]\"\n> meant that it was optional, you responded[1] that it was not, thus\n> correctly changed the typesetting to \"<type>\". However...\n\nRight, \"--type\" without a specifier means nothing (also, I missed this\nin my review, but \"input output\" in the quoted text?)\n\n> > diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> > @@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n> > +test_expect_success '--no-type unsets type specifiers' '\n> > +       echo \"10\" > expect &&\n> > +       git config --type=bool --no-type core.number >actual &&\n> > +       test_cmp expect actual\n> > +'\n> \n> What does --no-type mean and why is it being tested? If this is an\n> explicitly supported user-facing option, should it be documented? If\n> it's not meant to be user-facing, then why are we enforcing its\n> presence and behavior via a test?\n\nIt would be the same as if no --type option had been given. The current\ndocumentation says:\n\n  If no type specifier is passed, no checks or transformations are\n  performed on the value.\n\nThat's retained in the DESCRIPTION section, but it may be worth a\nmention of the \"--no-type\" behavior in the OPTIONS section, too. I\ndunno.\n\n-Peff\n"},{"id":"343924","messageId":"20180406050847.GA52220@syl.local","threadId":"48180","inReplyTo":"20180405221201.GA29929@sigill.intra.peff.net","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T05:08:47Z","receivedAt":"2018-04-06T05:09:03Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 05, 2018 at 06:12:02PM -0400, Jeff King wrote:\n> On Wed, Apr 04, 2018 at 07:02:38PM -0700, Taylor Blau wrote:\n>\n> > +test_expect_success '--no-type unsets type specifiers' '\n> > +\techo \"10\" > expect &&\n> > +\tgit config --type=bool --no-type core.number >actual &&\n> > +\ttest_cmp expect actual\n> > +'\n>\n> Actually, one minor nit (not worth a re-roll, but Junio may want to mark\n> it up): drop the space in \"> expect\".\n\nI'm re-rolling for some of the other review this round picked up, so\nI've included this in the forthcoming v5.\n\nThanks,\nTaylor\n"},{"id":"343925","messageId":"20180406051750.GA53143@syl.local","threadId":"48180","inReplyTo":"CAPig+cSr744Y293qvgLG8jLHdNsGypkHU6QUQ-AcOyk=-JAbDw@mail.gmail.com","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T05:17:50Z","receivedAt":"2018-04-06T05:29:09Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 05, 2018 at 06:29:18PM -0400, Eric Sunshine wrote:\n> On Wed, Apr 4, 2018 at 10:00 PM, Taylor Blau <me@ttaylorr.com> wrote:\n> > [...]\n> > In this patch, we prefer `--type=[int|bool|bool-or-int|...]` over\n> > `--int`, `--bool`, and etc. This allows the aforementioned upcoming\n> > patch to support querying a color value with a default via `--type=color\n> > --default=....`\n> >\n> > Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> > ---\n> > diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> > @@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n> > +A type specifier may be given as an argument to `--type` to make 'git config'\n>\n> In [1], you said that the argument to --type is required, so use of\n> \"may be given\" here is ambiguous; it makes it sound as if the argument\n> is optional. Perhaps rewrite something like:\n>\n>     The --type option requests `git config` to ...\n\nThanks; I agree that this is much clearer.\n\n> Not necessarily worth a re-roll, though. (But if you do need to\n> re-roll for some reason, it might make sense to combine this series\n> with the --default series to make it slightly easier to review them\n> together -- since the one depends upon the other -- and probably ease\n> the burden on Junio slightly.)\n\nI am re-rolling for some of the additional feedback, and have included\nthe above changes in it. That being the case, I think that I would like\nto hold off on merging the two together, since they are separate topics\nand should be merged as-such.\n\nIf this list feels differently, however, I can happily merge the two.\n\n> > diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> > @@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n> > +test_expect_success '--no-type unsets type specifiers' '\n> > +       echo \"10\" > expect &&\n> > +       git config --type=bool --no-type core.number >actual &&\n> > +       test_cmp expect actual\n> > +'\n>\n> What does --no-type mean and why is it being tested? If this is an\n> explicitly supported user-facing option, should it be documented? If\n> it's not meant to be user-facing, then why are we enforcing its\n> presence and behavior via a test?\n\nI think that --no-type should be documented. It means \"unset any\npreviously set --type=<type> or --<type>\". For example, \"git config\n--type=bool --no-type core.var\" is a synonym of \"git config core.var\".\n\nThis option is useful for callers that would like to overwrite scripts\nthat impose a specific type specifier. (For example, my mail script\ninvokes \"git format-patch --thread $@\", but I can disable threading if I\nrun my script with \"--no-thread\".)\n\nThanks,\nTaylor\n"},{"id":"343926","messageId":"20180406051914.GB53143@syl.local","threadId":"48180","inReplyTo":"20180405224051.GF29117@sigill.intra.peff.net","subject":"Re: [PATCH v4 2/2] builtin/config.c: prefer `--type=bool` over `--bool`, etc.","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T05:19:14Z","receivedAt":"2018-04-06T05:29:11Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 05, 2018 at 06:40:51PM -0400, Jeff King wrote:\n> On Thu, Apr 05, 2018 at 06:29:18PM -0400, Eric Sunshine wrote:\n>\n> > > +ensure that the variable(s) are of the given type and convert the value to the\n> > > +canonical form. If no type specifier is passed, no checks or transformations are\n> > > +performed on the value.\n> > > @@ -160,30 +158,34 @@ See also <<FILES>>.\n> > >  --list::\n> > >         List all variables set in config file, along with their values.\n> > >\n> > > ---bool::\n> > > -       'git config' will ensure that the output is \"true\" or \"false\"\n> > > +--type <type>::\n> > > +  'git config' will ensure that any input output is valid under the given type\n> > > +  constraint(s), and will canonicalize outgoing values in `<type>`'s canonical\n> > > +  form.\n> >\n> > In response to my question[2] about whether the typesetting \"[type]\"\n> > meant that it was optional, you responded[1] that it was not, thus\n> > correctly changed the typesetting to \"<type>\". However...\n>\n> Right, \"--type\" without a specifier means nothing (also, I missed this\n> in my review, but \"input output\" in the quoted text?)\n\nI missed this too; thanks for pointing it out. I have amended this in\nthe forthcoming re-roll.\n\n> > > diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> > > @@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n> > > +test_expect_success '--no-type unsets type specifiers' '\n> > > +       echo \"10\" > expect &&\n> > > +       git config --type=bool --no-type core.number >actual &&\n> > > +       test_cmp expect actual\n> > > +'\n> >\n> > What does --no-type mean and why is it being tested? If this is an\n> > explicitly supported user-facing option, should it be documented? If\n> > it's not meant to be user-facing, then why are we enforcing its\n> > presence and behavior via a test?\n>\n> It would be the same as if no --type option had been given. The current\n> documentation says:\n>\n>   If no type specifier is passed, no checks or transformations are\n>   performed on the value.\n>\n> That's retained in the DESCRIPTION section, but it may be worth a\n> mention of the \"--no-type\" behavior in the OPTIONS section, too. I\n> dunno.\n\nFair; my inclination is to document it, since it is potentially useful\nfor scripts (as I mentioned in my mail to Eric).\n\nThanks,\nTaylor\n"},{"id":"343942","messageId":"20180406063846.GA2908@syl.local","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v6 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T06:38:46Z","receivedAt":"2018-04-06T06:38:54Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nHere is v6 of this series. I have sent it in direct response to v4\ncorrectly (since v5 was sent elsewhere by mistake).\n\nIt contains responses to Eric's feedback in [1].\n\n\nThanks,\nTaylor\n\n[1]: https://public-inbox.org/git/20180324005556.8145-1-me@ttaylorr.com/T/#mab6aee239c023445fd72693728b566cad5e0fc66\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: support `--type=<type>` as preferred alias for\n    `--type`\n\n Documentation/git-config.txt | 73 +++++++++++++++++++---------------\n builtin/config.c             | 76 +++++++++++++++++++++++-------------\n t/t1300-repo-config.sh       | 29 ++++++++++++++\n 3 files changed, 119 insertions(+), 59 deletions(-)\n\n--\n2.17.0\n"},{"id":"343943","messageId":"20180406063909.GB2908@syl.local","threadId":"48180","inReplyTo":"cover.1522996619.git.me@ttaylorr.com","subject":"[PATCH v6 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T06:39:09Z","receivedAt":"2018-04-06T06:39:17Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd62..92fb8d56b 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n\n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n\n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n\n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n\n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n\n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n\n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n\n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fd..24de37d54 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n\n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n--\n2.17.0\n\n"},{"id":"343944","messageId":"20180406063915.GC2908@syl.local","threadId":"48180","inReplyTo":"cover.1522996619.git.me@ttaylorr.com","subject":"[PATCH v6 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-06T06:39:15Z","receivedAt":"2018-04-06T06:39:22Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to extend this functionality with\n`--type=color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--type=color` (in the traditional sense) in\nthe future, if that is desired.\n\nIn this patch, we support `--type=<int|bool|bool-or-int|...>` in\naddition to `--int`, `--bool`, and etc. This allows the aforementioned\nupcoming patch to support querying a color value with a default via\n`--type=color --default=...`, without squandering `--color`.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 73 ++++++++++++++++++++----------------\n builtin/config.c             | 27 +++++++++++++\n t/t1300-repo-config.sh       | 18 +++++++++\n 3 files changed, 86 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d..b7686fcbe 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] --add name value\n+'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,12 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n\n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+The `--type` option requests 'git config' to ensure that the configured values\n+associated with the given variable(s) are of the given type. When given\n+`--type`, 'git config' will ensure that the variable(s) are of the given type\n+and convert the value to the canonical form. If no type specifier is passed, no\n+checks or transformations are performed on the value. Callers may unset any\n+pre-existing type specifiers with `--no-type`.\n\n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +160,39 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n\n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input or output is valid under the given\n+  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n+  canonical form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n\n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n+\n+--no-type::\n+  Un-sets the previously set type specifier (if one was previously set). This\n+  option requests that 'git config' not canonicalize the retrieved variable.\n+  `--no-type` has no effect without `--type=<type>` or `--<type>`.\n\n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b..5c8952a17 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,32 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n\n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (unset) {\n+\t\t*type = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse\n+\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +110,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d54..f5ae80e9a 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n\n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n\n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n\n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" >expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n--\n2.17.0\n"},{"id":"343947","messageId":"CAPig+cQPB+74T3YenoZ_oVFGh2c38AbcFdc+yjw6f32Ojestwg@mail.gmail.com","threadId":"48180","inReplyTo":"20180406063915.GC2908@syl.local","subject":"Re: [PATCH v6 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-06T07:04:53Z","receivedAt":"2018-04-06T07:05:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Apr 6, 2018 at 2:39 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> [...]\n> In this patch, we support `--type=<int|bool|bool-or-int|...>` in\n> addition to `--int`, `--bool`, and etc. This allows the aforementioned\n> upcoming patch to support querying a color value with a default via\n> `--type=color --default=...`, without squandering `--color`.\n>\n> Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> ---\n> diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> @@ -38,12 +38,12 @@ existing values that match the regexp are updated or unset.  If\n> +The `--type` option requests 'git config' to ensure that the configured values\n> +associated with the given variable(s) are of the given type. When given\n> +`--type`, 'git config' will ensure that the variable(s) are of the given type\n> +and convert the value to the canonical form. If no type specifier is passed, no\n> +checks or transformations are performed on the value. Callers may unset any\n> +pre-existing type specifiers with `--no-type`.\n\nSorry for being such a stickler, but this is still too mushy. The\nfirst two sentences are saying effectively the same thing. One or the\nother should be dropped or they should somehow be combined in a way\nthat says everything that needs to be said without repetition.\n\nIf necessary, iterate over updates of this paragraph here in the email\nthread until a polished version is reached rather than re-rolling the\nentire series every few minutes.\n"},{"id":"344044","messageId":"20180407003912.GA78042@syl.local","threadId":"48180","inReplyTo":"CAPig+cQPB+74T3YenoZ_oVFGh2c38AbcFdc+yjw6f32Ojestwg@mail.gmail.com","subject":"Re: [PATCH v6 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-07T00:39:12Z","receivedAt":"2018-04-07T00:39:21Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Fri, Apr 06, 2018 at 03:04:53AM -0400, Eric Sunshine wrote:\n> On Fri, Apr 6, 2018 at 2:39 AM, Taylor Blau <me@ttaylorr.com> wrote:\n> > [...]\n> > In this patch, we support `--type=<int|bool|bool-or-int|...>` in\n> > addition to `--int`, `--bool`, and etc. This allows the aforementioned\n> > upcoming patch to support querying a color value with a default via\n> > `--type=color --default=...`, without squandering `--color`.\n> >\n> > Signed-off-by: Taylor Blau <me@ttaylorr.com>\n> > ---\n> > diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\n> > @@ -38,12 +38,12 @@ existing values that match the regexp are updated or unset.  If\n> > +The `--type` option requests 'git config' to ensure that the configured values\n> > +associated with the given variable(s) are of the given type. When given\n> > +`--type`, 'git config' will ensure that the variable(s) are of the given type\n> > +and convert the value to the canonical form. If no type specifier is passed, no\n> > +checks or transformations are performed on the value. Callers may unset any\n> > +pre-existing type specifiers with `--no-type`.\n>\n> Sorry for being such a stickler, but this is still too mushy. The\n> first two sentences are saying effectively the same thing. One or the\n> other should be dropped or they should somehow be combined in a way\n> that says everything that needs to be said without repetition.\n\nI agree, and please do not apologize for being a stickler. I think that\nit's important we reach consensus before landing this, since other\nhumans will read this.\n\nHow do you feel about?\n\n  The `--type=<type>` option instructs 'git config' to ensure that\n  incoming and outgoing values are canonicalize-able under the given\n  <type>.  If no `--type=<type>` is given, no canonicalization will be\n  performed. Callers may unset the existing `--type` specifier with\n  `--no-type`.\n\nI think documenting that `--no-type` unsets any pre-existing `--type`\nspecifier is worthwhile. That said, I also think that there's a way to\ncombine the last two sentences, but it might be clearer as two smaller\npieces rather than one big one.\n\nIf this is still off base, could you provide some pointers on how you\nwould word it?\n\n> If necessary, iterate over updates of this paragraph here in the email\n> thread until a polished version is reached rather than re-rolling the\n> entire series every few minutes.\n\nThanks, and will do :-). I am quite new to this and was unaware of the\nsituations when re-rolling is and isn't desirable. I am going to wait to\nre-roll this series until it has gathered more feedback, or at least\nconsensus, after which I think it will be ready for queueing as-is.\n\nThanks for your patience and guidance.\n\n\nThanks,\nTaylor\n"},{"id":"344055","messageId":"CAPig+cQXJuLWpxfwhQ98a23wfAAzKZpkOvqWBdXaKKHLiW-7Uw@mail.gmail.com","threadId":"48180","inReplyTo":"20180407003912.GA78042@syl.local","subject":"Re: [PATCH v6 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-07T08:25:00Z","receivedAt":"2018-04-07T08:25:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Apr 6, 2018 at 8:39 PM, Taylor Blau <me@ttaylorr.com> wrote:\n> On Fri, Apr 06, 2018 at 03:04:53AM -0400, Eric Sunshine wrote:\n>> Sorry for being such a stickler, but this is still too mushy. The\n>> first two sentences are saying effectively the same thing. One or the\n>> other should be dropped or they should somehow be combined in a way\n>> that says everything that needs to be said without repetition.\n>\n> How do you feel about?\n>\n>   The `--type=<type>` option instructs 'git config' to ensure that\n>   incoming and outgoing values are canonicalize-able under the given\n>   <type>.  If no `--type=<type>` is given, no canonicalization will be\n>   performed. Callers may unset the existing `--type` specifier with\n>   `--no-type`.\n\nThis sounds fine. (Maybe s/the existing/an existing/)\n\n> I think documenting that `--no-type` unsets any pre-existing `--type`\n> specifier is worthwhile. That said, I also think that there's a way to\n> combine the last two sentences, but it might be clearer as two smaller\n> pieces rather than one big one.\n\nSmaller, simpler, more easily digested sentences are a win.\n\n>> If necessary, iterate over updates of this paragraph here in the email\n>> thread until a polished version is reached rather than re-rolling the\n>> entire series every few minutes.\n>\n> Thanks, and will do :-). I am quite new to this and was unaware of the\n> situations when re-rolling is and isn't desirable. I am going to wait to\n> re-roll this series until it has gathered more feedback, or at least\n> consensus,\n\nJust as it's a burden for you to repeatedly re-roll, it's also a\nburden on reviewers to repeatedly re-read the series. Ideally, we'd\nlike fewer, rather than more, re-rolls, so it's good to nail down\nquestionable or ambiguous issues via normal email discussion before\ngoing for a re-roll, and some reviewers try to assist with deciding if\na re-roll is needed by saying whether or not a review comment warrants\none.\n\nAllowing time for others to review and possibly comment on a series\nbefore re-rolling is indeed a good idea.\n\nAnother useful tactic is to include, in the cover letter, an interdiff\nbetween the previous and new versions, which gives reviewers a way to\nquickly examine the changed parts of the series without necessarily\nhaving to re-review each patch in entirety (an often time-consuming\ntask).\n\n> after which I think it will be ready for queueing as-is.\n\nFamous last words ;-)\n"},{"id":"344270","messageId":"20180409224650.GA34547@syl.local","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v7 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-09T22:46:50Z","receivedAt":"2018-04-09T22:46:59Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nAttached is the seventh re-roll of my series to support '--type=<type>'\ninstead of '--<type>' in 'git-config(1)'.\n\nSince v6, I have changed only the wording in\nDocumentation/git-config.txt, which Eric and I reached consensus upon in\na sub-thread [1]. Per Eric's suggestion, I have also included an\ninter-diff between this re-roll and the one previous for easier\nconsumption.\n\nI anticipate that since relatively little has changed since the last\nre-roll, that this will be the final re-roll of this series. I apologize\nthat the re-roll count has gotten so high, though I am glad that we\nhashed out the important details. I have a better idea of how to discuss\non this list without increasing the re-roll count so much.\n\nThanks in advance for your review :-).\n\n\nThanks,\nTaylor\n\n[1]: https://public-inbox.org/git/CAPig+cQXJuLWpxfwhQ98a23wfAAzKZpkOvqWBdXaKKHLiW-7Uw@mail.gmail.com/\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: support `--type=<type>` as preferred alias for\n    `--type`\n\n Documentation/git-config.txt | 71 ++++++++++++++++++---------------\n builtin/config.c             | 76 +++++++++++++++++++++++-------------\n t/t1300-repo-config.sh       | 29 ++++++++++++++\n 3 files changed, 117 insertions(+), 59 deletions(-)\n\nInter-diff (since v6):\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex b7686fcbe3..a1e3ffe750 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n\n-The `--type` option requests 'git config' to ensure that the configured values\n-associated with the given variable(s) are of the given type. When given\n-`--type`, 'git config' will ensure that the variable(s) are of the given type\n-and convert the value to the canonical form. If no type specifier is passed, no\n-checks or transformations are performed on the value. Callers may unset any\n-pre-existing type specifiers with `--no-type`.\n+The `--type=<type>` option instructs 'git config' to ensure that incoming and\n+outgoing values are canonicalize-able under the given <type>.  If no\n+`--type=<type>` is given, no canonicalization will be performed. Callers may\n+unset an existing `--type` specifier with `--no-type`.\n\n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n--\n2.17.0\n"},{"id":"344271","messageId":"20180409224654.GB34547@syl.local","threadId":"48180","inReplyTo":"cover.1523313730.git.me@ttaylorr.com","subject":"[PATCH v7 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-09T22:46:54Z","receivedAt":"2018-04-09T22:47:02Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd628..92fb8d56b1 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n \n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n \n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fde..24de37d544 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.17.0\n\n"},{"id":"344272","messageId":"20180409224657.GC34547@syl.local","threadId":"48180","inReplyTo":"cover.1523313730.git.me@ttaylorr.com","subject":"[PATCH v7 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-09T22:46:57Z","receivedAt":"2018-04-09T22:47:06Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues are satisfiable under that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to extend this functionality with\n`--type=color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid inhabiting this option too soon, so that\n`git config` can support `--type=color` (in the traditional sense) in\nthe future, if that is desired.\n\nIn this patch, we support `--type=<int|bool|bool-or-int|...>` in\naddition to `--int`, `--bool`, and etc. This allows the aforementioned\nupcoming patch to support querying a color value with a default via\n`--type=color --default=...`, without squandering `--color`.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 71 ++++++++++++++++++++----------------\n builtin/config.c             | 27 ++++++++++++++\n t/t1300-repo-config.sh       | 18 +++++++++\n 3 files changed, 84 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d5..a1e3ffe750 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] --add name value\n+'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+The `--type=<type>` option instructs 'git config' to ensure that incoming and\n+outgoing values are canonicalize-able under the given <type>.  If no\n+`--type=<type>` is given, no canonicalization will be performed. Callers may\n+unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,39 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input or output is valid under the given\n+  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n+  canonical form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 prior to output.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n+\n+--no-type::\n+  Un-sets the previously set type specifier (if one was previously set). This\n+  option requests that 'git config' not canonicalize the retrieved variable.\n+  `--no-type` has no effect without `--type=<type>` or `--<type>`.\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b1..5c8952a17c 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,32 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tint *type = opt->value;\n+\n+\tif (unset) {\n+\t\t*type = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tif (!strcmp(arg, \"bool\"))\n+\t\t*type = TYPE_BOOL;\n+\telse if (!strcmp(arg, \"int\"))\n+\t\t*type = TYPE_INT;\n+\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t*type = TYPE_BOOL_OR_INT;\n+\telse if (!strcmp(arg, \"path\"))\n+\t\t*type = TYPE_PATH;\n+\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t*type = TYPE_EXPIRY_DATE;\n+\telse\n+\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,6 +110,7 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n+\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n \tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n \tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n \tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d544..f5ae80e9ae 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,6 +1613,7 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n EOF\n \n@@ -1622,4 +1623,21 @@ test_expect_success 'later legacy specifiers are given precedence' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" >expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n-- \n2.17.0\n"},{"id":"344273","messageId":"CAPig+cTw5ZXKGOcnombwiM4P+9iRpbYUvOagQ0XujLB0ttXsOg@mail.gmail.com","threadId":"48180","inReplyTo":"20180409224650.GA34547@syl.local","subject":"Re: [PATCH v7 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-09T23:11:45Z","receivedAt":"2018-04-09T23:11:51Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 9, 2018 at 6:46 PM, Taylor Blau <me@ttaylorr.com> wrote:\n> Attached is the seventh re-roll of my series to support '--type=<type>'\n> instead of '--<type>' in 'git-config(1)'.\n>\n> Since v6, I have changed only the wording in\n> Documentation/git-config.txt, which Eric and I reached consensus upon in\n> a sub-thread [1]. Per Eric's suggestion, I have also included an\n> inter-diff between this re-roll and the one previous for easier\n> consumption.\n\nThanks, this iteration seems to address the issues raised by my\nearlier reviews, and I didn't find anything else upon which to\ncomment.\n\nI had meant earlier to say that the subject of 2/2:\n\n    builtin/config.c: support `--type=<type>` as preferred alias for `--type`\n\nshould probably be typeset as:\n\n    builtin/config.c: support `--type=<type>` as preferred alias for `--<type>`\n\nbut I'd rather not see a re-roll just for that. (Perhaps Junio can\ntweak it when queuing.)\n"},{"id":"344286","messageId":"xmqqy3hv4zzi.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180409224654.GB34547@syl.local","subject":"Re: [PATCH v7 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-10T01:22:25Z","receivedAt":"2018-04-10T01:22:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n> Internally, we represent `git config`'s type specifiers as a bitset\n> using OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\n> allows for the representation of multiple type specifiers in the `int\n> types` field, but this multi-representation is left unused.\n>\n> In fact, `git config` will not accept multiple type specifiers at a\n> time, as indicated by:\n>\n>   $ git config --int --bool some.section\n>   error: only one type at a time.\n>\n> This patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\n> specifier, so that the above command would instead be valid, and a\n> synonym of:\n>\n>   $ git config --bool some.section\n>\n> This change is motivated by two urges: (1) it does not make sense to\n> represent a singular type specifier internally as a bitset, only to\n> complain when there are multiple bits in the set. `OPT_SET_INT` is more\n> well-suited to this task than `OPT_BIT` is. (2) a future patch will\n> introduce `--type=<type>`, and we would like not to complain in the\n> following situation:\n>\n>   $ git config --int --type=int\n\nThe above does not exactly argue for adopting the last-one-wins\nsemantics, and still leaves it unclear if we want to complain\nagainst\n\n    $ git config --bool --type=int\n\nIs it intentionally left vague if we want to (or not want to)\ncomplain when such a conflicting specification is given?\n\nWe could keep the traditional behaviour of \"only one type at a time\"\nerror and still move away from the bitset representation that does\nnot make sense, if we wanted to.  Initialize the \"type\" variable to\nan unset value, and use a callback to ensure either the variable is\nset to the unset value, or the value being set is already in the\nvariable.  I think if you use OPT_CMDMODE(), it would do all of that\nfor you automatically.\n\nI suspect that it may be OK to switch to last-one-wins, but then we\nshould give a justification that is a bit stronger than \"we want to\navoid complaining against --int --type=int\" (i.e. \"we want to switch\nto last-one-wins for such and such reasons\").\n\n> diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> index 4f8e6f5fde..24de37d544 100755\n> --- a/t/t1300-repo-config.sh\n> +++ b/t/t1300-repo-config.sh\n> @@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n>  \ttest_expect_code 128 nongit git config --local foo.bar\n>  '\n>  \n> +cat >.git/config <<-\\EOF &&\n> +[core]\n> +number = 10\n> +EOF\n> +\n> +test_expect_success 'later legacy specifiers are given precedence' '\n> +\tgit config --bool --int core.number >actual &&\n> +\techo 10 >expect &&\n> +\ttest_cmp expect actual\n> +'\n\nAnd this expects more than we gave justifications for in the\nproposed log message.  I do not think it is necessarily a bad idea\nto switch to last-one-wins, but if that is where we really want to\ngo, the proposed log message is being misleading.  It is true that\nOPT_SET_INT is more suited to complain when two conflicting things\nare given than OPT_BIT, but this example actually tells us that you\nno longer want to catch an error to give conflicting requests.\n\n"},{"id":"344287","messageId":"xmqqsh834yz1.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180409224657.GC34547@syl.local","subject":"Re: [PATCH v7 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-10T01:44:18Z","receivedAt":"2018-04-10T01:44:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n> `git config` has long allowed the ability for callers to provide a 'type\n> specifier', which instructs `git config` to (1) ensure that incoming\n> values are satisfiable under that type, and (2) that outgoing values are\n> canonicalized under that type.\n\nHmm, \"satisfiable\" is not the first word that comes to my mind to\ndescribe \"when you give me a string 'frotz', we cannot take it as an\ninteger\".  s/are satisfiable under/can be interpreted as/ perhaps?\n\n> In another series, we propose to extend this functionality with\n> `--type=color` and `--default` to replace `--get-color`.\n>\n> However, we traditionally use `--color` to mean \"colorize this output\",\n> instead of \"this value should be treated as a color\".\n\nMakes sense.\n\n> Currently, `git config` does not support this kind of colorization, but\n> we should be careful to avoid inhabiting this option too soon, so that\n> `git config` can support `--type=color` (in the traditional sense) in\n> the future, if that is desired.\n\nShouldn't the above `--color` instead?  That is, we avoid squatting\non `--color` because we might later want to use it in the\ntraditional way.  Also my reading stuttered around \"inhabiting\".  It\nmight be just me, but perhaps s/inhabiting/squating on/?\n\n> In this patch, we support `--type=<int|bool|bool-or-int|...>` in\n> addition to `--int`, `--bool`, and etc. This allows the aforementioned\n> upcoming patch to support querying a color value with a default via\n> `--type=color --default=...`, without squandering `--color`.\n\nGood.\n\n> @@ -160,30 +158,39 @@ See also <<FILES>>.\n>  --list::\n>  \tList all variables set in config file, along with their values.\n>  \n> ---bool::\n> -\t'git config' will ensure that the output is \"true\" or \"false\"\n> +--type <type>::\n> +  'git config' will ensure that any input or output is valid under the given\n> +  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n> +  canonical form.\n> ++\n> +Valid `<type>`'s include:\n> ++\n> +- 'bool': canonicalize values as either \"true\" or \"false\".\n> +- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n> +  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n> +  1073741824 prior to output.\n\nIt is not a new problem introduced by this patch, but these place\ntoo much weight on the output side and may end up giving a flawed\nmental model to the readers, I am afraid.\n\nIt's not like an 'int' value internally is \"2k\" and shown it as 2048\nwhen output.  In reality, we internalize it 2048 upon input and we\ndo not do anything special when showing, no?  A 'bool' value given\nwith a string 'no' internally becomes 0 upon input and is shown as\n'false' upon output.\n\nI suspect s/prior to output/upon input/ may alleviate the issue\nquite a bit.\n"},{"id":"344290","messageId":"20180410021253.GA937@syl.local","threadId":"48180","inReplyTo":"xmqqy3hv4zzi.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v7 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-10T02:12:53Z","receivedAt":"2018-04-10T02:13:01Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Tue, Apr 10, 2018 at 10:22:25AM +0900, Junio C Hamano wrote:\n> Taylor Blau <me@ttaylorr.com> writes:\n>\n> > Internally, we represent `git config`'s type specifiers as a bitset\n> > using OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\n> > allows for the representation of multiple type specifiers in the `int\n> > types` field, but this multi-representation is left unused.\n> >\n> > In fact, `git config` will not accept multiple type specifiers at a\n> > time, as indicated by:\n> >\n> >   $ git config --int --bool some.section\n> >   error: only one type at a time.\n> >\n> > This patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\n> > specifier, so that the above command would instead be valid, and a\n> > synonym of:\n> >\n> >   $ git config --bool some.section\n> >\n> > This change is motivated by two urges: (1) it does not make sense to\n> > represent a singular type specifier internally as a bitset, only to\n> > complain when there are multiple bits in the set. `OPT_SET_INT` is more\n> > well-suited to this task than `OPT_BIT` is. (2) a future patch will\n> > introduce `--type=<type>`, and we would like not to complain in the\n> > following situation:\n> >\n> >   $ git config --int --type=int\n>\n> The above does not exactly argue for adopting the last-one-wins\n> semantics, and still leaves it unclear if we want to complain\n> against\n>\n>     $ git config --bool --type=int\n>\n> Is it intentionally left vague if we want to (or not want to)\n> complain when such a conflicting specification is given?\n>\n> We could keep the traditional behaviour of \"only one type at a time\"\n> error and still move away from the bitset representation that does\n> not make sense, if we wanted to.  Initialize the \"type\" variable to\n> an unset value, and use a callback to ensure either the variable is\n> set to the unset value, or the value being set is already in the\n> variable.  I think if you use OPT_CMDMODE(), it would do all of that\n> for you automatically.\n>\n> I suspect that it may be OK to switch to last-one-wins, but then we\n> should give a justification that is a bit stronger than \"we want to\n> avoid complaining against --int --type=int\" (i.e. \"we want to switch\n> to last-one-wins for such and such reasons\").\n\nI think that the major justification is to treat --type=int as a _true_\nsynonym of --int, such that neither `--type=<t1> --type=<t2>` nor\n`--<t1> --<t2>` will complain. This, as well as the fact that\nOPT_SET_BIT brings us closer to the semantics of `--verbose=1\n--verbose=2`, which is something that Eric had mentioned above.\n\nI think that OPT_CMDMODE would not work quite in the way we desire,\nsince the error messages would not quite line up with the command typed.\nFor instance, after applying the following diff:\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5c8952a17c..d9b73b949a 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -111,11 +111,11 @@ static struct option builtin_config_options[] = {\n     OPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n     OPT_GROUP(N_(\"Type\")),\n     OPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n-    OPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-    OPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n-    OPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-    OPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-    OPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+    OPT_CMDMODE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+    OPT_CMDMODE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+    OPT_CMDMODE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+    OPT_CMDMODE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+    OPT_CMDMODE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n     OPT_GROUP(N_(\"Other\")),\n     OPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n     OPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n\nThe following occurs:\n\n  ~/g/git (tb/config-type-specifier-option!) $ ./git-config --type=int --bool foo.bar\n  error: option `bool' : incompatible with --int\n\nWhereas I would expect that to say:\n\n  error: option `bool' is incompatible with `--type=int'.\n\nI am not sure whether amending the implementation of OPT_CMDMODE is something\nthat you're interested in here.\n\nI can amend my patch to include this extra reasoning, if you think that\nwould be helpful.\n\n> > diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\n> > index 4f8e6f5fde..24de37d544 100755\n> > --- a/t/t1300-repo-config.sh\n> > +++ b/t/t1300-repo-config.sh\n> > @@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n> >  \ttest_expect_code 128 nongit git config --local foo.bar\n> >  '\n> >\n> > +cat >.git/config <<-\\EOF &&\n> > +[core]\n> > +number = 10\n> > +EOF\n> > +\n> > +test_expect_success 'later legacy specifiers are given precedence' '\n> > +\tgit config --bool --int core.number >actual &&\n> > +\techo 10 >expect &&\n> > +\ttest_cmp expect actual\n> > +'\n>\n> And this expects more than we gave justifications for in the\n> proposed log message.  I do not think it is necessarily a bad idea\n> to switch to last-one-wins, but if that is where we really want to\n> go, the proposed log message is being misleading.  It is true that\n> OPT_SET_INT is more suited to complain when two conflicting things\n> are given than OPT_BIT, but this example actually tells us that you\n> no longer want to catch an error to give conflicting requests.\n\nYes, since I'd like to be able to insert `--type=` and have it behave\nthe same way. Since `--type=bool --type=int` will not complain (and\ninstead adopt `--type=int` given that it comes later in the command\nstring), neither should `--bool --int`.\n\n\nThanks,\nTaylor\n"},{"id":"344291","messageId":"20180410021757.GB937@syl.local","threadId":"48180","inReplyTo":"xmqqsh834yz1.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v7 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-10T02:17:57Z","receivedAt":"2018-04-10T02:18:05Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Tue, Apr 10, 2018 at 10:44:18AM +0900, Junio C Hamano wrote:\n> Taylor Blau <me@ttaylorr.com> writes:\n>\n> > `git config` has long allowed the ability for callers to provide a 'type\n> > specifier', which instructs `git config` to (1) ensure that incoming\n> > values are satisfiable under that type, and (2) that outgoing values are\n> > canonicalized under that type.\n>\n> Hmm, \"satisfiable\" is not the first word that comes to my mind to\n> describe \"when you give me a string 'frotz', we cannot take it as an\n> integer\".  s/are satisfiable under/can be interpreted as/ perhaps?\n\nSure; I think that \"can be interpreted as\" is clearer. I have amended my\npatch locally, and will include this in the next re-roll, should one\nexist.\n\n> > In another series, we propose to extend this functionality with\n> > `--type=color` and `--default` to replace `--get-color`.\n> >\n> > However, we traditionally use `--color` to mean \"colorize this output\",\n> > instead of \"this value should be treated as a color\".\n>\n> Makes sense.\n>\n> > Currently, `git config` does not support this kind of colorization, but\n> > we should be careful to avoid inhabiting this option too soon, so that\n> > `git config` can support `--type=color` (in the traditional sense) in\n> > the future, if that is desired.\n>\n> Shouldn't the above `--color` instead?  That is, we avoid squatting\n> on `--color` because we might later want to use it in the\n> traditional way.  Also my reading stuttered around \"inhabiting\".  It\n> might be just me, but perhaps s/inhabiting/squating on/?\n\nAck, yes to both.\n\n> > In this patch, we support `--type=<int|bool|bool-or-int|...>` in\n> > addition to `--int`, `--bool`, and etc. This allows the aforementioned\n> > upcoming patch to support querying a color value with a default via\n> > `--type=color --default=...`, without squandering `--color`.\n>\n> Good.\n>\n> > @@ -160,30 +158,39 @@ See also <<FILES>>.\n> >  --list::\n> >  \tList all variables set in config file, along with their values.\n> >\n> > ---bool::\n> > -\t'git config' will ensure that the output is \"true\" or \"false\"\n> > +--type <type>::\n> > +  'git config' will ensure that any input or output is valid under the given\n> > +  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n> > +  canonical form.\n> > ++\n> > +Valid `<type>`'s include:\n> > ++\n> > +- 'bool': canonicalize values as either \"true\" or \"false\".\n> > +- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n> > +  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n> > +  1073741824 prior to output.\n>\n> It is not a new problem introduced by this patch, but these place\n> too much weight on the output side and may end up giving a flawed\n> mental model to the readers, I am afraid.\n>\n> It's not like an 'int' value internally is \"2k\" and shown it as 2048\n> when output.  In reality, we internalize it 2048 upon input and we\n> do not do anything special when showing, no?  A 'bool' value given\n> with a string 'no' internally becomes 0 upon input and is shown as\n> 'false' upon output.\n>\n> I suspect s/prior to output/upon input/ may alleviate the issue\n> quite a bit.\n\nI share your concern, and I think that your suggestion would alleviate\nthe issue entirely.\n\nI have applied these suggestions locally. :-)\n\nThanks,\nTaylor\n"},{"id":"344297","messageId":"CAPig+cT4mRQpvF4qNWOoVs-+94Bpg9ZJ3QnxZ4KF7HcKm-cUKQ@mail.gmail.com","threadId":"48180","inReplyTo":"20180410021253.GA937@syl.local","subject":"Re: [PATCH v7 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-04-10T04:13:24Z","receivedAt":"2018-04-10T04:13:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Apr 9, 2018 at 10:12 PM, Taylor Blau <me@ttaylorr.com> wrote:\n> On Tue, Apr 10, 2018 at 10:22:25AM +0900, Junio C Hamano wrote:\n>> I suspect that it may be OK to switch to last-one-wins, but then we\n>> should give a justification that is a bit stronger than \"we want to\n>> avoid complaining against --int --type=int\" (i.e. \"we want to switch\n>> to last-one-wins for such and such reasons\").\n>\n> I think that the major justification is to treat --type=int as a _true_\n> synonym of --int, such that neither `--type=<t1> --type=<t2>` nor\n> `--<t1> --<t2>` will complain. This, as well as the fact that\n> OPT_SET_BIT brings us closer to the semantics of `--verbose=1\n> --verbose=2`, which is something that Eric had mentioned above.\n\nI'm probably being dense, but even after reading this paragraph\nseveral times, I still don't have a good idea as to what it is trying\nto say.\n\nAs for my earlier reference to '--verbose=1 --verbose=2', that was\ncited merely as a \"last wins\" which I could buy; it was offered in\ncontrast to '--type=bool --type=int' \"last wins\" which this patch\ntries to sell, and which I have a tough time buying (though I defer to\nJunio's and Peff's judgments).\n\nThis patch (or perhaps its commit message) seems to conflate two\nindependent goals. (1) ridding this code of OPT_BIT() since its use in\nthis context is not very sensible, and (2) trying to sell \"last wins\"\n(for a not well justified argument) to support '--int --type=int'\nwithout complaint.\n\nGoal #1 makes plenty sense; no objection to that. Goal #2 isn't so\nobviously desirable (I already raised objections to the more general\n'--bool --type=int' \"last wins\" it implements).\n\n> I think that OPT_CMDMODE would not work quite in the way we desire,\n> since the error messages would not quite line up with the command typed.\n> For instance, after applying the following diff:\n>\n>      OPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n> -    OPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n> -    OPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n> -    OPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n> -    OPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n> -    OPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n> +    OPT_CMDMODE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n> +    OPT_CMDMODE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n> +    OPT_CMDMODE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n> +    OPT_CMDMODE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n> +    OPT_CMDMODE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n>\n>   ~/g/git (tb/config-type-specifier-option!) $ ./git-config --type=int --bool foo.bar\n>   error: option `bool' : incompatible with --int\n>\n> Whereas I would expect that to say:\n>\n>   error: option `bool' is incompatible with `--type=int'.\n\nCouldn't you achieve reasonable output (such as \"error: conflicting\ntypes requested: %s and %s\") by handling all those --<type>s with a\ncustom callback? You've already coded such a callback for\n--type=<type>, and it doesn't look like it would be much more effort\nto refactor it a bit to handle --<type> as well.\n"},{"id":"344425","messageId":"20180411010654.GA28561@syl.local","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v8 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-11T01:06:54Z","receivedAt":"2018-04-11T01:07:04Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nAttached is the eighth re-roll of my series to add `--type=<type>` as\nthe preferred alternative for `--<type>`.\n\nThe main changes since v7 concern handling degenerate cases, such as:\n\n  * git config --type=int --type=bool\n  * git config --type=int --int\n\nWe have previously had discussion about whether we should (1) retain the\nerror in previous versions when confronted with multiple, conflicting\ntype specifiers, (2) ignore the error, in favor of making --<type> and\n--type=<type> true synonyms, or (3) some combination of the two.\n\nI have thought some more about my argument that it would be favorable to\nmake \"--type=int\" and \"--int\" behave in the same way, and I am no\nlonger convinced that my argument makes sense. It's based on the premise\nthat \"--type=<type>\" must _necessarily_ allow multiple invocations, such\nas '--type=int --type=bool', and therefore \"--int --bool\" should be\nupdated to behave the same way.\n\nWe are not constrained to this behavior, so in v8, I have taught Git the\nfollowing:\n\n  1. Allow multiple non-conflicting types, such as '--int --int',\n     '--type=int --int', and '--int --type=int'.\n\n  2. Disallow multiple conflicting types, such as '--int --bool',\n     '--type=int --bool', and '--int --type=bool'.\n\n  3. Allow conflicting types following --no-type, such as '--int\n     --no-type --bool', '--type=int --no-type --bool', and '--int\n     --no-type --type=bool'. Note that this does _not_ introduce options\n     such as '--no-int' and whatnot.\n\nThis is accomplished by a new locally defined macro called\nOPT_CALLBACK_VALUE, which allows us to reuse option_parse_type() to\nhandle --int as well, by sending it through as opt->defval.\n\nI think that the above is the best-of-all-worlds choice, but I am\ncurious to hear everyone else's thoughts. Thanks in advance for your\nreview.\n\n\nThanks,\nTaylor\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: support `--type=<type>` as preferred alias for\n    `--type`\n\n Documentation/git-config.txt |  71 +++++++++++++-----------\n builtin/config.c             | 101 +++++++++++++++++++++++++----------\n t/t1300-repo-config.sh       |  63 ++++++++++++++++++++++\n 3 files changed, 176 insertions(+), 59 deletions(-)\n\nInter-diff (since v7):\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 5c8952a17c..7184c09582 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,28 +61,53 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n\n+#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n+\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n+\tPARSE_OPT_NONEG, (f), (i) }\n+\n+static struct option builtin_config_options[];\n+\n static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n-\tint *type = opt->value;\n-\n \tif (unset) {\n-\t\t*type = 0;\n+\t\ttype = 0;\n \t\treturn 0;\n \t}\n\n-\tif (!strcmp(arg, \"bool\"))\n-\t\t*type = TYPE_BOOL;\n-\telse if (!strcmp(arg, \"int\"))\n-\t\t*type = TYPE_INT;\n-\telse if (!strcmp(arg, \"bool-or-int\"))\n-\t\t*type = TYPE_BOOL_OR_INT;\n-\telse if (!strcmp(arg, \"path\"))\n-\t\t*type = TYPE_PATH;\n-\telse if (!strcmp(arg, \"expiry-date\"))\n-\t\t*type = TYPE_EXPIRY_DATE;\n-\telse\n-\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t/*\n+\t * To support '--<type>' style flags, begin with new_type equal to\n+\t * opt->defval.\n+\t */\n+\tint new_type = opt->defval;\n+\tif (!new_type) {\n+\t\tif (!strcmp(arg, \"bool\"))\n+\t\t\tnew_type = TYPE_BOOL;\n+\t\telse if (!strcmp(arg, \"int\"))\n+\t\t\tnew_type = TYPE_INT;\n+\t\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t\tnew_type = TYPE_BOOL_OR_INT;\n+\t\telse if (!strcmp(arg, \"path\"))\n+\t\t\tnew_type = TYPE_PATH;\n+\t\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t\tnew_type = TYPE_EXPIRY_DATE;\n+\t\telse\n+\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t}\n+\n+\tif (type != 0 && type != new_type) {\n+\t\t/*\n+\t\t * Complain when there is a new type not equal to the old type.\n+\t\t * This allows for combinations like '--int --type=int' and\n+\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n+\t\t * --int' and '--type=bool\n+\t\t * --type=int'.\n+\t\t */\n+\t\terror(\"only one type at a time.\");\n+\t\tusage_with_options(builtin_config_usage,\n+\t\t\tbuiltin_config_options);\n+\t}\n+\ttype = new_type;\n\n \treturn 0;\n }\n@@ -110,12 +135,12 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_CALLBACK('t', \"type\", &type, N_(\"type\"), N_(\"value is given this type\"), option_parse_type),\n-\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_CALLBACK('t', \"type\", NULL, \"\", N_(\"value is given this type\"), option_parse_type),\n+\tOPT_CALLBACK_VALUE(0, \"bool\", N_(\"value is \\\"true\\\" or \\\"false\\\"\"), option_parse_type, TYPE_BOOL),\n+\tOPT_CALLBACK_VALUE(0, \"int\", N_(\"value is decimal number\"), option_parse_type, TYPE_INT),\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", N_(\"value is --bool or --int\"), option_parse_type, TYPE_BOOL_OR_INT),\n+\tOPT_CALLBACK_VALUE(0, \"path\", N_(\"value is a path (file or directory name)\"), option_parse_type, TYPE_PATH),\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", N_(\"value is an expiry date\"), option_parse_type, TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex f5ae80e9ae..e06af3d337 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1615,14 +1615,42 @@ cat >.git/config <<-\\EOF &&\n [core]\n foo = true\n number = 10\n+big = 1M\n EOF\n\n-test_expect_success 'later legacy specifiers are given precedence' '\n-\tgit config --bool --int core.number >actual &&\n-\techo 10 >expect &&\n+test_expect_success 'identical modern --type specifiers are allowed' '\n+\tgit config --type=int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n \ttest_cmp expect actual\n '\n\n+test_expect_success 'identical legacy --type specifiers are allowed' '\n+\tgit config --int --int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'identical mixed --type specifiers are allowed' '\n+\tgit config --int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'non-identical modern --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --type=bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical legacy --type specifiers are not allowed' '\n+\ttest_must_fail git config --int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n test_expect_success '--type allows valid type specifiers' '\n \techo \"true\" >expect &&\n \tgit config --type=bool core.foo >actual &&\n@@ -1635,6 +1663,12 @@ test_expect_success '--no-type unsets type specifiers' '\n \ttest_cmp expect actual\n '\n\n+test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n+\techo 1048576 >expect &&\n+\tgit config --type=bool --no-type --type=int core.big >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success '--type rejects unknown specifiers' '\n \ttest_must_fail git config --type=nonsense core.foo 2>error &&\n \ttest_i18ngrep \"unrecognized --type argument\" error\n--\n2.17.0\n"},{"id":"344426","messageId":"20180411010658.GB28561@syl.local","threadId":"48180","inReplyTo":"cover.1523408336.git.me@ttaylorr.com","subject":"[PATCH v8 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-11T01:06:58Z","receivedAt":"2018-04-11T01:07:08Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd628..92fb8d56b1 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n \n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n \n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fde..24de37d544 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.17.0\n\n"},{"id":"344427","messageId":"20180411010700.GC28561@syl.local","threadId":"48180","inReplyTo":"cover.1523408336.git.me@ttaylorr.com","subject":"[PATCH v8 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-11T01:07:00Z","receivedAt":"2018-04-11T01:07:11Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues can be interpreted as that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to extend this functionality with\n`--type=color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid squatting on this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we support `--type=<int|bool|bool-or-int|...>` in\naddition to `--int`, `--bool`, and etc. This allows the aforementioned\nupcoming patch to support querying a color value with a default via\n`--type=color --default=...`, without squandering `--color`.\n\nWe retain the historic behavior of complaining when multiple,\nlegacy-style `--<type>` flags are given, as well as extend this to\nconflicting new-style `--type=<type>` flags. `--int --type=int` (and its\ncommutative pair) does not complain, but `--bool --type=int` (and its\ncommutative pair) does.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 71 ++++++++++++++++++++----------------\n builtin/config.c             | 62 ++++++++++++++++++++++++++++---\n t/t1300-repo-config.sh       | 58 +++++++++++++++++++++++++++--\n 3 files changed, 151 insertions(+), 40 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d5..b759009c75 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] --add name value\n+'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+The `--type=<type>` option instructs 'git config' to ensure that incoming and\n+outgoing values are canonicalize-able under the given <type>.  If no\n+`--type=<type>` is given, no canonicalization will be performed. Callers may\n+unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,39 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input or output is valid under the given\n+  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n+  canonical form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 upon input.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n+\n+--no-type::\n+  Un-sets the previously set type specifier (if one was previously set). This\n+  option requests that 'git config' not canonicalize the retrieved variable.\n+  `--no-type` has no effect without `--type=<type>` or `--<type>`.\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b1..7184c09582 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,57 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n+\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n+\tPARSE_OPT_NONEG, (f), (i) }\n+\n+static struct option builtin_config_options[];\n+\n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tif (unset) {\n+\t\ttype = 0;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * To support '--<type>' style flags, begin with new_type equal to\n+\t * opt->defval.\n+\t */\n+\tint new_type = opt->defval;\n+\tif (!new_type) {\n+\t\tif (!strcmp(arg, \"bool\"))\n+\t\t\tnew_type = TYPE_BOOL;\n+\t\telse if (!strcmp(arg, \"int\"))\n+\t\t\tnew_type = TYPE_INT;\n+\t\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t\tnew_type = TYPE_BOOL_OR_INT;\n+\t\telse if (!strcmp(arg, \"path\"))\n+\t\t\tnew_type = TYPE_PATH;\n+\t\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t\tnew_type = TYPE_EXPIRY_DATE;\n+\t\telse\n+\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t}\n+\n+\tif (type != 0 && type != new_type) {\n+\t\t/*\n+\t\t * Complain when there is a new type not equal to the old type.\n+\t\t * This allows for combinations like '--int --type=int' and\n+\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n+\t\t * --int' and '--type=bool\n+\t\t * --type=int'.\n+\t\t */\n+\t\terror(\"only one type at a time.\");\n+\t\tusage_with_options(builtin_config_usage,\n+\t\t\tbuiltin_config_options);\n+\t}\n+\ttype = new_type;\n+\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,11 +135,12 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_CALLBACK('t', \"type\", NULL, \"\", N_(\"value is given this type\"), option_parse_type),\n+\tOPT_CALLBACK_VALUE(0, \"bool\", N_(\"value is \\\"true\\\" or \\\"false\\\"\"), option_parse_type, TYPE_BOOL),\n+\tOPT_CALLBACK_VALUE(0, \"int\", N_(\"value is decimal number\"), option_parse_type, TYPE_INT),\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", N_(\"value is --bool or --int\"), option_parse_type, TYPE_BOOL_OR_INT),\n+\tOPT_CALLBACK_VALUE(0, \"path\", N_(\"value is a path (file or directory name)\"), option_parse_type, TYPE_PATH),\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", N_(\"value is an expiry date\"), option_parse_type, TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d544..e06af3d337 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,13 +1613,65 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n+big = 1M\n EOF\n \n-test_expect_success 'later legacy specifiers are given precedence' '\n-\tgit config --bool --int core.number >actual &&\n-\techo 10 >expect &&\n+test_expect_success 'identical modern --type specifiers are allowed' '\n+\tgit config --type=int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'identical legacy --type specifiers are allowed' '\n+\tgit config --int --int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'identical mixed --type specifiers are allowed' '\n+\tgit config --int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'non-identical modern --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --type=bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical legacy --type specifiers are not allowed' '\n+\ttest_must_fail git config --int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" >expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n+\techo 1048576 >expect &&\n+\tgit config --type=bool --no-type --type=int core.big >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n-- \n2.17.0\n"},{"id":"344429","messageId":"xmqqtvsizg9u.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180411010654.GA28561@syl.local","subject":"Re: [PATCH v8 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-11T01:24:45Z","receivedAt":"2018-04-11T01:24:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n> Attached is the eighth re-roll of my series to add `--type=<type>` as\n> the preferred alternative for `--<type>`.\n>\n> The main changes since v7 concern handling degenerate cases, such as:\n>\n>   * git config --type=int --type=bool\n>   * git config --type=int --int\n>\n> We have previously had discussion about whether we should (1) retain the\n> error in previous versions when confronted with multiple, conflicting\n> type specifiers, (2) ignore the error, in favor of making --<type> and\n> --type=<type> true synonyms, or (3) some combination of the two.\n>\n> I have thought some more about my argument that it would be favorable to\n> make \"--type=int\" and \"--int\" behave in the same way, and I am no\n> longer convinced that my argument makes sense. It's based on the premise\n> that \"--type=<type>\" must _necessarily_ allow multiple invocations, such\n> as '--type=int --type=bool', and therefore \"--int --bool\" should be\n> updated to behave the same way.\n>\n> We are not constrained to this behavior, so in v8, I have taught Git the\n> following:\n>\n>   1. Allow multiple non-conflicting types, such as '--int --int',\n>      '--type=int --int', and '--int --type=int'.\n>\n>   2. Disallow multiple conflicting types, such as '--int --bool',\n>      '--type=int --bool', and '--int --type=bool'.\n>\n>   3. Allow conflicting types following --no-type, such as '--int\n>      --no-type --bool', '--type=int --no-type --bool', and '--int\n>      --no-type --type=bool'. Note that this does _not_ introduce options\n>      such as '--no-int' and whatnot.\n>\n> This is accomplished by a new locally defined macro called\n> OPT_CALLBACK_VALUE, which allows us to reuse option_parse_type() to\n> handle --int as well, by sending it through as opt->defval.\n>\n> I think that the above is the best-of-all-worlds choice, but I am\n> curious to hear everyone else's thoughts. Thanks in advance for your\n> review.\n\nI too am curious.  Personally I do not think your \"last one wins\"\nwas necessarily bad--in fact it internally was consistent--I just\nthought that the log message did not justify the choice well.  And I\ndo not think the semantics defined by this one, \"once you choose,\nstick to it, or explicitly clear the previous choice\", is bad,\neither.\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 5c8952a17c..7184c09582 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -61,28 +61,53 @@ static int show_origin;\n>  #define TYPE_PATH\t\t4\n>  #define TYPE_EXPIRY_DATE\t5\n>\n> +#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n> +\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n> +\tPARSE_OPT_NONEG, (f), (i) }\n> +\n> +static struct option builtin_config_options[];\n\nOK.  I am not sure if OPT_CALLBACK_VALUE() needs to take 'f', as you\nalways pass the option_parse_type function to it.\n\n>  static int option_parse_type(const struct option *opt, const char *arg,\n>  \t\t\t     int unset)\n>  {\n> -\tint *type = opt->value;\n> -\n>  \tif (unset) {\n> -\t\t*type = 0;\n> +\t\ttype = 0;\n>  \t\treturn 0;\n>  \t}\n>\n> -\tif (!strcmp(arg, \"bool\"))\n> -\t\t*type = TYPE_BOOL;\n> -\telse if (!strcmp(arg, \"int\"))\n> -\t\t*type = TYPE_INT;\n> -\telse if (!strcmp(arg, \"bool-or-int\"))\n> -\t\t*type = TYPE_BOOL_OR_INT;\n> -\telse if (!strcmp(arg, \"path\"))\n> -\t\t*type = TYPE_PATH;\n> -\telse if (!strcmp(arg, \"expiry-date\"))\n> -\t\t*type = TYPE_EXPIRY_DATE;\n> -\telse\n> -\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n> +\t/*\n> +\t * To support '--<type>' style flags, begin with new_type equal to\n> +\t * opt->defval.\n> +\t */\n> +\tint new_type = opt->defval;\n> +\tif (!new_type) {\n> +\t\tif (!strcmp(arg, \"bool\"))\n> +\t\t\tnew_type = TYPE_BOOL;\n> +\t\telse if (!strcmp(arg, \"int\"))\n> +\t\t\tnew_type = TYPE_INT;\n> +\t\telse if (!strcmp(arg, \"bool-or-int\"))\n> +\t\t\tnew_type = TYPE_BOOL_OR_INT;\n> +\t\telse if (!strcmp(arg, \"path\"))\n> +\t\t\tnew_type = TYPE_PATH;\n> +\t\telse if (!strcmp(arg, \"expiry-date\"))\n> +\t\t\tnew_type = TYPE_EXPIRY_DATE;\n> +\t\telse\n> +\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n> +\t}\n> +\n> +\tif (type != 0 && type != new_type) {\n> +\t\t/*\n> +\t\t * Complain when there is a new type not equal to the old type.\n> +\t\t * This allows for combinations like '--int --type=int' and\n> +\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n> +\t\t * --int' and '--type=bool\n> +\t\t * --type=int'.\n> +\t\t */\n> +\t\terror(\"only one type at a time.\");\n> +\t\tusage_with_options(builtin_config_usage,\n> +\t\t\tbuiltin_config_options);\n> +\t}\n> +\ttype = new_type;\n\nDoes this rely on a file-scope global variable (type)?\n\n>\n>  \treturn 0;\n>  }\n"},{"id":"344430","messageId":"20180411013309.GA36066@syl.local","threadId":"48180","inReplyTo":"xmqqtvsizg9u.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v8 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-11T01:33:09Z","receivedAt":"2018-04-11T01:33:17Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Apr 11, 2018 at 10:24:45AM +0900, Junio C Hamano wrote:\n> Taylor Blau <me@ttaylorr.com> writes:\n>\n> > Attached is the eighth re-roll of my series to add `--type=<type>` as\n> > the preferred alternative for `--<type>`.\n> >\n> > The main changes since v7 concern handling degenerate cases, such as:\n> >\n> >   * git config --type=int --type=bool\n> >   * git config --type=int --int\n> >\n> > We have previously had discussion about whether we should (1) retain the\n> > error in previous versions when confronted with multiple, conflicting\n> > type specifiers, (2) ignore the error, in favor of making --<type> and\n> > --type=<type> true synonyms, or (3) some combination of the two.\n> >\n> > I have thought some more about my argument that it would be favorable to\n> > make \"--type=int\" and \"--int\" behave in the same way, and I am no\n> > longer convinced that my argument makes sense. It's based on the premise\n> > that \"--type=<type>\" must _necessarily_ allow multiple invocations, such\n> > as '--type=int --type=bool', and therefore \"--int --bool\" should be\n> > updated to behave the same way.\n> >\n> > We are not constrained to this behavior, so in v8, I have taught Git the\n> > following:\n> >\n> >   1. Allow multiple non-conflicting types, such as '--int --int',\n> >      '--type=int --int', and '--int --type=int'.\n> >\n> >   2. Disallow multiple conflicting types, such as '--int --bool',\n> >      '--type=int --bool', and '--int --type=bool'.\n> >\n> >   3. Allow conflicting types following --no-type, such as '--int\n> >      --no-type --bool', '--type=int --no-type --bool', and '--int\n> >      --no-type --type=bool'. Note that this does _not_ introduce options\n> >      such as '--no-int' and whatnot.\n> >\n> > This is accomplished by a new locally defined macro called\n> > OPT_CALLBACK_VALUE, which allows us to reuse option_parse_type() to\n> > handle --int as well, by sending it through as opt->defval.\n> >\n> > I think that the above is the best-of-all-worlds choice, but I am\n> > curious to hear everyone else's thoughts. Thanks in advance for your\n> > review.\n>\n> I too am curious.  Personally I do not think your \"last one wins\"\n> was necessarily bad--in fact it internally was consistent--I just\n> thought that the log message did not justify the choice well.  And I\n> do not think the semantics defined by this one, \"once you choose,\n> stick to it, or explicitly clear the previous choice\", is bad,\n> either.\n\n:-). If nothing else, I like that we retain more, stricter behavior from\nprevious versions.\n\n> > diff --git a/builtin/config.c b/builtin/config.c\n> > index 5c8952a17c..7184c09582 100644\n> > --- a/builtin/config.c\n> > +++ b/builtin/config.c\n> > @@ -61,28 +61,53 @@ static int show_origin;\n> >  #define TYPE_PATH\t\t4\n> >  #define TYPE_EXPIRY_DATE\t5\n> >\n> > +#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n> > +\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n> > +\tPARSE_OPT_NONEG, (f), (i) }\n> > +\n> > +static struct option builtin_config_options[];\n>\n> OK.  I am not sure if OPT_CALLBACK_VALUE() needs to take 'f', as you\n> always pass the option_parse_type function to it.\n\nThat's fair. I left this in as an indication that something like this\n_might_ want to make its way into parse-options.h as a general-purpose\nutility, but was not yet ready to do so. Thus, I defined it inside\nbuiltin/config.c.\n\n> >  static int option_parse_type(const struct option *opt, const char *arg,\n> >  \t\t\t     int unset)\n> >  {\n> > -\tint *type = opt->value;\n> > -\n> >  \tif (unset) {\n> > -\t\t*type = 0;\n> > +\t\ttype = 0;\n> >  \t\treturn 0;\n> >  \t}\n> >\n> > -\tif (!strcmp(arg, \"bool\"))\n> > -\t\t*type = TYPE_BOOL;\n> > -\telse if (!strcmp(arg, \"int\"))\n> > -\t\t*type = TYPE_INT;\n> > -\telse if (!strcmp(arg, \"bool-or-int\"))\n> > -\t\t*type = TYPE_BOOL_OR_INT;\n> > -\telse if (!strcmp(arg, \"path\"))\n> > -\t\t*type = TYPE_PATH;\n> > -\telse if (!strcmp(arg, \"expiry-date\"))\n> > -\t\t*type = TYPE_EXPIRY_DATE;\n> > -\telse\n> > -\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n> > +\t/*\n> > +\t * To support '--<type>' style flags, begin with new_type equal to\n> > +\t * opt->defval.\n> > +\t */\n> > +\tint new_type = opt->defval;\n> > +\tif (!new_type) {\n> > +\t\tif (!strcmp(arg, \"bool\"))\n> > +\t\t\tnew_type = TYPE_BOOL;\n> > +\t\telse if (!strcmp(arg, \"int\"))\n> > +\t\t\tnew_type = TYPE_INT;\n> > +\t\telse if (!strcmp(arg, \"bool-or-int\"))\n> > +\t\t\tnew_type = TYPE_BOOL_OR_INT;\n> > +\t\telse if (!strcmp(arg, \"path\"))\n> > +\t\t\tnew_type = TYPE_PATH;\n> > +\t\telse if (!strcmp(arg, \"expiry-date\"))\n> > +\t\t\tnew_type = TYPE_EXPIRY_DATE;\n> > +\t\telse\n> > +\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n> > +\t}\n> > +\n> > +\tif (type != 0 && type != new_type) {\n> > +\t\t/*\n> > +\t\t * Complain when there is a new type not equal to the old type.\n> > +\t\t * This allows for combinations like '--int --type=int' and\n> > +\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n> > +\t\t * --int' and '--type=bool\n> > +\t\t * --type=int'.\n> > +\t\t */\n> > +\t\terror(\"only one type at a time.\");\n> > +\t\tusage_with_options(builtin_config_usage,\n> > +\t\t\tbuiltin_config_options);\n> > +\t}\n> > +\ttype = new_type;\n>\n> Does this rely on a file-scope global variable (type)?\n\nI don't think it does. I think I had conflated the difference between\nopt->value and opt->defval while amending this patch. What do you think of the\nfollowing (which removes reaching outside the function for \"type\")?\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 7184c09582..53755ca461 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,8 +61,8 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n\n-#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n-\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n+#define OPT_CALLBACK_VALUE(s, l, v, h, f, i) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n \tPARSE_OPT_NONEG, (f), (i) }\n\n static struct option builtin_config_options[];\n@@ -71,7 +71,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n \tif (unset) {\n-\t\ttype = 0;\n+\t\t*((int *) opt->value) = 0;\n \t\treturn 0;\n \t}\n\n@@ -95,7 +95,8 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n \t}\n\n-\tif (type != 0 && type != new_type) {\n+\tint *to_type = opt->value;\n+\tif (*to_type && *to_type != new_type) {\n \t\t/*\n \t\t * Complain when there is a new type not equal to the old type.\n \t\t * This allows for combinations like '--int --type=int' and\n@@ -107,7 +108,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\tusage_with_options(builtin_config_usage,\n \t\t\tbuiltin_config_options);\n \t}\n-\ttype = new_type;\n+\t*to_type = new_type;\n\n \treturn 0;\n }\n@@ -135,12 +136,12 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_CALLBACK('t', \"type\", NULL, \"\", N_(\"value is given this type\"), option_parse_type),\n-\tOPT_CALLBACK_VALUE(0, \"bool\", N_(\"value is \\\"true\\\" or \\\"false\\\"\"), option_parse_type, TYPE_BOOL),\n-\tOPT_CALLBACK_VALUE(0, \"int\", N_(\"value is decimal number\"), option_parse_type, TYPE_INT),\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", N_(\"value is --bool or --int\"), option_parse_type, TYPE_BOOL_OR_INT),\n-\tOPT_CALLBACK_VALUE(0, \"path\", N_(\"value is a path (file or directory name)\"), option_parse_type, TYPE_PATH),\n-\tOPT_CALLBACK_VALUE(0, \"expiry-date\", N_(\"value is an expiry date\"), option_parse_type, TYPE_EXPIRY_DATE),\n+\tOPT_CALLBACK('t', \"type\", &type, \"\", N_(\"value is given this type\"), option_parse_type),\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), option_parse_type, TYPE_BOOL),\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), option_parse_type, TYPE_INT),\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), option_parse_type, TYPE_BOOL_OR_INT),\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), option_parse_type, TYPE_PATH),\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), option_parse_type, TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n\n\nThanks,\nTaylor\n"},{"id":"344436","messageId":"xmqq1sfmzbbg.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180411013309.GA36066@syl.local","subject":"Re: [PATCH v8 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-11T03:11:47Z","receivedAt":"2018-04-11T03:11:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n>> > +#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n>> > +\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n>> > +\tPARSE_OPT_NONEG, (f), (i) }\n>> > +\n>> > +static struct option builtin_config_options[];\n>>\n>> OK.  I am not sure if OPT_CALLBACK_VALUE() needs to take 'f', as you\n>> always pass the option_parse_type function to it.\n>\n> That's fair. I left this in as an indication that something like this\n> _might_ want to make its way into parse-options.h as a general-purpose\n> utility, but was not yet ready to do so. Thus, I defined it inside\n> builtin/config.c.\n\nI understood the reasoning, but as your current verdict is that this\nis not yet ready for parse-options.[ch], I think it is probably\npreferrable to reduce repeated passing of the same function to the\nmacro, at least while it is in this builgin/config.c file.\n\n> @@ -71,7 +71,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n>  \t\t\t     int unset)\n>  {\n>  \tif (unset) {\n> -\t\ttype = 0;\n> +\t\t*((int *) opt->value) = 0;\n>  \t\treturn 0;\n>  \t}\n\nYup.  This (if it works) is exactly what I imagined the function\nshould look like.\n"},{"id":"344437","messageId":"20180411034941.GA63158@syl.local","threadId":"48180","inReplyTo":"xmqq1sfmzbbg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v8 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-11T03:49:41Z","receivedAt":"2018-04-11T03:49:49Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Apr 11, 2018 at 12:11:47PM +0900, Junio C Hamano wrote:\n> Taylor Blau <me@ttaylorr.com> writes:\n>\n> >> > +#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n> >> > +\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n> >> > +\tPARSE_OPT_NONEG, (f), (i) }\n> >> > +\n> >> > +static struct option builtin_config_options[];\n> >>\n> >> OK.  I am not sure if OPT_CALLBACK_VALUE() needs to take 'f', as you\n> >> always pass the option_parse_type function to it.\n> >\n> > That's fair. I left this in as an indication that something like this\n> > _might_ want to make its way into parse-options.h as a general-purpose\n> > utility, but was not yet ready to do so. Thus, I defined it inside\n> > builtin/config.c.\n>\n> I understood the reasoning, but as your current verdict is that this\n> is not yet ready for parse-options.[ch], I think it is probably\n> preferrable to reduce repeated passing of the same function to the\n> macro, at least while it is in this builgin/config.c file.\n\nAh, that seems fair to me. I have removed the duplicate 'f' parameter\nand all of the option_parse_type()'s in builtin/config.c within my local\ncopy, and will happily include these changes in the subsequent re-roll.\n\nI'll wait for more feedback before sending this, however.\n\n\nThanks,\nTaylor\n"},{"id":"345024","messageId":"20180418214328.GA36803@syl.local","threadId":"48180","inReplyTo":"20180328234719.595-1-me@ttaylorr.com","subject":"[PATCH v9 0/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-18T21:43:28Z","receivedAt":"2018-04-18T21:43:40Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi,\n\nAttached is my final re-roll of the series to add `--type=<type>` as a\nsupported replacement for `--<type>`.\n\nSince the last time, I have changed the following:\n\n  * OPT_CALLBACK_VALUE no longer takes a function pointer, and instead\n    assumes option_parse_type().\n\n  * No longer rely on 'type' as a global from within\n    option_parse_type(), instead pass it through opt->value. (NB: it is\n    never NULL, and thus always safe to *(opt->value).)\n\nThanks to all who reviewed this series in its many iterations. I am very\nhappy with how it turned out, and was fortunate to receive feedback and\neventual consensus on the interface.\n\n\nThanks,\nTaylor\n\nTaylor Blau (2):\n  builtin/config.c: treat type specifiers singularly\n  builtin/config.c: support `--type=<type>` as preferred alias for\n    `--type`\n\n Documentation/git-config.txt |  71 +++++++++++++-----------\n builtin/config.c             | 102 +++++++++++++++++++++++++----------\n t/t1300-repo-config.sh       |  63 ++++++++++++++++++++++\n 3 files changed, 177 insertions(+), 59 deletions(-)\n\nInter-diff (since v8):\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 7184c09582..bd7a8d0ce7 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,9 +61,9 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n\n-#define OPT_CALLBACK_VALUE(s, l, h, f, i) \\\n-\t{ OPTION_CALLBACK, (s), (l), NULL, NULL, (h), PARSE_OPT_NOARG | \\\n-\tPARSE_OPT_NONEG, (f), (i) }\n+#define OPT_CALLBACK_VALUE(s, l, v, h, i) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n+\tPARSE_OPT_NONEG, option_parse_type, (i) }\n\n static struct option builtin_config_options[];\n\n@@ -71,7 +71,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n \tif (unset) {\n-\t\ttype = 0;\n+\t\t*((int *) opt->value) = 0;\n \t\treturn 0;\n \t}\n\n@@ -95,7 +95,8 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n \t}\n\n-\tif (type != 0 && type != new_type) {\n+\tint *to_type = opt->value;\n+\tif (*to_type && *to_type != new_type) {\n \t\t/*\n \t\t * Complain when there is a new type not equal to the old type.\n \t\t * This allows for combinations like '--int --type=int' and\n@@ -107,7 +108,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\tusage_with_options(builtin_config_usage,\n \t\t\tbuiltin_config_options);\n \t}\n-\ttype = new_type;\n+\t*to_type = new_type;\n\n \treturn 0;\n }\n@@ -135,12 +136,12 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_CALLBACK('t', \"type\", NULL, \"\", N_(\"value is given this type\"), option_parse_type),\n-\tOPT_CALLBACK_VALUE(0, \"bool\", N_(\"value is \\\"true\\\" or \\\"false\\\"\"), option_parse_type, TYPE_BOOL),\n-\tOPT_CALLBACK_VALUE(0, \"int\", N_(\"value is decimal number\"), option_parse_type, TYPE_INT),\n-\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", N_(\"value is --bool or --int\"), option_parse_type, TYPE_BOOL_OR_INT),\n-\tOPT_CALLBACK_VALUE(0, \"path\", N_(\"value is a path (file or directory name)\"), option_parse_type, TYPE_PATH),\n-\tOPT_CALLBACK_VALUE(0, \"expiry-date\", N_(\"value is an expiry date\"), option_parse_type, TYPE_EXPIRY_DATE),\n+\tOPT_CALLBACK('t', \"type\", &type, \"\", N_(\"value is given this type\"), option_parse_type),\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n\n\n--\n2.17.0\n"},{"id":"345025","messageId":"20180418214332.GB36803@syl.local","threadId":"48180","inReplyTo":"cover.1524087557.git.me@ttaylorr.com","subject":"[PATCH v9 1/2] builtin/config.c: treat type specifiers singularly","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-18T21:43:32Z","receivedAt":"2018-04-18T21:43:41Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Internally, we represent `git config`'s type specifiers as a bitset\nusing OPT_BIT. 'bool' is 1<<0, 'int' is 1<<1, and so on. This technique\nallows for the representation of multiple type specifiers in the `int\ntypes` field, but this multi-representation is left unused.\n\nIn fact, `git config` will not accept multiple type specifiers at a\ntime, as indicated by:\n\n  $ git config --int --bool some.section\n  error: only one type at a time.\n\nThis patch uses `OPT_SET_INT` to prefer the _last_ mentioned type\nspecifier, so that the above command would instead be valid, and a\nsynonym of:\n\n  $ git config --bool some.section\n\nThis change is motivated by two urges: (1) it does not make sense to\nrepresent a singular type specifier internally as a bitset, only to\ncomplain when there are multiple bits in the set. `OPT_SET_INT` is more\nwell-suited to this task than `OPT_BIT` is. (2) a future patch will\nintroduce `--type=<type>`, and we would like not to complain in the\nfollowing situation:\n\n  $ git config --int --type=int\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n builtin/config.c       | 49 +++++++++++++++++++-----------------------\n t/t1300-repo-config.sh | 11 ++++++++++\n 2 files changed, 33 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 01169dd628..92fb8d56b1 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -25,7 +25,7 @@ static char term = '\\n';\n \n static int use_global_config, use_system_config, use_local_config;\n static struct git_config_source given_config_source;\n-static int actions, types;\n+static int actions, type;\n static int end_null;\n static int respect_includes_opt = -1;\n static struct config_options config_options;\n@@ -55,11 +55,11 @@ static int show_origin;\n #define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \\\n \t\t\tACTION_GET_REGEXP | ACTION_GET_URLMATCH)\n \n-#define TYPE_BOOL (1<<0)\n-#define TYPE_INT (1<<1)\n-#define TYPE_BOOL_OR_INT (1<<2)\n-#define TYPE_PATH (1<<3)\n-#define TYPE_EXPIRY_DATE (1<<4)\n+#define TYPE_BOOL\t\t1\n+#define TYPE_INT\t\t2\n+#define TYPE_BOOL_OR_INT\t3\n+#define TYPE_PATH\t\t4\n+#define TYPE_EXPIRY_DATE\t5\n \n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n@@ -84,11 +84,11 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_BIT(0, \"bool\", &types, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_BIT(0, \"int\", &types, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_BIT(0, \"bool-or-int\", &types, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_BIT(0, \"path\", &types, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_BIT(0, \"expiry-date\", &types, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\n@@ -149,26 +149,26 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\tif (show_keys)\n \t\t\tstrbuf_addch(buf, key_delim);\n \n-\t\tif (types == TYPE_INT)\n+\t\tif (type == TYPE_INT)\n \t\t\tstrbuf_addf(buf, \"%\"PRId64,\n \t\t\t\t    git_config_int64(key_, value_ ? value_ : \"\"));\n-\t\telse if (types == TYPE_BOOL)\n+\t\telse if (type == TYPE_BOOL)\n \t\t\tstrbuf_addstr(buf, git_config_bool(key_, value_) ?\n \t\t\t\t      \"true\" : \"false\");\n-\t\telse if (types == TYPE_BOOL_OR_INT) {\n+\t\telse if (type == TYPE_BOOL_OR_INT) {\n \t\t\tint is_bool, v;\n \t\t\tv = git_config_bool_or_int(key_, value_, &is_bool);\n \t\t\tif (is_bool)\n \t\t\t\tstrbuf_addstr(buf, v ? \"true\" : \"false\");\n \t\t\telse\n \t\t\t\tstrbuf_addf(buf, \"%d\", v);\n-\t\t} else if (types == TYPE_PATH) {\n+\t\t} else if (type == TYPE_PATH) {\n \t\t\tconst char *v;\n \t\t\tif (git_config_pathname(&v, key_, value_) < 0)\n \t\t\t\treturn -1;\n \t\t\tstrbuf_addstr(buf, v);\n \t\t\tfree((char *)v);\n-\t\t} else if (types == TYPE_EXPIRY_DATE) {\n+\t\t} else if (type == TYPE_EXPIRY_DATE) {\n \t\t\ttimestamp_t t;\n \t\t\tif (git_config_expiry_date(&t, key_, value_) < 0)\n \t\t\t\treturn -1;\n@@ -287,7 +287,7 @@ static char *normalize_value(const char *key, const char *value)\n \tif (!value)\n \t\treturn NULL;\n \n-\tif (types == 0 || types == TYPE_PATH || types == TYPE_EXPIRY_DATE)\n+\tif (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE)\n \t\t/*\n \t\t * We don't do normalization for TYPE_PATH here: If\n \t\t * the path is like ~/foobar/, we prefer to store\n@@ -296,11 +296,11 @@ static char *normalize_value(const char *key, const char *value)\n \t\t * Also don't do normalization for expiry dates.\n \t\t */\n \t\treturn xstrdup(value);\n-\tif (types == TYPE_INT)\n+\tif (type == TYPE_INT)\n \t\treturn xstrfmt(\"%\"PRId64, git_config_int64(key, value));\n-\tif (types == TYPE_BOOL)\n+\tif (type == TYPE_BOOL)\n \t\treturn xstrdup(git_config_bool(key, value) ?  \"true\" : \"false\");\n-\tif (types == TYPE_BOOL_OR_INT) {\n+\tif (type == TYPE_BOOL_OR_INT) {\n \t\tint is_bool, v;\n \t\tv = git_config_bool_or_int(key, value, &is_bool);\n \t\tif (!is_bool)\n@@ -309,7 +309,7 @@ static char *normalize_value(const char *key, const char *value)\n \t\t\treturn xstrdup(v ? \"true\" : \"false\");\n \t}\n \n-\tdie(\"BUG: cannot normalize type %d\", types);\n+\tdie(\"BUG: cannot normalize type %d\", type);\n }\n \n static int get_color_found;\n@@ -566,12 +566,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)\n \t\tkey_delim = '\\n';\n \t}\n \n-\tif (HAS_MULTI_BITS(types)) {\n-\t\terror(\"only one type at a time.\");\n-\t\tusage_with_options(builtin_config_usage, builtin_config_options);\n-\t}\n-\n-\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {\n+\tif ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {\n \t\terror(\"--get-color and variable type are incoherent\");\n \t\tusage_with_options(builtin_config_usage, builtin_config_options);\n \t}\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 4f8e6f5fde..24de37d544 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1611,4 +1611,15 @@ test_expect_success '--local requires a repo' '\n \ttest_expect_code 128 nongit git config --local foo.bar\n '\n \n+cat >.git/config <<-\\EOF &&\n+[core]\n+number = 10\n+EOF\n+\n+test_expect_success 'later legacy specifiers are given precedence' '\n+\tgit config --bool --int core.number >actual &&\n+\techo 10 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.17.0\n\n"},{"id":"345026","messageId":"20180418214335.GC36803@syl.local","threadId":"48180","inReplyTo":"cover.1524087557.git.me@ttaylorr.com","subject":"[PATCH v9 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-18T21:43:35Z","receivedAt":"2018-04-18T21:43:46Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"`git config` has long allowed the ability for callers to provide a 'type\nspecifier', which instructs `git config` to (1) ensure that incoming\nvalues can be interpreted as that type, and (2) that outgoing values are\ncanonicalized under that type.\n\nIn another series, we propose to extend this functionality with\n`--type=color` and `--default` to replace `--get-color`.\n\nHowever, we traditionally use `--color` to mean \"colorize this output\",\ninstead of \"this value should be treated as a color\".\n\nCurrently, `git config` does not support this kind of colorization, but\nwe should be careful to avoid squatting on this option too soon, so that\n`git config` can support `--color` (in the traditional sense) in the\nfuture, if that is desired.\n\nIn this patch, we support `--type=<int|bool|bool-or-int|...>` in\naddition to `--int`, `--bool`, and etc. This allows the aforementioned\nupcoming patch to support querying a color value with a default via\n`--type=color --default=...`, without squandering `--color`.\n\nWe retain the historic behavior of complaining when multiple,\nlegacy-style `--<type>` flags are given, as well as extend this to\nconflicting new-style `--type=<type>` flags. `--int --type=int` (and its\ncommutative pair) does not complain, but `--bool --type=int` (and its\ncommutative pair) does.\n\nSigned-off-by: Taylor Blau <me@ttaylorr.com>\n---\n Documentation/git-config.txt | 71 ++++++++++++++++++++----------------\n builtin/config.c             | 63 +++++++++++++++++++++++++++++---\n t/t1300-repo-config.sh       | 58 +++++++++++++++++++++++++++--\n 3 files changed, 152 insertions(+), 40 deletions(-)\n\ndiff --git a/Documentation/git-config.txt b/Documentation/git-config.txt\nindex e09ed5d7d5..b759009c75 100644\n--- a/Documentation/git-config.txt\n+++ b/Documentation/git-config.txt\n@@ -9,13 +9,13 @@ git-config - Get and set repository or global options\n SYNOPSIS\n --------\n [verse]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] name [value [value_regex]]\n-'git config' [<file-option>] [type] --add name value\n-'git config' [<file-option>] [type] --replace-all name value [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] --get-all name [value_regex]\n-'git config' [<file-option>] [type] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n-'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] name [value [value_regex]]\n+'git config' [<file-option>] [--type=<type>] --add name value\n+'git config' [<file-option>] [--type=<type>] --replace-all name value [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] --get-all name [value_regex]\n+'git config' [<file-option>] [--type=<type>] [--show-origin] [-z|--null] [--name-only] --get-regexp name_regex [value_regex]\n+'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL\n 'git config' [<file-option>] --unset name [value_regex]\n 'git config' [<file-option>] --unset-all name [value_regex]\n 'git config' [<file-option>] --rename-section old_name new_name\n@@ -38,12 +38,10 @@ existing values that match the regexp are updated or unset.  If\n you want to handle the lines that do *not* match the regex, just\n prepend a single exclamation mark in front (see also <<EXAMPLES>>).\n \n-The type specifier can be either `--int` or `--bool`, to make\n-'git config' ensure that the variable(s) are of the given type and\n-convert the value to the canonical form (simple decimal number for int,\n-a \"true\" or \"false\" string for bool), or `--path`, which does some\n-path expansion (see `--path` below).  If no type specifier is passed, no\n-checks or transformations are performed on the value.\n+The `--type=<type>` option instructs 'git config' to ensure that incoming and\n+outgoing values are canonicalize-able under the given <type>.  If no\n+`--type=<type>` is given, no canonicalization will be performed. Callers may\n+unset an existing `--type` specifier with `--no-type`.\n \n When reading, the values are read from the system, global and\n repository local configuration files by default, and options\n@@ -160,30 +158,39 @@ See also <<FILES>>.\n --list::\n \tList all variables set in config file, along with their values.\n \n---bool::\n-\t'git config' will ensure that the output is \"true\" or \"false\"\n+--type <type>::\n+  'git config' will ensure that any input or output is valid under the given\n+  type constraint(s), and will canonicalize outgoing values in `<type>`'s\n+  canonical form.\n++\n+Valid `<type>`'s include:\n++\n+- 'bool': canonicalize values as either \"true\" or \"false\".\n+- 'int': canonicalize values as simple decimal numbers. An optional suffix of\n+  'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or\n+  1073741824 upon input.\n+- 'bool-or-int': canonicalize according to either 'bool' or 'int', as described\n+  above.\n+- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and\n+  `~user` to the home directory for the specified user. This specifier has no\n+  effect when setting the value (but you can use `git config section.variable\n+  ~/` from the command line to let your shell do the expansion.)\n+- 'expiry-date': canonicalize by converting from a fixed or relative date-string\n+  to a timestamp. This specifier has no effect when setting the value.\n++\n \n+--bool::\n --int::\n-\t'git config' will ensure that the output is a simple\n-\tdecimal number.  An optional value suffix of 'k', 'm', or 'g'\n-\tin the config file will cause the value to be multiplied\n-\tby 1024, 1048576, or 1073741824 prior to output.\n-\n --bool-or-int::\n-\t'git config' will ensure that the output matches the format of\n-\teither --bool or --int, as described above.\n-\n --path::\n-\t`git config` will expand a leading `~` to the value of\n-\t`$HOME`, and `~user` to the home directory for the\n-\tspecified user.  This option has no effect when setting the\n-\tvalue (but you can use `git config section.variable ~/`\n-\tfrom the command line to let your shell do the expansion).\n-\n --expiry-date::\n-\t`git config` will ensure that the output is converted from\n-\ta fixed or relative date-string to a timestamp. This option\n-\thas no effect when setting the value.\n+  Historical options for selecting a type specifier. Prefer instead `--type`,\n+  (see: above).\n+\n+--no-type::\n+  Un-sets the previously set type specifier (if one was previously set). This\n+  option requests that 'git config' not canonicalize the retrieved variable.\n+  `--no-type` has no effect without `--type=<type>` or `--<type>`.\n \n -z::\n --null::\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 92fb8d56b1..bd7a8d0ce7 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -61,6 +61,58 @@ static int show_origin;\n #define TYPE_PATH\t\t4\n #define TYPE_EXPIRY_DATE\t5\n \n+#define OPT_CALLBACK_VALUE(s, l, v, h, i) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n+\tPARSE_OPT_NONEG, option_parse_type, (i) }\n+\n+static struct option builtin_config_options[];\n+\n+static int option_parse_type(const struct option *opt, const char *arg,\n+\t\t\t     int unset)\n+{\n+\tif (unset) {\n+\t\t*((int *) opt->value) = 0;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * To support '--<type>' style flags, begin with new_type equal to\n+\t * opt->defval.\n+\t */\n+\tint new_type = opt->defval;\n+\tif (!new_type) {\n+\t\tif (!strcmp(arg, \"bool\"))\n+\t\t\tnew_type = TYPE_BOOL;\n+\t\telse if (!strcmp(arg, \"int\"))\n+\t\t\tnew_type = TYPE_INT;\n+\t\telse if (!strcmp(arg, \"bool-or-int\"))\n+\t\t\tnew_type = TYPE_BOOL_OR_INT;\n+\t\telse if (!strcmp(arg, \"path\"))\n+\t\t\tnew_type = TYPE_PATH;\n+\t\telse if (!strcmp(arg, \"expiry-date\"))\n+\t\t\tnew_type = TYPE_EXPIRY_DATE;\n+\t\telse\n+\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n+\t}\n+\n+\tint *to_type = opt->value;\n+\tif (*to_type && *to_type != new_type) {\n+\t\t/*\n+\t\t * Complain when there is a new type not equal to the old type.\n+\t\t * This allows for combinations like '--int --type=int' and\n+\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n+\t\t * --int' and '--type=bool\n+\t\t * --type=int'.\n+\t\t */\n+\t\terror(\"only one type at a time.\");\n+\t\tusage_with_options(builtin_config_usage,\n+\t\t\tbuiltin_config_options);\n+\t}\n+\t*to_type = new_type;\n+\n+\treturn 0;\n+}\n+\n static struct option builtin_config_options[] = {\n \tOPT_GROUP(N_(\"Config file location\")),\n \tOPT_BOOL(0, \"global\", &use_global_config, N_(\"use global config file\")),\n@@ -84,11 +136,12 @@ static struct option builtin_config_options[] = {\n \tOPT_BIT(0, \"get-color\", &actions, N_(\"find the color configured: slot [default]\"), ACTION_GET_COLOR),\n \tOPT_BIT(0, \"get-colorbool\", &actions, N_(\"find the color setting: slot [stdout-is-tty]\"), ACTION_GET_COLORBOOL),\n \tOPT_GROUP(N_(\"Type\")),\n-\tOPT_SET_INT(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n-\tOPT_SET_INT(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n-\tOPT_SET_INT(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n-\tOPT_SET_INT(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n-\tOPT_SET_INT(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n+\tOPT_CALLBACK('t', \"type\", &type, \"\", N_(\"value is given this type\"), option_parse_type),\n+\tOPT_CALLBACK_VALUE(0, \"bool\", &type, N_(\"value is \\\"true\\\" or \\\"false\\\"\"), TYPE_BOOL),\n+\tOPT_CALLBACK_VALUE(0, \"int\", &type, N_(\"value is decimal number\"), TYPE_INT),\n+\tOPT_CALLBACK_VALUE(0, \"bool-or-int\", &type, N_(\"value is --bool or --int\"), TYPE_BOOL_OR_INT),\n+\tOPT_CALLBACK_VALUE(0, \"path\", &type, N_(\"value is a path (file or directory name)\"), TYPE_PATH),\n+\tOPT_CALLBACK_VALUE(0, \"expiry-date\", &type, N_(\"value is an expiry date\"), TYPE_EXPIRY_DATE),\n \tOPT_GROUP(N_(\"Other\")),\n \tOPT_BOOL('z', \"null\", &end_null, N_(\"terminate values with NUL byte\")),\n \tOPT_BOOL(0, \"name-only\", &omit_values, N_(\"show variable names only\")),\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex 24de37d544..e06af3d337 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -1613,13 +1613,65 @@ test_expect_success '--local requires a repo' '\n \n cat >.git/config <<-\\EOF &&\n [core]\n+foo = true\n number = 10\n+big = 1M\n EOF\n \n-test_expect_success 'later legacy specifiers are given precedence' '\n-\tgit config --bool --int core.number >actual &&\n-\techo 10 >expect &&\n+test_expect_success 'identical modern --type specifiers are allowed' '\n+\tgit config --type=int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'identical legacy --type specifiers are allowed' '\n+\tgit config --int --int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'identical mixed --type specifiers are allowed' '\n+\tgit config --int --type=int core.big >actual &&\n+\techo 1048576 >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'non-identical modern --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --type=bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical legacy --type specifiers are not allowed' '\n+\ttest_must_fail git config --int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success 'non-identical mixed --type specifiers are not allowed' '\n+\ttest_must_fail git config --type=int --bool core.big 2>error &&\n+\ttest_i18ngrep \"only one type at a time\" error\n+'\n+\n+test_expect_success '--type allows valid type specifiers' '\n+\techo \"true\" >expect &&\n+\tgit config --type=bool core.foo >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-type unsets type specifiers' '\n+\techo \"10\" >expect &&\n+\tgit config --type=bool --no-type core.number >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'unset type specifiers may be reset to conflicting ones' '\n+\techo 1048576 >expect &&\n+\tgit config --type=bool --no-type --type=int core.big >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--type rejects unknown specifiers' '\n+\ttest_must_fail git config --type=nonsense core.foo 2>error &&\n+\ttest_i18ngrep \"unrecognized --type argument\" error\n+'\n+\n test_done\n-- \n2.17.0\n"},{"id":"345054","messageId":"xmqq8t9jgbe1.fsf@gitster-ct.c.googlers.com","threadId":"48180","inReplyTo":"20180418214335.GC36803@syl.local","subject":"Re: [PATCH v9 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-04-19T02:47:50Z","receivedAt":"2018-04-19T02:47:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Taylor Blau <me@ttaylorr.com> writes:\n\n> diff --git a/builtin/config.c b/builtin/config.c\n> index 92fb8d56b1..bd7a8d0ce7 100644\n> --- a/builtin/config.c\n> +++ b/builtin/config.c\n> @@ -61,6 +61,58 @@ static int show_origin;\n>  #define TYPE_PATH\t\t4\n>  #define TYPE_EXPIRY_DATE\t5\n>  \n> +#define OPT_CALLBACK_VALUE(s, l, v, h, i) \\\n> +\t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n> +\tPARSE_OPT_NONEG, option_parse_type, (i) }\n> +\n> +static struct option builtin_config_options[];\n> +\n> +static int option_parse_type(const struct option *opt, const char *arg,\n> +\t\t\t     int unset)\n> +{\n\nDeclare all local variables here.  We do not accept decl-after-statement.\n\n> +\tif (unset) {\n> +\t\t*((int *) opt->value) = 0;\n> +\t\treturn 0;\n> +\t}\n> +\n> +\t/*\n> +\t * To support '--<type>' style flags, begin with new_type equal to\n> +\t * opt->defval.\n> +\t */\n> +\tint new_type = opt->defval;\n\nLike this one and ...\n\n> +\tif (!new_type) {\n> +\t\tif (!strcmp(arg, \"bool\"))\n> +\t\t\tnew_type = TYPE_BOOL;\n> +\t\telse if (!strcmp(arg, \"int\"))\n> +\t\t\tnew_type = TYPE_INT;\n> +\t\telse if (!strcmp(arg, \"bool-or-int\"))\n> +\t\t\tnew_type = TYPE_BOOL_OR_INT;\n> +\t\telse if (!strcmp(arg, \"path\"))\n> +\t\t\tnew_type = TYPE_PATH;\n> +\t\telse if (!strcmp(arg, \"expiry-date\"))\n> +\t\t\tnew_type = TYPE_EXPIRY_DATE;\n> +\t\telse\n> +\t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n> +\t}\n> +\n> +\tint *to_type = opt->value;\n\n... this one.\n\n> +\tif (*to_type && *to_type != new_type) {\n> +\t\t/*\n> +\t\t * Complain when there is a new type not equal to the old type.\n> +\t\t * This allows for combinations like '--int --type=int' and\n> +\t\t * '--type=int --type=int', but disallows ones like '--type=bool\n> +\t\t * --int' and '--type=bool\n> +\t\t * --type=int'.\n> +\t\t */\n> +\t\terror(\"only one type at a time.\");\n> +\t\tusage_with_options(builtin_config_usage,\n> +\t\t\tbuiltin_config_options);\n> +\t}\n> +\t*to_type = new_type;\n> +\n> +\treturn 0;\n> +}\n> +\n"},{"id":"345055","messageId":"20180419030142.GA28273@syl.local","threadId":"48180","inReplyTo":"xmqq8t9jgbe1.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v9 2/2] builtin/config.c: support `--type=<type>` as preferred alias for `--type`","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2018-04-19T03:01:42Z","receivedAt":"2018-04-19T03:01:53Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Thu, Apr 19, 2018 at 11:47:50AM +0900, Junio C Hamano wrote:\n> Taylor Blau <me@ttaylorr.com> writes:\n>\n> > diff --git a/builtin/config.c b/builtin/config.c\n> > index 92fb8d56b1..bd7a8d0ce7 100644\n> > --- a/builtin/config.c\n> > +++ b/builtin/config.c\n> > @@ -61,6 +61,58 @@ static int show_origin;\n> >  #define TYPE_PATH\t\t4\n> >  #define TYPE_EXPIRY_DATE\t5\n> >\n> > +#define OPT_CALLBACK_VALUE(s, l, v, h, i) \\\n> > +\t{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \\\n> > +\tPARSE_OPT_NONEG, option_parse_type, (i) }\n> > +\n> > +static struct option builtin_config_options[];\n> > +\n> > +static int option_parse_type(const struct option *opt, const char *arg,\n> > +\t\t\t     int unset)\n> > +{\n>\n> Declare all local variables here.  We do not accept decl-after-statement.\n\nMy apologies, I will read Documentation/CodingGuidelines carefully. I\nhave generated the following patch locally:\n\ndiff --git a/builtin/config.c b/builtin/config.c\nindex bd7a8d0ce7..2f91ef15a4 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -70,6 +70,9 @@ static struct option builtin_config_options[];\n static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\t     int unset)\n {\n+\tint new_type;\n+\tint *to_type;\n+\n \tif (unset) {\n \t\t*((int *) opt->value) = 0;\n \t\treturn 0;\n@@ -79,7 +82,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t * To support '--<type>' style flags, begin with new_type equal to\n \t * opt->defval.\n \t */\n-\tint new_type = opt->defval;\n+\tnew_type = opt->defval;\n \tif (!new_type) {\n \t\tif (!strcmp(arg, \"bool\"))\n \t\t\tnew_type = TYPE_BOOL;\n@@ -95,7 +98,7 @@ static int option_parse_type(const struct option *opt, const char *arg,\n \t\t\tdie(_(\"unrecognized --type argument, %s\"), arg);\n \t}\n\n-\tint *to_type = opt->value;\n+\t*to_type = opt->value;\n \tif (*to_type && *to_type != new_type) {\n \t\t/*\n \t\t * Complain when there is a new type not equal to the old type.\n\n---\n\nAnd would be happy to apply it locally myself and send it to you via a\nre-roll. You are also free to apply it yourself if it would be easier. I\ndo not have a preference one way or another.\n\n\nThanks,\nTaylor\n"}]}