Merge branch 'tb/config-default'

"git config --get" learned the "--default" option, to help the
calling script.  Building on top of the tb/config-type topic, the
"git config" learns "--type=color" type.  Taken together, you can
do things like "git config --get foo.color --default blue" and get
the ANSI color sequence for the color given to foo.color variable,
or "blue" if the variable does not exist.

* tb/config-default:
  builtin/config: introduce `color` type specifier
  config.c: introduce 'git_config_color' to parse ANSI colors
  builtin/config: introduce `--default`
This commit is contained in:
Junio C Hamano 2018-05-08 15:59:27 +09:00
commit 00bb99c424
6 changed files with 127 additions and 0 deletions

View File

@ -177,6 +177,10 @@ Valid `<type>`'s include:
~/` from the command line to let your shell do the expansion.) ~/` from the command line to let your shell do the expansion.)
- 'expiry-date': canonicalize by converting from a fixed or relative date-string - 'expiry-date': canonicalize by converting from a fixed or relative date-string
to a timestamp. This specifier has no effect when setting the value. to a timestamp. This specifier has no effect when setting the value.
- 'color': When getting a value, canonicalize by converting to an ANSI color
escape sequence. When setting a value, a sanity-check is performed to ensure
that the given value is canonicalize-able as an ANSI color, but it is written
as-is.
+ +
--bool:: --bool::
@ -228,6 +232,8 @@ Valid `<type>`'s include:
output it as the ANSI color escape sequence to the standard output it as the ANSI color escape sequence to the standard
output. The optional `default` parameter is used instead, if output. The optional `default` parameter is used instead, if
there is no color configured for `name`. there is no color configured for `name`.
+
`--type=color [--default=<default>]` is preferred over `--get-color`.
-e:: -e::
--edit:: --edit::
@ -240,6 +246,10 @@ Valid `<type>`'s include:
using `--file`, `--global`, etc) and `on` when searching all using `--file`, `--global`, etc) and `on` when searching all
config files. config files.
--default <value>::
When using `--get`, and the requested variable is not found, behave as if
<value> were the value assigned to the that variable.
CONFIGURATION CONFIGURATION
------------- -------------
`pager.config` is only respected when listing configuration, i.e., when `pager.config` is only respected when listing configuration, i.e., when

View File

@ -26,6 +26,7 @@ static char term = '\n';
static int use_global_config, use_system_config, use_local_config; static int use_global_config, use_system_config, use_local_config;
static struct git_config_source given_config_source; static struct git_config_source given_config_source;
static int actions, type; static int actions, type;
static char *default_value;
static int end_null; static int end_null;
static int respect_includes_opt = -1; static int respect_includes_opt = -1;
static struct config_options config_options; static struct config_options config_options;
@ -60,6 +61,7 @@ static int show_origin;
#define TYPE_BOOL_OR_INT 3 #define TYPE_BOOL_OR_INT 3
#define TYPE_PATH 4 #define TYPE_PATH 4
#define TYPE_EXPIRY_DATE 5 #define TYPE_EXPIRY_DATE 5
#define TYPE_COLOR 6
#define OPT_CALLBACK_VALUE(s, l, v, h, i) \ #define OPT_CALLBACK_VALUE(s, l, v, h, i) \
{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
@ -93,6 +95,8 @@ static int option_parse_type(const struct option *opt, const char *arg,
new_type = TYPE_PATH; new_type = TYPE_PATH;
else if (!strcmp(arg, "expiry-date")) else if (!strcmp(arg, "expiry-date"))
new_type = TYPE_EXPIRY_DATE; new_type = TYPE_EXPIRY_DATE;
else if (!strcmp(arg, "color"))
new_type = TYPE_COLOR;
else else
die(_("unrecognized --type argument, %s"), arg); die(_("unrecognized --type argument, %s"), arg);
} }
@ -149,6 +153,7 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
OPT_END(), OPT_END(),
}; };
@ -228,6 +233,11 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value
if (git_config_expiry_date(&t, key_, value_) < 0) if (git_config_expiry_date(&t, key_, value_) < 0)
return -1; return -1;
strbuf_addf(buf, "%"PRItime, t); strbuf_addf(buf, "%"PRItime, t);
} else if (type == TYPE_COLOR) {
char v[COLOR_MAXLEN];
if (git_config_color(v, key_, value_) < 0)
return -1;
strbuf_addstr(buf, v);
} else if (value_) { } else if (value_) {
strbuf_addstr(buf, value_); strbuf_addstr(buf, value_);
} else { } else {
@ -313,6 +323,16 @@ static int get_value(const char *key_, const char *regex_)
config_with_options(collect_config, &values, config_with_options(collect_config, &values,
&given_config_source, &config_options); &given_config_source, &config_options);
if (!values.nr && default_value) {
struct strbuf *item;
ALLOC_GROW(values.items, values.nr + 1, values.alloc);
item = &values.items[values.nr++];
strbuf_init(item, 0);
if (format_config(item, key_, default_value) < 0)
die(_("failed to format default config value: %s"),
default_value);
}
ret = !values.nr; ret = !values.nr;
for (i = 0; i < values.nr; i++) { for (i = 0; i < values.nr; i++) {
@ -363,6 +383,20 @@ static char *normalize_value(const char *key, const char *value)
else else
return xstrdup(v ? "true" : "false"); return xstrdup(v ? "true" : "false");
} }
if (type == TYPE_COLOR) {
char v[COLOR_MAXLEN];
if (git_config_color(v, key, value))
die("cannot parse color '%s'", value);
/*
* The contents of `v` now contain an ANSI escape
* sequence, not suitable for including within a
* configuration file. Treat the above as a
* "sanity-check", and return the given value, which we
* know is representable as valid color code.
*/
return xstrdup(value);
}
die("BUG: cannot normalize type %d", type); die("BUG: cannot normalize type %d", type);
} }
@ -651,6 +685,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_config_usage, builtin_config_options); usage_with_options(builtin_config_usage, builtin_config_options);
} }
if (default_value && !(actions & ACTION_GET)) {
error("--default is only applicable to --get");
usage_with_options(builtin_config_usage,
builtin_config_options);
}
if (actions & PAGING_ACTIONS) if (actions & PAGING_ACTIONS)
setup_auto_pager("config", 1); setup_auto_pager("config", 1);

