diff --git a/src/fetch.c b/src/fetch.c index 6a726417c..4880772d5 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -75,13 +75,24 @@ static int filter_wants(git_remote *remote) } /* Wait until we get an ack from the */ -static int recv_pkt(gitno_buffer *buf) +static int recv_pkt(git_pkt **out, gitno_buffer *buf) { - const char *ptr = buf->data, *line_end; + const char *ptr = buf->data, *line_end = ptr; git_pkt *pkt; - int pkt_type, error; + int pkt_type, error = 0; do { + if (buf->offset > 0) + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); + else + error = GIT_EBUFS; + + if (error == 0) + break; /* return the pkt */ + + if (error < 0 && error != GIT_EBUFS) + return -1; + /* Wait for max. 1 second */ if ((error = gitno_select_in(buf, 1, 0)) < 0) { return -1; @@ -97,21 +108,41 @@ static int recv_pkt(gitno_buffer *buf) if ((error = gitno_recv(buf)) < 0) return -1; - - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); - if (error == GIT_EBUFS) - continue; - if (error < 0) - return -1; } while (error); gitno_consume(buf, line_end); pkt_type = pkt->type; - git__free(pkt); + if (out != NULL) + *out = pkt; + else + git__free(pkt); return pkt_type; } +static int store_common(git_transport *t) +{ + int done = 0; + git_pkt *pkt = NULL; + gitno_buffer *buf = &t->buffer; + + do { + if (recv_pkt(&pkt, buf) < 0) + return -1; + + if (pkt->type == GIT_PKT_ACK) { + if (git_vector_insert(&t->common, pkt) < 0) + return -1; + } else { + git__free(pkt); + return 0; + } + + } while (1); + + return 0; +} + /* * In this first version, we push all our refs in and start sending * them out. When we get an ACK we hide that commit and continue @@ -169,18 +200,25 @@ int git_fetch_negotiate(git_remote *remote) goto on_error; git_buf_clear(&data); - pkt_type = recv_pkt(buf); - - if (pkt_type == GIT_PKT_ACK) { - break; - } else if (pkt_type == GIT_PKT_NAK) { - continue; + if (t->caps.multi_ack) { + if (store_common(t) < 0) + goto on_error; } else { - giterr_set(GITERR_NET, "Unexpected pkt type"); - goto on_error; - } + pkt_type = recv_pkt(NULL, buf); + if (pkt_type == GIT_PKT_ACK) { + break; + } else if (pkt_type == GIT_PKT_NAK) { + continue; + } else { + giterr_set(GITERR_NET, "Unexpected pkt type"); + goto on_error; + } + } } + + if (t->common.length > 0) + break; } if (error < 0 && error != GIT_REVWALKOVER) @@ -195,7 +233,7 @@ int git_fetch_negotiate(git_remote *remote) git_revwalk_free(walk); /* Now let's eat up whatever the server gives us */ - pkt_type = recv_pkt(buf); + pkt_type = recv_pkt(NULL, buf); if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { giterr_set(GITERR_NET, "Unexpected pkt type"); return -1; diff --git a/src/pkt.c b/src/pkt.c index e003b97e2..e60e30d5b 100644 --- a/src/pkt.c +++ b/src/pkt.c @@ -283,20 +283,28 @@ int git_pkt_buffer_flush(git_buf *buf) static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf) { - char capstr[20]; + git_buf str = GIT_BUF_INIT; char oid[GIT_OID_HEXSZ +1] = {0}; unsigned int len; if (caps->ofs_delta) - strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr)); + git_buf_puts(&str, GIT_CAP_OFS_DELTA " "); + + if (caps->multi_ack) + git_buf_puts(&str, GIT_CAP_MULTI_ACK " "); + + if (git_buf_oom(&str)) + return -1; len = (unsigned int) (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + - strlen(capstr) + 1 /* LF */); + git_buf_len(&str) + 1 /* LF */); git_buf_grow(buf, git_buf_len(buf) + len); - git_oid_fmt(oid, &head->oid); - return git_buf_printf(buf, "%04xwant %s %s\n", len, oid, capstr); + git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str)); + git_buf_free(&str); + + return git_buf_oom(buf); } /* diff --git a/src/transport.h b/src/transport.h index 09afb0c72..3ef36f49f 100644 --- a/src/transport.h +++ b/src/transport.h @@ -20,10 +20,12 @@ #define GIT_CAP_OFS_DELTA "ofs-delta" +#define GIT_CAP_MULTI_ACK "multi_ack" typedef struct git_transport_caps { int common:1, - ofs_delta:1; + ofs_delta:1, + multi_ack: 1; } git_transport_caps; #ifdef GIT_SSL @@ -76,6 +78,7 @@ struct git_transport { #ifdef GIT_SSL struct gitno_ssl ssl; #endif + git_vector common; gitno_buffer buffer; GIT_SOCKET socket; git_transport_caps caps; diff --git a/src/transports/git.c b/src/transports/git.c index 4fcde2d37..f5cdfe7a4 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -179,6 +179,12 @@ static int detect_caps(transport_git *t) continue; } + if(!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) { + caps->common = caps->multi_ack = 1; + ptr += strlen(GIT_CAP_MULTI_ACK); + continue; + } + /* We don't know this capability, so skip it */ ptr = strchr(ptr, ' '); } @@ -303,6 +309,10 @@ int git_transport_git(git_transport **out) GITERR_CHECK_ALLOC(t); memset(t, 0x0, sizeof(transport_git)); + if (git_vector_init(&t->parent.common, 8, NULL)) { + git__free(t); + return -1; + } t->parent.connect = git_connect; t->parent.negotiation_step = git_negotiation_step;