fetch: add --negotiation-restrict option

The --negotiation-tip option to 'git fetch' and 'git pull' allows users to specify that they want to focus negotiation on a small set of references. This is a _restriction_ on the negotiation set, helping to focus the negotiation when the ref count is high. However, it doesn't allow for the ability to opportunistically select references beyond that list. This subtle detail that this is a 'maximum set' and not a 'minimum set' is not immediately clear from the option name. This makes it more complicated to add a new option that provides the complementary behavior of a minimum set. For now, create a new synonym option, --negotiation-restrict, that behaves identically to --negotiation-tip. Update the documentation to make it clear that this new name is the preferred option, but we keep the old name for compatibility. Mark --negotiation-tip as an alias of the new, preferred option. Update a few warning messages with the new option, but also make them translatable with the option name inserted by formatting. At least one of these messages will be reused later for a new option. Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 19, 2026 at 16:24 UTC 1a445fc60b84df95253d740f3a112343ab5ed8d2
8 files changed +46 -12
Documentation/config/fetch.adoc
+1 -1
@@ -76,7 +76,7 @@
76 default is `skipping`. Unknown values will cause `git fetch` to
77 error out.
78 +
79 -See also the `--negotiate-only` and `--negotiation-tip` options to
79 +See also the `--negotiate-only` and `--negotiation-restrict` options to
80 linkgit:git-fetch[1].
81
82 `fetch.showForcedUpdates`::
Documentation/fetch-options.adoc
+5 -1
@@ -49,6 +49,7 @@ the current repository has the same history as the source repository.
49 `.git/shallow`. This option updates `.git/shallow` and accepts such
50 refs.
51
52 +`--negotiation-restrict=(<commit>|<glob>)`::
53 `--negotiation-tip=(<commit>|<glob>)`::
54 By default, Git will report, to the server, commits reachable
55 from all local refs to find common commits in an attempt to
@@ -58,6 +59,9 @@ the current repository has the same history as the source repository.
59 local ref is likely to have commits in common with the
60 upstream ref being fetched.
61 +
62 +`--negotiation-restrict` is the preferred name for this option;
63 +`--negotiation-tip` is accepted as a synonym.
64 ++
65 This option may be specified more than once; if so, Git will report
66 commits reachable from any of the given commits.
67 +
@@ -71,7 +75,7 @@ configuration variables documented in linkgit:git-config[1], and the
75
76 `--negotiate-only`::
77 Do not fetch anything from the server, and instead print the
74 - ancestors of the provided `--negotiation-tip=` arguments,
78 + ancestors of the provided `--negotiation-restrict=` arguments,
79 which we have in common with the server.
80 +
81 This is incompatible with `--recurse-submodules=(yes|on-demand)`.
builtin/fetch.c
+8 -5
@@ -1558,8 +1558,8 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
1558 refs_for_each_ref_ext(get_main_ref_store(the_repository),
1559 add_oid, oids, &opts);
1560 if (old_nr == oids->nr)
1561 - warning("ignoring --negotiation-tip=%s because it does not match any refs",
1562 - s);
1561 + warning(_("ignoring %s=%s because it does not match any refs"),
1562 + "--negotiation-restrict", s);
1563 }
1564 smart_options->negotiation_tips = oids;
1565 }
@@ -1599,7 +1599,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen,
1599 if (transport->smart_options)
1600 add_negotiation_tips(transport->smart_options);
1601 else
1602 - warning("ignoring --negotiation-tip because the protocol does not support it");
1602 + warning(_("ignoring %s because the protocol does not support it"),
1603 + "--negotiation-restrict");
1604 }
1605 return transport;
1606 }
@@ -2565,8 +2566,9 @@ int cmd_fetch(int argc,
2566 N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
2567 OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
2568 OPT_IPVERSION(&family),
2568 - OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
2569 + OPT_STRING_LIST(0, "negotiation-restrict", &negotiation_tip, N_("revision"),
2570 N_("report that we have only objects reachable from this object")),
2571 + OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"),
2572 OPT_BOOL(0, "negotiate-only", &negotiate_only,
2573 N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
2574 OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
@@ -2657,7 +2659,8 @@ int cmd_fetch(int argc,
2659 }
2660
2661 if (negotiate_only && !negotiation_tip.nr)
2660 - die(_("--negotiate-only needs one or more --negotiation-tip=*"));
2662 + die(_("%s needs one or more %s"), "--negotiate-only",
2663 + "--negotiation-restrict=*");
2664
2665 if (deepen_relative) {
2666 if (deepen_relative < 0)
builtin/pull.c
+2 -1
@@ -996,9 +996,10 @@ int cmd_pull(int argc,
996 OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
997 N_("use IPv6 addresses only"),
998 PARSE_OPT_NOARG),
999 - OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
999 + OPT_PASSTHRU_ARGV(0, "negotiation-restrict", &opt_fetch, N_("revision"),
1000 N_("report that we have only objects reachable from this object"),
1001 0),
1002 + OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"),
1003 OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
1004 N_("check for forced-updates on all updated branches")),
1005 OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
send-pack.c
+1 -1
@@ -447,7 +447,7 @@ static void get_commons_through_negotiation(struct repository *r,
447 strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
448 for (ref = remote_refs; ref; ref = ref->next) {
449 if (!is_null_oid(&ref->new_oid)) {
450 - strvec_pushf(&child.args, "--negotiation-tip=%s",
450 + strvec_pushf(&child.args, "--negotiation-restrict=%s",
451 oid_to_hex(&ref->new_oid));
452 nr_negotiation_tip++;
453 }
t/t5510-fetch.sh
+25
@@ -1460,6 +1460,31 @@ EOF
1460 test_cmp fatal-expect fatal-actual
1461 '
1462
1463 +test_expect_success '--negotiation-restrict limits "have" lines sent' '
1464 + setup_negotiation_tip server server 0 &&
1465 + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1466 + --negotiation-restrict=alpha_1 --negotiation-restrict=beta_1 \
1467 + origin alpha_s beta_s &&
1468 + check_negotiation_tip
1469 +'
1470 +
1471 +test_expect_success '--negotiation-restrict understands globs' '
1472 + setup_negotiation_tip server server 0 &&
1473 + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1474 + --negotiation-restrict=*_1 \
1475 + origin alpha_s beta_s &&
1476 + check_negotiation_tip
1477 +'
1478 +
1479 +test_expect_success '--negotiation-restrict and --negotiation-tip can be mixed' '
1480 + setup_negotiation_tip server server 0 &&
1481 + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
1482 + --negotiation-restrict=alpha_1 \
1483 + --negotiation-tip=beta_1 \
1484 + origin alpha_s beta_s &&
1485 + check_negotiation_tip
1486 +'
1487 +
1488 test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' '
1489 git init df-conflict &&
1490 (
t/t5702-protocol-v2.sh
+2 -2
@@ -869,14 +869,14 @@ setup_negotiate_only () {
869 test_commit -C client three
870 }
871
872 -test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
872 +test_expect_success 'usage: --negotiate-only without --negotiation-restrict' '
873 SERVER="server" &&
874 URI="file://$(pwd)/server" &&
875
876 setup_negotiate_only "$SERVER" "$URI" &&
877
878 cat >err.expect <<-\EOF &&
879 - fatal: --negotiate-only needs one or more --negotiation-tip=*
879 + fatal: --negotiate-only needs one or more --negotiation-restrict=*
880 EOF
881
882 test_must_fail git -c protocol.version=2 -C client fetch \
transport-helper.c
+2 -1
@@ -755,7 +755,8 @@ static int fetch_refs(struct transport *transport,
755 }
756
757 if (data->transport_options.negotiation_tips)
758 - warning("Ignoring --negotiation-tip because the protocol does not support it.");
758 + warning(_("ignoring %s because the protocol does not support it."),
759 + "--negotiation-restrict");
760
761 if (data->fetch)
762 return fetch_with_fetch(transport, nr_heads, to_fetch);