View File

@ -16,6 +16,7 @@
#include "string-list.h" #include "string-list.h"
#include "utf8.h" #include "utf8.h"
#include "dir.h" #include "dir.h"
#include "color.h"
struct config_source { struct config_source {
struct config_source *prev; struct config_source *prev;
@ -1067,6 +1068,15 @@ int git_config_expiry_date(timestamp_t *timestamp, const char *var, const char *
return 0; return 0;
} }
int git_config_color(char *dest, const char *var, const char *value)
{
if (!value)
return config_error_nonbool(var);
if (color_parse(value, dest) < 0)
return -1;
return 0;
}
static int git_default_core_config(const char *var, const char *value) static int git_default_core_config(const char *var, const char *value)
{ {
/* This needs a better name */ /* This needs a better name */

View File

@ -84,6 +84,7 @@ extern int git_config_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *); extern int git_config_string(const char **, const char *, const char *);
extern int git_config_pathname(const char **, const char *, const char *); extern int git_config_pathname(const char **, const char *, const char *);
extern int git_config_expiry_date(timestamp_t *, const char *, const char *); extern int git_config_expiry_date(timestamp_t *, const char *, const char *);
extern int git_config_color(char *, const char *, const char *);
extern int git_config_set_in_file_gently(const char *, const char *, const char *); extern int git_config_set_in_file_gently(const char *, const char *, const char *);
extern void git_config_set_in_file(const char *, const char *, const char *); extern void git_config_set_in_file(const char *, const char *, const char *);
extern int git_config_set_gently(const char *, const char *); extern int git_config_set_gently(const char *, const char *);

View File

@ -933,6 +933,36 @@ test_expect_success 'get --expiry-date' '
test_must_fail git config --expiry-date date.invalid1 test_must_fail git config --expiry-date date.invalid1
' '
test_expect_success 'get --type=color' '
rm .git/config &&
git config foo.color "red" &&
git config --get --type=color foo.color >actual.raw &&
test_decode_color <actual.raw >actual &&
echo "<RED>" >expect &&
test_cmp expect actual
'
cat >expect << EOF
[foo]
color = red
EOF
test_expect_success 'set --type=color' '
rm .git/config &&
git config --type=color foo.color "red" &&
test_cmp expect .git/config
'
test_expect_success 'get --type=color barfs on non-color' '
echo "[foo]bar=not-a-color" >.git/config &&
test_must_fail git config --get --type=color foo.bar
'
test_expect_success 'set --type=color barfs on non-color' '
test_must_fail git config --type=color foo.color "not-a-color" 2>error &&
test_i18ngrep "cannot parse color" error
'
cat > expect << EOF cat > expect << EOF
[quote] [quote]
leading = " test" leading = " test"

36
t/t1310-config-default.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/sh
test_description='Test git config in different settings (with --default)'
. ./test-lib.sh
test_expect_success 'uses --default when entry missing' '
echo quux >expect &&
git config -f config --default=quux core.foo >actual &&
test_cmp expect actual
'
test_expect_success 'does not use --default when entry present' '
echo bar >expect &&
git -c core.foo=bar config --default=baz core.foo >actual &&
test_cmp expect actual
'
test_expect_success 'canonicalizes --default with appropriate type' '
echo true >expect &&
git config -f config --default=yes --bool core.foo >actual &&
test_cmp expect actual
'
test_expect_success 'dies when --default cannot be parsed' '
test_must_fail git config -f config --type=expiry-date --default=x --get \
not.a.section 2>error &&
test_i18ngrep "failed to format default config value" error
'
test_expect_success 'does not allow --default without --get' '
test_must_fail git config --default=quux --unset a.section >output 2>&1 &&
test_i18ngrep "\-\-default is only applicable to" output
'
test_done