]> git.ipfire.org Git - thirdparty/git.git/commitdiff
remote: add remote.*.negotiationRestrict config
authorDerrick Stolee <stolee@gmail.com>
Wed, 22 Apr 2026 15:25:43 +0000 (15:25 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 22 Apr 2026 23:10:33 +0000 (16:10 -0700)
In a previous change, the --negotiation-restrict command-line option of
'git fetch' was added as a synonym of --negotiation-tips. Both of these
options restrict the set of 'haves' the client can send as part of
negotiation.

This was previously not available via a configuration option. Add a new
'remote.<name>.negotiationRestrict' multi-valued config option that
updates 'git fetch <name>' to use these restrictions by default.

If the user provides even one --negotiation-restrict argument, then the
config is ignored.

An empty value resets the value list to allow ignoring earlier config
values, such as those that might be set in system or global config.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/remote.adoc
builtin/fetch.c
remote.c
remote.h
t/t5510-fetch.sh

index 91e46f66f5dd1ce42958ebc87f4a3066d4c9b871..f1d889d03e6a8f872afb609924dac7c50cb94164 100644 (file)
@@ -107,6 +107,25 @@ priority configuration file (e.g. `.git/config` in a repository) to clear
 the values inherited from a lower priority configuration files (e.g.
 `$HOME/.gitconfig`).
 
+remote.<name>.negotiationRestrict::
+       When negotiating with this remote during `git fetch` and `git push`,
+       restrict the commits advertised as "have" lines to only those
+       reachable from refs matching the given patterns.  This multi-valued
+       config option behaves like `--negotiation-restrict` on the command
+       line.
++
+Each value is either an exact ref name (e.g. `refs/heads/release`) or a
+glob pattern (e.g. `refs/heads/release/*`).  The pattern syntax is the
+same as for `--negotiation-restrict`.
++
+These config values are used as defaults for the `--negotiation-restrict`
+command-line option.  If `--negotiation-restrict` (or its synonym
+`--negotiation-tip`) is specified on the command line, then the config
+values are not used.
++
+Blank values signal to ignore all previous values, allowing a reset of
+the list from broader config scenarios.
+
 remote.<name>.followRemoteHEAD::
        How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`
        when fetching using the configured refspecs of a remote.
index 2ba0051d520cdf6195d3734468bbf8c4c22f12fa..a1960e3e0cf28fa8f059ee620cfbe2af2b95d9cc 100644 (file)
@@ -1601,6 +1601,19 @@ static struct transport *prepare_transport(struct remote *remote, int deepen,
                else
                        warning(_("ignoring %s because the protocol does not support it"),
                                "--negotiation-restrict");
+       } else if (remote->negotiation_restrict.nr) {
+               struct string_list_item *item;
+               for_each_string_list_item(item, &remote->negotiation_restrict)
+                       string_list_append(&negotiation_restrict, item->string);
+               if (transport->smart_options)
+                       add_negotiation_restrict_tips(transport->smart_options);
+               else {
+                       struct strbuf config_name = STRBUF_INIT;
+                       strbuf_addf(&config_name, "remote.%s.negotiationRestrict", remote->name);
+                       warning(_("ignoring %s because the protocol does not support it"),
+                               config_name.buf);
+                       strbuf_release(&config_name);
+               }
        }
        return transport;
 }
@@ -2658,10 +2671,6 @@ int cmd_fetch(int argc,
                config.display_format = DISPLAY_FORMAT_PORCELAIN;
        }
 
-       if (negotiate_only && !negotiation_restrict.nr)
-               die(_("%s needs one or more %s"), "--negotiate-only",
-                   "--negotiation-restrict=*");
-
        if (deepen_relative) {
                if (deepen_relative < 0)
                        die(_("negative depth in --deepen is not supported"));
@@ -2749,6 +2758,10 @@ int cmd_fetch(int argc,
                if (!remote)
                        die(_("must supply remote when using --negotiate-only"));
                gtransport = prepare_transport(remote, 1, &filter_options);
+               if (!gtransport->smart_options ||
+                   !gtransport->smart_options->negotiation_restrict_tips)
+                       die(_("%s needs one or more %s"), "--negotiate-only",
+                           "--negotiation-restrict=*");
                if (gtransport->smart_options) {
                        gtransport->smart_options->acked_commits = &acked_commits;
                } else {
index 7ca2a6501b4920587c86b70520358b55c58c6bdc..166a56408a72e79db9b0f6bb0c52a8f318e71c52 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -152,6 +152,7 @@ static struct remote *make_remote(struct remote_state *remote_state,
        refspec_init_push(&ret->push);
        refspec_init_fetch(&ret->fetch);
        string_list_init_dup(&ret->server_options);
+       string_list_init_dup(&ret->negotiation_restrict);
 
        ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
                   remote_state->remotes_alloc);
@@ -179,6 +180,7 @@ static void remote_clear(struct remote *remote)
        FREE_AND_NULL(remote->http_proxy);
        FREE_AND_NULL(remote->http_proxy_authmethod);
        string_list_clear(&remote->server_options, 0);
+       string_list_clear(&remote->negotiation_restrict, 0);
 }
 
 static void add_merge(struct branch *branch, const char *name)
@@ -562,6 +564,12 @@ static int handle_config(const char *key, const char *value,
        } else if (!strcmp(subkey, "serveroption")) {
                return parse_transport_option(key, value,
                                              &remote->server_options);
+       } else if (!strcmp(subkey, "negotiationrestrict")) {
+               /* reset list on empty value. */
+               if (!value || !*value)
+                       string_list_clear(&remote->negotiation_restrict, 0);
+               else
+                       string_list_append(&remote->negotiation_restrict, value);
        } else if (!strcmp(subkey, "followremotehead")) {
                const char *no_warn_branch;
                if (!strcmp(value, "never"))
index fc052945ee451d35510fe90404c6a7dcd0ab5b0e..e6ec37c39303557c4b482d6fa6a977ee4d4b6119 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -117,6 +117,7 @@ struct remote {
        char *http_proxy_authmethod;
 
        struct string_list server_options;
+       struct string_list negotiation_restrict;
 
        enum follow_remote_head_settings follow_remote_head;
        const char *no_warn_branch;
index dc3ce56d84c743bf7e65e718e95e25fc1c5b35b8..eff3ce8e2de89cbc513eb26396098e0307b7655e 100755 (executable)
@@ -1485,6 +1485,32 @@ test_expect_success '--negotiation-restrict and --negotiation-tip can be mixed'
        check_negotiation_tip
 '
 
+test_expect_success 'remote.<name>.negotiationRestrict used as default' '
+       setup_negotiation_tip server server 0 &&
+
+       # test the reset of the list on an empty value
+       git -C client config --add remote.origin.negotiationRestrict alpha_2 &&
+       git -C client config --add remote.origin.negotiationRestrict "" &&
+       git -C client config --add remote.origin.negotiationRestrict alpha_1 &&
+       git -C client config --add remote.origin.negotiationRestrict beta_1 &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+               origin alpha_s beta_s &&
+       check_negotiation_tip
+'
+
+test_expect_success 'CLI --negotiation-restrict overrides remote config' '
+       setup_negotiation_tip server server 0 &&
+       git -C client config --add remote.origin.negotiationRestrict alpha_1 &&
+       git -C client config --add remote.origin.negotiationRestrict beta_1 &&
+       ALPHA_1=$(git -C client rev-parse alpha_1) &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+               --negotiation-restrict=alpha_1 \
+               origin alpha_s beta_s &&
+       test_grep "fetch> have $ALPHA_1" trace &&
+       BETA_1=$(git -C client rev-parse beta_1) &&
+       test_grep ! "fetch> have $BETA_1" trace
+'
+
 test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
        git init df-conflict &&
        (