From: "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, jonathantanmy@google.com, chooglen@google.com,
ps@pks.im, Derrick Stolee <stolee@gmail.com>,
Derrick Stolee <stolee@gmail.com>
Subject: [PATCH 3/4] remote: add mustHave config as default for --must-have
Date: Wed, 08 Apr 2026 14:36:09 +0000 [thread overview]
Message-ID: <fbc98b0cbb93628e24b60163e4d3e55383ab9158.1775658970.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2085.git.1775658970.gitgitgadget@gmail.com>
From: Derrick Stolee <stolee@gmail.com>
Add a new multi-valued config option remote.<name>.mustHave that
specifies ref patterns whose tips should always be sent as "have"
commits during fetch negotiation with that remote.
Parse the option in handle_config() following the same pattern as
remote.<name>.serverOption. Store the values in a string_list on struct
remote so they are available per-remote.
In builtin/fetch.c, when no --must-have options are given on the command
line, use the remote.<name>.mustHave config values as the default. If
the user explicitly provides --must-have on the CLI, the config is not
used, giving CLI precedence.
Signed-off-by: Derrick Stolee <stolee@gmail.com>
---
Documentation/config/remote.adoc | 23 +++++++++++++++
Documentation/fetch-options.adoc | 4 +++
builtin/fetch.c | 3 ++
remote.c | 6 ++++
remote.h | 1 +
t/t5510-fetch.sh | 48 ++++++++++++++++++++++++++++++++
6 files changed, 85 insertions(+)
diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc
index 91e46f66f5..9df8be27eb 100644
--- a/Documentation/config/remote.adoc
+++ b/Documentation/config/remote.adoc
@@ -107,6 +107,29 @@ 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>.mustHave::
+ When negotiating with this remote during `git fetch` and `git push`,
+ the client advertises a list of commits that exist locally. In
+ repos with many references, this list of "haves" can be truncated.
+ Depending on data shape, dropping certain references may be
+ expensive. This multi-valued config option specifies ref patterns
+ whose tips should always be sent as "have" commits during fetch
+ negotiation with this remote.
++
+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-tip`.
++
+These config values are used as defaults for the `--must-have` command-line
+option. If `--must-have` is specified on the command line, then the config
+values are not used.
++
+This option is additive with the normal negotiation process: the
+negotiation algorithm still runs and advertises its own selected commits,
+but the refs matching `remote.<name>.mustHave` are sent unconditionally on
+top of those heuristically selected commits. This option is also used
+during push negotiation when `push.negotiate` is enabled.
+
remote.<name>.followRemoteHEAD::
How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`
when fetching using the configured refspecs of a remote.
diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc
index 852e30191e..fa3969d68d 100644
--- a/Documentation/fetch-options.adoc
+++ b/Documentation/fetch-options.adoc
@@ -86,6 +86,10 @@ is the same as for `--negotiation-tip`.
+
If `--negotiation-tip` is used, the have set is first restricted by that
option and then increased to include the tips specified by `--must-have`.
++
+If this option is not specified on the command line, then any
+`remote.<name>.mustHave` config values for the current remote are used
+instead.
`--negotiate-only`::
Do not fetch anything from the server, and instead print the
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5d29cc6b1a..fa491c106f 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1607,6 +1607,9 @@ static struct transport *prepare_transport(struct remote *remote, int deepen,
transport->smart_options->must_have = &must_have;
else
warning(_("ignoring %s because the protocol does not support it"), "--must-have");
+ } else if (remote->must_have.nr) {
+ if (transport->smart_options)
+ transport->smart_options->must_have = &remote->must_have;
}
return transport;
}
diff --git a/remote.c b/remote.c
index 7ca2a6501b..e07ec08fb3 100644
--- 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->must_have);
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->must_have, 0);
}
static void add_merge(struct branch *branch, const char *name)
@@ -562,6 +564,10 @@ 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, "musthave")) {
+ if (!value)
+ return config_error_nonbool(key);
+ string_list_append(&remote->must_have, value);
} else if (!strcmp(subkey, "followremotehead")) {
const char *no_warn_branch;
if (!strcmp(value, "never"))
diff --git a/remote.h b/remote.h
index fc052945ee..e125313f45 100644
--- a/remote.h
+++ b/remote.h
@@ -117,6 +117,7 @@ struct remote {
char *http_proxy_authmethod;
struct string_list server_options;
+ struct string_list must_have;
enum follow_remote_head_settings follow_remote_head;
const char *no_warn_branch;
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index c34f3805c1..09e7b613a5 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1801,6 +1801,54 @@ test_expect_success '--must-have avoids duplicates with negotiator' '
test_line_count = 1 matches
'
+test_expect_success 'remote.<name>.mustHave used as default for --must-have' '
+ test_when_finished rm -f trace &&
+ setup_negotiation_tip server server 0 &&
+
+ # No --must-have on CLI; config should be used as default.
+ git -C client config --add remote.origin.mustHave refs/tags/beta_1 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=alpha_1 \
+ origin alpha_s beta_s &&
+
+ ALPHA_1=$(git -C client rev-parse alpha_1) &&
+ 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 'remote.<name>.mustHave works with glob patterns' '
+ test_when_finished rm -f trace &&
+ setup_negotiation_tip server server 0 &&
+
+ git -C client config --add remote.origin.mustHave "refs/tags/beta_*" &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=alpha_1 \
+ origin alpha_s beta_s &&
+
+ BETA_1=$(git -C client rev-parse beta_1) &&
+ test_grep "fetch> have $BETA_1" trace &&
+ BETA_2=$(git -C client rev-parse beta_2) &&
+ test_grep "fetch> have $BETA_2" trace
+'
+
+test_expect_success 'CLI --must-have overrides remote.<name>.mustHave' '
+ test_when_finished rm -f trace &&
+ setup_negotiation_tip server server 0 &&
+
+ # Config says beta_2, CLI says beta_1; only CLI should be used.
+ git -C client config --add remote.origin.mustHave refs/tags/beta_2 &&
+ GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
+ --negotiation-tip=alpha_1 \
+ --must-have=refs/tags/beta_1 \
+ origin alpha_s beta_s &&
+
+ BETA_1=$(git -C client rev-parse beta_1) &&
+ test_grep "fetch> have $BETA_1" trace &&
+ BETA_2=$(git -C client rev-parse beta_2) &&
+ test_grep ! "fetch> have $BETA_2" trace
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
--
gitgitgadget
next prev parent reply other threads:[~2026-04-08 14:36 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-08 14:36 [PATCH 0/4] fetch: add --must-have and remote.*.mustHave Derrick Stolee via GitGitGadget
2026-04-08 14:36 ` [PATCH 1/4] t5516: fix test order flakiness Derrick Stolee via GitGitGadget
2026-04-08 14:36 ` [PATCH 2/4] fetch: add --must-have option for negotiation Derrick Stolee via GitGitGadget
2026-04-08 14:36 ` Derrick Stolee via GitGitGadget [this message]
2026-04-08 14:36 ` [PATCH 4/4] send-pack: pass --must-have for push negotiation Derrick Stolee via GitGitGadget
2026-04-08 18:59 ` [PATCH 0/4] fetch: add --must-have and remote.*.mustHave Junio C Hamano
2026-04-09 12:53 ` Derrick Stolee
2026-04-15 15:14 ` [PATCH v2 0/7] fetch: rework negotiation tip options Derrick Stolee via GitGitGadget
2026-04-15 15:14 ` [PATCH v2 1/7] t5516: fix test order flakiness Derrick Stolee via GitGitGadget
2026-04-15 15:14 ` [PATCH v2 2/7] fetch: add --negotiation-restrict option Derrick Stolee via GitGitGadget
2026-04-15 21:57 ` Junio C Hamano
2026-04-19 23:00 ` Derrick Stolee
2026-04-20 10:32 ` Junio C Hamano
2026-04-20 11:35 ` Derrick Stolee
2026-04-15 15:14 ` [PATCH v2 3/7] transport: rename negotiation_tips Derrick Stolee via GitGitGadget
2026-04-20 8:11 ` Patrick Steinhardt
2026-04-15 15:14 ` [PATCH v2 4/7] remote: add remote.*.negotiationRestrict config Derrick Stolee via GitGitGadget
2026-04-15 19:16 ` Junio C Hamano
2026-04-15 15:14 ` [PATCH v2 5/7] fetch: add --negotiation-require option for negotiation Derrick Stolee via GitGitGadget
2026-04-15 19:50 ` Junio C Hamano
2026-04-21 18:06 ` Derrick Stolee
2026-04-20 8:11 ` Patrick Steinhardt
2026-04-20 11:41 ` Derrick Stolee
2026-04-15 15:14 ` [PATCH v2 6/7] remote: add negotiationRequire config as default for --negotiation-require Derrick Stolee via GitGitGadget
2026-04-15 15:14 ` [PATCH v2 7/7] send-pack: pass negotiation config in push Derrick Stolee via GitGitGadget
2026-04-22 15:25 ` [PATCH v3 0/7] fetch: rework negotiation tip options Derrick Stolee via GitGitGadget
2026-04-22 15:25 ` [PATCH v3 1/7] t5516: fix test order flakiness Derrick Stolee via GitGitGadget
2026-05-12 10:50 ` Matthew John Cheetham
2026-04-22 15:25 ` [PATCH v3 2/7] fetch: add --negotiation-restrict option Derrick Stolee via GitGitGadget
2026-05-12 11:11 ` Matthew John Cheetham
2026-05-12 14:23 ` Derrick Stolee
2026-04-22 15:25 ` [PATCH v3 3/7] transport: rename negotiation_tips Derrick Stolee via GitGitGadget
2026-05-12 11:30 ` Matthew John Cheetham
2026-05-12 14:33 ` Derrick Stolee
2026-04-22 15:25 ` [PATCH v3 4/7] remote: add remote.*.negotiationRestrict config Derrick Stolee via GitGitGadget
2026-05-12 12:29 ` Matthew John Cheetham
2026-05-12 14:52 ` Derrick Stolee
2026-04-22 15:25 ` [PATCH v3 5/7] fetch: add --negotiation-include option for negotiation Derrick Stolee via GitGitGadget
2026-05-12 14:38 ` Matthew John Cheetham
2026-05-12 16:54 ` Derrick Stolee
2026-04-22 15:25 ` [PATCH v3 6/7] remote: add remote.*.negotiationInclude config Derrick Stolee via GitGitGadget
2026-05-12 14:54 ` Matthew John Cheetham
2026-05-12 17:55 ` Derrick Stolee
2026-04-22 15:25 ` [PATCH v3 7/7] send-pack: pass negotiation config in push Derrick Stolee via GitGitGadget
2026-05-12 15:14 ` Matthew John Cheetham
2026-05-14 12:41 ` [PATCH v4 0/8] fetch: rework negotiation tip options Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 1/8] t5516: fix test order flakiness Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 2/8] fetch: add --negotiation-restrict option Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 3/8] transport: rename negotiation_tips Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 4/8] remote: add remote.*.negotiationRestrict config Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 5/8] negotiator: add have_sent() interface Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 6/8] fetch: add --negotiation-include option for negotiation Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 7/8] remote: add remote.*.negotiationInclude config Derrick Stolee via GitGitGadget
2026-05-14 12:41 ` [PATCH v4 8/8] send-pack: pass negotiation config in push Derrick Stolee via GitGitGadget
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=fbc98b0cbb93628e24b60163e4d3e55383ab9158.1775658970.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=chooglen@google.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jonathantanmy@google.com \
--cc=ps@pks.im \
--cc=stolee@gmail.com \
/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.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox