From: Jonathan Tan <jonathantanmy@google.com>
To: git@vger.kernel.org
Cc: Jonathan Tan <jonathantanmy@google.com>,
peff@peff.net, gitster@pobox.com, avarab@gmail.com
Subject: [WIP v2 2/2] config: include file if remote URL matches a glob
Date: Fri, 29 Oct 2021 10:31:10 -0700 [thread overview]
Message-ID: <3c0d51ee39b8e149b5be57b8cd3f8cd403fe49c9.1635527389.git.jonathantanmy@google.com> (raw)
In-Reply-To: <cover.1635527389.git.jonathantanmy@google.com>
This is a feature that supports config file inclusion conditional on
whether the repo has a remote with a URL that matches a glob.
Similar to my previous work on remote-suggested hooks [1], the main
motivation is to allow remote repo administrators to provide recommended
configs in a way that can be consumed more easily (e.g. through a
package installable by a package manager).
NEEDSWORK: The way this works is that if we see such an include, we
shunt all subsequent configs into a stash (while looking for URLs), then
process the stash. In particular, this means that more memory is needed,
and the nature of error reporting changes (currently, if a callback
returns nonzero for a variable, processing halts immediately, but with
this patch, all the config might be read from disk before the callback
even sees the variable). I'll need to expand on this and write a
documentation section.
One alternative is to rerun the config parsing mechanism upon noticing
the first URL-conditional include in order to find all URLs. This would
require the config files to be read from disk twice, though.
[1] https://lore.kernel.org/git/cover.1623881977.git.jonathantanmy@google.com/
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
---
config.c | 132 +++++++++++++++++++++++++++++++++++++++++-----
t/t1300-config.sh | 60 +++++++++++++++++++++
2 files changed, 180 insertions(+), 12 deletions(-)
diff --git a/config.c b/config.c
index 94ad5ce913..63a37e0a5d 100644
--- a/config.c
+++ b/config.c
@@ -120,13 +120,30 @@ static long config_buf_ftell(struct config_source *conf)
return conf->u.buf.pos;
}
+struct stashed_var {
+ char *var;
+ char *value;
+ int depth;
+
+ char *url;
+};
+
struct config_include_data {
int depth;
config_fn_t fn;
void *data;
const struct config_options *opts;
+
+ /*
+ * All remote URLs discovered when reading all config files.
+ */
+ struct string_list remote_urls;
+
+ struct stashed_var *stashed;
+ size_t stashed_nr, stashed_alloc;
+ int current_stash_depth;
};
-#define CONFIG_INCLUDE_INIT { 0 }
+#define CONFIG_INCLUDE_INIT { .remote_urls = STRING_LIST_INIT_DUP }
static int git_config_include(const char *var, const char *value, void *data);
@@ -316,28 +333,110 @@ static int include_condition_is_true(const struct config_options *opts,
return 0;
}
+static int execute_stashed(struct config_include_data *inc)
+{
+ size_t i = 0;
+ while (i < inc->stashed_nr) {
+ int ret = inc->fn(inc->stashed[i].var, inc->stashed[i].value,
+ inc->data);
+ if (ret)
+ return ret;
+
+ /*
+ * If it is an include, skip to next entry of the same depth if
+ * the URL doesn't match
+ */
+ if (inc->stashed[i].url) {
+ struct strbuf pattern = STRBUF_INIT;
+ struct string_list_item *url_item;
+ int found = 0;
+
+ strbuf_addstr(&pattern, inc->stashed[i].url);
+ add_trailing_starstar_for_dir(&pattern);
+ for_each_string_list_item(url_item, &inc->remote_urls) {
+ if (!wildmatch(pattern.buf, url_item->string,
+ WM_PATHNAME)) {
+ found = 1;
+ break;
+ }
+ }
+ strbuf_release(&pattern);
+ if (found) {
+ i++;
+ } else {
+ int depth = inc->stashed[i].depth;
+
+ i++;
+ while (i < inc->stashed_nr &&
+ inc->stashed[i].depth != depth)
+ i++;
+ }
+ } else {
+ i++;
+ }
+ }
+ return 0;
+}
+
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
+ const char *remote_name;
+ size_t remote_name_len;
const char *cond, *key;
size_t cond_len;
- int ret;
+ int ret = 0;
+
+ if (!parse_config_key(var, "remote", &remote_name, &remote_name_len,
+ &key) &&
+ remote_name &&
+ !strcmp(key, "url"))
+ string_list_append(&inc->remote_urls, value);
/*
* 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;
+ if (inc->stashed_nr || starts_with(var, "includeif.hasremoteurl:")) {
+ struct stashed_var *last;
+
+ /*
+ * Start or continue using the stash. (A false positive on
+ * "includeif.hasremoteurl:?.path" is fine here - this just
+ * means that some config variables unnecessarily go through
+ * the stash before being passed to the callback.)
+ */
+ ALLOC_GROW_BY(inc->stashed, inc->stashed_nr, 1,
+ inc->stashed_alloc);
+ last = &inc->stashed[inc->stashed_nr - 1];
+ last->var = xstrdup(var);
+ last->value = xstrdup(value);
+ last->depth = inc->current_stash_depth;
+ } else {
+ ret = inc->fn(var, value, inc->data);
+ if (ret < 0)
+ return ret;
+ }
if (!strcmp(var, "include.path"))
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"))
- ret = handle_path_include(value, inc);
+ cond && !strcmp(key, "path")) {
+ const char *url;
+ size_t url_len;
+
+ if (skip_prefix_mem(cond, cond_len, "hasremoteurl:", &url,
+ &url_len)) {
+ inc->stashed[inc->stashed_nr - 1].url =
+ xmemdupz(url, url_len);
+ inc->current_stash_depth++;
+ ret = handle_path_include(value, inc);
+ inc->current_stash_depth--;
+ } else if (include_condition_is_true(inc->opts, cond, cond_len)) {
+ ret = handle_path_include(value, inc);
+ }
+ }
return ret;
}
@@ -1933,6 +2032,7 @@ 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;
@@ -1950,17 +2050,25 @@ 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.stashed_nr) {
+ execute_stashed(&inc);
+ inc.stashed_nr = 0;
+ }
+
+ string_list_clear(&inc.remote_urls, 0);
+ return ret;
}
static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 9ff46f3b04..ea15f7fd46 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2387,4 +2387,64 @@ test_expect_success '--get and --get-all with --fixed-value' '
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
+test_expect_success 'includeIf.hasremoteurl' '
+ test_create_repo hasremoteurlTest &&
+
+ cat >"$(pwd)"/include-this <<-\EOF &&
+ [user]
+ this = this-is-included
+ EOF
+ cat >"$(pwd)"/dont-include-that <<-\EOF &&
+ [user]
+ that = that-is-not-included
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [includeIf "hasremoteurl:foo"]
+ path = "$(pwd)/include-this"
+ [includeIf "hasremoteurl:bar"]
+ path = "$(pwd)/dont-include-that"
+ [remote "foo"]
+ url = foo
+ EOF
+
+ echo this-is-included >expect-this &&
+ git -C hasremoteurlTest config --get user.this >actual-this &&
+ test_cmp expect-this actual-this &&
+
+ test_must_fail git -C hasremoteurlTest config --get user.that
+'
+
+test_expect_success 'includeIf.hasremoteurl respects last-config-wins' '
+ test_create_repo hasremoteurlTestOverride &&
+
+ cat >"$(pwd)"/include-two-three <<-\EOF &&
+ [user]
+ two = included-config
+ three = included-config
+ EOF
+ cat >>hasremoteurlTestOverride/.git/config <<-EOF &&
+ [remote "foo"]
+ url = foo
+ [user]
+ one = main-config
+ two = main-config
+ [includeIf "hasremoteurl:foo"]
+ path = "$(pwd)/include-two-three"
+ [user]
+ three = main-config
+ EOF
+
+ echo main-config >expect-main-config &&
+ echo included-config >expect-included-config &&
+
+ git -C hasremoteurlTestOverride config --get user.one >actual &&
+ test_cmp expect-main-config actual &&
+
+ git -C hasremoteurlTestOverride config --get user.two >actual &&
+ test_cmp expect-included-config actual &&
+
+ git -C hasremoteurlTestOverride config --get user.three >actual &&
+ test_cmp expect-main-config actual
+'
+
test_done
--
2.33.1.1089.g2158813163f-goog
next prev parent reply other threads:[~2021-10-29 17:31 UTC|newest]
Thread overview: 87+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-12 22:57 [RFC PATCH 0/2] Conditional config includes based on remote URL Jonathan Tan
2021-10-12 22:57 ` [RFC PATCH 1/2] config: make git_config_include() static Jonathan Tan
2021-10-12 23:07 ` Jeff King
2021-10-12 23:26 ` Junio C Hamano
2021-10-13 8:26 ` Ævar Arnfjörð Bjarmason
2021-10-13 17:00 ` Junio C Hamano
2021-10-13 18:13 ` Jonathan Tan
2021-10-12 22:57 ` [RFC PATCH 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-10-12 23:30 ` Jeff King
2021-10-13 18:33 ` Jonathan Tan
2021-10-27 11:40 ` Jeff King
2021-10-27 17:23 ` Jonathan Tan
2021-10-12 23:48 ` Junio C Hamano
2021-10-13 19:52 ` Jonathan Tan
2021-10-13 0:46 ` [RFC PATCH 0/2] Conditional config includes based on remote URL brian m. carlson
2021-10-13 18:17 ` Jonathan Tan
2021-10-18 20:48 ` Jonathan Tan
2021-10-22 3:12 ` Emily Shaffer
2021-10-27 11:55 ` Jeff King
2021-10-27 17:52 ` Jonathan Tan
2021-10-27 20:32 ` Jeff King
2021-10-25 13:03 ` Ævar Arnfjörð Bjarmason
2021-10-25 18:53 ` Jonathan Tan
2021-10-26 10:12 ` Ævar Arnfjörð Bjarmason
2021-10-29 17:31 ` [WIP v2 " Jonathan Tan
2021-10-29 17:31 ` [WIP v2 1/2] config: make git_config_include() static Jonathan Tan
2021-11-05 19:45 ` Emily Shaffer
2021-10-29 17:31 ` Jonathan Tan [this message]
2021-11-05 20:24 ` [WIP v2 2/2] config: include file if remote URL matches a glob Emily Shaffer
2021-11-06 4:41 ` Ævar Arnfjörð Bjarmason
2021-11-09 0:25 ` Jonathan Tan
2021-11-09 0:22 ` Jonathan Tan
2021-11-16 0:00 ` [PATCH v3 0/2] Conditional config includes based on remote URL Jonathan Tan
2021-11-16 0:00 ` [PATCH v3 1/2] config: make git_config_include() static Jonathan Tan
2021-11-16 0:00 ` [PATCH v3 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-11-22 22:59 ` Glen Choo
2021-11-29 17:53 ` Jonathan Tan
2021-11-23 1:22 ` Junio C Hamano
2021-11-29 18:18 ` Jonathan Tan
2021-12-01 18:51 ` Junio C Hamano
2021-12-02 23:14 ` Jonathan Tan
2021-11-23 1:27 ` Ævar Arnfjörð Bjarmason
2021-11-29 18:33 ` Jonathan Tan
2021-11-29 20:50 ` Ævar Arnfjörð Bjarmason
2021-11-29 20:23 ` [PATCH v4 0/2] Conditional config includes based on remote URL Jonathan Tan
2021-11-29 20:23 ` [PATCH v4 1/2] config: make git_config_include() static Jonathan Tan
2021-11-29 20:23 ` [PATCH v4 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-12-02 6:57 ` Junio C Hamano
2021-12-02 17:41 ` Jonathan Tan
2021-11-29 20:48 ` [PATCH v4 0/2] Conditional config includes based on remote URL Ævar Arnfjörð Bjarmason
2021-11-30 7:51 ` Junio C Hamano
2021-12-02 23:31 ` [PATCH v5 " Jonathan Tan
2021-12-02 23:31 ` [PATCH v5 1/2] config: make git_config_include() static Jonathan Tan
2021-12-02 23:31 ` [PATCH v5 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-12-06 22:32 ` Glen Choo
2021-12-07 17:53 ` Jonathan Tan
2021-12-06 18:57 ` [PATCH v5 0/2] Conditional config includes based on remote URL Ævar Arnfjörð Bjarmason
2021-12-07 17:46 ` Jonathan Tan
2021-12-07 17:56 ` Ævar Arnfjörð Bjarmason
2021-12-07 18:52 ` Jonathan Tan
2021-12-07 23:23 ` [PATCH v6 " Jonathan Tan
2021-12-07 23:23 ` [PATCH v6 1/2] config: make git_config_include() static Jonathan Tan
2021-12-07 23:23 ` [PATCH v6 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-12-08 19:19 ` Glen Choo
2021-12-09 22:16 ` Jonathan Tan
2021-12-08 19:55 ` Glen Choo
2021-12-09 22:39 ` Jonathan Tan
2021-12-09 23:33 ` Glen Choo
2021-12-13 23:35 ` Jonathan Tan
2021-12-10 21:45 ` Junio C Hamano
2021-12-13 23:37 ` Jonathan Tan
2021-12-14 21:31 ` [PATCH v7 0/2] Conditional config includes based on remote URL Jonathan Tan
2021-12-14 21:31 ` [PATCH v7 1/2] config: make git_config_include() static Jonathan Tan
2021-12-14 21:31 ` [PATCH v7 2/2] config: include file if remote URL matches a glob Jonathan Tan
2021-12-16 21:54 ` Glen Choo
2021-12-28 0:55 ` Elijah Newren
2022-01-10 18:58 ` Jonathan Tan
2021-12-16 21:57 ` [PATCH v7 0/2] Conditional config includes based on remote URL Glen Choo
2021-12-28 1:13 ` Elijah Newren
2021-12-28 23:13 ` Glen Choo
2022-01-10 19:22 ` Jonathan Tan
2022-01-10 20:17 ` Elijah Newren
2022-01-25 13:26 ` Scalar vs JGit, was " Johannes Schindelin
2022-01-18 17:47 ` [PATCH v8 " Jonathan Tan
2022-01-18 17:47 ` [PATCH v8 1/2] config: make git_config_include() static Jonathan Tan
2022-01-18 17:47 ` [PATCH v8 2/2] config: include file if remote URL matches a glob Jonathan Tan
2022-01-18 20:54 ` [PATCH v8 0/2] Conditional config includes based on remote URL Elijah Newren
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3c0d51ee39b8e149b5be57b8cd3f8cd403fe49c9.1635527389.git.jonathantanmy@google.com \
--to=jonathantanmy@google.com \
--cc=avarab@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).