зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/config-include'
* jk/config-include: : An assignment to the include.path pseudo-variable causes the named file : to be included in-place when Git looks up configuration variables. config: add include directive config: eliminate config_exclusive_filename config: stop using config_exclusive_filename config: provide a version of git_config with more options config: teach git_config_rename_section a file argument config: teach git_config_set_multivar_in_file a default path config: copy the return value of prefix_filename t1300: add missing &&-chaining docs/api-config: minor clarifications docs: add a basic description of the config API
This commit is contained in:
Коммит
fd1727f5fa
|
@ -84,6 +84,17 @@ customary UNIX fashion.
|
|||
|
||||
Some variables may require a special value format.
|
||||
|
||||
Includes
|
||||
~~~~~~~~
|
||||
|
||||
You can include one config file from another by setting the special
|
||||
`include.path` variable to the name of the file to be included. The
|
||||
included file is expanded immediately, as if its contents had been
|
||||
found at the location of the include directive. If the value of the
|
||||
`include.path` variable is a relative path, the path is considered to be
|
||||
relative to the configuration file in which the include directive was
|
||||
found. See below for examples.
|
||||
|
||||
Example
|
||||
~~~~~~~
|
||||
|
||||
|
@ -106,6 +117,10 @@ Example
|
|||
gitProxy="ssh" for "kernel.org"
|
||||
gitProxy=default-proxy ; for the rest
|
||||
|
||||
[include]
|
||||
path = /path/to/foo.inc ; include by absolute path
|
||||
path = foo ; expand "foo" relative to the current file
|
||||
|
||||
Variables
|
||||
~~~~~~~~~
|
||||
|
||||
|
|
|
@ -178,6 +178,11 @@ See also <<FILES>>.
|
|||
Opens an editor to modify the specified config file; either
|
||||
'--system', '--global', or repository (default).
|
||||
|
||||
--includes::
|
||||
--no-includes::
|
||||
Respect `include.*` directives in config files when looking up
|
||||
values. Defaults to on.
|
||||
|
||||
[[FILES]]
|
||||
FILES
|
||||
-----
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
config API
|
||||
==========
|
||||
|
||||
The config API gives callers a way to access git configuration files
|
||||
(and files which have the same syntax). See linkgit:git-config[1] for a
|
||||
discussion of the config file syntax.
|
||||
|
||||
General Usage
|
||||
-------------
|
||||
|
||||
Config files are parsed linearly, and each variable found is passed to a
|
||||
caller-provided callback function. The callback function is responsible
|
||||
for any actions to be taken on the config option, and is free to ignore
|
||||
some options. It is not uncommon for the configuration to be parsed
|
||||
several times during the run of a git program, with different callbacks
|
||||
picking out different variables useful to themselves.
|
||||
|
||||
A config callback function takes three parameters:
|
||||
|
||||
- the name of the parsed variable. This is in canonical "flat" form: the
|
||||
section, subsection, and variable segments will be separated by dots,
|
||||
and the section and variable segments will be all lowercase. E.g.,
|
||||
`core.ignorecase`, `diff.SomeType.textconv`.
|
||||
|
||||
- the value of the found variable, as a string. If the variable had no
|
||||
value specified, the value will be NULL (typically this means it
|
||||
should be interpreted as boolean true).
|
||||
|
||||
- a void pointer passed in by the caller of the config API; this can
|
||||
contain callback-specific data
|
||||
|
||||
A config callback should return 0 for success, or -1 if the variable
|
||||
could not be parsed properly.
|
||||
|
||||
Basic Config Querying
|
||||
---------------------
|
||||
|
||||
Most programs will simply want to look up variables in all config files
|
||||
that git knows about, using the normal precedence rules. To do this,
|
||||
call `git_config` with a callback function and void data pointer.
|
||||
|
||||
`git_config` will read all config sources in order of increasing
|
||||
priority. Thus a callback should typically overwrite previously-seen
|
||||
entries with new ones (e.g., if both the user-wide `~/.gitconfig` and
|
||||
repo-specific `.git/config` contain `color.ui`, the config machinery
|
||||
will first feed the user-wide one to the callback, and then the
|
||||
repo-specific one; by overwriting, the higher-priority repo-specific
|
||||
value is left at the end).
|
||||
|
||||
The `git_config_with_options` function lets the caller examine config
|
||||
while adjusting some of the default behavior of `git_config`. It should
|
||||
almost never be used by "regular" git code that is looking up
|
||||
configuration variables. It is intended for advanced callers like
|
||||
`git-config`, which are intentionally tweaking the normal config-lookup
|
||||
process. It takes two extra parameters:
|
||||
|
||||
`filename`::
|
||||
If this parameter is non-NULL, it specifies the name of a file to
|
||||
parse for configuration, rather than looking in the usual files. Regular
|
||||
`git_config` defaults to `NULL`.
|
||||
|
||||
`respect_includes`::
|
||||
Specify whether include directives should be followed in parsed files.
|
||||
Regular `git_config` defaults to `1`.
|
||||
|
||||
There is a special version of `git_config` called `git_config_early`.
|
||||
This version takes an additional parameter to specify the repository
|
||||
config, instead of having it looked up via `git_path`. This is useful
|
||||
early in a git program before the repository has been found. Unless
|
||||
you're working with early setup code, you probably don't want to use
|
||||
this.
|
||||
|
||||
Reading Specific Files
|
||||
----------------------
|
||||
|
||||
To read a specific file in git-config format, use
|
||||
`git_config_from_file`. This takes the same callback and data parameters
|
||||
as `git_config`.
|
||||
|
||||
Value Parsing Helpers
|
||||
---------------------
|
||||
|
||||
To aid in parsing string values, the config API provides callbacks with
|
||||
a number of helper functions, including:
|
||||
|
||||
`git_config_int`::
|
||||
Parse the string to an integer, including unit factors. Dies on error;
|
||||
otherwise, returns the parsed result.
|
||||
|
||||
`git_config_ulong`::
|
||||
Identical to `git_config_int`, but for unsigned longs.
|
||||
|
||||
`git_config_bool`::
|
||||
Parse a string into a boolean value, respecting keywords like "true" and
|
||||
"false". Integer values are converted into true/false values (when they
|
||||
are non-zero or zero, respectively). Other values cause a die(). If
|
||||
parsing is successful, the return value is the result.
|
||||
|
||||
`git_config_bool_or_int`::
|
||||
Same as `git_config_bool`, except that integers are returned as-is, and
|
||||
an `is_bool` flag is unset.
|
||||
|
||||
`git_config_maybe_bool`::
|
||||
Same as `git_config_bool`, except that it returns -1 on error rather
|
||||
than dying.
|
||||
|
||||
`git_config_string`::
|
||||
Allocates and copies the value string into the `dest` parameter; if no
|
||||
string is given, prints an error message and returns -1.
|
||||
|
||||
`git_config_pathname`::
|
||||
Similar to `git_config_string`, but expands `~` or `~user` into the
|
||||
user's home directory when found at the beginning of the path.
|
||||
|
||||
Include Directives
|
||||
------------------
|
||||
|
||||
By default, the config parser does not respect include directives.
|
||||
However, a caller can use the special `git_config_include` wrapper
|
||||
callback to support them. To do so, you simply wrap your "real" callback
|
||||
function and data pointer in a `struct config_include_data`, and pass
|
||||
the wrapper to the regular config-reading functions. For example:
|
||||
|
||||
-------------------------------------------
|
||||
int read_file_with_include(const char *file, config_fn_t fn, void *data)
|
||||
{
|
||||
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
||||
inc.fn = fn;
|
||||
inc.data = data;
|
||||
return git_config_from_file(git_config_include, file, &inc);
|
||||
}
|
||||
-------------------------------------------
|
||||
|
||||
`git_config` respects includes automatically. The lower-level
|
||||
`git_config_from_file` does not.
|
||||
|
||||
Writing Config Files
|
||||
--------------------
|
||||
|
||||
TODO
|
|
@ -25,6 +25,7 @@ static const char *given_config_file;
|
|||
static int actions, types;
|
||||
static const char *get_color_slot, *get_colorbool_slot;
|
||||
static int end_null;
|
||||
static int respect_includes = -1;
|
||||
|
||||
#define ACTION_GET (1<<0)
|
||||
#define ACTION_GET_ALL (1<<1)
|
||||
|
@ -74,6 +75,7 @@ static struct option builtin_config_options[] = {
|
|||
OPT_BIT(0, "path", &types, "value is a path (file or directory name)", TYPE_PATH),
|
||||
OPT_GROUP("Other"),
|
||||
OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
|
||||
OPT_BOOL(0, "includes", &respect_includes, "respect include directives on lookup"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
|
@ -161,8 +163,11 @@ static int get_value(const char *key_, const char *regex_)
|
|||
int ret = -1;
|
||||
char *global = NULL, *repo_config = NULL;
|
||||
const char *system_wide = NULL, *local;
|
||||
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
||||
config_fn_t fn;
|
||||
void *data;
|
||||
|
||||
local = config_exclusive_filename;
|
||||
local = given_config_file;
|
||||
if (!local) {
|
||||
const char *home = getenv("HOME");
|
||||
local = repo_config = git_pathdup("config");
|
||||
|
@ -213,19 +218,28 @@ static int get_value(const char *key_, const char *regex_)
|
|||
}
|
||||
}
|
||||
|
||||
fn = show_config;
|
||||
data = NULL;
|
||||
if (respect_includes) {
|
||||
inc.fn = fn;
|
||||
inc.data = data;
|
||||
fn = git_config_include;
|
||||
data = &inc;
|
||||
}
|
||||
|
||||
if (do_all && system_wide)
|
||||
git_config_from_file(show_config, system_wide, NULL);
|
||||
git_config_from_file(fn, system_wide, data);
|
||||
if (do_all && global)
|
||||
git_config_from_file(show_config, global, NULL);
|
||||
git_config_from_file(fn, global, data);
|
||||
if (do_all)
|
||||
git_config_from_file(show_config, local, NULL);
|
||||
git_config_from_parameters(show_config, NULL);
|
||||
git_config_from_file(fn, local, data);
|
||||
git_config_from_parameters(fn, data);
|
||||
if (!do_all && !seen)
|
||||
git_config_from_file(show_config, local, NULL);
|
||||
git_config_from_file(fn, local, data);
|
||||
if (!do_all && !seen && global)
|
||||
git_config_from_file(show_config, global, NULL);
|
||||
git_config_from_file(fn, global, data);
|
||||
if (!do_all && !seen && system_wide)
|
||||
git_config_from_file(show_config, system_wide, NULL);
|
||||
git_config_from_file(fn, system_wide, data);
|
||||
|
||||
free(key);
|
||||
if (regexp) {
|
||||
|
@ -301,7 +315,8 @@ static void get_color(const char *def_color)
|
|||
{
|
||||
get_color_found = 0;
|
||||
parsed_color[0] = '\0';
|
||||
git_config(git_get_color_config, NULL);
|
||||
git_config_with_options(git_get_color_config, NULL,
|
||||
given_config_file, respect_includes);
|
||||
|
||||
if (!get_color_found && def_color)
|
||||
color_parse(def_color, "command line", parsed_color);
|
||||
|
@ -328,7 +343,8 @@ static int get_colorbool(int print)
|
|||
{
|
||||
get_colorbool_found = -1;
|
||||
get_diff_color_found = -1;
|
||||
git_config(git_get_colorbool_config, NULL);
|
||||
git_config_with_options(git_get_colorbool_config, NULL,
|
||||
given_config_file, respect_includes);
|
||||
|
||||
if (get_colorbool_found < 0) {
|
||||
if (!strcmp(get_colorbool_slot, "color.diff"))
|
||||
|
@ -351,7 +367,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
int nongit = !startup_info->have_repository;
|
||||
char *value;
|
||||
|
||||
config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
|
||||
given_config_file = getenv(CONFIG_ENVIRONMENT);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_config_options,
|
||||
builtin_config_usage,
|
||||
|
@ -366,24 +382,28 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
char *home = getenv("HOME");
|
||||
if (home) {
|
||||
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
|
||||
config_exclusive_filename = user_config;
|
||||
given_config_file = user_config;
|
||||
} else {
|
||||
die("$HOME not set");
|
||||
}
|
||||
}
|
||||
else if (use_system_config)
|
||||
config_exclusive_filename = git_etc_gitconfig();
|
||||
given_config_file = git_etc_gitconfig();
|
||||
else if (use_local_config)
|
||||
config_exclusive_filename = git_pathdup("config");
|
||||
given_config_file = git_pathdup("config");
|
||||
else if (given_config_file) {
|
||||
if (!is_absolute_path(given_config_file) && prefix)
|
||||
config_exclusive_filename = prefix_filename(prefix,
|
||||
strlen(prefix),
|
||||
given_config_file);
|
||||
given_config_file =
|
||||
xstrdup(prefix_filename(prefix,
|
||||
strlen(prefix),
|
||||
given_config_file));
|
||||
else
|
||||
config_exclusive_filename = given_config_file;
|
||||
given_config_file = given_config_file;
|
||||
}
|
||||
|
||||
if (respect_includes == -1)
|
||||
respect_includes = !given_config_file;
|
||||
|
||||
if (end_null) {
|
||||
term = '\0';
|
||||
delim = '\n';
|
||||
|
@ -420,28 +440,30 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
|
||||
if (actions == ACTION_LIST) {
|
||||
check_argc(argc, 0, 0);
|
||||
if (git_config(show_all_config, NULL) < 0) {
|
||||
if (config_exclusive_filename)
|
||||
if (git_config_with_options(show_all_config, NULL,
|
||||
given_config_file,
|
||||
respect_includes) < 0) {
|
||||
if (given_config_file)
|
||||
die_errno("unable to read config file '%s'",
|
||||
config_exclusive_filename);
|
||||
given_config_file);
|
||||
else
|
||||
die("error processing config file(s)");
|
||||
}
|
||||
}
|
||||
else if (actions == ACTION_EDIT) {
|
||||
check_argc(argc, 0, 0);
|
||||
if (!config_exclusive_filename && nongit)
|
||||
if (!given_config_file && nongit)
|
||||
die("not in a git directory");
|
||||
git_config(git_default_config, NULL);
|
||||
launch_editor(config_exclusive_filename ?
|
||||
config_exclusive_filename : git_path("config"),
|
||||
launch_editor(given_config_file ?
|
||||
given_config_file : git_path("config"),
|
||||
NULL, NULL);
|
||||
}
|
||||
else if (actions == ACTION_SET) {
|
||||
int ret;
|
||||
check_argc(argc, 2, 2);
|
||||
value = normalize_value(argv[0], argv[1]);
|
||||
ret = git_config_set(argv[0], value);
|
||||
ret = git_config_set_in_file(given_config_file, argv[0], value);
|
||||
if (ret == CONFIG_NOTHING_SET)
|
||||
error("cannot overwrite multiple values with a single value\n"
|
||||
" Use a regexp, --add or --replace-all to change %s.", argv[0]);
|
||||
|
@ -450,17 +472,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
else if (actions == ACTION_SET_ALL) {
|
||||
check_argc(argc, 2, 3);
|
||||
value = normalize_value(argv[0], argv[1]);
|
||||
return git_config_set_multivar(argv[0], value, argv[2], 0);
|
||||
return git_config_set_multivar_in_file(given_config_file,
|
||||
argv[0], value, argv[2], 0);
|
||||
}
|
||||
else if (actions == ACTION_ADD) {
|
||||
check_argc(argc, 2, 2);
|
||||
value = normalize_value(argv[0], argv[1]);
|
||||
return git_config_set_multivar(argv[0], value, "^$", 0);
|
||||
return git_config_set_multivar_in_file(given_config_file,
|
||||
argv[0], value, "^$", 0);
|
||||
}
|
||||
else if (actions == ACTION_REPLACE_ALL) {
|
||||
check_argc(argc, 2, 3);
|
||||
value = normalize_value(argv[0], argv[1]);
|
||||
return git_config_set_multivar(argv[0], value, argv[2], 1);
|
||||
return git_config_set_multivar_in_file(given_config_file,
|
||||
argv[0], value, argv[2], 1);
|
||||
}
|
||||
else if (actions == ACTION_GET) {
|
||||
check_argc(argc, 1, 2);
|
||||
|
@ -481,18 +506,22 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
else if (actions == ACTION_UNSET) {
|
||||
check_argc(argc, 1, 2);
|
||||
if (argc == 2)
|
||||
return git_config_set_multivar(argv[0], NULL, argv[1], 0);
|
||||
return git_config_set_multivar_in_file(given_config_file,
|
||||
argv[0], NULL, argv[1], 0);
|
||||
else
|
||||
return git_config_set(argv[0], NULL);
|
||||
return git_config_set_in_file(given_config_file,
|
||||
argv[0], NULL);
|
||||
}
|
||||
else if (actions == ACTION_UNSET_ALL) {
|
||||
check_argc(argc, 1, 2);
|
||||
return git_config_set_multivar(argv[0], NULL, argv[1], 1);
|
||||
return git_config_set_multivar_in_file(given_config_file,
|
||||
argv[0], NULL, argv[1], 1);
|
||||
}
|
||||
else if (actions == ACTION_RENAME_SECTION) {
|
||||
int ret;
|
||||
check_argc(argc, 2, 2);
|
||||
ret = git_config_rename_section(argv[0], argv[1]);
|
||||
ret = git_config_rename_section_in_file(given_config_file,
|
||||
argv[0], argv[1]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
|
@ -501,7 +530,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
|||
else if (actions == ACTION_REMOVE_SECTION) {
|
||||
int ret;
|
||||
check_argc(argc, 1, 1);
|
||||
ret = git_config_rename_section(argv[0], NULL);
|
||||
ret = git_config_rename_section_in_file(given_config_file,
|
||||
argv[0], NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0)
|
||||
|
|
11
cache.h
11
cache.h
|
@ -1115,6 +1115,8 @@ extern int git_config_from_file(config_fn_t fn, const char *, void *);
|
|||
extern void git_config_push_parameter(const char *text);
|
||||
extern int git_config_from_parameters(config_fn_t fn, void *data);
|
||||
extern int git_config(config_fn_t fn, void *);
|
||||
extern int git_config_with_options(config_fn_t fn, void *,
|
||||
const char *filename, int respect_includes);
|
||||
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
|
||||
extern int git_parse_ulong(const char *, unsigned long *);
|
||||
extern int git_config_int(const char *, const char *);
|
||||
|
@ -1130,6 +1132,7 @@ extern int git_config_parse_key(const char *, char **, int *);
|
|||
extern int git_config_set_multivar(const char *, const char *, const char *, int);
|
||||
extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
|
||||
extern int git_config_rename_section(const char *, const char *);
|
||||
extern int git_config_rename_section_in_file(const char *, const char *, const char *);
|
||||
extern const char *git_etc_gitconfig(void);
|
||||
extern int check_repository_format_version(const char *var, const char *value, void *cb);
|
||||
extern int git_env_bool(const char *, int);
|
||||
|
@ -1140,7 +1143,13 @@ extern const char *get_commit_output_encoding(void);
|
|||
|
||||
extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
|
||||
|
||||
extern const char *config_exclusive_filename;
|
||||
struct config_include_data {
|
||||
int depth;
|
||||
config_fn_t fn;
|
||||
void *data;
|
||||
};
|
||||
#define CONFIG_INCLUDE_INIT { 0 }
|
||||
extern int git_config_include(const char *name, const char *value, void *data);
|
||||
|
||||
#define MAX_GITNAME (1000)
|
||||
extern char git_default_email[MAX_GITNAME];
|
||||
|
|
127
config.c
127
config.c
|
@ -26,7 +26,68 @@ static config_file *cf;
|
|||
|
||||
static int zlib_compression_seen;
|
||||
|
||||
const char *config_exclusive_filename = NULL;
|
||||
#define MAX_INCLUDE_DEPTH 10
|
||||
static const char include_depth_advice[] =
|
||||
"exceeded maximum include depth (%d) while including\n"
|
||||
" %s\n"
|
||||
"from\n"
|
||||
" %s\n"
|
||||
"Do you have circular includes?";
|
||||
static int handle_path_include(const char *path, struct config_include_data *inc)
|
||||
{
|
||||
int ret = 0;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
/*
|
||||
* Use an absolute path as-is, but interpret relative paths
|
||||
* based on the including config file.
|
||||
*/
|
||||
if (!is_absolute_path(path)) {
|
||||
char *slash;
|
||||
|
||||
if (!cf || !cf->name)
|
||||
return error("relative config includes must come from files");
|
||||
|
||||
slash = find_last_dir_sep(cf->name);
|
||||
if (slash)
|
||||
strbuf_add(&buf, cf->name, slash - cf->name + 1);
|
||||
strbuf_addstr(&buf, path);
|
||||
path = buf.buf;
|
||||
}
|
||||
|
||||
if (!access(path, R_OK)) {
|
||||
if (++inc->depth > MAX_INCLUDE_DEPTH)
|
||||
die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
|
||||
cf && cf->name ? cf->name : "the command line");
|
||||
ret = git_config_from_file(git_config_include, path, inc);
|
||||
inc->depth--;
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int git_config_include(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct config_include_data *inc = data;
|
||||
const char *type;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Pass along all values, including "include" directives; this makes it
|
||||
* possible to query information on the includes themselves.
|
||||
*/
|
||||
ret = inc->fn(var, value, inc->data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
type = skip_prefix(var, "include.");
|
||||
if (!type)
|
||||
return ret;
|
||||
|
||||
if (!strcmp(type, "path"))
|
||||
ret = handle_path_include(value, inc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lowercase(char *p)
|
||||
{
|
||||
|
@ -879,9 +940,6 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
|
|||
int ret = 0, found = 0;
|
||||
const char *home = NULL;
|
||||
|
||||
/* Setting $GIT_CONFIG makes git read _only_ the given config file. */
|
||||
if (config_exclusive_filename)
|
||||
return git_config_from_file(fn, config_exclusive_filename, data);
|
||||
if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
|
||||
ret += git_config_from_file(fn, git_etc_gitconfig(),
|
||||
data);
|
||||
|
@ -917,10 +975,26 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
|
|||
return ret == 0 ? found : ret;
|
||||
}
|
||||
|
||||
int git_config(config_fn_t fn, void *data)
|
||||
int git_config_with_options(config_fn_t fn, void *data,
|
||||
const char *filename, int respect_includes)
|
||||
{
|
||||
char *repo_config = NULL;
|
||||
int ret;
|
||||
struct config_include_data inc = CONFIG_INCLUDE_INIT;
|
||||
|
||||
if (respect_includes) {
|
||||
inc.fn = fn;
|
||||
inc.data = data;
|
||||
fn = git_config_include;
|
||||
data = &inc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a specific filename, use it. Otherwise, follow the
|
||||
* regular lookup sequence.
|
||||
*/
|
||||
if (filename)
|
||||
return git_config_from_file(fn, filename, data);
|
||||
|
||||
repo_config = git_pathdup("config");
|
||||
ret = git_config_early(fn, data, repo_config);
|
||||
|
@ -929,6 +1003,11 @@ int git_config(config_fn_t fn, void *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int git_config(config_fn_t fn, void *data)
|
||||
{
|
||||
return git_config_with_options(fn, data, NULL, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all the stuff for git_config_set() below.
|
||||
*/
|
||||
|
@ -1233,6 +1312,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
|
|||
int fd = -1, in_fd;
|
||||
int ret;
|
||||
struct lock_file *lock = NULL;
|
||||
char *filename_buf = NULL;
|
||||
|
||||
/* parse-key returns negative; flip the sign to feed exit(3) */
|
||||
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
|
||||
|
@ -1241,6 +1321,8 @@ int git_config_set_multivar_in_file(const char *config_filename,
|
|||
|
||||
store.multi_replace = multi_replace;
|
||||
|
||||
if (!config_filename)
|
||||
config_filename = filename_buf = git_pathdup("config");
|
||||
|
||||
/*
|
||||
* The lock serves a purpose in addition to locking: the new
|
||||
|
@ -1410,6 +1492,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
|
|||
out_free:
|
||||
if (lock)
|
||||
rollback_lock_file(lock);
|
||||
free(filename_buf);
|
||||
return ret;
|
||||
|
||||
write_err_out:
|
||||
|
@ -1421,19 +1504,8 @@ write_err_out:
|
|||
int git_config_set_multivar(const char *key, const char *value,
|
||||
const char *value_regex, int multi_replace)
|
||||
{
|
||||
const char *config_filename;
|
||||
char *buf = NULL;
|
||||
int ret;
|
||||
|
||||
if (config_exclusive_filename)
|
||||
config_filename = config_exclusive_filename;
|
||||
else
|
||||
config_filename = buf = git_pathdup("config");
|
||||
|
||||
ret = git_config_set_multivar_in_file(config_filename, key, value,
|
||||
value_regex, multi_replace);
|
||||
free(buf);
|
||||
return ret;
|
||||
return git_config_set_multivar_in_file(NULL, key, value, value_regex,
|
||||
multi_replace);
|
||||
}
|
||||
|
||||
static int section_name_match (const char *buf, const char *name)
|
||||
|
@ -1476,19 +1548,19 @@ static int section_name_match (const char *buf, const char *name)
|
|||
}
|
||||
|
||||
/* if new_name == NULL, the section is removed instead */
|
||||
int git_config_rename_section(const char *old_name, const char *new_name)
|
||||
int git_config_rename_section_in_file(const char *config_filename,
|
||||
const char *old_name, const char *new_name)
|
||||
{
|
||||
int ret = 0, remove = 0;
|
||||
char *config_filename;
|
||||
char *filename_buf = NULL;
|
||||
struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
|
||||
int out_fd;
|
||||
char buf[1024];
|
||||
FILE *config_file;
|
||||
|
||||
if (config_exclusive_filename)
|
||||
config_filename = xstrdup(config_exclusive_filename);
|
||||
else
|
||||
config_filename = git_pathdup("config");
|
||||
if (!config_filename)
|
||||
config_filename = filename_buf = git_pathdup("config");
|
||||
|
||||
out_fd = hold_lock_file_for_update(lock, config_filename, 0);
|
||||
if (out_fd < 0) {
|
||||
ret = error("could not lock config file %s", config_filename);
|
||||
|
@ -1552,10 +1624,15 @@ unlock_and_out:
|
|||
if (commit_lock_file(lock) < 0)
|
||||
ret = error("could not commit config file %s", config_filename);
|
||||
out:
|
||||
free(config_filename);
|
||||
free(filename_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int git_config_rename_section(const char *old_name, const char *new_name)
|
||||
{
|
||||
return git_config_rename_section_in_file(NULL, old_name, new_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this to report error for your variable that should not
|
||||
* get a boolean value (i.e. "[my] var" means "true").
|
||||
|
|
|
@ -451,13 +451,21 @@ test_expect_success 'refer config from subdirectory' '
|
|||
mkdir x &&
|
||||
(
|
||||
cd x &&
|
||||
echo strasse >expect
|
||||
echo strasse >expect &&
|
||||
git config --get --file ../other-config ein.bahn >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'refer config from subdirectory via GIT_CONFIG' '
|
||||
(
|
||||
cd x &&
|
||||
GIT_CONFIG=../other-config git config --get ein.bahn >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
[ein]
|
||||
bahn = strasse
|
||||
|
@ -960,4 +968,21 @@ test_expect_success 'git -c complains about empty key and value' '
|
|||
test_must_fail git -c "" rev-parse
|
||||
'
|
||||
|
||||
test_expect_success 'git config --edit works' '
|
||||
git config -f tmp test.value no &&
|
||||
echo test.value=yes >expect &&
|
||||
GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit &&
|
||||
git config -f tmp --list >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'git config --edit respects core.editor' '
|
||||
git config -f tmp test.value no &&
|
||||
echo test.value=yes >expect &&
|
||||
test_config core.editor "echo [test]value=yes >" &&
|
||||
git config -f tmp --edit &&
|
||||
git config -f tmp --list >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='test config file include directives'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'include file by absolute path' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
|
||||
echo 1 >expect &&
|
||||
git config test.one >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'include file by relative path' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
echo 1 >expect &&
|
||||
git config test.one >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'chained relative paths' '
|
||||
mkdir subdir &&
|
||||
echo "[test]three = 3" >subdir/three &&
|
||||
echo "[include]path = three" >subdir/two &&
|
||||
echo "[include]path = subdir/two" >.gitconfig &&
|
||||
echo 3 >expect &&
|
||||
git config test.three >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'include options can still be examined' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
echo one >expect &&
|
||||
git config include.path >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'listing includes option and expansion' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
cat >expect <<-\EOF &&
|
||||
include.path=one
|
||||
test.one=1
|
||||
EOF
|
||||
git config --list >actual.full &&
|
||||
grep -v ^core actual.full >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'single file lookup does not expand includes by default' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
test_must_fail git config -f .gitconfig test.one &&
|
||||
test_must_fail git config --global test.one &&
|
||||
echo 1 >expect &&
|
||||
git config --includes -f .gitconfig test.one >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'single file list does not expand includes by default' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
echo "include.path=one" >expect &&
|
||||
git config -f .gitconfig --list >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'writing config file does not expand includes' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
git config test.two 2 &&
|
||||
echo 2 >expect &&
|
||||
git config --no-includes test.two >actual &&
|
||||
test_cmp expect actual &&
|
||||
test_must_fail git config --no-includes test.one
|
||||
'
|
||||
|
||||
test_expect_success 'config modification does not affect includes' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo "[include]path = one" >.gitconfig &&
|
||||
git config test.one 2 &&
|
||||
echo 1 >expect &&
|
||||
git config -f one test.one >actual &&
|
||||
test_cmp expect actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
1
|
||||
2
|
||||
EOF
|
||||
git config --get-all test.one >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'missing include files are ignored' '
|
||||
cat >.gitconfig <<-\EOF &&
|
||||
[include]path = foo
|
||||
[test]value = yes
|
||||
EOF
|
||||
echo yes >expect &&
|
||||
git config test.value >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'absolute includes from command line work' '
|
||||
echo "[test]one = 1" >one &&
|
||||
echo 1 >expect &&
|
||||
git -c include.path="$PWD/one" config test.one >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'relative includes from command line fail' '
|
||||
echo "[test]one = 1" >one &&
|
||||
test_must_fail git -c include.path=one config test.one
|
||||
'
|
||||
|
||||
test_expect_success 'include cycles are detected' '
|
||||
cat >.gitconfig <<-\EOF &&
|
||||
[test]value = gitconfig
|
||||
[include]path = cycle
|
||||
EOF
|
||||
cat >cycle <<-\EOF &&
|
||||
[test]value = cycle
|
||||
[include]path = .gitconfig
|
||||
EOF
|
||||
cat >expect <<-\EOF &&
|
||||
gitconfig
|
||||
cycle
|
||||
EOF
|
||||
test_must_fail git config --get-all test.value 2>stderr &&
|
||||
grep "exceeded maximum include depth" stderr
|
||||
'
|
||||
|
||||
test_done
|
Загрузка…
Ссылка в новой задаче