git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 3/4] remote: add mustHave config as default for --must-have

From
Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
Date
Apr 8, 2026, 14:36 UTC
Message-ID
<fbc98b0cbb93628e24b60163e4d3e55383ab9158.1775658970.git.gitgitgadget@gmail.com>
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
Previous: Derrick Stolee via GitGitGadgetNext: Derrick Stolee via GitGitGadget
Message 4 of 86 in “fetch: add --must-have and remote.*.mustHave”
  1. 0/4 fetch: add --must-have and remote.*.mustHaveDerrick Stolee via GitGitGadget, Apr 8, 2026
  2. 1/4 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, Apr 8, 2026
  3. 2/4 fetch: add --must-have option for negotiationDerrick Stolee via GitGitGadget, Apr 8, 2026
  4. 3/4 remote: add mustHave config as default for --must-haveDerrick Stolee via GitGitGadget, Apr 8, 2026
  5. 4/4 send-pack: pass --must-have for push negotiationDerrick Stolee via GitGitGadget, Apr 8, 2026
  6. Junio C HamanoApr 8, 2026
  7. Derrick StoleeApr 9, 2026
  8. 0/7 fetch: rework negotiation tip optionsDerrick Stolee via GitGitGadget, Apr 15, 2026
  9. 1/7 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, Apr 15, 2026
  10. 2/7 fetch: add --negotiation-restrict optionDerrick Stolee via GitGitGadget, Apr 15, 2026
  11. Junio C HamanoApr 15, 2026
  12. Derrick StoleeApr 19, 2026
  13. Junio C HamanoApr 20, 2026
  14. Derrick StoleeApr 20, 2026
  15. 3/7 transport: rename negotiation_tipsDerrick Stolee via GitGitGadget, Apr 15, 2026
  16. Patrick SteinhardtApr 20, 2026
  17. 4/7 remote: add remote.*.negotiationRestrict configDerrick Stolee via GitGitGadget, Apr 15, 2026
  18. Junio C HamanoApr 15, 2026
  19. 5/7 fetch: add --negotiation-require option for negotiationDerrick Stolee via GitGitGadget, Apr 15, 2026
  20. Junio C HamanoApr 15, 2026
  21. Derrick StoleeApr 21, 2026
  22. Patrick SteinhardtApr 20, 2026
  23. Derrick StoleeApr 20, 2026
  24. 6/7 remote: add negotiationRequire config as default for --negotiation-requireDerrick Stolee via GitGitGadget, Apr 15, 2026
  25. 7/7 send-pack: pass negotiation config in pushDerrick Stolee via GitGitGadget, Apr 15, 2026
  26. 0/7 fetch: rework negotiation tip optionsDerrick Stolee via GitGitGadget, Apr 22, 2026
  27. 1/7 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, Apr 22, 2026
  28. Matthew John CheethamMay 12, 2026
  29. 2/7 fetch: add --negotiation-restrict optionDerrick Stolee via GitGitGadget, Apr 22, 2026
  30. Matthew John CheethamMay 12, 2026
  31. Derrick StoleeMay 12, 2026
  32. 3/7 transport: rename negotiation_tipsDerrick Stolee via GitGitGadget, Apr 22, 2026
  33. Matthew John CheethamMay 12, 2026
  34. Derrick StoleeMay 12, 2026
  35. 4/7 remote: add remote.*.negotiationRestrict configDerrick Stolee via GitGitGadget, Apr 22, 2026
  36. Matthew John CheethamMay 12, 2026
  37. Derrick StoleeMay 12, 2026
  38. 5/7 fetch: add --negotiation-include option for negotiationDerrick Stolee via GitGitGadget, Apr 22, 2026
  39. Matthew John CheethamMay 12, 2026
  40. Derrick StoleeMay 12, 2026
  41. 6/7 remote: add remote.*.negotiationInclude configDerrick Stolee via GitGitGadget, Apr 22, 2026
  42. Matthew John CheethamMay 12, 2026
  43. Derrick StoleeMay 12, 2026
  44. 7/7 send-pack: pass negotiation config in pushDerrick Stolee via GitGitGadget, Apr 22, 2026
  45. Matthew John CheethamMay 12, 2026
  46. 0/8 fetch: rework negotiation tip optionsDerrick Stolee via GitGitGadget, May 14, 2026
  47. 1/8 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, May 14, 2026
  48. Matthew John CheethamMay 18, 2026
  49. 2/8 fetch: add --negotiation-restrict optionDerrick Stolee via GitGitGadget, May 14, 2026
  50. Matthew John CheethamMay 18, 2026
  51. 3/8 transport: rename negotiation_tipsDerrick Stolee via GitGitGadget, May 14, 2026
  52. Matthew John CheethamMay 18, 2026
  53. 4/8 remote: add remote.*.negotiationRestrict configDerrick Stolee via GitGitGadget, May 14, 2026
  54. Matthew John CheethamMay 18, 2026
  55. 5/8 negotiator: add have_sent() interfaceDerrick Stolee via GitGitGadget, May 14, 2026
  56. Matthew John CheethamMay 18, 2026
  57. 6/8 fetch: add --negotiation-include option for negotiationDerrick Stolee via GitGitGadget, May 14, 2026
  58. Matthew John CheethamMay 18, 2026
  59. 7/8 remote: add remote.*.negotiationInclude configDerrick Stolee via GitGitGadget, May 14, 2026
  60. Matthew John CheethamMay 18, 2026
  61. 8/8 send-pack: pass negotiation config in pushDerrick Stolee via GitGitGadget, May 14, 2026
  62. Matthew John CheethamMay 18, 2026
  63. Matthew John CheethamMay 18, 2026
  64. Derrick StoleeMay 18, 2026
  65. 0/8 fetch: rework negotiation tip optionsDerrick Stolee via GitGitGadget, May 18, 2026
  66. 2/8 fetch: add --negotiation-restrict optionDerrick Stolee via GitGitGadget, May 18, 2026
  67. 3/8 transport: rename negotiation_tipsDerrick Stolee via GitGitGadget, May 18, 2026
  68. 4/8 remote: add remote.*.negotiationRestrict configDerrick Stolee via GitGitGadget, May 18, 2026
  69. 5/8 negotiator: add have_sent() interfaceDerrick Stolee via GitGitGadget, May 18, 2026
  70. 6/8 fetch: add --negotiation-include option for negotiationDerrick Stolee via GitGitGadget, May 18, 2026
  71. 7/8 remote: add remote.*.negotiationInclude configDerrick Stolee via GitGitGadget, May 18, 2026
  72. 8/8 send-pack: pass negotiation config in pushDerrick Stolee via GitGitGadget, May 18, 2026
  73. 1/8 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, May 18, 2026
  74. Matthew John CheethamMay 19, 2026
  75. Derrick StoleeMay 19, 2026
  76. 0/8 fetch: rework negotiation tip optionsDerrick Stolee via GitGitGadget, May 19, 2026
  77. 1/8 t5516: fix test order flakinessDerrick Stolee via GitGitGadget, May 19, 2026
  78. 2/8 fetch: add --negotiation-restrict optionDerrick Stolee via GitGitGadget, May 19, 2026
  79. 3/8 transport: rename negotiation_tipsDerrick Stolee via GitGitGadget, May 19, 2026
  80. 4/8 remote: add remote.*.negotiationRestrict configDerrick Stolee via GitGitGadget, May 19, 2026
  81. 5/8 negotiator: add have_sent() interfaceDerrick Stolee via GitGitGadget, May 19, 2026
  82. 6/8 fetch: add --negotiation-include option for negotiationDerrick Stolee via GitGitGadget, May 19, 2026
  83. 7/8 remote: add remote.*.negotiationInclude configDerrick Stolee via GitGitGadget, May 19, 2026
  84. 8/8 send-pack: pass negotiation config in pushDerrick Stolee via GitGitGadget, May 19, 2026
  85. Matthew John CheethamMay 19, 2026
  86. Junio C HamanoMay 20, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.