From 0a5f57592728e7667d9e60e309f6270db9fdb67b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 16 Feb 2012 03:04:05 -0500 Subject: config: teach git_config_set_multivar_in_file a default path The git_config_set_multivar_in_file function takes a filename argument to specify the file into which the values should be written. Currently, this value must be non-NULL. Callers which want to write to the default location must use the regular, non-"in_file" version, which will either write to config_exclusive_filename, or to the repo config if the exclusive filename is NULL. Let's migrate the "default to using repo config" logic into the "in_file" form. That will let callers get the same default-if-NULL behavior as one gets with config_exclusive_filename, but without having to use the global variable. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 40f9c6d10..351ae0b62 100644 --- a/config.c +++ b/config.c @@ -1233,6 +1233,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 +1242,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 +1413,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 +1425,9 @@ 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(config_exclusive_filename, + key, value, value_regex, + multi_replace); } static int section_name_match (const char *buf, const char *name) -- cgit v1.2.1 From 42bd39b57fee80a9fd136f89c7728e640d13964a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 16 Feb 2012 03:04:25 -0500 Subject: config: teach git_config_rename_section a file argument The other config-writing functions (git_config_set and git_config_set_multivar) each have an -"in_file" version to write a specific file. Let's add one for rename_section, with the eventual goal of moving away from the magic config_exclusive_filename global. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 351ae0b62..bc2e23379 100644 --- a/config.c +++ b/config.c @@ -1470,19 +1470,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); @@ -1546,10 +1546,16 @@ 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(config_exclusive_filename, + 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"). -- cgit v1.2.1 From c9b5e2a57d2a69e0c6183758445da2f230b5a9f0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 16 Feb 2012 03:05:56 -0500 Subject: config: provide a version of git_config with more options Callers may want to provide a specific version of a file in which to look for config. Right now this can be done by setting the magic global config_exclusive_filename variable. By providing a version of git_config that takes a filename, we can take a step towards making this magic global go away. Furthermore, by providing a more "advanced" interface, we now have a a natural place to add new options for callers like git-config, which care about tweaking the specifics of config lookup, without disturbing the large number of "simple" users (i.e., every other part of git). The astute reader of this patch may notice that the logic for handling config_exclusive_filename was taken out of git_config_early, but added into git_config. This means that git_config_early will no longer respect config_exclusive_filename. That's OK, because the only other caller of git_config_early is check_repository_format_gently, but the only function which sets config_exclusive_filename is cmd_config, which does not call check_repository_format_gently (and if it did, it would have been a bug, anyway, as we would be checking the repository format in the wrong file). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index bc2e23379..531d4d43e 100644 --- a/config.c +++ b/config.c @@ -879,9 +879,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,11 +914,19 @@ 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) { char *repo_config = NULL; int ret; + /* + * 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); if (repo_config) @@ -929,6 +934,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, config_exclusive_filename); +} + /* * Find all the stuff for git_config_set() below. */ -- cgit v1.2.1 From 4a7bb5ba950f08d1e46c4bd2e8b1c903b4d024c8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 16 Feb 2012 03:09:32 -0500 Subject: config: eliminate config_exclusive_filename This is a magic global variable that was intended as an override to the usual git-config lookup process. Once upon a time, you could specify GIT_CONFIG to any git program, and it would look only at that file. This turned out to be confusing and cause a lot of bugs for little gain. As a result, dc87183 (Only use GIT_CONFIG in "git config", not other programs, 2008-06-30) took this away for all callers except git-config. Since git-config no longer uses it either, the variable can just go away. As the diff shows, nobody was setting to anything except NULL, so we can just replace any sites where it was read with NULL. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 531d4d43e..1e30ad9d1 100644 --- a/config.c +++ b/config.c @@ -26,8 +26,6 @@ static config_file *cf; static int zlib_compression_seen; -const char *config_exclusive_filename = NULL; - static void lowercase(char *p) { for (; *p; p++) @@ -936,7 +934,7 @@ int git_config_with_options(config_fn_t fn, void *data, int git_config(config_fn_t fn, void *data) { - return git_config_with_options(fn, data, config_exclusive_filename); + return git_config_with_options(fn, data, NULL); } /* @@ -1435,8 +1433,7 @@ write_err_out: int git_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { - return git_config_set_multivar_in_file(config_exclusive_filename, - key, value, value_regex, + return git_config_set_multivar_in_file(NULL, key, value, value_regex, multi_replace); } @@ -1562,8 +1559,7 @@ out: int git_config_rename_section(const char *old_name, const char *new_name) { - return git_config_rename_section_in_file(config_exclusive_filename, - old_name, new_name); + return git_config_rename_section_in_file(NULL, old_name, new_name); } /* -- cgit v1.2.1 From 9b25a0b52e09400719366f0a33d0d0da98bbf7b0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 6 Feb 2012 04:54:04 -0500 Subject: config: add include directive It can be useful to split your ~/.gitconfig across multiple files. For example, you might have a "main" file which is used on many machines, but a small set of per-machine tweaks. Or you may want to make some of your config public (e.g., clever aliases) while keeping other data back (e.g., your name or other identifying information). Or you may want to include a number of config options in some subset of your repos without copying and pasting (e.g., you want to reference them from the .git/config of participating repos). This patch introduces an include directive for config files. It looks like: [include] path = /path/to/file This is syntactically backwards-compatible with existing git config parsers (i.e., they will see it as another config entry and ignore it unless you are looking up include.path). The implementation provides a "git_config_include" callback which wraps regular config callbacks. Callers can pass it to git_config_from_file, and it will transparently follow any include directives, passing all of the discovered options to the real callback. Include directives are turned on automatically for "regular" git config parsing. This includes calls to git_config, as well as calls to the "git config" program that do not specify a single file (e.g., using "-f", "--global", etc). They are not turned on in other cases, including: 1. Parsing of other config-like files, like .gitmodules. There isn't a real need, and I'd rather be conservative and avoid unnecessary incompatibility or confusion. 2. Reading single files via "git config". This is for two reasons: a. backwards compatibility with scripts looking at config-like files. b. inspection of a specific file probably means you care about just what's in that file, not a general lookup for "do we have this value anywhere at all". If that is not the case, the caller can always specify "--includes". 3. Writing files via "git config"; we want to treat include.* variables as literal items to be copied (or modified), and not expand them. So "git config --unset-all foo.bar" would operate _only_ on .git/config, not any of its included files (just as it also does not operate on ~/.gitconfig). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) (limited to 'config.c') diff --git a/config.c b/config.c index 1e30ad9d1..ad0390819 100644 --- a/config.c +++ b/config.c @@ -26,6 +26,69 @@ static config_file *cf; static int zlib_compression_seen; +#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) { for (; *p; p++) @@ -913,10 +976,18 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) } int git_config_with_options(config_fn_t fn, void *data, - const char *filename) + 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 @@ -934,7 +1005,7 @@ int git_config_with_options(config_fn_t fn, void *data, int git_config(config_fn_t fn, void *data) { - return git_config_with_options(fn, data, NULL); + return git_config_with_options(fn, data, NULL, 1); } /* -- cgit v1.2.1