mirror of
https://github.com/git/git.git
synced 2026-02-28 10:47:33 +00:00
Merge branch 'ds/config-list-with-type' into jch
"git config list" is taught to show the values interpreted for specific type with "--type=<X>" option. * ds/config-list-with-type: config: use an enum for type config: restructure format_config() config: format colors quietly color: add color_parse_quietly() config: format expiry dates quietly config: format paths gently config: format bools or strings in helper config: format bools or ints gently config: format bools gently config: format int64s gently config: make 'git config list --type=<X>' work config: add 'gently' parameter to format_config() config: move show_all_config()
This commit is contained in:
@@ -240,6 +240,9 @@ Valid `<type>`'s include:
|
||||
that the given value is canonicalize-able as an ANSI color, but it is written
|
||||
as-is.
|
||||
+
|
||||
If the command is in `list` mode, then the `--type <type>` argument will apply
|
||||
to each listed config value. If the value does not successfully parse in that
|
||||
format, then it will be omitted from the list.
|
||||
|
||||
--bool::
|
||||
--int::
|
||||
|
||||
308
builtin/config.c
308
builtin/config.c
@@ -3,6 +3,7 @@
|
||||
#include "abspath.h"
|
||||
#include "config.h"
|
||||
#include "color.h"
|
||||
#include "date.h"
|
||||
#include "editor.h"
|
||||
#include "environment.h"
|
||||
#include "gettext.h"
|
||||
@@ -85,6 +86,17 @@ struct config_location_options {
|
||||
.respect_includes_opt = -1, \
|
||||
}
|
||||
|
||||
enum config_type {
|
||||
TYPE_NONE = 0,
|
||||
TYPE_BOOL,
|
||||
TYPE_INT,
|
||||
TYPE_BOOL_OR_INT,
|
||||
TYPE_PATH,
|
||||
TYPE_EXPIRY_DATE,
|
||||
TYPE_COLOR,
|
||||
TYPE_BOOL_OR_STR,
|
||||
};
|
||||
|
||||
#define CONFIG_TYPE_OPTIONS(type) \
|
||||
OPT_GROUP(N_("Type")), \
|
||||
OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
|
||||
@@ -110,7 +122,7 @@ struct config_display_options {
|
||||
int show_origin;
|
||||
int show_scope;
|
||||
int show_keys;
|
||||
int type;
|
||||
enum config_type type;
|
||||
char *default_value;
|
||||
/* Populated via `display_options_init()`. */
|
||||
int term;
|
||||
@@ -121,16 +133,9 @@ struct config_display_options {
|
||||
.term = '\n', \
|
||||
.delim = '=', \
|
||||
.key_delim = ' ', \
|
||||
.type = TYPE_NONE, \
|
||||
}
|
||||
|
||||
#define TYPE_BOOL 1
|
||||
#define TYPE_INT 2
|
||||
#define TYPE_BOOL_OR_INT 3
|
||||
#define TYPE_PATH 4
|
||||
#define TYPE_EXPIRY_DATE 5
|
||||
#define TYPE_COLOR 6
|
||||
#define TYPE_BOOL_OR_STR 7
|
||||
|
||||
#define OPT_CALLBACK_VALUE(s, l, v, h, i) { \
|
||||
.type = OPTION_CALLBACK, \
|
||||
.short_name = (s), \
|
||||
@@ -231,104 +236,231 @@ static void show_config_scope(const struct config_display_options *opts,
|
||||
strbuf_addch(buf, term);
|
||||
}
|
||||
|
||||
static int show_all_config(const char *key_, const char *value_,
|
||||
const struct config_context *ctx,
|
||||
void *cb)
|
||||
{
|
||||
const struct config_display_options *opts = cb;
|
||||
const struct key_value_info *kvi = ctx->kvi;
|
||||
|
||||
if (opts->show_origin || opts->show_scope) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
if (opts->show_scope)
|
||||
show_config_scope(opts, kvi, &buf);
|
||||
if (opts->show_origin)
|
||||
show_config_origin(opts, kvi, &buf);
|
||||
/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
|
||||
fwrite(buf.buf, 1, buf.len, stdout);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
if (!opts->omit_values && value_)
|
||||
printf("%s%c%s%c", key_, opts->delim, value_, opts->term);
|
||||
else
|
||||
printf("%s%c", key_, opts->term);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct strbuf_list {
|
||||
struct strbuf *items;
|
||||
int nr;
|
||||
int alloc;
|
||||
};
|
||||
|
||||
static int format_config_int64(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
const struct key_value_info *kvi,
|
||||
int gently)
|
||||
{
|
||||
int64_t v = 0;
|
||||
if (gently) {
|
||||
if (!git_parse_int64(value_, &v))
|
||||
return -1;
|
||||
} else {
|
||||
/* may die() */
|
||||
v = git_config_int64(key_, value_ ? value_ : "", kvi);
|
||||
}
|
||||
|
||||
strbuf_addf(buf, "%"PRId64, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_config_bool(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
int gently)
|
||||
{
|
||||
int v = 0;
|
||||
if (gently) {
|
||||
if ((v = git_parse_maybe_bool(value_)) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
/* may die() */
|
||||
v = git_config_bool(key_, value_);
|
||||
}
|
||||
|
||||
strbuf_addstr(buf, v ? "true" : "false");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_config_bool_or_int(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
const struct key_value_info *kvi,
|
||||
int gently)
|
||||
{
|
||||
int v, is_bool = 0;
|
||||
|
||||
if (gently) {
|
||||
v = git_parse_maybe_bool_text(value_);
|
||||
|
||||
if (v >= 0)
|
||||
is_bool = 1;
|
||||
else if (!git_parse_int(value_, &v))
|
||||
return -1;
|
||||
} else {
|
||||
v = git_config_bool_or_int(key_, value_, kvi,
|
||||
&is_bool);
|
||||
}
|
||||
|
||||
if (is_bool)
|
||||
strbuf_addstr(buf, v ? "true" : "false");
|
||||
else
|
||||
strbuf_addf(buf, "%d", v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This mode is always gentle. */
|
||||
static int format_config_bool_or_str(struct strbuf *buf,
|
||||
const char *value_)
|
||||
{
|
||||
int v = git_parse_maybe_bool(value_);
|
||||
if (v < 0)
|
||||
strbuf_addstr(buf, value_);
|
||||
else
|
||||
strbuf_addstr(buf, v ? "true" : "false");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_config_path(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
int gently)
|
||||
{
|
||||
char *v;
|
||||
|
||||
if (git_config_pathname(&v, key_, value_) < 0)
|
||||
return -1;
|
||||
|
||||
if (v)
|
||||
strbuf_addstr(buf, v);
|
||||
else
|
||||
return gently ? -1 : 1; /* :(optional)no-such-file */
|
||||
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_config_expiry_date(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
int quietly)
|
||||
{
|
||||
timestamp_t t;
|
||||
if (quietly) {
|
||||
if (parse_expiry_date(value_, &t))
|
||||
return -1;
|
||||
} else if (git_config_expiry_date(&t, key_, value_) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_addf(buf, "%"PRItime, t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int format_config_color(struct strbuf *buf,
|
||||
const char *key_,
|
||||
const char *value_,
|
||||
int gently)
|
||||
{
|
||||
char v[COLOR_MAXLEN];
|
||||
|
||||
if (gently) {
|
||||
if (color_parse_quietly(value_, v) < 0)
|
||||
return -1;
|
||||
} else if (git_config_color(v, key_, value_) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_addstr(buf, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Format the configuration key-value pair (`key_`, `value_`) and
|
||||
* append it into strbuf `buf`. Returns a negative value on failure,
|
||||
* 0 on success, 1 on a missing optional value (i.e., telling the
|
||||
* caller to pretend that <key_,value_> did not exist).
|
||||
*
|
||||
* Note: 'gently' is currently ignored, but will be implemented in
|
||||
* a future change.
|
||||
*/
|
||||
static int format_config(const struct config_display_options *opts,
|
||||
struct strbuf *buf, const char *key_,
|
||||
const char *value_, const struct key_value_info *kvi)
|
||||
const char *value_, const struct key_value_info *kvi,
|
||||
int gently)
|
||||
{
|
||||
int res = 0;
|
||||
if (opts->show_scope)
|
||||
show_config_scope(opts, kvi, buf);
|
||||
if (opts->show_origin)
|
||||
show_config_origin(opts, kvi, buf);
|
||||
if (opts->show_keys)
|
||||
strbuf_addstr(buf, key_);
|
||||
if (!opts->omit_values) {
|
||||
if (opts->show_keys)
|
||||
strbuf_addch(buf, opts->key_delim);
|
||||
|
||||
if (opts->type == TYPE_INT)
|
||||
strbuf_addf(buf, "%"PRId64,
|
||||
git_config_int64(key_, value_ ? value_ : "", kvi));
|
||||
else if (opts->type == TYPE_BOOL)
|
||||
strbuf_addstr(buf, git_config_bool(key_, value_) ?
|
||||
"true" : "false");
|
||||
else if (opts->type == TYPE_BOOL_OR_INT) {
|
||||
int is_bool, v;
|
||||
v = git_config_bool_or_int(key_, value_, kvi,
|
||||
&is_bool);
|
||||
if (is_bool)
|
||||
strbuf_addstr(buf, v ? "true" : "false");
|
||||
else
|
||||
strbuf_addf(buf, "%d", v);
|
||||
} else if (opts->type == TYPE_BOOL_OR_STR) {
|
||||
int v = git_parse_maybe_bool(value_);
|
||||
if (v < 0)
|
||||
strbuf_addstr(buf, value_);
|
||||
else
|
||||
strbuf_addstr(buf, v ? "true" : "false");
|
||||
} else if (opts->type == TYPE_PATH) {
|
||||
char *v;
|
||||
if (git_config_pathname(&v, key_, value_) < 0)
|
||||
return -1;
|
||||
if (v)
|
||||
strbuf_addstr(buf, v);
|
||||
else
|
||||
return 1; /* :(optional)no-such-file */
|
||||
free((char *)v);
|
||||
} else if (opts->type == TYPE_EXPIRY_DATE) {
|
||||
timestamp_t t;
|
||||
if (git_config_expiry_date(&t, key_, value_) < 0)
|
||||
return -1;
|
||||
strbuf_addf(buf, "%"PRItime, t);
|
||||
} else if (opts->type == TYPE_COLOR) {
|
||||
char v[COLOR_MAXLEN];
|
||||
if (git_config_color(v, key_, value_) < 0)
|
||||
return -1;
|
||||
strbuf_addstr(buf, v);
|
||||
} else if (value_) {
|
||||
if (opts->omit_values)
|
||||
goto terminator;
|
||||
|
||||
if (opts->show_keys)
|
||||
strbuf_addch(buf, opts->key_delim);
|
||||
|
||||
switch (opts->type) {
|
||||
case TYPE_INT:
|
||||
res = format_config_int64(buf, key_, value_, kvi, gently);
|
||||
break;
|
||||
|
||||
case TYPE_BOOL:
|
||||
res = format_config_bool(buf, key_, value_, gently);
|
||||
break;
|
||||
|
||||
case TYPE_BOOL_OR_INT:
|
||||
res = format_config_bool_or_int(buf, key_, value_, kvi, gently);
|
||||
break;
|
||||
|
||||
case TYPE_BOOL_OR_STR:
|
||||
res = format_config_bool_or_str(buf, value_);
|
||||
break;
|
||||
|
||||
case TYPE_PATH:
|
||||
res = format_config_path(buf, key_, value_, gently);
|
||||
break;
|
||||
|
||||
case TYPE_EXPIRY_DATE:
|
||||
res = format_config_expiry_date(buf, key_, value_, gently);
|
||||
break;
|
||||
|
||||
case TYPE_COLOR:
|
||||
res = format_config_color(buf, key_, value_, gently);
|
||||
break;
|
||||
|
||||
case TYPE_NONE:
|
||||
if (value_) {
|
||||
strbuf_addstr(buf, value_);
|
||||
} else {
|
||||
/* Just show the key name; back out delimiter */
|
||||
if (opts->show_keys)
|
||||
strbuf_setlen(buf, buf->len - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG("undefined type %d", opts->type);
|
||||
}
|
||||
|
||||
terminator:
|
||||
strbuf_addch(buf, opts->term);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int show_all_config(const char *key_, const char *value_,
|
||||
const struct config_context *ctx,
|
||||
void *cb)
|
||||
{
|
||||
const struct config_display_options *opts = cb;
|
||||
const struct key_value_info *kvi = ctx->kvi;
|
||||
struct strbuf formatted = STRBUF_INIT;
|
||||
|
||||
if (format_config(opts, &formatted, key_, value_, kvi, 1) >= 0)
|
||||
fwrite(formatted.buf, 1, formatted.len, stdout);
|
||||
|
||||
strbuf_release(&formatted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -372,7 +504,7 @@ static int collect_config(const char *key_, const char *value_,
|
||||
strbuf_init(&values->items[values->nr], 0);
|
||||
|
||||
status = format_config(data->display_opts, &values->items[values->nr++],
|
||||
key_, value_, kvi);
|
||||
key_, value_, kvi, 0);
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (status) {
|
||||
@@ -463,7 +595,7 @@ static int get_value(const struct config_location_options *opts,
|
||||
strbuf_init(item, 0);
|
||||
|
||||
status = format_config(display_opts, item, key_,
|
||||
display_opts->default_value, &kvi);
|
||||
display_opts->default_value, &kvi, 0);
|
||||
if (status < 0)
|
||||
die(_("failed to format default config value: %s"),
|
||||
display_opts->default_value);
|
||||
@@ -743,7 +875,7 @@ static int get_urlmatch(const struct config_location_options *opts,
|
||||
|
||||
status = format_config(&display_opts, &buf, item->string,
|
||||
matched->value_is_null ? NULL : matched->value.buf,
|
||||
&matched->kvi);
|
||||
&matched->kvi, 0);
|
||||
if (!status)
|
||||
fwrite(buf.buf, 1, buf.len, stdout);
|
||||
strbuf_release(&buf);
|
||||
@@ -868,6 +1000,19 @@ static void display_options_init(struct config_display_options *opts)
|
||||
}
|
||||
}
|
||||
|
||||
static void display_options_init_list(struct config_display_options *opts)
|
||||
{
|
||||
opts->show_keys = 1;
|
||||
|
||||
if (opts->end_nul) {
|
||||
display_options_init(opts);
|
||||
} else {
|
||||
opts->term = '\n';
|
||||
opts->delim = ' ';
|
||||
opts->key_delim = '=';
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_config_list(int argc, const char **argv, const char *prefix,
|
||||
struct repository *repo UNUSED)
|
||||
{
|
||||
@@ -886,7 +1031,7 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix,
|
||||
check_argc(argc, 0, 0);
|
||||
|
||||
location_options_init(&location_opts, prefix);
|
||||
display_options_init(&display_opts);
|
||||
display_options_init_list(&display_opts);
|
||||
|
||||
setup_auto_pager("config", 1);
|
||||
|
||||
@@ -1317,6 +1462,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (actions == ACTION_LIST) {
|
||||
check_argc(argc, 0, 0);
|
||||
display_options_init_list(&display_opts);
|
||||
if (config_with_options(show_all_config, &display_opts,
|
||||
&location_opts.source, the_repository,
|
||||
&location_opts.options) < 0) {
|
||||
|
||||
25
color.c
25
color.c
@@ -223,11 +223,6 @@ static int parse_attr(const char *name, size_t len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int color_parse(const char *value, char *dst)
|
||||
{
|
||||
return color_parse_mem(value, strlen(value), dst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the ANSI color codes for "c" to "out"; the string should
|
||||
* already have the ANSI escape code in it. "out" should have enough
|
||||
@@ -264,7 +259,8 @@ static int color_empty(const struct color *c)
|
||||
return c->type <= COLOR_NORMAL;
|
||||
}
|
||||
|
||||
int color_parse_mem(const char *value, int value_len, char *dst)
|
||||
static int color_parse_mem_1(const char *value, int value_len,
|
||||
char *dst, int quiet)
|
||||
{
|
||||
const char *ptr = value;
|
||||
int len = value_len;
|
||||
@@ -365,10 +361,25 @@ int color_parse_mem(const char *value, int value_len, char *dst)
|
||||
OUT(0);
|
||||
return 0;
|
||||
bad:
|
||||
return error(_("invalid color value: %.*s"), value_len, value);
|
||||
return quiet ? -1 : error(_("invalid color value: %.*s"), value_len, value);
|
||||
#undef OUT
|
||||
}
|
||||
|
||||
int color_parse_mem(const char *value, int value_len, char *dst)
|
||||
{
|
||||
return color_parse_mem_1(value, value_len, dst, 0);
|
||||
}
|
||||
|
||||
int color_parse(const char *value, char *dst)
|
||||
{
|
||||
return color_parse_mem(value, strlen(value), dst);
|
||||
}
|
||||
|
||||
int color_parse_quietly(const char *value, char *dst)
|
||||
{
|
||||
return color_parse_mem_1(value, strlen(value), dst, 1);
|
||||
}
|
||||
|
||||
enum git_colorbool git_config_colorbool(const char *var, const char *value)
|
||||
{
|
||||
if (value) {
|
||||
|
||||
1
color.h
1
color.h
@@ -118,6 +118,7 @@ bool want_color_fd(int fd, enum git_colorbool var);
|
||||
* terminal.
|
||||
*/
|
||||
int color_parse(const char *value, char *dst);
|
||||
int color_parse_quietly(const char *value, char *dst);
|
||||
int color_parse_mem(const char *value, int len, char *dst);
|
||||
|
||||
/*
|
||||
|
||||
@@ -2459,9 +2459,15 @@ done
|
||||
|
||||
cat >.git/config <<-\EOF &&
|
||||
[section]
|
||||
foo = true
|
||||
foo = True
|
||||
number = 10
|
||||
big = 1M
|
||||
path = ~/dir
|
||||
red = red
|
||||
blue = Blue
|
||||
date = Fri Jun 4 15:46:55 2010
|
||||
missing=:(optional)no-such-path
|
||||
exists=:(optional)expect
|
||||
EOF
|
||||
|
||||
test_expect_success 'identical modern --type specifiers are allowed' '
|
||||
@@ -2503,6 +2509,82 @@ test_expect_success 'unset type specifiers may be reset to conflicting ones' '
|
||||
test_cmp_config 1048576 --type=bool --no-type --type=int section.big
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=int shows only canonicalizable int values' '
|
||||
cat >expect <<-EOF &&
|
||||
section.number=10
|
||||
section.big=1048576
|
||||
EOF
|
||||
|
||||
git config ${mode_prefix}list --type=int >actual 2>err &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=bool shows only canonicalizable bool values' '
|
||||
cat >expect <<-EOF &&
|
||||
section.foo=true
|
||||
section.number=true
|
||||
section.big=true
|
||||
EOF
|
||||
|
||||
git config ${mode_prefix}list --type=bool >actual 2>err &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=bool-or-int shows only canonicalizable values' '
|
||||
cat >expect <<-EOF &&
|
||||
section.foo=true
|
||||
section.number=10
|
||||
section.big=1048576
|
||||
EOF
|
||||
|
||||
git config ${mode_prefix}list --type=bool-or-int >actual 2>err &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=path shows only canonicalizable path values' '
|
||||
cat >expect <<-EOF &&
|
||||
section.foo=True
|
||||
section.number=10
|
||||
section.big=1M
|
||||
section.path=$HOME/dir
|
||||
section.red=red
|
||||
section.blue=Blue
|
||||
section.date=Fri Jun 4 15:46:55 2010
|
||||
section.exists=expect
|
||||
EOF
|
||||
|
||||
git config ${mode_prefix}list --type=path >actual 2>err &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=expiry-date shows only canonicalizable dates' '
|
||||
git config ${mode_prefix}list --type=expiry-date >actual 2>err &&
|
||||
|
||||
# section.number and section.big parse as relative dates that could
|
||||
# have clock skew in their results.
|
||||
test_grep section.big actual &&
|
||||
test_grep section.number actual &&
|
||||
test_grep "section.date=$(git config --type=expiry-date section.$key)" actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'list --type=color shows only canonicalizable color values' '
|
||||
cat >expect <<-EOF &&
|
||||
section.number=<>
|
||||
section.red=<RED>
|
||||
section.blue=<BLUE>
|
||||
EOF
|
||||
|
||||
git config ${mode_prefix}list --type=color >actual.raw 2>err &&
|
||||
test_decode_color <actual.raw >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success '--type rejects unknown specifiers' '
|
||||
test_must_fail git config --type=nonsense section.foo 2>error &&
|
||||
test_grep "unrecognized --type argument" error
|
||||
|
||||
Reference in New Issue
Block a user