зеркало из https://github.com/microsoft/git.git
Merge branch 'jt/push-negotiation'
"git push" learns to discover common ancestor with the receiving end over protocol v2. * jt/push-negotiation: send-pack: support push negotiation fetch: teach independent negotiation (no packfile) fetch-pack: refactor command and capability write fetch-pack: refactor add_haves() fetch-pack: refactor process_acks()
This commit is contained in:
Коммит
644f4a2046
|
@ -120,3 +120,10 @@ push.useForceIfIncludes::
|
||||||
`--force-if-includes` as an option to linkgit:git-push[1]
|
`--force-if-includes` as an option to linkgit:git-push[1]
|
||||||
in the command line. Adding `--no-force-if-includes` at the
|
in the command line. Adding `--no-force-if-includes` at the
|
||||||
time of push overrides this configuration setting.
|
time of push overrides this configuration setting.
|
||||||
|
|
||||||
|
push.negotiate::
|
||||||
|
If set to "true", attempt to reduce the size of the packfile
|
||||||
|
sent by rounds of negotiation in which the client and the
|
||||||
|
server attempt to find commits in common. If "false", Git will
|
||||||
|
rely solely on the server's ref advertisement to find commits
|
||||||
|
in common.
|
||||||
|
|
|
@ -346,6 +346,14 @@ explained below.
|
||||||
client should download from all given URIs. Currently, the
|
client should download from all given URIs. Currently, the
|
||||||
protocols supported are "http" and "https".
|
protocols supported are "http" and "https".
|
||||||
|
|
||||||
|
If the 'wait-for-done' feature is advertised, the following argument
|
||||||
|
can be included in the client's request.
|
||||||
|
|
||||||
|
wait-for-done
|
||||||
|
Indicates to the server that it should never send "ready", but
|
||||||
|
should wait for the client to say "done" before sending the
|
||||||
|
packfile.
|
||||||
|
|
||||||
The response of `fetch` is broken into a number of sections separated by
|
The response of `fetch` is broken into a number of sections separated by
|
||||||
delimiter packets (0001), with each section beginning with its section
|
delimiter packets (0001), with each section beginning with its section
|
||||||
header. Most sections are sent only when the packfile is sent.
|
header. Most sections are sent only when the packfile is sent.
|
||||||
|
|
|
@ -83,6 +83,7 @@ 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_tip = STRING_LIST_INIT_NODUP;
|
||||||
static int fetch_write_commit_graph = -1;
|
static int fetch_write_commit_graph = -1;
|
||||||
static int stdin_refspecs = 0;
|
static int stdin_refspecs = 0;
|
||||||
|
static int negotiate_only;
|
||||||
|
|
||||||
static int git_fetch_config(const char *k, const char *v, void *cb)
|
static int git_fetch_config(const char *k, const char *v, void *cb)
|
||||||
{
|
{
|
||||||
|
@ -205,6 +206,8 @@ static struct option builtin_fetch_options[] = {
|
||||||
TRANSPORT_FAMILY_IPV6),
|
TRANSPORT_FAMILY_IPV6),
|
||||||
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
||||||
N_("report that we have only objects reachable from this object")),
|
N_("report that we have only objects reachable from this object")),
|
||||||
|
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),
|
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
|
||||||
OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
|
OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
|
||||||
N_("run 'maintenance --auto' after fetching")),
|
N_("run 'maintenance --auto' after fetching")),
|
||||||
|
@ -2043,7 +2046,29 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remote) {
|
if (negotiate_only) {
|
||||||
|
struct oidset acked_commits = OIDSET_INIT;
|
||||||
|
struct oidset_iter iter;
|
||||||
|
const struct object_id *oid;
|
||||||
|
|
||||||
|
if (!remote)
|
||||||
|
die(_("must supply remote when using --negotiate-only"));
|
||||||
|
gtransport = prepare_transport(remote, 1);
|
||||||
|
if (gtransport->smart_options) {
|
||||||
|
gtransport->smart_options->acked_commits = &acked_commits;
|
||||||
|
} else {
|
||||||
|
warning(_("Protocol does not support --negotiate-only, exiting."));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (server_options.nr)
|
||||||
|
gtransport->server_options = &server_options;
|
||||||
|
result = transport_fetch_refs(gtransport, NULL);
|
||||||
|
|
||||||
|
oidset_iter_init(&acked_commits, &iter);
|
||||||
|
while ((oid = oidset_iter_next(&iter)))
|
||||||
|
printf("%s\n", oid_to_hex(oid));
|
||||||
|
oidset_clear(&acked_commits);
|
||||||
|
} else if (remote) {
|
||||||
if (filter_options.choice || has_promisor_remote())
|
if (filter_options.choice || has_promisor_remote())
|
||||||
fetch_one_setup_partial(remote);
|
fetch_one_setup_partial(remote);
|
||||||
result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
|
result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
|
||||||
|
|
266
fetch-pack.c
266
fetch-pack.c
|
@ -23,6 +23,8 @@
|
||||||
#include "fetch-negotiator.h"
|
#include "fetch-negotiator.h"
|
||||||
#include "fsck.h"
|
#include "fsck.h"
|
||||||
#include "shallow.h"
|
#include "shallow.h"
|
||||||
|
#include "commit-reach.h"
|
||||||
|
#include "commit-graph.h"
|
||||||
|
|
||||||
static int transfer_unpack_limit = -1;
|
static int transfer_unpack_limit = -1;
|
||||||
static int fetch_unpack_limit = -1;
|
static int fetch_unpack_limit = -1;
|
||||||
|
@ -45,6 +47,8 @@ static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
|
||||||
/* Remember to update object flag allocation in object.h */
|
/* Remember to update object flag allocation in object.h */
|
||||||
#define COMPLETE (1U << 0)
|
#define COMPLETE (1U << 0)
|
||||||
#define ALTERNATE (1U << 1)
|
#define ALTERNATE (1U << 1)
|
||||||
|
#define COMMON (1U << 6)
|
||||||
|
#define REACH_SCRATCH (1U << 7)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After sending this many "have"s if we do not get any new ACK , we
|
* After sending this many "have"s if we do not get any new ACK , we
|
||||||
|
@ -1195,11 +1199,9 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_haves(struct fetch_negotiator *negotiator,
|
static int add_haves(struct fetch_negotiator *negotiator,
|
||||||
int seen_ack,
|
|
||||||
struct strbuf *req_buf,
|
struct strbuf *req_buf,
|
||||||
int *haves_to_send, int *in_vain)
|
int *haves_to_send)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
int haves_added = 0;
|
int haves_added = 0;
|
||||||
const struct object_id *oid;
|
const struct object_id *oid;
|
||||||
|
|
||||||
|
@ -1209,17 +1211,42 @@ static int add_haves(struct fetch_negotiator *negotiator,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
*in_vain += haves_added;
|
|
||||||
if (!haves_added || (seen_ack && *in_vain >= MAX_IN_VAIN)) {
|
|
||||||
/* Send Done */
|
|
||||||
packet_buf_write(req_buf, "done\n");
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increase haves to send on next round */
|
/* Increase haves to send on next round */
|
||||||
*haves_to_send = next_flush(1, *haves_to_send);
|
*haves_to_send = next_flush(1, *haves_to_send);
|
||||||
|
|
||||||
return ret;
|
return haves_added;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_fetch_command_and_capabilities(struct strbuf *req_buf,
|
||||||
|
const struct string_list *server_options)
|
||||||
|
{
|
||||||
|
const char *hash_name;
|
||||||
|
|
||||||
|
if (server_supports_v2("fetch", 1))
|
||||||
|
packet_buf_write(req_buf, "command=fetch");
|
||||||
|
if (server_supports_v2("agent", 0))
|
||||||
|
packet_buf_write(req_buf, "agent=%s", git_user_agent_sanitized());
|
||||||
|
if (advertise_sid && server_supports_v2("session-id", 0))
|
||||||
|
packet_buf_write(req_buf, "session-id=%s", trace2_session_id());
|
||||||
|
if (server_options && server_options->nr &&
|
||||||
|
server_supports_v2("server-option", 1)) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < server_options->nr; i++)
|
||||||
|
packet_buf_write(req_buf, "server-option=%s",
|
||||||
|
server_options->items[i].string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_feature_v2("object-format", &hash_name)) {
|
||||||
|
int hash_algo = hash_algo_by_name(hash_name);
|
||||||
|
if (hash_algo_by_ptr(the_hash_algo) != hash_algo)
|
||||||
|
die(_("mismatched algorithms: client %s; server %s"),
|
||||||
|
the_hash_algo->name, hash_name);
|
||||||
|
packet_buf_write(req_buf, "object-format=%s", the_hash_algo->name);
|
||||||
|
} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
|
||||||
|
die(_("the server does not support algorithm '%s'"),
|
||||||
|
the_hash_algo->name);
|
||||||
|
}
|
||||||
|
packet_buf_delim(req_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
|
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
|
||||||
|
@ -1228,36 +1255,12 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
|
||||||
int *haves_to_send, int *in_vain,
|
int *haves_to_send, int *in_vain,
|
||||||
int sideband_all, int seen_ack)
|
int sideband_all, int seen_ack)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int haves_added;
|
||||||
const char *hash_name;
|
int done_sent = 0;
|
||||||
struct strbuf req_buf = STRBUF_INIT;
|
struct strbuf req_buf = STRBUF_INIT;
|
||||||
|
|
||||||
if (server_supports_v2("fetch", 1))
|
write_fetch_command_and_capabilities(&req_buf, args->server_options);
|
||||||
packet_buf_write(&req_buf, "command=fetch");
|
|
||||||
if (server_supports_v2("agent", 0))
|
|
||||||
packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized());
|
|
||||||
if (advertise_sid && server_supports_v2("session-id", 0))
|
|
||||||
packet_buf_write(&req_buf, "session-id=%s", trace2_session_id());
|
|
||||||
if (args->server_options && args->server_options->nr &&
|
|
||||||
server_supports_v2("server-option", 1)) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < args->server_options->nr; i++)
|
|
||||||
packet_buf_write(&req_buf, "server-option=%s",
|
|
||||||
args->server_options->items[i].string);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server_feature_v2("object-format", &hash_name)) {
|
|
||||||
int hash_algo = hash_algo_by_name(hash_name);
|
|
||||||
if (hash_algo_by_ptr(the_hash_algo) != hash_algo)
|
|
||||||
die(_("mismatched algorithms: client %s; server %s"),
|
|
||||||
the_hash_algo->name, hash_name);
|
|
||||||
packet_buf_write(&req_buf, "object-format=%s", the_hash_algo->name);
|
|
||||||
} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
|
|
||||||
die(_("the server does not support algorithm '%s'"),
|
|
||||||
the_hash_algo->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
packet_buf_delim(&req_buf);
|
|
||||||
if (args->use_thin_pack)
|
if (args->use_thin_pack)
|
||||||
packet_buf_write(&req_buf, "thin-pack");
|
packet_buf_write(&req_buf, "thin-pack");
|
||||||
if (args->no_progress)
|
if (args->no_progress)
|
||||||
|
@ -1312,9 +1315,13 @@ 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 all of the common commits we've found in previous rounds */
|
||||||
add_common(&req_buf, common);
|
add_common(&req_buf, common);
|
||||||
|
|
||||||
/* Add initial haves */
|
haves_added = add_haves(negotiator, &req_buf, haves_to_send);
|
||||||
ret = add_haves(negotiator, seen_ack, &req_buf,
|
*in_vain += haves_added;
|
||||||
haves_to_send, in_vain);
|
if (!haves_added || (seen_ack && *in_vain >= MAX_IN_VAIN)) {
|
||||||
|
/* Send Done */
|
||||||
|
packet_buf_write(&req_buf, "done\n");
|
||||||
|
done_sent = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send request */
|
/* Send request */
|
||||||
packet_buf_flush(&req_buf);
|
packet_buf_flush(&req_buf);
|
||||||
|
@ -1322,7 +1329,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
|
||||||
die_errno(_("unable to write request to remote"));
|
die_errno(_("unable to write request to remote"));
|
||||||
|
|
||||||
strbuf_release(&req_buf);
|
strbuf_release(&req_buf);
|
||||||
return ret;
|
return done_sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1351,35 +1358,11 @@ static int process_section_header(struct packet_reader *reader,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum common_found {
|
static int process_ack(struct fetch_negotiator *negotiator,
|
||||||
/*
|
|
||||||
* No commit was found to be possessed by both the client and the
|
|
||||||
* server, and "ready" was not received.
|
|
||||||
*/
|
|
||||||
NO_COMMON_FOUND,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At least one commit was found to be possessed by both the client and
|
|
||||||
* the server, and "ready" was not received.
|
|
||||||
*/
|
|
||||||
COMMON_FOUND,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "ready" was received, indicating that the server is ready to send
|
|
||||||
* the packfile without any further negotiation.
|
|
||||||
*/
|
|
||||||
READY
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum common_found process_acks(struct fetch_negotiator *negotiator,
|
|
||||||
struct packet_reader *reader,
|
struct packet_reader *reader,
|
||||||
struct oidset *common)
|
struct object_id *common_oid,
|
||||||
|
int *received_ready)
|
||||||
{
|
{
|
||||||
/* received */
|
|
||||||
int received_ready = 0;
|
|
||||||
int received_ack = 0;
|
|
||||||
|
|
||||||
process_section_header(reader, "acknowledgments", 0);
|
|
||||||
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
|
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
|
||||||
const char *arg;
|
const char *arg;
|
||||||
|
|
||||||
|
@ -1387,20 +1370,17 @@ static enum common_found process_acks(struct fetch_negotiator *negotiator,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (skip_prefix(reader->line, "ACK ", &arg)) {
|
if (skip_prefix(reader->line, "ACK ", &arg)) {
|
||||||
struct object_id oid;
|
if (!get_oid_hex(arg, common_oid)) {
|
||||||
received_ack = 1;
|
|
||||||
if (!get_oid_hex(arg, &oid)) {
|
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
oidset_insert(common, &oid);
|
commit = lookup_commit(the_repository, common_oid);
|
||||||
commit = lookup_commit(the_repository, &oid);
|
|
||||||
if (negotiator)
|
if (negotiator)
|
||||||
negotiator->ack(negotiator, commit);
|
negotiator->ack(negotiator, commit);
|
||||||
}
|
}
|
||||||
continue;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(reader->line, "ready")) {
|
if (!strcmp(reader->line, "ready")) {
|
||||||
received_ready = 1;
|
*received_ready = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,13 +1398,12 @@ static enum common_found process_acks(struct fetch_negotiator *negotiator,
|
||||||
* sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH
|
* sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
if (received_ready && reader->status != PACKET_READ_DELIM)
|
if (*received_ready && reader->status != PACKET_READ_DELIM)
|
||||||
die(_("expected packfile to be sent after 'ready'"));
|
die(_("expected packfile to be sent after 'ready'"));
|
||||||
if (!received_ready && reader->status != PACKET_READ_FLUSH)
|
if (!*received_ready && reader->status != PACKET_READ_FLUSH)
|
||||||
die(_("expected no other sections to be sent after no 'ready'"));
|
die(_("expected no other sections to be sent after no 'ready'"));
|
||||||
|
|
||||||
return received_ready ? READY :
|
return 0;
|
||||||
(received_ack ? COMMON_FOUND : NO_COMMON_FOUND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void receive_shallow_info(struct fetch_pack_args *args,
|
static void receive_shallow_info(struct fetch_pack_args *args,
|
||||||
|
@ -1548,10 +1527,10 @@ enum fetch_state {
|
||||||
FETCH_DONE,
|
FETCH_DONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void do_check_stateless_delimiter(const struct fetch_pack_args *args,
|
static void do_check_stateless_delimiter(int stateless_rpc,
|
||||||
struct packet_reader *reader)
|
struct packet_reader *reader)
|
||||||
{
|
{
|
||||||
check_stateless_delimiter(args->stateless_rpc, reader,
|
check_stateless_delimiter(stateless_rpc, reader,
|
||||||
_("git fetch-pack: expected response end packet"));
|
_("git fetch-pack: expected response end packet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1573,6 +1552,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||||
struct fetch_negotiator negotiator_alloc;
|
struct fetch_negotiator negotiator_alloc;
|
||||||
struct fetch_negotiator *negotiator;
|
struct fetch_negotiator *negotiator;
|
||||||
int seen_ack = 0;
|
int seen_ack = 0;
|
||||||
|
struct object_id common_oid;
|
||||||
|
int received_ready = 0;
|
||||||
struct string_list packfile_uris = STRING_LIST_INIT_DUP;
|
struct string_list packfile_uris = STRING_LIST_INIT_DUP;
|
||||||
int i;
|
int i;
|
||||||
struct strvec index_pack_args = STRVEC_INIT;
|
struct strvec index_pack_args = STRVEC_INIT;
|
||||||
|
@ -1631,22 +1612,22 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||||
break;
|
break;
|
||||||
case FETCH_PROCESS_ACKS:
|
case FETCH_PROCESS_ACKS:
|
||||||
/* Process ACKs/NAKs */
|
/* Process ACKs/NAKs */
|
||||||
switch (process_acks(negotiator, &reader, &common)) {
|
process_section_header(&reader, "acknowledgments", 0);
|
||||||
case READY:
|
while (process_ack(negotiator, &reader, &common_oid,
|
||||||
|
&received_ready)) {
|
||||||
|
in_vain = 0;
|
||||||
|
seen_ack = 1;
|
||||||
|
oidset_insert(&common, &common_oid);
|
||||||
|
}
|
||||||
|
if (received_ready) {
|
||||||
/*
|
/*
|
||||||
* Don't check for response delimiter; get_pack() will
|
* Don't check for response delimiter; get_pack() will
|
||||||
* read the rest of this response.
|
* read the rest of this response.
|
||||||
*/
|
*/
|
||||||
state = FETCH_GET_PACK;
|
state = FETCH_GET_PACK;
|
||||||
break;
|
} else {
|
||||||
case COMMON_FOUND:
|
do_check_stateless_delimiter(args->stateless_rpc, &reader);
|
||||||
in_vain = 0;
|
|
||||||
seen_ack = 1;
|
|
||||||
/* fallthrough */
|
|
||||||
case NO_COMMON_FOUND:
|
|
||||||
do_check_stateless_delimiter(args, &reader);
|
|
||||||
state = FETCH_SEND_REQUEST;
|
state = FETCH_SEND_REQUEST;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FETCH_GET_PACK:
|
case FETCH_GET_PACK:
|
||||||
|
@ -1668,7 +1649,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||||
packfile_uris.nr ? &index_pack_args : NULL,
|
packfile_uris.nr ? &index_pack_args : NULL,
|
||||||
sought, nr_sought, &fsck_options.gitmodules_found))
|
sought, nr_sought, &fsck_options.gitmodules_found))
|
||||||
die(_("git fetch-pack: fetch failed."));
|
die(_("git fetch-pack: fetch failed."));
|
||||||
do_check_stateless_delimiter(args, &reader);
|
do_check_stateless_delimiter(args->stateless_rpc, &reader);
|
||||||
|
|
||||||
state = FETCH_DONE;
|
state = FETCH_DONE;
|
||||||
break;
|
break;
|
||||||
|
@ -1985,6 +1966,105 @@ cleanup:
|
||||||
return ref_cpy;
|
return ref_cpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int add_to_object_array(const struct object_id *oid, void *data)
|
||||||
|
{
|
||||||
|
struct object_array *a = data;
|
||||||
|
|
||||||
|
add_object_array(lookup_object(the_repository, oid), "", a);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_common_flag(struct oidset *s)
|
||||||
|
{
|
||||||
|
struct oidset_iter iter;
|
||||||
|
const struct object_id *oid;
|
||||||
|
oidset_iter_init(s, &iter);
|
||||||
|
|
||||||
|
while ((oid = oidset_iter_next(&iter))) {
|
||||||
|
struct object *obj = lookup_object(the_repository, oid);
|
||||||
|
obj->flags &= ~COMMON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 fetch_negotiator negotiator;
|
||||||
|
struct packet_reader reader;
|
||||||
|
struct object_array nt_object_array = OBJECT_ARRAY_INIT;
|
||||||
|
struct strbuf req_buf = STRBUF_INIT;
|
||||||
|
int haves_to_send = INITIAL_FLUSH;
|
||||||
|
int in_vain = 0;
|
||||||
|
int seen_ack = 0;
|
||||||
|
int last_iteration = 0;
|
||||||
|
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
|
||||||
|
|
||||||
|
fetch_negotiator_init(the_repository, &negotiator);
|
||||||
|
mark_tips(&negotiator, negotiation_tips);
|
||||||
|
|
||||||
|
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,
|
||||||
|
add_to_object_array,
|
||||||
|
&nt_object_array);
|
||||||
|
|
||||||
|
while (!last_iteration) {
|
||||||
|
int haves_added;
|
||||||
|
struct object_id common_oid;
|
||||||
|
int received_ready = 0;
|
||||||
|
|
||||||
|
strbuf_reset(&req_buf);
|
||||||
|
write_fetch_command_and_capabilities(&req_buf, server_options);
|
||||||
|
|
||||||
|
packet_buf_write(&req_buf, "wait-for-done");
|
||||||
|
|
||||||
|
haves_added = add_haves(&negotiator, &req_buf, &haves_to_send);
|
||||||
|
in_vain += haves_added;
|
||||||
|
if (!haves_added || (seen_ack && in_vain >= MAX_IN_VAIN))
|
||||||
|
last_iteration = 1;
|
||||||
|
|
||||||
|
/* Send request */
|
||||||
|
packet_buf_flush(&req_buf);
|
||||||
|
if (write_in_full(fd[1], req_buf.buf, req_buf.len) < 0)
|
||||||
|
die_errno(_("unable to write request to remote"));
|
||||||
|
|
||||||
|
/* Process ACKs/NAKs */
|
||||||
|
process_section_header(&reader, "acknowledgments", 0);
|
||||||
|
while (process_ack(&negotiator, &reader, &common_oid,
|
||||||
|
&received_ready)) {
|
||||||
|
struct commit *commit = lookup_commit(the_repository,
|
||||||
|
&common_oid);
|
||||||
|
if (commit) {
|
||||||
|
timestamp_t generation;
|
||||||
|
|
||||||
|
parse_commit_or_die(commit);
|
||||||
|
commit->object.flags |= COMMON;
|
||||||
|
generation = commit_graph_generation(commit);
|
||||||
|
if (generation < min_generation)
|
||||||
|
min_generation = generation;
|
||||||
|
}
|
||||||
|
in_vain = 0;
|
||||||
|
seen_ack = 1;
|
||||||
|
oidset_insert(acked_commits, &common_oid);
|
||||||
|
}
|
||||||
|
if (received_ready)
|
||||||
|
die(_("unexpected 'ready' from remote"));
|
||||||
|
else
|
||||||
|
do_check_stateless_delimiter(stateless_rpc, &reader);
|
||||||
|
if (can_all_from_reach_with_flag(&nt_object_array, COMMON,
|
||||||
|
REACH_SCRATCH, 0,
|
||||||
|
min_generation))
|
||||||
|
last_iteration = 1;
|
||||||
|
}
|
||||||
|
clear_common_flag(acked_commits);
|
||||||
|
strbuf_release(&req_buf);
|
||||||
|
}
|
||||||
|
|
||||||
int report_unmatched_refs(struct ref **sought, int nr_sought)
|
int report_unmatched_refs(struct ref **sought, int nr_sought)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
|
|
14
fetch-pack.h
14
fetch-pack.h
|
@ -5,6 +5,7 @@
|
||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "list-objects-filter-options.h"
|
#include "list-objects-filter-options.h"
|
||||||
|
#include "oidset.h"
|
||||||
|
|
||||||
struct oid_array;
|
struct oid_array;
|
||||||
|
|
||||||
|
@ -81,6 +82,19 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||||
struct string_list *pack_lockfiles,
|
struct string_list *pack_lockfiles,
|
||||||
enum protocol_version version);
|
enum protocol_version version);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute the --negotiate-only mode of "git fetch", adding all known common
|
||||||
|
* commits to acked_commits.
|
||||||
|
*
|
||||||
|
* 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,
|
||||||
|
const struct string_list *server_options,
|
||||||
|
int stateless_rpc,
|
||||||
|
int fd[],
|
||||||
|
struct oidset *acked_commits);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print an appropriate error message for each sought ref that wasn't
|
* Print an appropriate error message for each sought ref that wasn't
|
||||||
* matched. Return 0 if all sought refs were matched, otherwise 1.
|
* matched. Return 0 if all sought refs were matched, otherwise 1.
|
||||||
|
|
2
object.h
2
object.h
|
@ -60,7 +60,7 @@ struct object_array {
|
||||||
/*
|
/*
|
||||||
* object flag allocation:
|
* object flag allocation:
|
||||||
* revision.h: 0---------10 15 23------26
|
* revision.h: 0---------10 15 23------26
|
||||||
* fetch-pack.c: 01
|
* fetch-pack.c: 01 67
|
||||||
* negotiator/default.c: 2--5
|
* negotiator/default.c: 2--5
|
||||||
* walker.c: 0-2
|
* walker.c: 0-2
|
||||||
* upload-pack.c: 4 11-----14 16-----19
|
* upload-pack.c: 4 11-----14 16-----19
|
||||||
|
|
61
send-pack.c
61
send-pack.c
|
@ -56,7 +56,9 @@ static void feed_object(const struct object_id *oid, FILE *fh, int negative)
|
||||||
/*
|
/*
|
||||||
* Make a pack stream and spit it out into file descriptor fd
|
* Make a pack stream and spit it out into file descriptor fd
|
||||||
*/
|
*/
|
||||||
static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struct send_pack_args *args)
|
static int pack_objects(int fd, struct ref *refs, struct oid_array *advertised,
|
||||||
|
struct oid_array *negotiated,
|
||||||
|
struct send_pack_args *args)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The child becomes pack-objects --revs; we feed
|
* The child becomes pack-objects --revs; we feed
|
||||||
|
@ -94,8 +96,10 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
|
||||||
* parameters by writing to the pipe.
|
* parameters by writing to the pipe.
|
||||||
*/
|
*/
|
||||||
po_in = xfdopen(po.in, "w");
|
po_in = xfdopen(po.in, "w");
|
||||||
for (i = 0; i < extra->nr; i++)
|
for (i = 0; i < advertised->nr; i++)
|
||||||
feed_object(&extra->oid[i], po_in, 1);
|
feed_object(&advertised->oid[i], po_in, 1);
|
||||||
|
for (i = 0; i < negotiated->nr; i++)
|
||||||
|
feed_object(&negotiated->oid[i], po_in, 1);
|
||||||
|
|
||||||
while (refs) {
|
while (refs) {
|
||||||
if (!is_null_oid(&refs->old_oid))
|
if (!is_null_oid(&refs->old_oid))
|
||||||
|
@ -409,11 +413,55 @@ static void reject_invalid_nonce(const char *nonce, int len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_commons_through_negotiation(const char *url,
|
||||||
|
const struct ref *remote_refs,
|
||||||
|
struct oid_array *commons)
|
||||||
|
{
|
||||||
|
struct child_process child = CHILD_PROCESS_INIT;
|
||||||
|
const struct ref *ref;
|
||||||
|
int len = the_hash_algo->hexsz + 1; /* hash + NL */
|
||||||
|
|
||||||
|
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)
|
||||||
|
strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
|
||||||
|
strvec_push(&child.args, url);
|
||||||
|
|
||||||
|
if (start_command(&child))
|
||||||
|
die(_("send-pack: unable to fork off fetch subprocess"));
|
||||||
|
|
||||||
|
do {
|
||||||
|
char hex_hash[GIT_MAX_HEXSZ + 1];
|
||||||
|
int read_len = read_in_full(child.out, hex_hash, len);
|
||||||
|
struct object_id oid;
|
||||||
|
const char *end;
|
||||||
|
|
||||||
|
if (!read_len)
|
||||||
|
break;
|
||||||
|
if (read_len != len)
|
||||||
|
die("invalid length read %d", read_len);
|
||||||
|
if (parse_oid_hex(hex_hash, &oid, &end) || *end != '\n')
|
||||||
|
die("invalid hash");
|
||||||
|
oid_array_append(commons, &oid);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
if (finish_command(&child)) {
|
||||||
|
/*
|
||||||
|
* The information that push negotiation provides is useful but
|
||||||
|
* not mandatory.
|
||||||
|
*/
|
||||||
|
warning(_("push negotiation failed; proceeding anyway with push"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int send_pack(struct send_pack_args *args,
|
int send_pack(struct send_pack_args *args,
|
||||||
int fd[], struct child_process *conn,
|
int fd[], struct child_process *conn,
|
||||||
struct ref *remote_refs,
|
struct ref *remote_refs,
|
||||||
struct oid_array *extra_have)
|
struct oid_array *extra_have)
|
||||||
{
|
{
|
||||||
|
struct oid_array commons = OID_ARRAY_INIT;
|
||||||
int in = fd[0];
|
int in = fd[0];
|
||||||
int out = fd[1];
|
int out = fd[1];
|
||||||
struct strbuf req_buf = STRBUF_INIT;
|
struct strbuf req_buf = STRBUF_INIT;
|
||||||
|
@ -426,6 +474,7 @@ int send_pack(struct send_pack_args *args,
|
||||||
int quiet_supported = 0;
|
int quiet_supported = 0;
|
||||||
int agent_supported = 0;
|
int agent_supported = 0;
|
||||||
int advertise_sid = 0;
|
int advertise_sid = 0;
|
||||||
|
int push_negotiate = 0;
|
||||||
int use_atomic = 0;
|
int use_atomic = 0;
|
||||||
int atomic_supported = 0;
|
int atomic_supported = 0;
|
||||||
int use_push_options = 0;
|
int use_push_options = 0;
|
||||||
|
@ -437,6 +486,10 @@ int send_pack(struct send_pack_args *args,
|
||||||
const char *push_cert_nonce = NULL;
|
const char *push_cert_nonce = NULL;
|
||||||
struct packet_reader reader;
|
struct packet_reader reader;
|
||||||
|
|
||||||
|
git_config_get_bool("push.negotiate", &push_negotiate);
|
||||||
|
if (push_negotiate)
|
||||||
|
get_commons_through_negotiation(args->url, remote_refs, &commons);
|
||||||
|
|
||||||
git_config_get_bool("transfer.advertisesid", &advertise_sid);
|
git_config_get_bool("transfer.advertisesid", &advertise_sid);
|
||||||
|
|
||||||
/* Does the other end support the reporting? */
|
/* Does the other end support the reporting? */
|
||||||
|
@ -625,7 +678,7 @@ int send_pack(struct send_pack_args *args,
|
||||||
PACKET_READ_DIE_ON_ERR_PACKET);
|
PACKET_READ_DIE_ON_ERR_PACKET);
|
||||||
|
|
||||||
if (need_pack_data && cmds_sent) {
|
if (need_pack_data && cmds_sent) {
|
||||||
if (pack_objects(out, remote_refs, extra_have, args) < 0) {
|
if (pack_objects(out, remote_refs, extra_have, &commons, args) < 0) {
|
||||||
if (args->stateless_rpc)
|
if (args->stateless_rpc)
|
||||||
close(out);
|
close(out);
|
||||||
if (git_connection_is_socket(conn))
|
if (git_connection_is_socket(conn))
|
||||||
|
|
|
@ -191,6 +191,41 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
grep_wrote () {
|
||||||
|
object_count=$1
|
||||||
|
file_name=$2
|
||||||
|
grep 'write_pack_file/wrote.*"value":"'$1'"' $2
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'push with negotiation' '
|
||||||
|
# Without negotiation
|
||||||
|
mk_empty testrepo &&
|
||||||
|
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
|
||||||
|
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
|
||||||
|
echo now pushing without negotiation &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 push testrepo refs/heads/main:refs/remotes/origin/main &&
|
||||||
|
grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
|
||||||
|
|
||||||
|
# Same commands, but with negotiation
|
||||||
|
rm event &&
|
||||||
|
mk_empty testrepo &&
|
||||||
|
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
|
||||||
|
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
|
||||||
|
GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main &&
|
||||||
|
grep_wrote 2 event # 1 commit, 1 tree
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'push with negotiation proceeds anyway even if negotiation fails' '
|
||||||
|
rm event &&
|
||||||
|
mk_empty testrepo &&
|
||||||
|
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
|
||||||
|
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
|
||||||
|
GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
|
||||||
|
git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
|
||||||
|
grep_wrote 5 event && # 2 commits, 2 trees, 1 blob
|
||||||
|
test_i18ngrep "push negotiation failed" err
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'push without wildcard' '
|
test_expect_success 'push without wildcard' '
|
||||||
mk_empty testrepo &&
|
mk_empty testrepo &&
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ test_expect_success 'test capability advertisement' '
|
||||||
version 2
|
version 2
|
||||||
agent=git/$(git version | cut -d" " -f3)
|
agent=git/$(git version | cut -d" " -f3)
|
||||||
ls-refs=unborn
|
ls-refs=unborn
|
||||||
fetch=shallow
|
fetch=shallow wait-for-done
|
||||||
server-option
|
server-option
|
||||||
object-format=$(test_oid algo)
|
object-format=$(test_oid algo)
|
||||||
object-info
|
object-info
|
||||||
|
|
|
@ -585,6 +585,49 @@ test_expect_success 'deepen-relative' '
|
||||||
test_cmp expected actual
|
test_cmp expected actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
setup_negotiate_only () {
|
||||||
|
SERVER="$1"
|
||||||
|
URI="$2"
|
||||||
|
|
||||||
|
rm -rf "$SERVER" client
|
||||||
|
|
||||||
|
git init "$SERVER"
|
||||||
|
test_commit -C "$SERVER" one
|
||||||
|
test_commit -C "$SERVER" two
|
||||||
|
|
||||||
|
git clone "$URI" client
|
||||||
|
test_commit -C client three
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'file:// --negotiate-only' '
|
||||||
|
SERVER="server" &&
|
||||||
|
URI="file://$(pwd)/server" &&
|
||||||
|
|
||||||
|
setup_negotiate_only "$SERVER" "$URI" &&
|
||||||
|
|
||||||
|
git -c protocol.version=2 -C client fetch \
|
||||||
|
--no-tags \
|
||||||
|
--negotiate-only \
|
||||||
|
--negotiation-tip=$(git -C client rev-parse HEAD) \
|
||||||
|
origin >out &&
|
||||||
|
COMMON=$(git -C "$SERVER" rev-parse two) &&
|
||||||
|
grep "$COMMON" out
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'file:// --negotiate-only with protocol v0' '
|
||||||
|
SERVER="server" &&
|
||||||
|
URI="file://$(pwd)/server" &&
|
||||||
|
|
||||||
|
setup_negotiate_only "$SERVER" "$URI" &&
|
||||||
|
|
||||||
|
test_must_fail git -c protocol.version=0 -C client fetch \
|
||||||
|
--no-tags \
|
||||||
|
--negotiate-only \
|
||||||
|
--negotiation-tip=$(git -C client rev-parse HEAD) \
|
||||||
|
origin 2>err &&
|
||||||
|
test_i18ngrep "negotiate-only requires protocol v2" err
|
||||||
|
'
|
||||||
|
|
||||||
# Test protocol v2 with 'http://' transport
|
# Test protocol v2 with 'http://' transport
|
||||||
#
|
#
|
||||||
. "$TEST_DIRECTORY"/lib-httpd.sh
|
. "$TEST_DIRECTORY"/lib-httpd.sh
|
||||||
|
@ -1035,6 +1078,52 @@ test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodul
|
||||||
test_i18ngrep "disallowed submodule name" err
|
test_i18ngrep "disallowed submodule name" err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http:// --negotiate-only' '
|
||||||
|
SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
|
||||||
|
URI="$HTTPD_URL/smart/server" &&
|
||||||
|
|
||||||
|
setup_negotiate_only "$SERVER" "$URI" &&
|
||||||
|
|
||||||
|
git -c protocol.version=2 -C client fetch \
|
||||||
|
--no-tags \
|
||||||
|
--negotiate-only \
|
||||||
|
--negotiation-tip=$(git -C client rev-parse HEAD) \
|
||||||
|
origin >out &&
|
||||||
|
COMMON=$(git -C "$SERVER" rev-parse two) &&
|
||||||
|
grep "$COMMON" out
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http:// --negotiate-only without wait-for-done support' '
|
||||||
|
SERVER="server" &&
|
||||||
|
URI="$HTTPD_URL/one_time_perl/server" &&
|
||||||
|
|
||||||
|
setup_negotiate_only "$SERVER" "$URI" &&
|
||||||
|
|
||||||
|
echo "s/ wait-for-done/ xxxx-xxx-xxxx/" \
|
||||||
|
>"$HTTPD_ROOT_PATH/one-time-perl" &&
|
||||||
|
|
||||||
|
test_must_fail git -c protocol.version=2 -C client fetch \
|
||||||
|
--no-tags \
|
||||||
|
--negotiate-only \
|
||||||
|
--negotiation-tip=$(git -C client rev-parse HEAD) \
|
||||||
|
origin 2>err &&
|
||||||
|
test_i18ngrep "server does not support wait-for-done" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http:// --negotiate-only with protocol v0' '
|
||||||
|
SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
|
||||||
|
URI="$HTTPD_URL/smart/server" &&
|
||||||
|
|
||||||
|
setup_negotiate_only "$SERVER" "$URI" &&
|
||||||
|
|
||||||
|
test_must_fail git -c protocol.version=0 -C client fetch \
|
||||||
|
--no-tags \
|
||||||
|
--negotiate-only \
|
||||||
|
--negotiation-tip=$(git -C client rev-parse HEAD) \
|
||||||
|
origin 2>err &&
|
||||||
|
test_i18ngrep "negotiate-only requires protocol v2" err
|
||||||
|
'
|
||||||
|
|
||||||
# DO NOT add non-httpd-specific tests here, because the last part of this
|
# DO NOT add non-httpd-specific tests here, because the last part of this
|
||||||
# test script is only executed when httpd is available and enabled.
|
# test script is only executed when httpd is available and enabled.
|
||||||
|
|
||||||
|
|
|
@ -684,6 +684,16 @@ static int fetch(struct transport *transport,
|
||||||
return transport->vtable->fetch(transport, nr_heads, to_fetch);
|
return transport->vtable->fetch(transport, nr_heads, to_fetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we reach here, then the server, the client, and/or the transport
|
||||||
|
* helper does not support protocol v2. --negotiate-only requires
|
||||||
|
* protocol v2.
|
||||||
|
*/
|
||||||
|
if (data->transport_options.acked_commits) {
|
||||||
|
warning(_("--negotiate-only requires protocol v2"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!data->get_refs_list_called)
|
if (!data->get_refs_list_called)
|
||||||
get_refs_list_using_list(transport, 0);
|
get_refs_list_using_list(transport, 0);
|
||||||
|
|
||||||
|
|
30
transport.c
30
transport.c
|
@ -392,16 +392,29 @@ static int fetch_refs_via_pack(struct transport *transport,
|
||||||
else if (data->version <= protocol_v1)
|
else if (data->version <= protocol_v1)
|
||||||
die_if_server_options(transport);
|
die_if_server_options(transport);
|
||||||
|
|
||||||
|
if (data->options.acked_commits) {
|
||||||
|
if (data->version < protocol_v2) {
|
||||||
|
warning(_("--negotiate-only requires protocol v2"));
|
||||||
|
ret = -1;
|
||||||
|
} else if (!server_supports_feature("fetch", "wait-for-done", 0)) {
|
||||||
|
warning(_("server does not support wait-for-done"));
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
negotiate_using_fetch(data->options.negotiation_tips,
|
||||||
|
transport->server_options,
|
||||||
|
transport->stateless_rpc,
|
||||||
|
data->fd,
|
||||||
|
data->options.acked_commits);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
refs = fetch_pack(&args, data->fd,
|
refs = fetch_pack(&args, data->fd,
|
||||||
refs_tmp ? refs_tmp : transport->remote_refs,
|
refs_tmp ? refs_tmp : transport->remote_refs,
|
||||||
to_fetch, nr_heads, &data->shallow,
|
to_fetch, nr_heads, &data->shallow,
|
||||||
&transport->pack_lockfiles, data->version);
|
&transport->pack_lockfiles, data->version);
|
||||||
|
|
||||||
close(data->fd[0]);
|
|
||||||
close(data->fd[1]);
|
|
||||||
if (finish_connect(data->conn))
|
|
||||||
ret = -1;
|
|
||||||
data->conn = NULL;
|
|
||||||
data->got_remote_heads = 0;
|
data->got_remote_heads = 0;
|
||||||
data->options.self_contained_and_connected =
|
data->options.self_contained_and_connected =
|
||||||
args.self_contained_and_connected;
|
args.self_contained_and_connected;
|
||||||
|
@ -412,6 +425,13 @@ static int fetch_refs_via_pack(struct transport *transport,
|
||||||
if (report_unmatched_refs(to_fetch, nr_heads))
|
if (report_unmatched_refs(to_fetch, nr_heads))
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
close(data->fd[0]);
|
||||||
|
close(data->fd[1]);
|
||||||
|
if (finish_connect(data->conn))
|
||||||
|
ret = -1;
|
||||||
|
data->conn = NULL;
|
||||||
|
|
||||||
free_refs(refs_tmp);
|
free_refs(refs_tmp);
|
||||||
free_refs(refs);
|
free_refs(refs);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -47,6 +47,12 @@ struct git_transport_options {
|
||||||
* transport_set_option().
|
* transport_set_option().
|
||||||
*/
|
*/
|
||||||
struct oid_array *negotiation_tips;
|
struct oid_array *negotiation_tips;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If allocated, whenever transport_fetch_refs() is called, add known
|
||||||
|
* common commits to this oidset instead of fetching any packfiles.
|
||||||
|
*/
|
||||||
|
struct oidset *acked_commits;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum transport_family {
|
enum transport_family {
|
||||||
|
|
|
@ -103,6 +103,7 @@ struct upload_pack_data {
|
||||||
unsigned use_ofs_delta : 1;
|
unsigned use_ofs_delta : 1;
|
||||||
unsigned no_progress : 1;
|
unsigned no_progress : 1;
|
||||||
unsigned use_include_tag : 1;
|
unsigned use_include_tag : 1;
|
||||||
|
unsigned wait_for_done : 1;
|
||||||
unsigned allow_filter : 1;
|
unsigned allow_filter : 1;
|
||||||
unsigned allow_filter_fallback : 1;
|
unsigned allow_filter_fallback : 1;
|
||||||
unsigned long tree_filter_max_depth;
|
unsigned long tree_filter_max_depth;
|
||||||
|
@ -1496,6 +1497,10 @@ static void process_args(struct packet_reader *request,
|
||||||
data->done = 1;
|
data->done = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "wait-for-done")) {
|
||||||
|
data->wait_for_done = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Shallow related arguments */
|
/* Shallow related arguments */
|
||||||
if (process_shallow(arg, &data->shallows))
|
if (process_shallow(arg, &data->shallows))
|
||||||
|
@ -1578,7 +1583,7 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
|
||||||
oid_to_hex(&acks->oid[i]));
|
oid_to_hex(&acks->oid[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok_to_give_up(data)) {
|
if (!data->wait_for_done && ok_to_give_up(data)) {
|
||||||
/* Send Ready */
|
/* Send Ready */
|
||||||
packet_writer_write(&data->writer, "ready\n");
|
packet_writer_write(&data->writer, "ready\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1668,10 +1673,13 @@ int upload_pack_v2(struct repository *r, struct strvec *keys,
|
||||||
case FETCH_PROCESS_ARGS:
|
case FETCH_PROCESS_ARGS:
|
||||||
process_args(request, &data);
|
process_args(request, &data);
|
||||||
|
|
||||||
if (!data.want_obj.nr) {
|
if (!data.want_obj.nr && !data.wait_for_done) {
|
||||||
/*
|
/*
|
||||||
* Request didn't contain any 'want' lines,
|
* Request didn't contain any 'want' lines (and
|
||||||
* guess they didn't want anything.
|
* the request does not contain
|
||||||
|
* "wait-for-done", in which it is reasonable
|
||||||
|
* to just send 'have's without 'want's); guess
|
||||||
|
* they didn't want anything.
|
||||||
*/
|
*/
|
||||||
state = FETCH_DONE;
|
state = FETCH_DONE;
|
||||||
} else if (data.haves.nr) {
|
} else if (data.haves.nr) {
|
||||||
|
@ -1723,7 +1731,7 @@ int upload_pack_advertise(struct repository *r,
|
||||||
int allow_sideband_all_value;
|
int allow_sideband_all_value;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
|
|
||||||
strbuf_addstr(value, "shallow");
|
strbuf_addstr(value, "shallow wait-for-done");
|
||||||
|
|
||||||
if (!repo_config_get_bool(the_repository,
|
if (!repo_config_get_bool(the_repository,
|
||||||
"uploadpack.allowfilter",
|
"uploadpack.allowfilter",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче