Merge branch 'ps/config-env-pairs'
Introduce two new ways to feed configuration variable-value pairs via environment variables, and tweak the way GIT_CONFIG_PARAMETERS encodes variable/value pairs to make it more robust. * ps/config-env-pairs: config: allow specifying config entries via envvar pairs environment: make `getenv_safe()` a public function config: store "git -c" variables using more robust format config: parse more robust format in GIT_CONFIG_PARAMETERS config: extract function to parse config pairs quote: make sq_dequote_step() a public function config: add new way to pass config via `--config-env` git: add `--super-prefix` to usage string
This commit is contained in:
commit
294e949fa2
@ -346,6 +346,22 @@ GIT_CONFIG_NOSYSTEM::
|
||||
|
||||
See also <<FILES>>.
|
||||
|
||||
GIT_CONFIG_COUNT::
|
||||
GIT_CONFIG_KEY_<n>::
|
||||
GIT_CONFIG_VALUE_<n>::
|
||||
If GIT_CONFIG_COUNT is set to a positive number, all environment pairs
|
||||
GIT_CONFIG_KEY_<n> and GIT_CONFIG_VALUE_<n> up to that number will be
|
||||
added to the process's runtime configuration. The config pairs are
|
||||
zero-indexed. Any missing key or value is treated as an error. An empty
|
||||
GIT_CONFIG_COUNT is treated the same as GIT_CONFIG_COUNT=0, namely no
|
||||
pairs are processed. These environment variables will override values
|
||||
in configuration files, but will be overridden by any explicit options
|
||||
passed via `git -c`.
|
||||
+
|
||||
This is useful for cases where you want to spawn multiple git commands
|
||||
with a common configuration but cannot depend on a configuration file,
|
||||
for example when writing scripts.
|
||||
|
||||
|
||||
[[EXAMPLES]]
|
||||
EXAMPLES
|
||||
|
@ -13,7 +13,7 @@ SYNOPSIS
|
||||
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
|
||||
[-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
|
||||
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
|
||||
[--super-prefix=<path>]
|
||||
[--super-prefix=<path>] [--config-env <name>=<envvar>]
|
||||
<command> [<args>]
|
||||
|
||||
DESCRIPTION
|
||||
@ -80,6 +80,28 @@ config file). Including the equals but with an empty value (like `git -c
|
||||
foo.bar= ...`) sets `foo.bar` to the empty string which `git config
|
||||
--type=bool` will convert to `false`.
|
||||
|
||||
--config-env=<name>=<envvar>::
|
||||
Like `-c <name>=<value>`, give configuration variable
|
||||
'<name>' a value, where <envvar> is the name of an
|
||||
environment variable from which to retrieve the value. Unlike
|
||||
`-c` there is no shortcut for directly setting the value to an
|
||||
empty string, instead the environment variable itself must be
|
||||
set to the empty string. It is an error if the `<envvar>` does not exist
|
||||
in the environment. `<envvar>` may not contain an equals sign
|
||||
to avoid ambiguity with `<name>`s which contain one.
|
||||
+
|
||||
This is useful for cases where you want to pass transitory
|
||||
configuration options to git, but are doing so on OS's where
|
||||
other processes might be able to read your cmdline
|
||||
(e.g. `/proc/self/cmdline`), but not your environ
|
||||
(e.g. `/proc/self/environ`). That behavior is the default on
|
||||
Linux, but may not be on your system.
|
||||
+
|
||||
Note that this might add security for variables such as
|
||||
`http.extraHeader` where the sensitive information is part of
|
||||
the value, but not e.g. `url.<base>.insteadOf` where the
|
||||
sensitive information can be part of the key.
|
||||
|
||||
--exec-path[=<path>]::
|
||||
Path to wherever your core Git programs are installed.
|
||||
This can also be controlled by setting the GIT_EXEC_PATH
|
||||
|
1
cache.h
1
cache.h
@ -472,6 +472,7 @@ static inline enum object_type object_type(unsigned int mode)
|
||||
#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
|
||||
#define CONFIG_ENVIRONMENT "GIT_CONFIG"
|
||||
#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
|
||||
#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
|
||||
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
|
||||
#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
|
||||
#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
|
||||
|
209
config.c
209
config.c
@ -8,6 +8,7 @@
|
||||
#include "cache.h"
|
||||
#include "branch.h"
|
||||
#include "config.h"
|
||||
#include "environment.h"
|
||||
#include "repository.h"
|
||||
#include "lockfile.h"
|
||||
#include "exec-cmd.h"
|
||||
@ -332,7 +333,7 @@ int git_config_include(const char *var, const char *value, void *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void git_config_push_parameter(const char *text)
|
||||
static void git_config_push_split_parameter(const char *key, const char *value)
|
||||
{
|
||||
struct strbuf env = STRBUF_INIT;
|
||||
const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
|
||||
@ -340,11 +341,74 @@ void git_config_push_parameter(const char *text)
|
||||
strbuf_addstr(&env, old);
|
||||
strbuf_addch(&env, ' ');
|
||||
}
|
||||
sq_quote_buf(&env, text);
|
||||
sq_quote_buf(&env, key);
|
||||
strbuf_addch(&env, '=');
|
||||
if (value)
|
||||
sq_quote_buf(&env, value);
|
||||
setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
|
||||
strbuf_release(&env);
|
||||
}
|
||||
|
||||
void git_config_push_parameter(const char *text)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
/*
|
||||
* When we see:
|
||||
*
|
||||
* section.subsection=with=equals.key=value
|
||||
*
|
||||
* we cannot tell if it means:
|
||||
*
|
||||
* [section "subsection=with=equals"]
|
||||
* key = value
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* [section]
|
||||
* subsection = with=equals.key=value
|
||||
*
|
||||
* We parse left-to-right for the first "=", meaning we'll prefer to
|
||||
* keep the value intact over the subsection. This is historical, but
|
||||
* also sensible since values are more likely to contain odd or
|
||||
* untrusted input than a section name.
|
||||
*
|
||||
* A missing equals is explicitly allowed (as a bool-only entry).
|
||||
*/
|
||||
value = strchr(text, '=');
|
||||
if (value) {
|
||||
char *key = xmemdupz(text, value - text);
|
||||
git_config_push_split_parameter(key, value + 1);
|
||||
free(key);
|
||||
} else {
|
||||
git_config_push_split_parameter(text, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void git_config_push_env(const char *spec)
|
||||
{
|
||||
char *key;
|
||||
const char *env_name;
|
||||
const char *env_value;
|
||||
|
||||
env_name = strrchr(spec, '=');
|
||||
if (!env_name)
|
||||
die(_("invalid config format: %s"), spec);
|
||||
key = xmemdupz(spec, env_name - spec);
|
||||
env_name++;
|
||||
if (!*env_name)
|
||||
die(_("missing environment variable name for configuration '%.*s'"),
|
||||
(int)(env_name - spec - 1), spec);
|
||||
|
||||
env_value = getenv(env_name);
|
||||
if (!env_value)
|
||||
die(_("missing environment variable '%s' for configuration '%.*s'"),
|
||||
env_name, (int)(env_name - spec - 1), spec);
|
||||
|
||||
git_config_push_split_parameter(key, env_value);
|
||||
free(key);
|
||||
}
|
||||
|
||||
static inline int iskeychar(int c)
|
||||
{
|
||||
return isalnum(c) || c == '-';
|
||||
@ -437,11 +501,26 @@ int git_config_key_is_valid(const char *key)
|
||||
return !git_config_parse_key_1(key, NULL, NULL, 1);
|
||||
}
|
||||
|
||||
static int config_parse_pair(const char *key, const char *value,
|
||||
config_fn_t fn, void *data)
|
||||
{
|
||||
char *canonical_name;
|
||||
int ret;
|
||||
|
||||
if (!strlen(key))
|
||||
return error(_("empty config key"));
|
||||
if (git_config_parse_key(key, &canonical_name, NULL))
|
||||
return -1;
|
||||
|
||||
ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
|
||||
free(canonical_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int git_config_parse_parameter(const char *text,
|
||||
config_fn_t fn, void *data)
|
||||
{
|
||||
const char *value;
|
||||
char *canonical_name;
|
||||
struct strbuf **pair;
|
||||
int ret;
|
||||
|
||||
@ -462,51 +541,131 @@ int git_config_parse_parameter(const char *text,
|
||||
return error(_("bogus config parameter: %s"), text);
|
||||
}
|
||||
|
||||
if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
|
||||
free(canonical_name);
|
||||
}
|
||||
ret = config_parse_pair(pair[0]->buf, value, fn, data);
|
||||
strbuf_list_free(pair);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_config_env_list(char *env, config_fn_t fn, void *data)
|
||||
{
|
||||
char *cur = env;
|
||||
while (cur && *cur) {
|
||||
const char *key = sq_dequote_step(cur, &cur);
|
||||
if (!key)
|
||||
return error(_("bogus format in %s"),
|
||||
CONFIG_DATA_ENVIRONMENT);
|
||||
|
||||
if (!cur || isspace(*cur)) {
|
||||
/* old-style 'key=value' */
|
||||
if (git_config_parse_parameter(key, fn, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
else if (*cur == '=') {
|
||||
/* new-style 'key'='value' */
|
||||
const char *value;
|
||||
|
||||
cur++;
|
||||
if (*cur == '\'') {
|
||||
/* quoted value */
|
||||
value = sq_dequote_step(cur, &cur);
|
||||
if (!value || (cur && !isspace(*cur))) {
|
||||
return error(_("bogus format in %s"),
|
||||
CONFIG_DATA_ENVIRONMENT);
|
||||
}
|
||||
} else if (!*cur || isspace(*cur)) {
|
||||
/* implicit bool: 'key'= */
|
||||
value = NULL;
|
||||
} else {
|
||||
return error(_("bogus format in %s"),
|
||||
CONFIG_DATA_ENVIRONMENT);
|
||||
}
|
||||
|
||||
if (config_parse_pair(key, value, fn, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* unknown format */
|
||||
return error(_("bogus format in %s"),
|
||||
CONFIG_DATA_ENVIRONMENT);
|
||||
}
|
||||
|
||||
if (cur) {
|
||||
while (isspace(*cur))
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_from_parameters(config_fn_t fn, void *data)
|
||||
{
|
||||
const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
|
||||
const char *env;
|
||||
struct strbuf envvar = STRBUF_INIT;
|
||||
struct strvec to_free = STRVEC_INIT;
|
||||
int ret = 0;
|
||||
char *envw;
|
||||
const char **argv = NULL;
|
||||
int nr = 0, alloc = 0;
|
||||
int i;
|
||||
char *envw = NULL;
|
||||
struct config_source source;
|
||||
|
||||
if (!env)
|
||||
return 0;
|
||||
|
||||
memset(&source, 0, sizeof(source));
|
||||
source.prev = cf;
|
||||
source.origin_type = CONFIG_ORIGIN_CMDLINE;
|
||||
cf = &source;
|
||||
|
||||
/* sq_dequote will write over it */
|
||||
envw = xstrdup(env);
|
||||
env = getenv(CONFIG_COUNT_ENVIRONMENT);
|
||||
if (env) {
|
||||
unsigned long count;
|
||||
char *endp;
|
||||
int i;
|
||||
|
||||
if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
|
||||
ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT);
|
||||
goto out;
|
||||
count = strtoul(env, &endp, 10);
|
||||
if (*endp) {
|
||||
ret = error(_("bogus count in %s"), CONFIG_COUNT_ENVIRONMENT);
|
||||
goto out;
|
||||
}
|
||||
if (count > INT_MAX) {
|
||||
ret = error(_("too many entries in %s"), CONFIG_COUNT_ENVIRONMENT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
const char *key, *value;
|
||||
|
||||
strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i);
|
||||
key = getenv_safe(&to_free, envvar.buf);
|
||||
if (!key) {
|
||||
ret = error(_("missing config key %s"), envvar.buf);
|
||||
goto out;
|
||||
}
|
||||
strbuf_reset(&envvar);
|
||||
|
||||
strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i);
|
||||
value = getenv_safe(&to_free, envvar.buf);
|
||||
if (!value) {
|
||||
ret = error(_("missing config value %s"), envvar.buf);
|
||||
goto out;
|
||||
}
|
||||
strbuf_reset(&envvar);
|
||||
|
||||
if (config_parse_pair(key, value, fn, data) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
if (git_config_parse_parameter(argv[i], fn, data) < 0) {
|
||||
env = getenv(CONFIG_DATA_ENVIRONMENT);
|
||||
if (env) {
|
||||
/* sq_dequote will write over it */
|
||||
envw = xstrdup(env);
|
||||
if (parse_config_env_list(envw, fn, data) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(argv);
|
||||
strbuf_release(&envvar);
|
||||
strvec_clear(&to_free);
|
||||
free(envw);
|
||||
cf = source.prev;
|
||||
return ret;
|
||||
|
1
config.h
1
config.h
@ -138,6 +138,7 @@ int git_config_from_mem(config_fn_t fn,
|
||||
int git_config_from_blob_oid(config_fn_t fn, const char *name,
|
||||
const struct object_id *oid, void *data);
|
||||
void git_config_push_parameter(const char *text);
|
||||
void git_config_push_env(const char *spec);
|
||||
int git_config_from_parameters(config_fn_t fn, void *data);
|
||||
void read_early_config(config_fn_t cb, void *data);
|
||||
void read_very_early_config(config_fn_t cb, void *data);
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "branch.h"
|
||||
#include "environment.h"
|
||||
#include "repository.h"
|
||||
#include "config.h"
|
||||
#include "refs.h"
|
||||
@ -116,6 +117,7 @@ const char * const local_repo_env[] = {
|
||||
ALTERNATE_DB_ENVIRONMENT,
|
||||
CONFIG_ENVIRONMENT,
|
||||
CONFIG_DATA_ENVIRONMENT,
|
||||
CONFIG_COUNT_ENVIRONMENT,
|
||||
DB_ENVIRONMENT,
|
||||
GIT_DIR_ENVIRONMENT,
|
||||
GIT_WORK_TREE_ENVIRONMENT,
|
||||
@ -152,11 +154,7 @@ static char *expand_namespace(const char *raw_namespace)
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper of getenv() that returns a strdup value. This value is kept
|
||||
* in argv to be freed later.
|
||||
*/
|
||||
static const char *getenv_safe(struct strvec *argv, const char *name)
|
||||
const char *getenv_safe(struct strvec *argv, const char *name)
|
||||
{
|
||||
const char *value = getenv(name);
|
||||
|
||||
|
12
environment.h
Normal file
12
environment.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef ENVIRONMENT_H
|
||||
#define ENVIRONMENT_H
|
||||
|
||||
#include "strvec.h"
|
||||
|
||||
/*
|
||||
* Wrapper of getenv() that returns a strdup value. This value is kept
|
||||
* in argv to be freed later.
|
||||
*/
|
||||
const char *getenv_safe(struct strvec *argv, const char *name);
|
||||
|
||||
#endif
|
3
git.c
3
git.c
@ -29,6 +29,7 @@ const char git_usage_string[] =
|
||||
" [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
|
||||
" [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
|
||||
" [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
|
||||
" [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
|
||||
" <command> [<args>]");
|
||||
|
||||
const char git_more_info_string[] =
|
||||
@ -254,6 +255,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
git_config_push_parameter((*argv)[1]);
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (skip_prefix(cmd, "--config-env=", &cmd)) {
|
||||
git_config_push_env(cmd);
|
||||
} else if (!strcmp(cmd, "--literal-pathspecs")) {
|
||||
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
|
||||
if (envchanged)
|
||||
|
15
quote.c
15
quote.c
@ -116,7 +116,7 @@ void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
static char *sq_dequote_step(char *arg, char **next)
|
||||
char *sq_dequote_step(char *arg, char **next)
|
||||
{
|
||||
char *dst = arg;
|
||||
char *src = arg;
|
||||
@ -153,11 +153,8 @@ static char *sq_dequote_step(char *arg, char **next)
|
||||
}
|
||||
/* Fallthrough */
|
||||
default:
|
||||
if (!next || !isspace(*src))
|
||||
if (!next)
|
||||
return NULL;
|
||||
do {
|
||||
c = *++src;
|
||||
} while (isspace(c));
|
||||
*dst = 0;
|
||||
*next = src;
|
||||
return arg;
|
||||
@ -182,6 +179,14 @@ static int sq_dequote_to_argv_internal(char *arg,
|
||||
char *dequoted = sq_dequote_step(next, &next);
|
||||
if (!dequoted)
|
||||
return -1;
|
||||
if (next) {
|
||||
char c;
|
||||
if (!isspace(*next))
|
||||
return -1;
|
||||
do {
|
||||
c = *++next;
|
||||
} while (isspace(c));
|
||||
}
|
||||
if (argv) {
|
||||
ALLOC_GROW(*argv, *nr + 1, *alloc);
|
||||
(*argv)[(*nr)++] = dequoted;
|
||||
|
18
quote.h
18
quote.h
@ -42,12 +42,26 @@ void sq_quote_buf_pretty(struct strbuf *, const char *src);
|
||||
void sq_quote_argv_pretty(struct strbuf *, const char **argv);
|
||||
void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv);
|
||||
|
||||
/* This unwraps what sq_quote() produces in place, but returns
|
||||
/*
|
||||
* This unwraps what sq_quote() produces in place, but returns
|
||||
* NULL if the input does not look like what sq_quote would have
|
||||
* produced.
|
||||
* produced (the full string must be a single quoted item).
|
||||
*/
|
||||
char *sq_dequote(char *);
|
||||
|
||||
/*
|
||||
* Like sq_dequote(), but dequote a single item, and leave "next" pointing to
|
||||
* the next character. E.g., in the string:
|
||||
*
|
||||
* 'one' 'two' 'three'
|
||||
*
|
||||
* after the first call, the return value would be the unquoted string "one",
|
||||
* with "next" pointing to the space between "one" and "two"). The caller is
|
||||
* responsible for advancing the pointer to the start of the next item before
|
||||
* calling sq_dequote_step() again.
|
||||
*/
|
||||
char *sq_dequote_step(char *src, char **next);
|
||||
|
||||
/*
|
||||
* Same as the above, but can be used to unwrap many arguments in the
|
||||
* same string separated by space. Like sq_quote, it works in place,
|
||||
|
@ -1289,6 +1289,58 @@ test_expect_success 'git -c is not confused by empty environment' '
|
||||
GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
|
||||
v="${SQ}key.one=foo${SQ}" &&
|
||||
v="$v ${SQ}key.two=bar${SQ}" &&
|
||||
v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
|
||||
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
key.one foo
|
||||
key.two bar
|
||||
key.ambiguous section.whatever=value
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
|
||||
v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
|
||||
v="$v ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
|
||||
v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
|
||||
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
key.one foo
|
||||
key.two bar
|
||||
key.ambiguous=section.whatever value
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'old and new-style entries can mix' '
|
||||
v="${SQ}key.oldone=oldfoo${SQ}" &&
|
||||
v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
|
||||
v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
|
||||
v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
|
||||
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
key.oldone oldfoo
|
||||
key.newone newfoo
|
||||
key.oldtwo oldbar
|
||||
key.newtwo newbar
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'old and new bools with ambiguous subsection' '
|
||||
v="${SQ}key.with=equals.oldbool${SQ}" &&
|
||||
v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
|
||||
GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
key.with equals.oldbool
|
||||
key.with=equals.newbool
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
|
||||
cat >expect <<-\EOF &&
|
||||
env.one one
|
||||
@ -1311,6 +1363,173 @@ test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
|
||||
git config --get-regexp "env.*"
|
||||
'
|
||||
|
||||
test_expect_success 'git --config-env=key=envvar support' '
|
||||
cat >expect <<-\EOF &&
|
||||
value
|
||||
value
|
||||
false
|
||||
EOF
|
||||
{
|
||||
ENVVAR=value git --config-env=core.name=ENVVAR config core.name &&
|
||||
ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config foo.camelcase &&
|
||||
ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag
|
||||
} >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git --config-env fails with invalid parameters' '
|
||||
test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
|
||||
test_i18ngrep "invalid config format: foo.flag" error &&
|
||||
test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
|
||||
test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
|
||||
sane_unset NONEXISTENT &&
|
||||
test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
|
||||
test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
|
||||
'
|
||||
|
||||
test_expect_success 'git -c and --config-env work together' '
|
||||
cat >expect <<-\EOF &&
|
||||
bar.cmd cmd-value
|
||||
bar.env env-value
|
||||
EOF
|
||||
ENVVAR=env-value git \
|
||||
-c bar.cmd=cmd-value \
|
||||
--config-env=bar.env=ENVVAR \
|
||||
config --get-regexp "^bar.*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git -c and --config-env override each other' '
|
||||
cat >expect <<-\EOF &&
|
||||
env
|
||||
cmd
|
||||
EOF
|
||||
{
|
||||
ENVVAR=env git -c bar.bar=cmd --config-env=bar.bar=ENVVAR config bar.bar &&
|
||||
ENVVAR=env git --config-env=bar.bar=ENVVAR -c bar.bar=cmd config bar.bar
|
||||
} >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--config-env handles keys with equals' '
|
||||
echo value=with=equals >expect &&
|
||||
ENVVAR=value=with=equals git \
|
||||
--config-env=section.subsection=with=equals.key=ENVVAR \
|
||||
config section.subsection=with=equals.key >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git config handles environment config pairs' '
|
||||
GIT_CONFIG_COUNT=2 \
|
||||
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
|
||||
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
|
||||
git config --get-regexp "pair.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
pair.one foo
|
||||
pair.two bar
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git config ignores pairs without count' '
|
||||
test_must_fail env GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
|
||||
git config pair.one 2>error &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
||||
test_expect_success 'git config ignores pairs with zero count' '
|
||||
test_must_fail env \
|
||||
GIT_CONFIG_COUNT=0 \
|
||||
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
|
||||
git config pair.one
|
||||
'
|
||||
|
||||
test_expect_success 'git config ignores pairs exceeding count' '
|
||||
GIT_CONFIG_COUNT=1 \
|
||||
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
|
||||
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
|
||||
git config --get-regexp "pair.*" >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
pair.one value
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git config ignores pairs with zero count' '
|
||||
test_must_fail env \
|
||||
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
|
||||
git config pair.one >error &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
||||
test_expect_success 'git config ignores pairs with empty count' '
|
||||
test_must_fail env \
|
||||
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
|
||||
git config pair.one >error &&
|
||||
test_must_be_empty error
|
||||
'
|
||||
|
||||
test_expect_success 'git config fails with invalid count' '
|
||||
test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
|
||||
test_i18ngrep "bogus count" error &&
|
||||
test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
|
||||
test_i18ngrep "too many entries" error
|
||||
'
|
||||
|
||||
test_expect_success 'git config fails with missing config key' '
|
||||
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
|
||||
git config --list 2>error &&
|
||||
test_i18ngrep "missing config key" error
|
||||
'
|
||||
|
||||
test_expect_success 'git config fails with missing config value' '
|
||||
test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
|
||||
git config --list 2>error &&
|
||||
test_i18ngrep "missing config value" error
|
||||
'
|
||||
|
||||
test_expect_success 'git config fails with invalid config pair key' '
|
||||
test_must_fail env GIT_CONFIG_COUNT=1 \
|
||||
GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
|
||||
git config --list &&
|
||||
test_must_fail env GIT_CONFIG_COUNT=1 \
|
||||
GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
|
||||
git config --list
|
||||
'
|
||||
|
||||
test_expect_success 'environment overrides config file' '
|
||||
test_when_finished "rm -f .git/config" &&
|
||||
cat >.git/config <<-EOF &&
|
||||
[pair]
|
||||
one = value
|
||||
EOF
|
||||
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
|
||||
git config pair.one >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
override
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
|
||||
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
|
||||
GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
|
||||
git config pair.one >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
override
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'command line overrides environment config' '
|
||||
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
|
||||
git -c pair.one=override config pair.one >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
override
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git config --edit works' '
|
||||
git config -f tmp test.value no &&
|
||||
echo test.value=yes >expect &&
|
||||
@ -1656,9 +1875,11 @@ test_expect_success '--show-origin with --list' '
|
||||
file:.git/config user.override=local
|
||||
file:.git/config include.path=../include/relative.include
|
||||
file:.git/../include/relative.include user.relative=include
|
||||
command line: user.environ=true
|
||||
command line: user.cmdline=true
|
||||
EOF
|
||||
git -c user.cmdline=true config --list --show-origin >output &&
|
||||
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
|
||||
git -c user.cmdline=true config --list --show-origin >output &&
|
||||
test_cmp expect output
|
||||
'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user