mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-10 13:02:41 -05:00
Merge branch 'ds/fetch-negotiation-options'
The negotiation tip options in "git fetch" have been reworked to allow requiring certain refs to be sent as "have" lines, and to restrict negotiation to a specific set of refs. * ds/fetch-negotiation-options: send-pack: pass negotiation config in push remote: add remote.*.negotiationInclude config fetch: add --negotiation-include option for negotiation negotiator: add have_sent() interface remote: add remote.*.negotiationRestrict config transport: rename negotiation_tips fetch: add --negotiation-restrict option t5516: fix test order flakiness
This commit is contained in:
@@ -76,7 +76,7 @@
|
||||
default is `skipping`. Unknown values will cause `git fetch` to
|
||||
error out.
|
||||
+
|
||||
See also the `--negotiate-only` and `--negotiation-tip` options to
|
||||
See also the `--negotiate-only` and `--negotiation-restrict` options to
|
||||
linkgit:git-fetch[1].
|
||||
|
||||
`fetch.showForcedUpdates`::
|
||||
|
||||
@@ -107,6 +107,55 @@ 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`, 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.
|
||||
+
|
||||
These values also influence negotiation during `git push` if
|
||||
`push.negotiate` is enabled.
|
||||
+
|
||||
Blank values signal to ignore all previous values, allowing a reset of
|
||||
the list from broader config scenarios.
|
||||
|
||||
remote.<name>.negotiationInclude::
|
||||
When negotiating with this remote during `git fetch`, 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 references, commit hashes,
|
||||
or ref pattern globs 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`), a
|
||||
commit hash, or a glob pattern (e.g. `refs/heads/release/*`). The
|
||||
pattern syntax is the same as for `--negotiation-include`.
|
||||
+
|
||||
These config values are used as defaults for the `--negotiation-include`
|
||||
command-line option. If `--negotiation-include` 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>.negotiationInclude` are sent
|
||||
unconditionally on top of those heuristically selected commits.
|
||||
+
|
||||
These values also influence negotiation during `git push` if
|
||||
`push.negotiate` is enabled.
|
||||
+
|
||||
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.
|
||||
|
||||
@@ -49,6 +49,7 @@ the current repository has the same history as the source repository.
|
||||
`.git/shallow`. This option updates `.git/shallow` and accepts such
|
||||
refs.
|
||||
|
||||
`--negotiation-restrict=(<commit>|<glob>)`::
|
||||
`--negotiation-tip=(<commit>|<glob>)`::
|
||||
By default, Git will report, to the server, commits reachable
|
||||
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.
|
||||
local ref is likely to have commits in common with the
|
||||
upstream ref being fetched.
|
||||
+
|
||||
`--negotiation-restrict` is the preferred name for this option;
|
||||
`--negotiation-tip` is accepted as a synonym.
|
||||
+
|
||||
This option may be specified more than once; if so, Git will report
|
||||
commits reachable from any of the given commits.
|
||||
+
|
||||
@@ -69,9 +73,32 @@ See also the `fetch.negotiationAlgorithm` and `push.negotiate`
|
||||
configuration variables documented in linkgit:git-config[1], and the
|
||||
`--negotiate-only` option below.
|
||||
|
||||
`--negotiation-include=(<commit>|<glob>)`::
|
||||
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`.
|
||||
+
|
||||
If this option is not specified on the command line, then any
|
||||
`remote.<name>.negotiationInclude` config values for the current remote
|
||||
are used instead.
|
||||
|
||||
`--negotiate-only`::
|
||||
Do not fetch anything from the server, and instead print the
|
||||
ancestors of the provided `--negotiation-tip=` arguments,
|
||||
ancestors of the provided `--negotiation-restrict=` arguments,
|
||||
which we have in common with the server.
|
||||
+
|
||||
This is incompatible with `--recurse-submodules=(yes|on-demand)`.
|
||||
|
||||
@@ -98,7 +98,8 @@ static struct transport *gtransport;
|
||||
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_tip = STRING_LIST_INIT_NODUP;
|
||||
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,29 @@ static int add_oid(const struct reference *ref, void *cb_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_negotiation_tips(struct git_transport_options *smart_options)
|
||||
static void add_negotiation_tips(struct string_list *input_list,
|
||||
struct oid_array **output_list,
|
||||
const char *argname)
|
||||
{
|
||||
struct oid_array *oids = xcalloc(1, sizeof(*oids));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < negotiation_tip.nr; i++) {
|
||||
const char *s = negotiation_tip.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;
|
||||
}
|
||||
@@ -1558,10 +1565,10 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
|
||||
refs_for_each_ref_ext(get_main_ref_store(the_repository),
|
||||
add_oid, oids, &opts);
|
||||
if (old_nr == oids->nr)
|
||||
warning("ignoring --negotiation-tip=%s because it does not match any refs",
|
||||
s);
|
||||
warning(_("ignoring %s=%s because it does not match any refs"),
|
||||
argname, s);
|
||||
}
|
||||
smart_options->negotiation_tips = oids;
|
||||
*output_list = oids;
|
||||
}
|
||||
|
||||
static struct transport *prepare_transport(struct remote *remote, int deepen,
|
||||
@@ -1595,11 +1602,50 @@ static struct transport *prepare_transport(struct remote *remote, int deepen,
|
||||
set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
|
||||
set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
|
||||
}
|
||||
if (negotiation_tip.nr) {
|
||||
if (negotiation_restrict.nr) {
|
||||
if (transport->smart_options)
|
||||
add_negotiation_tips(transport->smart_options);
|
||||
add_negotiation_tips(&negotiation_restrict,
|
||||
&transport->smart_options->negotiation_restrict_tips,
|
||||
"--negotiation-restrict");
|
||||
else
|
||||
warning("ignoring --negotiation-tip because the protocol does not support it");
|
||||
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_tips(&negotiation_restrict,
|
||||
&transport->smart_options->negotiation_restrict_tips,
|
||||
"--negotiation-restrict");
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (negotiation_include.nr) {
|
||||
if (transport->smart_options)
|
||||
add_negotiation_tips(&negotiation_include,
|
||||
&transport->smart_options->negotiation_include_tips,
|
||||
"--negotiation-include");
|
||||
else
|
||||
warning(_("ignoring %s because the protocol does not support it"),
|
||||
"--negotiation-include");
|
||||
} else if (remote->negotiation_include.nr) {
|
||||
if (transport->smart_options) {
|
||||
add_negotiation_tips(&remote->negotiation_include,
|
||||
&transport->smart_options->negotiation_include_tips,
|
||||
"--negotiation-include");
|
||||
} else {
|
||||
struct strbuf config_name = STRBUF_INIT;
|
||||
strbuf_addf(&config_name, "remote.%s.negotiationInclude", remote->name);
|
||||
warning(_("ignoring %s because the protocol does not support it"),
|
||||
config_name.buf);
|
||||
strbuf_release(&config_name);
|
||||
}
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
@@ -2565,8 +2611,11 @@ int cmd_fetch(int argc,
|
||||
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
|
||||
OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
|
||||
OPT_IPVERSION(&family),
|
||||
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
||||
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),
|
||||
@@ -2656,9 +2705,6 @@ int cmd_fetch(int argc,
|
||||
config.display_format = DISPLAY_FORMAT_PORCELAIN;
|
||||
}
|
||||
|
||||
if (negotiate_only && !negotiation_tip.nr)
|
||||
die(_("--negotiate-only needs one or more --negotiation-tip=*"));
|
||||
|
||||
if (deepen_relative) {
|
||||
if (deepen_relative < 0)
|
||||
die(_("negative depth in --deepen is not supported"));
|
||||
@@ -2746,14 +2792,19 @@ 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->acked_commits = &acked_commits;
|
||||
} else {
|
||||
|
||||
if (!gtransport->smart_options) {
|
||||
warning(_("protocol does not support --negotiate-only, exiting"));
|
||||
result = 1;
|
||||
trace2_region_leave("fetch", "negotiate-only", the_repository);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!gtransport->smart_options->negotiation_restrict_tips)
|
||||
die(_("%s needs one or more %s"), "--negotiate-only",
|
||||
"--negotiation-restrict=*");
|
||||
|
||||
gtransport->smart_options->acked_commits = &acked_commits;
|
||||
|
||||
if (server_options.nr)
|
||||
gtransport->server_options = &server_options;
|
||||
result = transport_fetch_refs(gtransport, NULL);
|
||||
|
||||
@@ -996,9 +996,13 @@ int cmd_pull(int argc,
|
||||
OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
|
||||
N_("use IPv6 addresses only"),
|
||||
PARSE_OPT_NOARG),
|
||||
OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
|
||||
OPT_PASSTHRU_ARGV(0, "negotiation-restrict", &opt_fetch, N_("revision"),
|
||||
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,
|
||||
|
||||
@@ -47,6 +47,15 @@ struct fetch_negotiator {
|
||||
*/
|
||||
int (*ack)(struct fetch_negotiator *, struct commit *);
|
||||
|
||||
/*
|
||||
* Inform the negotiator that this commit has already been sent as
|
||||
* a "have" line outside of the negotiator's control. The negotiator
|
||||
* should avoid outputting it from next() and may use it to optimize
|
||||
* further negotiation (e.g., by treating it and its ancestors as
|
||||
* common).
|
||||
*/
|
||||
void (*have_sent)(struct fetch_negotiator *, struct commit *);
|
||||
|
||||
void (*release)(struct fetch_negotiator *);
|
||||
|
||||
/* internal use */
|
||||
|
||||
99
fetch-pack.c
99
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"
|
||||
@@ -290,21 +291,21 @@ static int next_flush(int stateless_rpc, int count)
|
||||
}
|
||||
|
||||
static void mark_tips(struct fetch_negotiator *negotiator,
|
||||
const struct oid_array *negotiation_tips)
|
||||
const struct oid_array *negotiation_restrict_tips)
|
||||
{
|
||||
struct refs_for_each_ref_options opts = {
|
||||
.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (!negotiation_tips) {
|
||||
if (!negotiation_restrict_tips) {
|
||||
refs_for_each_ref_ext(get_main_ref_store(the_repository),
|
||||
rev_list_insert_ref_oid, negotiator, &opts);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < negotiation_tips->nr; i++)
|
||||
rev_list_insert_ref(negotiator, &negotiation_tips->oid[i]);
|
||||
for (i = 0; i < negotiation_restrict_tips->nr; i++)
|
||||
rev_list_insert_ref(negotiator, &negotiation_restrict_tips->oid[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,6 +332,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);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_common(struct fetch_negotiator *negotiator,
|
||||
struct fetch_pack_args *args,
|
||||
int fd[2], struct object_id *result_oid,
|
||||
@@ -346,6 +362,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");
|
||||
@@ -354,7 +371,7 @@ static int find_common(struct fetch_negotiator *negotiator,
|
||||
PACKET_READ_CHOMP_NEWLINE |
|
||||
PACKET_READ_DIE_ON_ERR_PACKET);
|
||||
|
||||
mark_tips(negotiator, args->negotiation_tips);
|
||||
mark_tips(negotiator, args->negotiation_restrict_tips);
|
||||
for_each_cached_alternate(negotiator, insert_one_alternate_object);
|
||||
|
||||
fetching = 0;
|
||||
@@ -473,6 +490,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));
|
||||
@@ -583,6 +621,7 @@ done:
|
||||
flushes++;
|
||||
}
|
||||
strbuf_release(&req_buf);
|
||||
oidset_clear(&negotiation_include_oids);
|
||||
|
||||
if (!got_ready || !no_done)
|
||||
consume_shallow_list(args, &reader);
|
||||
@@ -1304,11 +1343,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)
|
||||
@@ -1357,7 +1412,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;
|
||||
@@ -1412,7 +1468,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;
|
||||
@@ -1730,7 +1788,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
else
|
||||
state = FETCH_SEND_REQUEST;
|
||||
|
||||
mark_tips(negotiator, args->negotiation_tips);
|
||||
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;
|
||||
@@ -1749,7 +1809,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);
|
||||
@@ -1886,6 +1947,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
|
||||
fsck_options_clear(&fsck_options);
|
||||
oidset_clear(&common);
|
||||
oidset_clear(&negotiation_include_oids);
|
||||
return ref;
|
||||
}
|
||||
|
||||
@@ -2180,16 +2242,18 @@ static void clear_common_flag(struct oidset *s)
|
||||
}
|
||||
}
|
||||
|
||||
void negotiate_using_fetch(const struct oid_array *negotiation_tips,
|
||||
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;
|
||||
@@ -2198,13 +2262,16 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
|
||||
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
|
||||
|
||||
fetch_negotiator_init(the_repository, &negotiator);
|
||||
mark_tips(&negotiator, negotiation_tips);
|
||||
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);
|
||||
|
||||
oid_array_for_each((struct oid_array *) negotiation_tips,
|
||||
oid_array_for_each((struct oid_array *) negotiation_restrict_tips,
|
||||
add_to_object_array,
|
||||
&nt_object_array);
|
||||
|
||||
@@ -2224,7 +2291,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_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;
|
||||
@@ -2276,6 +2344,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
|
||||
|
||||
clear_common_flag(acked_commits);
|
||||
object_array_clear(&nt_object_array);
|
||||
oidset_clear(&negotiation_include_oids);
|
||||
negotiator.release(&negotiator);
|
||||
strbuf_release(&req_buf);
|
||||
}
|
||||
|
||||
10
fetch-pack.h
10
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_tips;
|
||||
const struct oid_array *negotiation_restrict_tips;
|
||||
const struct oid_array *negotiation_include_tips;
|
||||
|
||||
unsigned deepen_relative:1;
|
||||
unsigned quiet:1;
|
||||
@@ -89,11 +90,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||
* In the capability advertisement that has happened prior to invoking this
|
||||
* function, the "wait-for-done" capability must be present.
|
||||
*/
|
||||
void negotiate_using_fetch(const struct oid_array *negotiation_tips,
|
||||
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
|
||||
|
||||
@@ -175,6 +175,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
|
||||
return known_to_be_common;
|
||||
}
|
||||
|
||||
static void have_sent(struct fetch_negotiator *n, struct commit *c)
|
||||
{
|
||||
if (repo_parse_commit(the_repository, c))
|
||||
return;
|
||||
mark_common(n->data, c, 0, 0);
|
||||
}
|
||||
|
||||
static void release(struct fetch_negotiator *n)
|
||||
{
|
||||
clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
|
||||
@@ -188,6 +195,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator)
|
||||
negotiator->add_tip = add_tip;
|
||||
negotiator->next = next;
|
||||
negotiator->ack = ack;
|
||||
negotiator->have_sent = have_sent;
|
||||
negotiator->release = release;
|
||||
negotiator->data = CALLOC_ARRAY(ns, 1);
|
||||
ns->rev_list.compare = compare_commits_by_commit_date;
|
||||
|
||||
@@ -29,6 +29,12 @@ static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void have_sent(struct fetch_negotiator *n UNUSED,
|
||||
struct commit *c UNUSED)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static void release(struct fetch_negotiator *n UNUSED)
|
||||
{
|
||||
/* nothing to release */
|
||||
@@ -40,6 +46,7 @@ void noop_negotiator_init(struct fetch_negotiator *negotiator)
|
||||
negotiator->add_tip = add_tip;
|
||||
negotiator->next = next;
|
||||
negotiator->ack = ack;
|
||||
negotiator->have_sent = have_sent;
|
||||
negotiator->release = release;
|
||||
negotiator->data = NULL;
|
||||
}
|
||||
|
||||
@@ -243,6 +243,13 @@ static int ack(struct fetch_negotiator *n, struct commit *c)
|
||||
return known_to_be_common;
|
||||
}
|
||||
|
||||
static void have_sent(struct fetch_negotiator *n, struct commit *c)
|
||||
{
|
||||
if (repo_parse_commit(the_repository, c))
|
||||
return;
|
||||
mark_common(n->data, c);
|
||||
}
|
||||
|
||||
static void release(struct fetch_negotiator *n)
|
||||
{
|
||||
struct data *data = n->data;
|
||||
@@ -259,6 +266,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator)
|
||||
negotiator->add_tip = add_tip;
|
||||
negotiator->next = next;
|
||||
negotiator->ack = ack;
|
||||
negotiator->have_sent = have_sent;
|
||||
negotiator->release = release;
|
||||
negotiator->data = CALLOC_ARRAY(data, 1);
|
||||
data->rev_list.compare = compare;
|
||||
|
||||
10
remote.c
10
remote.c
@@ -153,6 +153,8 @@ 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);
|
||||
string_list_init_dup(&ret->negotiation_include);
|
||||
|
||||
ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
|
||||
remote_state->remotes_alloc);
|
||||
@@ -180,6 +182,8 @@ 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);
|
||||
string_list_clear(&remote->negotiation_include, 0);
|
||||
}
|
||||
|
||||
static void add_merge(struct branch *branch, const char *name)
|
||||
@@ -563,6 +567,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")) {
|
||||
return parse_transport_option(key, value,
|
||||
&remote->negotiation_restrict);
|
||||
} else if (!strcmp(subkey, "negotiationinclude")) {
|
||||
return parse_transport_option(key, value,
|
||||
&remote->negotiation_include);
|
||||
} else if (!strcmp(subkey, "followremotehead")) {
|
||||
const char *no_warn_branch;
|
||||
if (!strcmp(value, "never"))
|
||||
|
||||
2
remote.h
2
remote.h
@@ -117,6 +117,8 @@ struct remote {
|
||||
char *http_proxy_authmethod;
|
||||
|
||||
struct string_list server_options;
|
||||
struct string_list negotiation_restrict;
|
||||
struct string_list negotiation_include;
|
||||
|
||||
enum follow_remote_head_settings follow_remote_head;
|
||||
const char *no_warn_branch;
|
||||
|
||||
39
send-pack.c
39
send-pack.c
@@ -434,28 +434,48 @@ static void reject_invalid_nonce(const char *nonce, int len)
|
||||
|
||||
static void get_commons_through_negotiation(struct repository *r,
|
||||
const char *url,
|
||||
const struct string_list *negotiation_include,
|
||||
const struct string_list *negotiation_restrict,
|
||||
const struct ref *remote_refs,
|
||||
struct oid_array *commons)
|
||||
{
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
const struct ref *ref;
|
||||
int len = r->hash_algo->hexsz + 1; /* hash + NL */
|
||||
int nr_negotiation_tip = 0;
|
||||
int nr_negotiation = 0;
|
||||
|
||||
child.git_cmd = 1;
|
||||
child.no_stdin = 1;
|
||||
child.out = -1;
|
||||
strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
if (!is_null_oid(&ref->new_oid)) {
|
||||
strvec_pushf(&child.args, "--negotiation-tip=%s",
|
||||
oid_to_hex(&ref->new_oid));
|
||||
nr_negotiation_tip++;
|
||||
|
||||
if (negotiation_restrict && negotiation_restrict->nr) {
|
||||
struct string_list_item *item;
|
||||
for_each_string_list_item(item, negotiation_restrict)
|
||||
strvec_pushf(&child.args, "--negotiation-restrict=%s",
|
||||
item->string);
|
||||
nr_negotiation = negotiation_restrict->nr;
|
||||
} else {
|
||||
for (ref = remote_refs; ref; ref = ref->next) {
|
||||
if (!is_null_oid(&ref->new_oid)) {
|
||||
strvec_pushf(&child.args, "--negotiation-restrict=%s",
|
||||
oid_to_hex(&ref->new_oid));
|
||||
nr_negotiation++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_include && negotiation_include->nr) {
|
||||
struct string_list_item *item;
|
||||
for_each_string_list_item(item, negotiation_include)
|
||||
strvec_pushf(&child.args, "--negotiation-include=%s",
|
||||
item->string);
|
||||
nr_negotiation += negotiation_include->nr;
|
||||
}
|
||||
|
||||
strvec_push(&child.args, url);
|
||||
|
||||
if (!nr_negotiation_tip) {
|
||||
if (!nr_negotiation) {
|
||||
child_process_clear(&child);
|
||||
return;
|
||||
}
|
||||
@@ -529,7 +549,10 @@ int send_pack(struct repository *r,
|
||||
repo_config_get_bool(r, "push.negotiate", &push_negotiate);
|
||||
if (push_negotiate) {
|
||||
trace2_region_enter("send_pack", "push_negotiate", r);
|
||||
get_commons_through_negotiation(r, args->url, remote_refs, &commons);
|
||||
get_commons_through_negotiation(r, args->url,
|
||||
args->negotiation_include,
|
||||
args->negotiation_restrict,
|
||||
remote_refs, &commons);
|
||||
trace2_region_leave("send_pack", "push_negotiate", r);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ struct repository;
|
||||
|
||||
struct send_pack_args {
|
||||
const char *url;
|
||||
const struct string_list *negotiation_include;
|
||||
const struct string_list *negotiation_restrict;
|
||||
unsigned verbose:1,
|
||||
quiet:1,
|
||||
porcelain:1,
|
||||
|
||||
191
t/t5510-fetch.sh
191
t/t5510-fetch.sh
@@ -1465,6 +1465,197 @@ 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
|
||||
'
|
||||
|
||||
test_expect_success '--negotiation-restrict limits "have" lines sent' '
|
||||
setup_negotiation_tip server server 0 &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=alpha_1 --negotiation-restrict=beta_1 \
|
||||
origin alpha_s beta_s &&
|
||||
check_negotiation_tip
|
||||
'
|
||||
|
||||
test_expect_success '--negotiation-restrict understands globs' '
|
||||
setup_negotiation_tip server server 0 &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=*_1 \
|
||||
origin alpha_s beta_s &&
|
||||
check_negotiation_tip
|
||||
'
|
||||
|
||||
test_expect_success '--negotiation-restrict and --negotiation-tip can be mixed' '
|
||||
setup_negotiation_tip server server 0 &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=alpha_1 \
|
||||
--negotiation-tip=beta_1 \
|
||||
origin alpha_s beta_s &&
|
||||
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 '--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 'remote.<name>.negotiationInclude used as default for --negotiation-include' '
|
||||
test_when_finished rm -f trace &&
|
||||
setup_negotiation_tip server server 0 &&
|
||||
|
||||
# test the reset of the list on an empty value
|
||||
git -C client config --add remote.origin.negotiationInclude refs/tags/alpha_1 &&
|
||||
git -C client config --add remote.origin.negotiationInclude "" &&
|
||||
git -C client config --add remote.origin.negotiationInclude refs/tags/beta_1 &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=beta_2 \
|
||||
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>.negotiationInclude works with glob patterns' '
|
||||
test_when_finished rm -f trace &&
|
||||
setup_negotiation_tip server server 0 &&
|
||||
|
||||
git -C client config --add remote.origin.negotiationInclude "refs/tags/beta_*" &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=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 --negotiation-include overrides remote.<name>.negotiationInclude' '
|
||||
test_when_finished rm -f trace &&
|
||||
setup_negotiation_tip server server 0 &&
|
||||
|
||||
git -C client config --add remote.origin.negotiationInclude refs/tags/beta_2 &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace" git -C client fetch \
|
||||
--negotiation-restrict=alpha_1 \
|
||||
--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 &&
|
||||
BETA_2=$(git -C client rev-parse beta_2) &&
|
||||
test_grep ! "fetch> have $BETA_2" trace
|
||||
'
|
||||
|
||||
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 &&
|
||||
(
|
||||
|
||||
@@ -254,6 +254,36 @@ test_expect_success 'push with negotiation does not attempt to fetch submodules'
|
||||
! grep "Fetching submodule" err
|
||||
'
|
||||
|
||||
test_expect_success 'push with negotiation and remote.<name>.negotiationInclude' '
|
||||
test_when_finished rm -rf negotiation_include &&
|
||||
mk_empty negotiation_include &&
|
||||
git push negotiation_include $the_first_commit:refs/remotes/origin/first_commit &&
|
||||
test_commit -C negotiation_include unrelated_commit &&
|
||||
git -C negotiation_include config receive.hideRefs refs/remotes/origin/first_commit &&
|
||||
test_when_finished "rm event" &&
|
||||
GIT_TRACE2_EVENT="$(pwd)/event" \
|
||||
git -c protocol.version=2 -c push.negotiate=1 \
|
||||
-c remote.negotiation_include.negotiationInclude=refs/heads/main \
|
||||
push negotiation_include refs/heads/main:refs/remotes/origin/main &&
|
||||
test_grep \"key\":\"total_rounds\" event &&
|
||||
grep_wrote 2 event # 1 commit, 1 tree
|
||||
'
|
||||
|
||||
test_expect_success 'push with negotiation and remote.<name>.negotiationRestrict' '
|
||||
test_when_finished rm -rf negotiation_restrict &&
|
||||
mk_empty negotiation_restrict &&
|
||||
git push negotiation_restrict $the_first_commit:refs/remotes/origin/first_commit &&
|
||||
test_commit -C negotiation_restrict unrelated_commit &&
|
||||
git -C negotiation_restrict config receive.hideRefs refs/remotes/origin/first_commit &&
|
||||
test_when_finished "rm event" &&
|
||||
GIT_TRACE2_EVENT="$(pwd)/event" \
|
||||
git -c protocol.version=2 -c push.negotiate=1 \
|
||||
-c remote.negotiation_restrict.negotiationRestrict=refs/heads/main \
|
||||
push negotiation_restrict refs/heads/main:refs/remotes/origin/main &&
|
||||
test_grep \"key\":\"total_rounds\" event &&
|
||||
grep_wrote 2 event # 1 commit, 1 tree
|
||||
'
|
||||
|
||||
test_expect_success 'push without wildcard' '
|
||||
mk_empty testrepo &&
|
||||
|
||||
@@ -1349,7 +1379,7 @@ test_expect_success 'fetch follows tags by default' '
|
||||
git for-each-ref >tmp1 &&
|
||||
sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
|
||||
sed -n "p; s|refs/heads/main$|refs/remotes/origin/HEAD|p" |
|
||||
sort -k 4 >../expect
|
||||
sort -k 3 >../expect
|
||||
) &&
|
||||
test_when_finished "rm -rf dst" &&
|
||||
git init dst &&
|
||||
|
||||
@@ -869,14 +869,14 @@ setup_negotiate_only () {
|
||||
test_commit -C client three
|
||||
}
|
||||
|
||||
test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
|
||||
test_expect_success 'usage: --negotiate-only without --negotiation-restrict' '
|
||||
SERVER="server" &&
|
||||
URI="file://$(pwd)/server" &&
|
||||
|
||||
setup_negotiate_only "$SERVER" "$URI" &&
|
||||
|
||||
cat >err.expect <<-\EOF &&
|
||||
fatal: --negotiate-only needs one or more --negotiation-tip=*
|
||||
fatal: --negotiate-only needs one or more --negotiation-restrict=*
|
||||
EOF
|
||||
|
||||
test_must_fail git -c protocol.version=2 -C client fetch \
|
||||
|
||||
@@ -754,8 +754,9 @@ static int fetch_refs(struct transport *transport,
|
||||
set_helper_option(transport, "filter", spec);
|
||||
}
|
||||
|
||||
if (data->transport_options.negotiation_tips)
|
||||
warning("Ignoring --negotiation-tip because the protocol does not support it.");
|
||||
if (data->transport_options.negotiation_restrict_tips)
|
||||
warning(_("ignoring %s because the protocol does not support it."),
|
||||
"--negotiation-restrict");
|
||||
|
||||
if (data->fetch)
|
||||
return fetch_with_fetch(transport, nr_heads, to_fetch);
|
||||
|
||||
20
transport.c
20
transport.c
@@ -464,7 +464,8 @@ static int fetch_refs_via_pack(struct transport *transport,
|
||||
args.refetch = data->options.refetch;
|
||||
args.stateless_rpc = transport->stateless_rpc;
|
||||
args.server_options = transport->server_options;
|
||||
args.negotiation_tips = data->options.negotiation_tips;
|
||||
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) {
|
||||
@@ -492,11 +493,12 @@ static int fetch_refs_via_pack(struct transport *transport,
|
||||
warning(_("server does not support wait-for-done"));
|
||||
ret = -1;
|
||||
} else {
|
||||
negotiate_using_fetch(data->options.negotiation_tips,
|
||||
negotiate_using_fetch(data->options.negotiation_restrict_tips,
|
||||
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;
|
||||
@@ -920,6 +922,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
|
||||
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
|
||||
args.push_options = transport->push_options;
|
||||
args.url = transport->url;
|
||||
args.negotiation_include = &transport->remote->negotiation_include;
|
||||
args.negotiation_restrict = &transport->remote->negotiation_restrict;
|
||||
|
||||
if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
|
||||
args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
|
||||
@@ -980,9 +984,13 @@ static int disconnect_git(struct transport *transport)
|
||||
finish_connect(data->conn);
|
||||
}
|
||||
|
||||
if (data->options.negotiation_tips) {
|
||||
oid_array_clear(data->options.negotiation_tips);
|
||||
free(data->options.negotiation_tips);
|
||||
if (data->options.negotiation_restrict_tips) {
|
||||
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);
|
||||
|
||||
@@ -40,13 +40,14 @@ struct git_transport_options {
|
||||
|
||||
/*
|
||||
* This is only used during fetch. See the documentation of
|
||||
* negotiation_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_tips;
|
||||
struct oid_array *negotiation_restrict_tips;
|
||||
struct oid_array *negotiation_include_tips;
|
||||
|
||||
/*
|
||||
* If allocated, whenever transport_fetch_refs() is called, add known
|
||||
|
||||
Reference in New Issue
Block a user