From patchwork Mon Nov 29 20:23:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Tan X-Patchwork-Id: 12645943 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2227EC433EF for ; Mon, 29 Nov 2021 22:33:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234031AbhK2Wgy (ORCPT ); Mon, 29 Nov 2021 17:36:54 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234181AbhK2WgK (ORCPT ); Mon, 29 Nov 2021 17:36:10 -0500 Received: from mail-pj1-x104a.google.com (mail-pj1-x104a.google.com [IPv6:2607:f8b0:4864:20::104a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 876CFC08ED69 for ; Mon, 29 Nov 2021 12:23:48 -0800 (PST) Received: by mail-pj1-x104a.google.com with SMTP id x18-20020a17090a789200b001a7317f995cso10240545pjk.4 for ; Mon, 29 Nov 2021 12:23:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=NoERD4dVZ7CXLEZp3FY4c8Q0SBMDrd/FbsDgs/50fjk=; b=Orvb3OypLW9G2vLxbP/Pvuste843kWSMI0i98azaVYdv5cVxyfBvCM9JVSqoQsVdkS yDulxtXVJTuHaZrn7VoJ3vlMy6bhT/R7EYm8wS3NZRWbh5umt2SeYxDn9/2qAY/oWOOZ OgnLT/py+8SP6g41h7peYlqcprSz3+WRCpZuRHDQ/kJ8C3QnBH1HJQn+KCH6WI8mr71W B0I/VFzeqJGNdYoOcrychlWptUtjY/MgzPMMvChwyzRV3ijmgQKGLbc8pzqJasItDcGD tir8TVMrKqs0Pd3aDqPhY+h/7/yUpzUEJFXGDP0j5iDi9+PnvbSm18W+w83h3aaiDfy2 l/yQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=NoERD4dVZ7CXLEZp3FY4c8Q0SBMDrd/FbsDgs/50fjk=; b=K4PLcjinhshyu1zeB/JY3ntCtkpAhAXbe/QZ7+SvD1TwxJB4N2pBBZRuQU1gz9vawy BFl3FqkYP3FFL+/JsIPDpk2JAqzCHQCuNT3DTd2roaWBDHrRr8nce/8W66fjTRuYdlfV SFj+wvX004PQ9jFOy8B+tzOlCn3mxR2JKdm/EW8Nur+4Hw5AQRLz+kUPhoWnXw2WW9jd YzVPHQUe3MJp6olDn4ImddrTmDjqd+6iMbcCBHhgc/3sKo5wSk50YVTvPjqjMFQBjQU4 XaYSZ6WatQrA2KD23trpHNw4AKJP7986KzuxaU1gg8wBGHh/XaKep/e/zKO3wfJMuA08 IT7A== X-Gm-Message-State: AOAM531B4eaPyG14TmOTdgpoETtRor0sqBq/Hbz2K9x2w3CGYj9D+UUF j2ryhnE+4S+RDSYiJw7e4QtXHxXOols+HAQjCQrL2AnmyA8aMyhcS0Dfy7cnwN/zXdnsxn+OBJS XfbbmTAgzEqTqozkHwWrW4Yk4dnPyN2QlB7i03/xRUxbeUVk3PH89bC+MQ8IaJBxylzDOHf9132 zL X-Google-Smtp-Source: ABdhPJzc+O6MvuepssQ6+ACTHSi+pvPcl+hnhrxA/X7ME2xjydqjPFyEYPh4H9gWdBDsdBrnn1YBrbjQ5k83pzE/kL6d X-Received: from twelve4.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:437a]) (user=jonathantanmy job=sendgmr) by 2002:a17:90b:4a83:: with SMTP id lp3mr440810pjb.34.1638217428023; Mon, 29 Nov 2021 12:23:48 -0800 (PST) Date: Mon, 29 Nov 2021 12:23:41 -0800 In-Reply-To: Message-Id: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.34.0.rc2.393.gf8c9666880-goog Subject: [PATCH v4 1/2] config: make git_config_include() static From: Jonathan Tan To: git@vger.kernel.org Cc: Jonathan Tan , chooglen@google.com, gitster@pobox.com, avarab@gmail.com Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org It is not used from outside the file in which it is declared. Signed-off-by: Jonathan Tan --- config.c | 12 +++++++++++- config.h | 37 ++++--------------------------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/config.c b/config.c index 2dcbe901b6..94ad5ce913 100644 --- a/config.c +++ b/config.c @@ -120,6 +120,16 @@ 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; +}; +#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" @@ -306,7 +316,7 @@ static int include_condition_is_true(const struct config_options *opts, 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; diff --git a/config.h b/config.h index f119de0130..48a5e472ca 100644 --- a/config.h +++ b/config.h @@ -126,6 +126,8 @@ int git_default_config(const char *, const char *, void *); /** * Read a specific file in git-config format. * This function takes the same callback and data parameters as `git_config`. + * + * Unlike git_config(), this function does not respect includes. */ int git_config_from_file(config_fn_t fn, const char *, void *); @@ -158,6 +160,8 @@ void read_very_early_config(config_fn_t cb, void *data); * 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). + * + * Unlike git_config_from_file(), this function respects includes. */ void git_config(config_fn_t fn, void *); @@ -338,39 +342,6 @@ const char *current_config_origin_type(void); const char *current_config_name(void); int current_config_line(void); -/** - * 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. - * - */ -struct config_include_data { - int depth; - config_fn_t fn; - void *data; - const struct config_options *opts; -}; -#define CONFIG_INCLUDE_INIT { 0 } -int git_config_include(const char *name, const char *value, void *data); - /* * Match and parse a config key of the form: * From patchwork Mon Nov 29 20:23:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Tan X-Patchwork-Id: 12646003 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AE86DC433EF for ; Mon, 29 Nov 2021 22:53:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235887AbhK2W4k (ORCPT ); Mon, 29 Nov 2021 17:56:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37510 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236179AbhK2W4M (ORCPT ); Mon, 29 Nov 2021 17:56:12 -0500 Received: from mail-pf1-x44a.google.com (mail-pf1-x44a.google.com [IPv6:2607:f8b0:4864:20::44a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6746FC08ED6A for ; Mon, 29 Nov 2021 12:23:50 -0800 (PST) Received: by mail-pf1-x44a.google.com with SMTP id q82-20020a627555000000b004a4f8cadb6fso11434420pfc.20 for ; Mon, 29 Nov 2021 12:23:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=iix3ST+jGkk/e66UMZs6tX3LlAtWHqCksONANNRN+O4=; b=XtZpo7AhhnKIqQ31RXSUpwScyqaejgtuQmK8lspDfQCQrUD1OdK7+dbjJnf9efn0YX hCf/+8jQfVrf6KuSVS5C+NIZWb+5NDWoLgrKRdDoe3wjqVhqI2hR9y0ZVil3/E68UYo4 cOtWbrx0of3f39lfbRk4Ww6JjmliLW7ogeLdGU00xtWbLSsDiWhtleazVuef8r8QrDD3 9q4qjq1sMUWNdgoBr/ffLSyKz9ywdRmS4f0ASGo2vO0nT2U7vhhwggh5QilTsuoOr4cQ IKAbeqTz9+1MfZe2zEWaYE2WYKoThquwC2IIzx8ILexkcfdvqh8M7L3UD1sXjZcy5bVa 97cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=iix3ST+jGkk/e66UMZs6tX3LlAtWHqCksONANNRN+O4=; b=fVX9Sk3SPEmhyrjJNLtuspLb1NMAH7MzrDYbRGTHpOcmAu3TycyR8F9QcewVsYKMJT Qcvb8bzhdh/mXjSEnNw7a9lodeyEmhkBHgAyyIwwWu5YQUQSKAwT4GhSaiohi5m1ayu/ bLSLGDbIOAFtVqqnxVE5MdpSrQnvHccHvrwtq9XORn1RrVdXIJdUVj0QEvzuXMG3yLZ/ ycqMdXVqKnjzQs9lJiyUsTFQ5Vqb1FzV91Rb6UYkNss/P3FUBk53Ze8iPadiUKV4wyxm Uc9xswa3ZkqemvZ87DqWDQ369toQEj9XkimVYhJlSnO+EypxegnWcl61NtYT61tlLIsb QEfQ== X-Gm-Message-State: AOAM531XANgIqaq/Rko1WVUWoDx7+HXGKrZ1cT3lB2PGyZ6KoQW4Zd5X yf9KXqj9bNAd9XglNh682FlJmrb3VQXxiyhhXqrhMeIss0iC8BtVjDUH0ecg1HDAV22670xjrV4 Bn05bnfE2HnQWeyF+VHm7yekTEpKV6fbWxVlAfowSHDp9x8zfGFsGfl1F3nZ+/G5m8klJkszxJb pD X-Google-Smtp-Source: ABdhPJywotGRHFqQZsTLxB16SCnaIKmBAxCi0GHIOBWomhu3FkvKZbtVGptKaxZMhccgE+VFgL627zlVyMePHGmDqGy5 X-Received: from twelve4.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:437a]) (user=jonathantanmy job=sendgmr) by 2002:a17:902:b189:b0:143:8079:3d3b with SMTP id s9-20020a170902b18900b0014380793d3bmr61326753plr.71.1638217429694; Mon, 29 Nov 2021 12:23:49 -0800 (PST) Date: Mon, 29 Nov 2021 12:23:42 -0800 In-Reply-To: Message-Id: <3b3af0da983c65ac8282b14141495f6859cd575a.1638217387.git.jonathantanmy@google.com> Mime-Version: 1.0 References: X-Mailer: git-send-email 2.34.0.rc2.393.gf8c9666880-goog Subject: [PATCH v4 2/2] config: include file if remote URL matches a glob From: Jonathan Tan To: git@vger.kernel.org Cc: Jonathan Tan , chooglen@google.com, gitster@pobox.com, avarab@gmail.com Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org 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 - it could, for example, contain a file to be included conditionally and a post-install script that adds the include directive to the system-wide config file). In order to do this, Git reruns the config parsing mechanism upon noticing the first URL-conditional include in order to find all remote URLs, and these remote URLs are then used to determine if that first and all subsequent includes are executed. Remote URLs are not allowed to be configued in any URL-conditionally-included file. [1] https://lore.kernel.org/git/cover.1623881977.git.jonathantanmy@google.com/ Signed-off-by: Jonathan Tan --- Documentation/config.txt | 12 ++++ config.c | 121 ++++++++++++++++++++++++++++++++++++--- config.h | 7 +++ t/t1300-config.sh | 118 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+), 8 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 0c0e6b859f..bfc9e22d78 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -159,6 +159,18 @@ all branches that begin with `foo/`. This is useful if your branches are organized hierarchically and you would like to apply a configuration to all the branches in that hierarchy. +`hasremoteurl`:: + The data that follows the keyword `hasremoteurl:` is taken to + be a pattern with standard globbing wildcards and two + additional ones, `**/` and `/**`, that can match multiple + components. The first time this keyword is seen, the rest of + the config files will be scanned for remote URLs (without + applying any values). If there exists at least one remote URL + that matches this pattern, the include condition is met. ++ +Files included by this option (directly or indirectly) are not allowed +to contain remote URLs. + A few more notes on matching via `gitdir` and `gitdir/i`: * Symlinks in `$GIT_DIR` are not resolved before matching. diff --git a/config.c b/config.c index 94ad5ce913..4ffc1e87e9 100644 --- a/config.c +++ b/config.c @@ -125,6 +125,12 @@ struct config_include_data { 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 } @@ -316,12 +322,83 @@ static int include_condition_is_true(const struct config_options *opts, return 0; } +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.hasremoteurl")); + 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 git_config_include(const char *var, const char *value, void *data) { struct config_include_data *inc = data; const char *cond, *key; size_t cond_len; - int ret; + int ret = 0; /* * Pass along all values, including "include" directives; this makes it @@ -335,9 +412,29 @@ static 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")) - 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)) { + if (inc->opts->unconditional_remote_url) { + config_fn_t old_fn = inc->fn; + + inc->fn = forbid_remote_url; + ret = handle_path_include(value, inc); + inc->fn = old_fn; + } else { + if (!inc->remote_urls) + populate_remote_urls(inc); + if (at_least_one_url_matches_glob( + url, url_len, inc->remote_urls)) + ret = handle_path_include(value, inc); + } + } else if (include_condition_is_true(inc->opts, cond, cond_len)) { + ret = handle_path_include(value, inc); + } + } return ret; } @@ -1933,11 +2030,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; } @@ -1950,17 +2049,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) diff --git a/config.h b/config.h index 48a5e472ca..c24458b10a 100644 --- a/config.h +++ b/config.h @@ -89,6 +89,13 @@ struct config_options { unsigned int ignore_worktree : 1; unsigned int ignore_cmdline : 1; unsigned int system_gently : 1; + + /* + * For internal use. Include all includeif.hasremoteurl paths without + * checking if the repo has that remote URL. + */ + unsigned int unconditional_remote_url : 1; + const char *commondir; const char *git_dir; config_parser_event_fn_t event_fn; diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 9ff46f3b04..9bd299d3f8 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2387,4 +2387,122 @@ 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' ' + git init hasremoteurlTest && + test_when_finished "rm -rf 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' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + cat >"$(pwd)"/include-two-three <<-\EOF && + [user] + two = included-config + three = included-config + EOF + cat >>hasremoteurlTest/.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 hasremoteurlTest config --get user.one >actual && + test_cmp expect-main-config actual && + + git -C hasremoteurlTest config --get user.two >actual && + test_cmp expect-included-config actual && + + git -C hasremoteurlTest config --get user.three >actual && + test_cmp expect-main-config actual +' + +test_expect_success 'includeIf.hasremoteurl globs' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + printf "[user]\ndss = yes\n" >"$(pwd)/double-star-start" && + printf "[user]\ndse = yes\n" >"$(pwd)/double-star-end" && + printf "[user]\ndsm = yes\n" >"$(pwd)/double-star-middle" && + printf "[user]\nssm = yes\n" >"$(pwd)/single-star-middle" && + printf "[user]\nno = no\n" >"$(pwd)/no" && + + cat >>hasremoteurlTest/.git/config <<-EOF && + [remote "foo"] + url = https://foo/bar/baz + [includeIf "hasremoteurl:**/baz"] + path = "$(pwd)/double-star-start" + [includeIf "hasremoteurl:**/nomatch"] + path = "$(pwd)/no" + [includeIf "hasremoteurl:https:/**"] + path = "$(pwd)/double-star-end" + [includeIf "hasremoteurl:nomatch:/**"] + path = "$(pwd)/no" + [includeIf "hasremoteurl:https:/**/baz"] + path = "$(pwd)/double-star-middle" + [includeIf "hasremoteurl:https:/**/nomatch"] + path = "$(pwd)/no" + [includeIf "hasremoteurl:https://*/bar/baz"] + path = "$(pwd)/single-star-middle" + [includeIf "hasremoteurl:https://*/baz"] + path = "$(pwd)/no" + EOF + + git -C hasremoteurlTest config --get user.dss && + git -C hasremoteurlTest config --get user.dse && + git -C hasremoteurlTest config --get user.dsm && + git -C hasremoteurlTest config --get user.ssm && + test_must_fail git -C hasremoteurlTest config --get user.no +' + +test_expect_success 'includeIf.hasremoteurl forbids remote url in such included files' ' + git init hasremoteurlTest && + test_when_finished "rm -rf hasremoteurlTest" && + + cat >"$(pwd)"/include-with-url <<-\EOF && + [remote "bar"] + url = bar + EOF + cat >>hasremoteurlTest/.git/config <<-EOF && + [includeIf "hasremoteurl:foo"] + path = "$(pwd)/include-with-url" + EOF + + # test with any Git command + test_must_fail git -C hasremoteurlTest status 2>err && + grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasremoteurl" err +' + test_done