Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Documentation/config/remote.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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>.haveRefs::
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`.
+
If `--negotiation-tip` is used, then the have set is first restricted by
that option and then increased to include the tips specified by the
`remote.<name>.haveRefs` config values.
+
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>.haveRefs` 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.
Expand Down
3 changes: 2 additions & 1 deletion Documentation/fetch-options.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ this option multiple times, one for each matching ref name.
+
See also the `fetch.negotiationAlgorithm` and `push.negotiate`
configuration variables documented in linkgit:git-config[1], and the
`--negotiate-only` option below.
`--negotiate-only` option below. Also, these restrictions do not apply
to any `remote.<name>.haveRefs` config values.

`--negotiate-only`::
Do not fetch anything from the server, and instead print the
Expand Down
92 changes: 86 additions & 6 deletions fetch-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -332,6 +333,40 @@ static void send_filter(struct fetch_pack_args *args,
}
}

static int add_oid_to_oidset(const struct reference *ref, void *cb_data)
{
struct oidset *set = cb_data;
oidset_insert(set, ref->oid);
return 0;
}

static void resolve_have_refs(const struct string_list *have_refs,
struct oidset *result)
{
struct string_list_item *item;

if (!have_refs || !have_refs->nr)
return;

for_each_string_list_item(item, have_refs) {
if (!has_glob_specials(item->string)) {
struct object_id oid;
if (repo_get_oid(the_repository, item->string, &oid))
continue;
if (!odb_has_object(the_repository->objects, &oid, 0))
continue;
oidset_insert(result, &oid);
} else {
struct refs_for_each_ref_options opts = {
.pattern = item->string,
};
refs_for_each_ref_ext(
get_main_ref_store(the_repository),
add_oid_to_oidset, result, &opts);
}
}
}

static int find_common(struct fetch_negotiator *negotiator,
struct fetch_pack_args *args,
int fd[2], struct object_id *result_oid,
Expand All @@ -347,6 +382,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 have_refs_oids = OIDSET_INIT;

if (args->stateless_rpc && multi_ack == 1)
die(_("the option '%s' requires '%s'"), "--stateless-rpc", "multi_ack_detailed");
Expand Down Expand Up @@ -474,7 +510,24 @@ 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 remote.<name>.haveRefs */
resolve_have_refs(args->have_refs, &have_refs_oids);
if (oidset_size(&have_refs_oids)) {
struct oidset_iter iter;
oidset_iter_init(&have_refs_oids, &iter);

while ((oid = oidset_iter_next(&iter))) {
packet_buf_write(&req_buf, "have %s\n",
oid_to_hex(oid));
print_verbose(args, "have %s", oid_to_hex(oid));
}
}

while ((oid = negotiator->next(negotiator))) {
/* avoid duplicate oids from remote.<name>.haveRefs */
if (oidset_contains(&have_refs_oids, oid))
continue;
packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
print_verbose(args, "have %s", oid_to_hex(oid));
in_vain++;
Expand Down Expand Up @@ -584,6 +637,7 @@ static int find_common(struct fetch_negotiator *negotiator,
flushes++;
}
strbuf_release(&req_buf);
oidset_clear(&have_refs_oids);

if (!got_ready || !no_done)
consume_shallow_list(args, &reader);
Expand Down Expand Up @@ -1305,12 +1359,25 @@ 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 *have_refs_oids)
{
int haves_added = 0;
const struct object_id *oid;

/* Send unconditional haves from remote.<name>.haveRefs */
if (have_refs_oids) {
struct oidset_iter iter;
oidset_iter_init(have_refs_oids, &iter);

while ((oid = oidset_iter_next(&iter)))
packet_buf_write(req_buf, "have %s\n",
oid_to_hex(oid));
}

while ((oid = negotiator->next(negotiator))) {
if (have_refs_oids && oidset_contains(have_refs_oids, oid))
continue;
packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
if (++haves_added >= *haves_to_send)
break;
Expand Down Expand Up @@ -1358,7 +1425,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 *have_refs_oids)
{
int haves_added;
int done_sent = 0;
Expand Down Expand Up @@ -1413,7 +1481,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,
have_refs_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);
Expand Down Expand Up @@ -1657,6 +1726,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 have_refs_oids = OIDSET_INIT;
struct packet_reader reader;
int in_vain = 0, negotiation_started = 0;
int negotiation_round = 0;
Expand Down Expand Up @@ -1708,6 +1778,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
reader.me = "fetch-pack";
}

resolve_have_refs(args->have_refs, &have_refs_oids);

while (state != FETCH_DONE) {
switch (state) {
case FETCH_CHECK_LOCAL:
Expand Down Expand Up @@ -1747,7 +1819,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,
&have_refs_oids)) {
trace2_region_leave_printf("negotiation_v2", "round",
the_repository, "%d",
negotiation_round);
Expand Down Expand Up @@ -1883,6 +1956,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
negotiator->release(negotiator);

oidset_clear(&common);
oidset_clear(&have_refs_oids);
return ref;
}

Expand Down Expand Up @@ -2181,12 +2255,14 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
const struct string_list *server_options,
int stateless_rpc,
int fd[],
struct oidset *acked_commits)
struct oidset *acked_commits,
const struct string_list *have_refs)
{
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 have_refs_oids = OIDSET_INIT;
int haves_to_send = INITIAL_FLUSH;
int in_vain = 0;
int seen_ack = 0;
Expand All @@ -2205,6 +2281,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
add_to_object_array,
&nt_object_array);

