fetch-pack: perform a fetch using v2
When communicating with a v2 server, perform a fetch by requesting the 'fetch' command. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
3145ea957d
commit
685fbd3291
270
fetch-pack.c
270
fetch-pack.c
@ -303,9 +303,9 @@ static void insert_one_alternate_object(struct object *obj)
|
||||
#define PIPESAFE_FLUSH 32
|
||||
#define LARGE_FLUSH 16384
|
||||
|
||||
static int next_flush(struct fetch_pack_args *args, int count)
|
||||
static int next_flush(int stateless_rpc, int count)
|
||||
{
|
||||
if (args->stateless_rpc) {
|
||||
if (stateless_rpc) {
|
||||
if (count < LARGE_FLUSH)
|
||||
count <<= 1;
|
||||
else
|
||||
@ -461,7 +461,7 @@ static int find_common(struct fetch_pack_args *args,
|
||||
send_request(args, fd[1], &req_buf);
|
||||
strbuf_setlen(&req_buf, state_len);
|
||||
flushes++;
|
||||
flush_at = next_flush(args, count);
|
||||
flush_at = next_flush(args->stateless_rpc, count);
|
||||
|
||||
/*
|
||||
* We keep one window "ahead" of the other side, and
|
||||
@ -1008,6 +1008,259 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
return ref;
|
||||
}
|
||||
|
||||
static void add_wants(const struct ref *wants, struct strbuf *req_buf)
|
||||
{
|
||||
for ( ; wants ; wants = wants->next) {
|
||||
const struct object_id *remote = &wants->old_oid;
|
||||
const char *remote_hex;
|
||||
struct object *o;
|
||||
|
||||
/*
|
||||
* If that object is complete (i.e. it is an ancestor of a
|
||||
* local ref), we tell them we have it but do not have to
|
||||
* tell them about its ancestors, which they already know
|
||||
* about.
|
||||
*
|
||||
* We use lookup_object here because we are only
|
||||
* interested in the case we *know* the object is
|
||||
* reachable and we have already scanned it.
|
||||
*/
|
||||
if (((o = lookup_object(remote->hash)) != NULL) &&
|
||||
(o->flags & COMPLETE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remote_hex = oid_to_hex(remote);
|
||||
packet_buf_write(req_buf, "want %s\n", remote_hex);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_common(struct strbuf *req_buf, struct oidset *common)
|
||||
{
|
||||
struct oidset_iter iter;
|
||||
const struct object_id *oid;
|
||||
oidset_iter_init(common, &iter);
|
||||
|
||||
while ((oid = oidset_iter_next(&iter))) {
|
||||
packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
|
||||
}
|
||||
}
|
||||
|
||||
static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain)
|
||||
{
|
||||
int ret = 0;
|
||||
int haves_added = 0;
|
||||
const struct object_id *oid;
|
||||
|
||||
while ((oid = get_rev())) {
|
||||
packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
|
||||
if (++haves_added >= *haves_to_send)
|
||||
break;
|
||||
}
|
||||
|
||||
*in_vain += haves_added;
|
||||
if (!haves_added || *in_vain >= MAX_IN_VAIN) {
|
||||
/* Send Done */
|
||||
packet_buf_write(req_buf, "done\n");
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
/* Increase haves to send on next round */
|
||||
*haves_to_send = next_flush(1, *haves_to_send);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
|
||||
const struct ref *wants, struct oidset *common,
|
||||
int *haves_to_send, int *in_vain)
|
||||
{
|
||||
int ret = 0;
|
||||
struct strbuf req_buf = STRBUF_INIT;
|
||||
|
||||
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());
|
||||
|
||||
packet_buf_delim(&req_buf);
|
||||
if (args->use_thin_pack)
|
||||
packet_buf_write(&req_buf, "thin-pack");
|
||||
if (args->no_progress)
|
||||
packet_buf_write(&req_buf, "no-progress");
|
||||
if (args->include_tag)
|
||||
packet_buf_write(&req_buf, "include-tag");
|
||||
if (prefer_ofs_delta)
|
||||
packet_buf_write(&req_buf, "ofs-delta");
|
||||
|
||||
/* add wants */
|
||||
add_wants(wants, &req_buf);
|
||||
|
||||
/* Add all of the common commits we've found in previous rounds */
|
||||
add_common(&req_buf, common);
|
||||
|
||||
/* Add initial haves */
|
||||
ret = add_haves(&req_buf, haves_to_send, in_vain);
|
||||
|
||||
/* Send request */
|
||||
packet_buf_flush(&req_buf);
|
||||
write_or_die(fd_out, req_buf.buf, req_buf.len);
|
||||
|
||||
strbuf_release(&req_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a section header in a server's response and checks if it matches
|
||||
* `section`. If the value of `peek` is 1, the header line will be peeked (and
|
||||
* not consumed); if 0, the line will be consumed and the function will die if
|
||||
* the section header doesn't match what was expected.
|
||||
*/
|
||||
static int process_section_header(struct packet_reader *reader,
|
||||
const char *section, int peek)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (packet_reader_peek(reader) != PACKET_READ_NORMAL)
|
||||
die("error reading packet");
|
||||
|
||||
ret = !strcmp(reader->line, section);
|
||||
|
||||
if (!peek) {
|
||||
if (!ret)
|
||||
die("expected '%s', received '%s'",
|
||||
section, reader->line);
|
||||
packet_reader_read(reader);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int process_acks(struct packet_reader *reader, struct oidset *common)
|
||||
{
|
||||
/* received */
|
||||
int received_ready = 0;
|
||||
int received_ack = 0;
|
||||
|
||||
process_section_header(reader, "acknowledgments", 0);
|
||||
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
|
||||
const char *arg;
|
||||
|
||||
if (!strcmp(reader->line, "NAK"))
|
||||
continue;
|
||||
|
||||
if (skip_prefix(reader->line, "ACK ", &arg)) {
|
||||
struct object_id oid;
|
||||
if (!get_oid_hex(arg, &oid)) {
|
||||
struct commit *commit;
|
||||
oidset_insert(common, &oid);
|
||||
commit = lookup_commit(&oid);
|
||||
mark_common(commit, 0, 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(reader->line, "ready")) {
|
||||
clear_prio_queue(&rev_list);
|
||||
received_ready = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
die("unexpected acknowledgment line: '%s'", reader->line);
|
||||
}
|
||||
|
||||
if (reader->status != PACKET_READ_FLUSH &&
|
||||
reader->status != PACKET_READ_DELIM)
|
||||
die("error processing acks: %d", reader->status);
|
||||
|
||||
/* return 0 if no common, 1 if there are common, or 2 if ready */
|
||||
return received_ready ? 2 : (received_ack ? 1 : 0);
|
||||
}
|
||||
|
||||
enum fetch_state {
|
||||
FETCH_CHECK_LOCAL = 0,
|
||||
FETCH_SEND_REQUEST,
|
||||
FETCH_PROCESS_ACKS,
|
||||
FETCH_GET_PACK,
|
||||
FETCH_DONE,
|
||||
};
|
||||
|
||||
static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
int fd[2],
|
||||
const struct ref *orig_ref,
|
||||
struct ref **sought, int nr_sought,
|
||||
char **pack_lockfile)
|
||||
{
|
||||
struct ref *ref = copy_ref_list(orig_ref);
|
||||
enum fetch_state state = FETCH_CHECK_LOCAL;
|
||||
struct oidset common = OIDSET_INIT;
|
||||
struct packet_reader reader;
|
||||
int in_vain = 0;
|
||||
int haves_to_send = INITIAL_FLUSH;
|
||||
packet_reader_init(&reader, fd[0], NULL, 0,
|
||||
PACKET_READ_CHOMP_NEWLINE);
|
||||
|
||||
while (state != FETCH_DONE) {
|
||||
switch (state) {
|
||||
case FETCH_CHECK_LOCAL:
|
||||
sort_ref_list(&ref, ref_compare_name);
|
||||
QSORT(sought, nr_sought, cmp_ref_by_name);
|
||||
|
||||
/* v2 supports these by default */
|
||||
allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
|
||||
use_sideband = 2;
|
||||
|
||||
if (marked)
|
||||
for_each_ref(clear_marks, NULL);
|
||||
marked = 1;
|
||||
|
||||
for_each_ref(rev_list_insert_ref_oid, NULL);
|
||||
for_each_cached_alternate(insert_one_alternate_object);
|
||||
|
||||
/* Filter 'ref' by 'sought' and those that aren't local */
|
||||
if (everything_local(args, &ref, sought, nr_sought))
|
||||
state = FETCH_DONE;
|
||||
else
|
||||
state = FETCH_SEND_REQUEST;
|
||||
break;
|
||||
case FETCH_SEND_REQUEST:
|
||||
if (send_fetch_request(fd[1], args, ref, &common,
|
||||
&haves_to_send, &in_vain))
|
||||
state = FETCH_GET_PACK;
|
||||
else
|
||||
state = FETCH_PROCESS_ACKS;
|
||||
break;
|
||||
case FETCH_PROCESS_ACKS:
|
||||
/* Process ACKs/NAKs */
|
||||
switch (process_acks(&reader, &common)) {
|
||||
case 2:
|
||||
state = FETCH_GET_PACK;
|
||||
break;
|
||||
case 1:
|
||||
in_vain = 0;
|
||||
/* fallthrough */
|
||||
default:
|
||||
state = FETCH_SEND_REQUEST;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FETCH_GET_PACK:
|
||||
/* get the pack */
|
||||
process_section_header(&reader, "packfile", 0);
|
||||
if (get_pack(args, fd, pack_lockfile))
|
||||
die(_("git fetch-pack: fetch failed."));
|
||||
|
||||
state = FETCH_DONE;
|
||||
break;
|
||||
case FETCH_DONE:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
oidset_clear(&common);
|
||||
return ref;
|
||||
}
|
||||
|
||||
static void fetch_pack_config(void)
|
||||
{
|
||||
git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit);
|
||||
@ -1153,7 +1406,8 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||
const char *dest,
|
||||
struct ref **sought, int nr_sought,
|
||||
struct oid_array *shallow,
|
||||
char **pack_lockfile)
|
||||
char **pack_lockfile,
|
||||
enum protocol_version version)
|
||||
{
|
||||
struct ref *ref_cpy;
|
||||
struct shallow_info si;
|
||||
@ -1167,8 +1421,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||
die(_("no matching remote head"));
|
||||
}
|
||||
prepare_shallow_info(&si, shallow);
|
||||
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
|
||||
&si, pack_lockfile);
|
||||
if (version == protocol_v2)
|
||||
ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
|
||||
pack_lockfile);
|
||||
else
|
||||
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
|
||||
&si, pack_lockfile);
|
||||
reprepare_packed_git();
|
||||
update_shallow(args, sought, nr_sought, &si);
|
||||
clear_shallow_info(&si);
|
||||
|
Reference in New Issue
Block a user