Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2022-02-10 01:20:58 +0300
committerJunio C Hamano <gitster@pobox.com>2022-02-10 01:20:59 +0300
commit13ce8f9f148637a885003c63e11a020d0aeca3f7 (patch)
tree31db6aa9e46a93c381783f857786135894a0f4cf /config.c
parent87bfbd52e21b6e9373cd70fc148f09dcdad39f09 (diff)
parent399b198489a041e2842fb4b257bea5adb02d28d1 (diff)
Merge branch 'jt/conditional-config-on-remote-url'
The conditional inclusion mechanism of configuration files using "[includeIf <condition>]" learns to base its decision on the URL of the remote repository the repository interacts with. * jt/conditional-config-on-remote-url: config: include file if remote URL matches a glob config: make git_config_include() static
Diffstat (limited to 'config.c')
-rw-r--r--config.c132
1 files changed, 124 insertions, 8 deletions
diff --git a/config.c b/config.c
index 2bffa8d4a0..e0c03d154c 100644
--- a/config.c
+++ b/config.c
@@ -120,6 +120,22 @@ static long config_buf_ftell(struct config_source *conf)
return conf->u.buf.pos;
}
+struct config_include_data {
+ int depth;
+ config_fn_t fn;
+ void *data;
+ const struct config_options *opts;
+ struct git_config_source *config_source;
+
+ /*
+ * All remote URLs discovered when reading all config files.
+ */
+ struct string_list *remote_urls;
+};
+#define CONFIG_INCLUDE_INIT { 0 }
+
+static int git_config_include(const char *var, const char *value, void *data);
+
#define MAX_INCLUDE_DEPTH 10
static const char include_depth_advice[] = N_(
"exceeded maximum include depth (%d) while including\n"
@@ -294,9 +310,92 @@ static int include_by_branch(const char *cond, size_t cond_len)
return ret;
}
-static int include_condition_is_true(const struct config_options *opts,
+static int add_remote_url(const char *var, const char *value, void *data)
+{
+ struct string_list *remote_urls = data;
+ const char *remote_name;
+ size_t remote_name_len;
+ const char *key;
+
+ if (!parse_config_key(var, "remote", &remote_name, &remote_name_len,
+ &key) &&
+ remote_name &&
+ !strcmp(key, "url"))
+ string_list_append(remote_urls, value);
+ return 0;
+}
+
+static void populate_remote_urls(struct config_include_data *inc)
+{
+ struct config_options opts;
+
+ struct config_source *store_cf = cf;
+ struct key_value_info *store_kvi = current_config_kvi;
+ enum config_scope store_scope = current_parsing_scope;
+
+ opts = *inc->opts;
+ opts.unconditional_remote_url = 1;
+
+ cf = NULL;
+ current_config_kvi = NULL;
+ current_parsing_scope = 0;
+
+ inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
+ string_list_init_dup(inc->remote_urls);
+ config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
+
+ cf = store_cf;
+ current_config_kvi = store_kvi;
+ current_parsing_scope = store_scope;
+}
+
+static int forbid_remote_url(const char *var, const char *value, void *data)
+{
+ const char *remote_name;
+ size_t remote_name_len;
+ const char *key;
+
+ if (!parse_config_key(var, "remote", &remote_name, &remote_name_len,
+ &key) &&
+ remote_name &&
+ !strcmp(key, "url"))
+ die(_("remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url"));
+ return 0;
+}
+
+static int at_least_one_url_matches_glob(const char *glob, int glob_len,
+ struct string_list *remote_urls)
+{
+ struct strbuf pattern = STRBUF_INIT;
+ struct string_list_item *url_item;
+ int found = 0;
+
+ strbuf_add(&pattern, glob, glob_len);
+ for_each_string_list_item(url_item, remote_urls) {
+ if (!wildmatch(pattern.buf, url_item->string, WM_PATHNAME)) {
+ found = 1;
+ break;
+ }
+ }
+ strbuf_release(&pattern);
+ return found;
+}
+
+static int include_by_remote_url(struct config_include_data *inc,
+ const char *cond, size_t cond_len)
+{
+ if (inc->opts->unconditional_remote_url)
+ return 1;
+ if (!inc->remote_urls)
+ populate_remote_urls(inc);
+ return at_least_one_url_matches_glob(cond, cond_len,
+ inc->remote_urls);
+}
+
+static int include_condition_is_true(struct config_include_data *inc,
const char *cond, size_t cond_len)
{
+ const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
return include_by_gitdir(opts, cond, cond_len, 0);
@@ -304,12 +403,15 @@ static int include_condition_is_true(const struct config_options *opts,
return include_by_gitdir(opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
+ else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
+ &cond_len))
+ return include_by_remote_url(inc, cond, cond_len);
/* unknown conditionals are always false */
return 0;
}
-int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
const char *cond, *key;
@@ -328,9 +430,15 @@ int git_config_include(const char *var, const char *value, void *data)
ret = handle_path_include(value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- (cond && include_condition_is_true(inc->opts, cond, cond_len)) &&
- !strcmp(key, "path"))
+ cond && include_condition_is_true(inc, cond, cond_len) &&
+ !strcmp(key, "path")) {
+ config_fn_t old_fn = inc->fn;
+
+ if (inc->opts->unconditional_remote_url)
+ inc->fn = forbid_remote_url;
ret = handle_path_include(value, inc);
+ inc->fn = old_fn;
+ }
return ret;
}
@@ -1929,11 +2037,13 @@ int config_with_options(config_fn_t fn, void *data,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ int ret;
if (opts->respect_includes) {
inc.fn = fn;
inc.data = data;
inc.opts = opts;
+ inc.config_source = config_source;
fn = git_config_include;
data = &inc;
}
@@ -1946,17 +2056,23 @@ int config_with_options(config_fn_t fn, void *data,
* regular lookup sequence.
*/
if (config_source && config_source->use_stdin) {
- return git_config_from_stdin(fn, data);
+ ret = git_config_from_stdin(fn, data);
} else if (config_source && config_source->file) {
- return git_config_from_file(fn, config_source->file, data);
+ ret = git_config_from_file(fn, config_source->file, data);
} else if (config_source && config_source->blob) {
struct repository *repo = config_source->repo ?
config_source->repo : the_repository;
- return git_config_from_blob_ref(fn, repo, config_source->blob,
+ ret = git_config_from_blob_ref(fn, repo, config_source->blob,
data);
+ } else {
+ ret = do_git_config_sequence(opts, fn, data);
}
- return do_git_config_sequence(opts, fn, data);
+ if (inc.remote_urls) {
+ string_list_clear(inc.remote_urls, 0);
+ FREE_AND_NULL(inc.remote_urls);
+ }
+ return ret;
}
static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)