resolve_have_refs(have_refs, &have_refs_oids);

trace2_region_enter("fetch-pack", "negotiate_using_fetch", the_repository);
while (!last_iteration) {
int haves_added;
Expand All @@ -2221,7 +2299,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,
&have_refs_oids);
in_vain += haves_added;
if (!haves_added || (seen_ack && in_vain >= MAX_IN_VAIN))
last_iteration = 1;
Expand Down Expand Up @@ -2273,6 +2352,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,

clear_common_flag(acked_commits);
object_array_clear(&nt_object_array);
oidset_clear(&have_refs_oids);
negotiator.release(&negotiator);
strbuf_release(&req_buf);
}
Expand Down
10 changes: 9 additions & 1 deletion fetch-pack.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ struct fetch_pack_args {
*/
const struct oid_array *negotiation_tips;

/*
* If non-empty, ref patterns whose tips should always be sent
* as "have" lines during negotiation, regardless of what the
* negotiation algorithm selects.
*/
const struct string_list *have_refs;

unsigned deepen_relative:1;
unsigned quiet:1;
unsigned keep_pack:1;
Expand Down Expand Up @@ -93,7 +100,8 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
const struct string_list *server_options,
int stateless_rpc,
int fd[],
struct oidset *acked_commits);
struct oidset *acked_commits,
const struct string_list *have_refs);

/*
* Print an appropriate error message for each sought ref that wasn't
Expand Down
6 changes: 6 additions & 0 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -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->have_refs);

ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
remote_state->remotes_alloc);
Expand Down Expand Up @@ -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->have_refs, 0);
}

static void add_merge(struct branch *branch, const char *name)
Expand Down Expand Up @@ -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, "haverefs")) {
if (!value)
return config_error_nonbool(key);
string_list_append(&remote->have_refs, value);
} else if (!strcmp(subkey, "followremotehead")) {
const char *no_warn_branch;
if (!strcmp(value, "never"))
Expand Down
1 change: 1 addition & 0 deletions remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct remote {
char *http_proxy_authmethod;

struct string_list server_options;
struct string_list have_refs;

enum follow_remote_head_settings follow_remote_head;
const char *no_warn_branch;
Expand Down
11 changes: 9 additions & 2 deletions send-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ static void reject_invalid_nonce(const char *nonce, int len)

static void get_commons_through_negotiation(struct repository *r,
const char *url,
const char *remote_name,
const struct ref *remote_refs,
struct oid_array *commons)
{
Expand All @@ -452,7 +453,12 @@ static void get_commons_through_negotiation(struct repository *r,
nr_negotiation_tip++;
}
}
strvec_push(&child.args, url);
/*
* Use the remote name so the subprocess can find
* remote.<name>.haveRefs config. Fall back to the URL if no
* remote name is available.
*/
strvec_push(&child.args, remote_name ? remote_name : url);

if (!nr_negotiation_tip) {
child_process_clear(&child);
Expand Down Expand Up @@ -528,7 +534,8 @@ 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->remote_name,
remote_refs, &commons);
trace2_region_leave("send_pack", "push_negotiate", r);
}

Expand Down
1 change: 1 addition & 0 deletions send-pack.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct repository;

struct send_pack_args {
const char *url;
const char *remote_name;
unsigned verbose:1,
quiet:1,
porcelain:1,
Expand Down
Loading
Loading