From: Matthew John Cheetham Date: Mon, 18 May 2026 17:12:27 GMT Subject: Re: [PATCH v4 6/8] fetch: add --negotiation-include option for negotiation Message-ID: In-Reply-To: On 2026-05-14 13:41, Derrick Stolee via GitGitGadget wrote: > From: Derrick Stolee > > Add a new --negotiation-include option to 'git fetch', which ensures > that certain ref tips are always sent as 'have' lines during fetch > negotiation, regardless of what the negotiation algorithm selects. > > This is useful when the repository has a large number of references, so > the normal negotiation algorithm truncates the list. This is especially > important in repositories with long parallel commit histories. For > example, a repo could have a 'dev' branch for development and a > 'release' branch for released versions. If the 'dev' branch isn't > selected for negotiation, then it's not a big deal because there are > many in-progress development branches with a shared history. However, if > 'release' is not selected for negotiation, then the server may think > that this is the first time the client has asked for that reference, > causing a full download of its parallel commit history (and any extra > data that may be unique to that branch). This is based on a real example > where certain fetches would grow to 60+ GB when a release branch > updated. > > This option is a complement to --negotiation-restrict, which reduces the > negotiation ref set to a specific list. In the earlier example, using > --negotiation-restrict to focus the negotiation to 'dev' and 'release' > would avoid those problematic downloads, but would still not allow > advertising potentially-relevant user branches. In this way, the > 'include' version solves the problem I mention while allowing > negotiation to pick other references opportunistically. The two options > can also be combined to allow the best of both worlds. brances/branches from v3 fixed - thanks! > The argument may be an exact ref name or a glob pattern. Non-existent > refs are silently ignored. This behavior is also updated in the ref matching > logic for the related --negotiation-restrict option to match. > > The implementation outputs the requested objects as haves before the > negotiator performs its own algorithm to choose the next haves. Use the new > have_sent() interface to signal these have commits were sent before engaging > with the negotiator's next() iterator. Now references the new have_sent() API - good! > Also add --negotiation-include to 'git pull' passthrough options. > > Signed-off-by: Derrick Stolee > --- > Documentation/fetch-options.adoc | 19 +++++++ > builtin/fetch.c | 32 ++++++++--- > builtin/pull.c | 3 ++ > fetch-pack.c | 81 +++++++++++++++++++++++++--- > fetch-pack.h | 6 ++- > t/t5510-fetch.sh | 91 ++++++++++++++++++++++++++++++++ > transport.c | 8 ++- > transport.h | 5 +- > 8 files changed, 227 insertions(+), 18 deletions(-) > > diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc > index d39cecb446..7b897a7202 100644 > --- a/Documentation/fetch-options.adoc > +++ b/Documentation/fetch-options.adoc > @@ -73,6 +73,25 @@ See also the `fetch.negotiationAlgorithm` and `push.negotiate` > configuration variables documented in linkgit:git-config[1], and the > `--negotiate-only` option below. > > +`--negotiation-include=(|)`:: > + Ensure that the commits at the given tips are always sent as "have" > + lines during fetch negotiation, regardless of what the negotiation > + algorithm selects. This is useful to guarantee that common > + history reachable from specific refs is always considered, even > + when `--negotiation-restrict` restricts the set of tips or when > + the negotiation algorithm would otherwise skip them. > ++ > +This option may be specified more than once; if so, each commit is sent > +unconditionally. > ++ > +The argument may be an exact ref name (e.g. `refs/heads/release`), an > +object hash, or a glob pattern (e.g. `refs/heads/release/{asterisk}`). > +The pattern syntax is the same as for `--negotiation-restrict`. > ++ > +If `--negotiation-restrict` is used, the have set is first restricted by > +that option and then increased to include the tips specified by > +`--negotiation-include`. > + Good - the syntax placeholder matches --negotiate-restrict, and we mention object hashes being valid input. > `--negotiate-only`:: > Do not fetch anything from the server, and instead print the > ancestors of the provided `--negotiation-restrict=` arguments, > diff --git a/builtin/fetch.c b/builtin/fetch.c > index a957739f37..6b456b3689 100644 > --- a/builtin/fetch.c > +++ b/builtin/fetch.c > @@ -99,6 +99,7 @@ static struct transport *gsecondary; > static struct refspec refmap = REFSPEC_INIT_FETCH; > static struct string_list server_options = STRING_LIST_INIT_DUP; > static struct string_list negotiation_restrict = STRING_LIST_INIT_NODUP; > +static struct string_list negotiation_include = STRING_LIST_INIT_NODUP; > > struct fetch_config { > enum display_format display_format; > @@ -1534,23 +1535,28 @@ static int add_oid(const struct reference *ref, void *cb_data) > return 0; > } > > -static void add_negotiation_restrict_tips(struct git_transport_options *smart_options) > +static void add_negotiation_tips(struct string_list *input_list, > + struct oid_array **output_list) > { > struct oid_array *oids = xcalloc(1, sizeof(*oids)); > int i; > > - for (i = 0; i < negotiation_restrict.nr; i++) { > - const char *s = negotiation_restrict.items[i].string; > + for (i = 0; i < input_list->nr; i++) { > + const char *s = input_list->items[i].string; > struct refs_for_each_ref_options opts = { > .pattern = s, > }; > int old_nr; > if (!has_glob_specials(s)) { > struct object_id oid; > + > + /* Ignore missing reference. */ > if (repo_get_oid(the_repository, s, &oid)) > - die(_("%s is not a valid object"), s); > + continue; > + /* Fail on missing object pointed by ref. */ > if (!odb_has_object(the_repository->objects, &oid, 0)) > die(_("the object %s does not exist"), s); > + > oid_array_append(oids, &oid); > continue; > } > @@ -1561,7 +1567,7 @@ static void add_negotiation_restrict_tips(struct git_transport_options *smart_op > warning(_("ignoring %s=%s because it does not match any refs"), > "--negotiation-restrict", s); > } > - smart_options->negotiation_restrict_tips = oids; > + *output_list = oids; > } > > static struct transport *prepare_transport(struct remote *remote, int deepen, Great! Now we have a unified implementation that takes a string_list and ouputs an oid_array, used for both restrict and include lists. We pre-resolve so that fetch-pack gets the oid_array so the ref resolution happens in the same layer for both. Just one small issue I see - the function emits a warning for "--negotiate-restrict" when we don't match a ref, even if this is handling a --negotiate-include value. Perhaps we should pass the option name in as a parameter? add_negotiation_tips(&negotiation_include, "--negotiation-include", &transport->smart_opt->negotiation_include_tips); > @@ -1597,7 +1603,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen, > } > if (negotiation_restrict.nr) { > if (transport->smart_options) > - add_negotiation_restrict_tips(transport->smart_options); > + add_negotiation_tips(&negotiation_restrict, > + &transport->smart_options->negotiation_restrict_tips); > else > warning(_("ignoring %s because the protocol does not support it"), > "--negotiation-restrict"); > @@ -1606,7 +1613,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen, > 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); > + add_negotiation_tips(&negotiation_restrict, > + &transport->smart_options->negotiation_restrict_tips); > else { > struct strbuf config_name = STRBUF_INIT; > strbuf_addf(&config_name, "remote.%s.negotiationRestrict", remote->name); > @@ -1615,6 +1623,14 @@ static struct transport *prepare_transport(struct remote *remote, int deepen, > strbuf_release(&config_name); > } > } > + if (negotiation_include.nr) { > + if (transport->smart_options) > + add_negotiation_tips(&negotiation_include, > + &transport->smart_options->negotiation_include_tips); > + else > + warning(_("ignoring %s because the protocol does not support it"), > + "--negotiation-include"); > + } > return transport; > } Having the shared helper makes this much nicer. Good! > @@ -2582,6 +2598,8 @@ int cmd_fetch(int argc, > OPT_STRING_LIST(0, "negotiation-restrict", &negotiation_restrict, N_("revision"), > N_("report that we have only objects reachable from this object")), > OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"), > + OPT_STRING_LIST(0, "negotiation-include", &negotiation_include, N_("revision"), > + N_("ensure this ref is always sent as a negotiation have")), > OPT_BOOL(0, "negotiate-only", &negotiate_only, > N_("do not fetch a packfile; instead, print ancestors of negotiation tips")), > OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), > diff --git a/builtin/pull.c b/builtin/pull.c > index cc6ce485fc..d49b09114a 100644 > --- a/builtin/pull.c > +++ b/builtin/pull.c > @@ -1000,6 +1000,9 @@ int cmd_pull(int argc, > N_("report that we have only objects reachable from this object"), > 0), > OPT_ALIAS(0, "negotiation-tip", "negotiation-restrict"), > + OPT_PASSTHRU_ARGV(0, "negotiation-include", &opt_fetch, N_("revision"), > + N_("ensure this ref is always sent as a negotiation have"), > + 0), > OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates, > N_("check for forced-updates on all updated branches")), > OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL, LGTM > diff --git a/fetch-pack.c b/fetch-pack.c > index baf239adf9..96071434b8 100644 > --- a/fetch-pack.c > +++ b/fetch-pack.c > @@ -25,6 +25,7 @@ > #include "oidset.h" > #include "packfile.h" > #include "odb.h" > +#include "object-name.h" > #include "path.h" > #include "connected.h" > #include "fetch-negotiator.h" > @@ -332,6 +333,21 @@ static void send_filter(struct fetch_pack_args *args, > } > } > > +static void add_oids_to_set(const struct oid_array *array, > + struct oidset *set) > +{ > + if (!array) > + return; > + > + for (size_t i = 0; i < array->nr; i++) { > + struct object_id *oid = &array->oid[i]; > + if (!odb_has_object(the_repository->objects, oid, 0)) > + die(_("the object %s does not exist"), oid_to_hex(oid)); > + > + oidset_insert(set, oid); > + } > +} > + Nice - this is much simpler since ref resolution has been hoisted up and we only deal with pre-resolved OIDs. > static int find_common(struct fetch_negotiator *negotiator, > struct fetch_pack_args *args, > int fd[2], struct object_id *result_oid, > @@ -347,6 +363,7 @@ static int find_common(struct fetch_negotiator *negotiator, > struct strbuf req_buf = STRBUF_INIT; > size_t state_len = 0; > struct packet_reader reader; > + struct oidset negotiation_include_oids = OIDSET_INIT; > > if (args->stateless_rpc && multi_ack == 1) > die(_("the option '%s' requires '%s'"), "--stateless-rpc", "multi_ack_detailed"); > @@ -474,6 +491,27 @@ static int find_common(struct fetch_negotiator *negotiator, > trace2_region_enter("fetch-pack", "negotiation_v0_v1", the_repository); > flushes = 0; > retval = -1; > + > + /* Send unconditional haves from --negotiation-include */ > + add_oids_to_set(args->negotiation_include_tips, > + &negotiation_include_oids); > + if (oidset_size(&negotiation_include_oids)) { > + struct oidset_iter iter; > + oidset_iter_init(&negotiation_include_oids, &iter); > + > + while ((oid = oidset_iter_next(&iter))) { > + struct commit *commit; > + packet_buf_write(&req_buf, "have %s\n", > + oid_to_hex(oid)); > + print_verbose(args, "have %s", oid_to_hex(oid)); > + count++; > + > + commit = lookup_commit(the_repository, oid); > + if (commit) > + negotiator->have_sent(negotiator, commit); > + } > + } > + > while ((oid = negotiator->next(negotiator))) { > packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); > print_verbose(args, "have %s", oid_to_hex(oid)); > @@ -584,6 +622,7 @@ done: > flushes++; > } > strbuf_release(&req_buf); > + oidset_clear(&negotiation_include_oids); > > if (!got_ready || !no_done) > consume_shallow_list(args, &reader); > @@ -1305,11 +1344,27 @@ static void add_common(struct strbuf *req_buf, struct oidset *common) > > static int add_haves(struct fetch_negotiator *negotiator, > struct strbuf *req_buf, > - int *haves_to_send) > + int *haves_to_send, > + struct oidset *negotiation_include_oids) > { > int haves_added = 0; > const struct object_id *oid; > > + /* Send unconditional haves from --negotiation-include */ > + if (negotiation_include_oids) { > + struct oidset_iter iter; > + oidset_iter_init(negotiation_include_oids, &iter); > + > + while ((oid = oidset_iter_next(&iter))) { > + struct commit *commit = lookup_commit(the_repository, oid); > + if (commit) { > + packet_buf_write(req_buf, "have %s\n", > + oid_to_hex(oid)); > + negotiator->have_sent(negotiator, commit); > + } > + } > + } > + > while ((oid = negotiator->next(negotiator))) { > packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid)); > if (++haves_added >= *haves_to_send) Here we're using the new have_sent() API and replaces the manual COMMON bit flipping. > @@ -1358,7 +1413,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, > struct fetch_pack_args *args, > const struct ref *wants, struct oidset *common, > int *haves_to_send, int *in_vain, > - int sideband_all, int seen_ack) > + int sideband_all, int seen_ack, > + struct oidset *negotiation_include_oids) > { > int haves_added; > int done_sent = 0; > @@ -1413,7 +1469,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, > /* Add all of the common commits we've found in previous rounds */ > add_common(&req_buf, common); > > - haves_added = add_haves(negotiator, &req_buf, haves_to_send); > + haves_added = add_haves(negotiator, &req_buf, haves_to_send, > + negotiation_include_oids); > *in_vain += haves_added; > trace2_data_intmax("negotiation_v2", the_repository, "haves_added", haves_added); > trace2_data_intmax("negotiation_v2", the_repository, "in_vain", *in_vain); > @@ -1657,6 +1714,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, > struct ref *ref = copy_ref_list(orig_ref); > enum fetch_state state = FETCH_CHECK_LOCAL; > struct oidset common = OIDSET_INIT; > + struct oidset negotiation_include_oids = OIDSET_INIT; > struct packet_reader reader; > int in_vain = 0, negotiation_started = 0; > int negotiation_round = 0; > @@ -1729,6 +1787,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, > state = FETCH_SEND_REQUEST; > > mark_tips(negotiator, args->negotiation_restrict_tips); > + add_oids_to_set(args->negotiation_include_tips, > + &negotiation_include_oids); > for_each_cached_alternate(negotiator, > insert_one_alternate_object); > break; > @@ -1747,7 +1807,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, > &common, > &haves_to_send, &in_vain, > reader.use_sideband, > - seen_ack)) { > + seen_ack, > + &negotiation_include_oids)) { > trace2_region_leave_printf("negotiation_v2", "round", > the_repository, "%d", > negotiation_round); > @@ -1883,6 +1944,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, > negotiator->release(negotiator); > > oidset_clear(&common); > + oidset_clear(&negotiation_include_oids); > return ref; > } The v2 wiring looks correct. > @@ -2181,12 +2243,14 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips, > const struct string_list *server_options, > int stateless_rpc, > int fd[], > - struct oidset *acked_commits) > + struct oidset *acked_commits, > + const struct oid_array *negotiation_include_tips) > { > struct fetch_negotiator negotiator; > struct packet_reader reader; > struct object_array nt_object_array = OBJECT_ARRAY_INIT; > struct strbuf req_buf = STRBUF_INIT; > + struct oidset negotiation_include_oids = OIDSET_INIT; > int haves_to_send = INITIAL_FLUSH; > int in_vain = 0; > int seen_ack = 0; > @@ -2197,6 +2261,9 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips, > fetch_negotiator_init(the_repository, &negotiator); > mark_tips(&negotiator, negotiation_restrict_tips); > > + add_oids_to_set(negotiation_include_tips, > + &negotiation_include_oids); > + > packet_reader_init(&reader, fd[0], NULL, 0, > PACKET_READ_CHOMP_NEWLINE | > PACKET_READ_DIE_ON_ERR_PACKET); > @@ -2221,7 +2288,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips, > > packet_buf_write(&req_buf, "wait-for-done"); > > - haves_added = add_haves(&negotiator, &req_buf, &haves_to_send); > + haves_added = add_haves(&negotiator, &req_buf, &haves_to_send, > + &negotiation_include_oids); > in_vain += haves_added; > if (!haves_added || (seen_ack && in_vain >= MAX_IN_VAIN)) > last_iteration = 1; > @@ -2273,6 +2341,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips, > > clear_common_flag(acked_commits); > object_array_clear(&nt_object_array); > + oidset_clear(&negotiation_include_oids); > negotiator.release(&negotiator); > strbuf_release(&req_buf); > } > diff --git a/fetch-pack.h b/fetch-pack.h > index 6c70c942c2..6d0dec7f41 100644 > --- a/fetch-pack.h > +++ b/fetch-pack.h > @@ -19,9 +19,10 @@ struct fetch_pack_args { > > /* > * If not NULL, during packfile negotiation, fetch-pack will send "have" > - * lines only with these tips and their ancestors. > + * lines for all _include_ tips and then a subset of the _restrict_ tips. > */ > const struct oid_array *negotiation_restrict_tips; > + const struct oid_array *negotiation_include_tips; > > unsigned deepen_relative:1; > unsigned quiet:1; > @@ -93,7 +94,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips, > const struct string_list *server_options, > int stateless_rpc, > int fd[], > - struct oidset *acked_commits); > + struct oidset *acked_commits, > + const struct oid_array *negotiation_include_tips); > > /* > * Print an appropriate error message for each sought ref that wasn't > diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh > index eff3ce8e2d..bc2e2af959 100755 > --- a/t/t5510-fetch.sh > +++ b/t/t5510-fetch.sh > @@ -1460,6 +1460,16 @@ EOF > test_cmp fatal-expect fatal-actual > ' > > +test_expect_success '--negotiation-tip ignores missing refs and invalid hashes' ' > + setup_negotiation_tip server server 0 && > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > + --negotiation-tip=alpha_1 --negotiation-tip=beta_1 \ > + --negotiation-tip=no-such-ref \ > + --negotiation-tip=invalid-hash \ > + origin alpha_s beta_s && > + check_negotiation_tip > +' > + Good! This checks for missing refs (not just invalid ones with all zeros). > test_expect_success '--negotiation-restrict limits "have" lines sent' ' > setup_negotiation_tip server server 0 && > GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > @@ -1511,6 +1521,87 @@ test_expect_success 'CLI --negotiation-restrict overrides remote config' ' > test_grep ! "fetch> have $BETA_1" trace > ' > > +test_expect_success '--negotiation-include includes configured refs as haves' ' > + test_when_finished rm -f trace && > + setup_negotiation_tip server server 0 && > + > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > + --negotiation-restrict=alpha_1 \ > + --negotiation-include=refs/tags/beta_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 '--negotiation-include works with glob patterns' ' > + test_when_finished rm -f trace && > + setup_negotiation_tip server server 0 && > + > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > + --negotiation-restrict=alpha_1 \ > + --negotiation-include="refs/tags/beta_*" \ > + 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 '--negotiation-include is additive with negotiation' ' > + test_when_finished rm -f trace && > + setup_negotiation_tip server server 0 && > + > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > + --negotiation-include=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 > +' > + > +test_expect_success '--negotiation-include ignores non-existent refs silently' ' > + setup_negotiation_tip server server 0 && > + > + git -C client fetch --quiet \ > + --negotiation-restrict=alpha_1 \ > + --negotiation-include=refs/tags/nonexistent \ > + origin alpha_s beta_s 2>err && > + test_must_be_empty err > +' > + > +test_expect_success '--negotiation-include avoids duplicates with negotiator' ' > + test_when_finished rm -f trace && > + setup_negotiation_tip server server 0 && > + > + ALPHA_1=$(git -C client rev-parse alpha_1) && > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \ > + --negotiation-restrict=alpha_1 \ > + --negotiation-include=refs/tags/alpha_1 \ > + origin alpha_s beta_s && > + > + test_grep "fetch> have $ALPHA_1" trace >matches && > + test_line_count = 1 matches > +' > + > +test_expect_success '--negotiation-include avoids duplicates with v0' ' > + test_when_finished rm -f trace && > + setup_negotiation_tip server server 0 && > + > + ALPHA_1=$(git -C client rev-parse alpha_1) && > + GIT_TRACE_PACKET="$(pwd)/trace" git -C client \ > + -c protocol.version=0 fetch \ > + --negotiation-restrict=alpha_1 \ > + --negotiation-include=refs/tags/alpha_1 \ > + origin alpha_s beta_s && > + > + test_grep "fetch> have $ALPHA_1" trace >matches && > + test_line_count = 1 matches > +' > + > test_expect_success SYMLINKS 'clone does not get confused by a D/F conflict' ' > git init df-conflict && > ( > diff --git a/transport.c b/transport.c > index a3051f6733..fa54928966 100644 > --- a/transport.c > +++ b/transport.c > @@ -464,6 +464,7 @@ static int fetch_refs_via_pack(struct transport *transport, > args.stateless_rpc = transport->stateless_rpc; > args.server_options = transport->server_options; > args.negotiation_restrict_tips = data->options.negotiation_restrict_tips; > + args.negotiation_include_tips = data->options.negotiation_include_tips; > args.reject_shallow_remote = transport->smart_options->reject_shallow; > > if (!data->finished_handshake) { > @@ -495,7 +496,8 @@ static int fetch_refs_via_pack(struct transport *transport, > transport->server_options, > transport->stateless_rpc, > data->fd, > - data->options.acked_commits); > + data->options.acked_commits, > + data->options.negotiation_include_tips); > ret = 0; > } > goto cleanup; > @@ -983,6 +985,10 @@ static int disconnect_git(struct transport *transport) > oid_array_clear(data->options.negotiation_restrict_tips); > free(data->options.negotiation_restrict_tips); > } > + if (data->options.negotiation_include_tips) { > + oid_array_clear(data->options.negotiation_include_tips); > + free(data->options.negotiation_include_tips); > + } > list_objects_filter_release(&data->options.filter_options); > oid_array_clear(&data->extra_have); > oid_array_clear(&data->shallow); > diff --git a/transport.h b/transport.h > index cdeb33c16f..97d905ecc0 100644 > --- a/transport.h > +++ b/transport.h > @@ -40,13 +40,14 @@ struct git_transport_options { > > /* > * This is only used during fetch. See the documentation of > - * negotiation_restrict_tips in struct fetch_pack_args. > + * these member names in struct fetch_pack_args. > * > - * This field is only supported by transports that support connect or > + * These fields are only supported by transports that support connect or > * stateless_connect. Set this field directly instead of using > * transport_set_option(). > */ > struct oid_array *negotiation_restrict_tips; > + struct oid_array *negotiation_include_tips; > > /* > * If allocated, whenever transport_fetch_refs() is called, add known Overall this version of the patch is much better IMO! Just that one comment about the incorrect option name in the warning message. Thanks, Matthew