diff --git a/src/pkt.c b/src/pkt.c index fa0ee7701..12f1478b8 100644 --- a/src/pkt.c +++ b/src/pkt.c @@ -269,13 +269,35 @@ int git_pkt_send_flush(int s) return gitno_send(s, flush, STRLEN(flush), 0); } +static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, int fd) +{ + char capstr[20]; /* Longer than we need */ + char oid[GIT_OID_HEXSZ +1] = {0}, *cmd; + int error, len; + + if (caps->ofs_delta) + strcpy(capstr, GIT_CAP_OFS_DELTA); + + len = STRLEN("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + strlen(capstr) + 1 /* LF */; + cmd = git__malloc(len + 1); + if (cmd == NULL) + return GIT_ENOMEM; + + git_oid_fmt(oid, &head->oid); + memset(cmd, 0x0, len + 1); + snprintf(cmd, len + 1, "%04xwant %s%c%s\n", len, oid, 0, capstr); + error = gitno_send(fd, cmd, len, 0); + free(cmd); + return error; +} + /* * All "want" packets have the same length and format, so what we do * is overwrite the OID each time. */ #define WANT_PREFIX "0032want " -int git_pkt_send_wants(git_headarray *refs, int fd) +int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd) { unsigned int i; int ret = GIT_SUCCESS; @@ -286,10 +308,18 @@ int git_pkt_send_wants(git_headarray *refs, int fd) buf[sizeof(buf) - 2] = '\n'; buf[sizeof(buf) - 1] = '\0'; - for (i = 0; i < refs->len; ++i) { + if (refs->len > 0 && caps->common) { + /* Some capabilities are active, so we need to send what we support */ + send_want_with_caps(refs->heads[0], caps, fd); + i = 1; + } else { + i = 0; + } + + for (; i < refs->len; ++i) { head = refs->heads[i]; if (head->type != GIT_WHN_WANT) - continue; + continue; /* FIXME: return? refs shouldn't have any other type */ git_oid_fmt(buf + STRLEN(WANT_PREFIX), &head->oid); gitno_send(fd, buf, STRLEN(buf), 0); diff --git a/src/pkt.h b/src/pkt.h index 3a8fac5e1..1c6a20659 100644 --- a/src/pkt.h +++ b/src/pkt.h @@ -27,6 +27,7 @@ #define INCLUDE_pkt_h__ #include "common.h" +#include "transport.h" #include "git2/net.h" enum git_pkt_type { @@ -76,7 +77,7 @@ typedef struct { int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_send_flush(int s); int git_pkt_send_done(int s); -int git_pkt_send_wants(git_headarray *refs, int fd); +int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd); int git_pkt_send_have(git_oid *oid, int fd); void git_pkt_free(git_pkt *pkt); diff --git a/src/transport.h b/src/transport.h index 0e0869752..e809734a7 100644 --- a/src/transport.h +++ b/src/transport.h @@ -5,6 +5,13 @@ #include "git2/net.h" #include "vector.h" +#define GIT_CAP_OFS_DELTA "ofs-delta" + +typedef struct git_transport_caps { + int common:1, + ofs_delta:1; +} git_transport_caps; + /* * A day in the life of a network operation * ======================================== diff --git a/src/transport_git.c b/src/transport_git.c index 46678a28e..4a10f17cd 100644 --- a/src/transport_git.c +++ b/src/transport_git.c @@ -41,6 +41,7 @@ typedef struct { int socket; git_vector refs; git_remote_head **heads; + git_transport_caps caps; } transport_git; /* @@ -218,6 +219,36 @@ static int store_refs(transport_git *t) return error; } +static int detect_caps(transport_git *t) +{ + git_vector *refs = &t->refs; + git_pkt_ref *pkt; + git_transport_caps *caps = &t->caps; + const char *ptr; + + pkt = git_vector_get(refs, 0); + /* No refs or capabilites, odd but not a problem */ + if (pkt == NULL || pkt->capabilities == NULL) + return GIT_SUCCESS; + + ptr = pkt->capabilities; + while (ptr != NULL && *ptr != '\0') { + if (*ptr == ' ') + ptr++; + + if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { + caps->common = caps->ofs_delta = 1; + ptr += STRLEN(GIT_CAP_OFS_DELTA); + continue; + } + + /* We don't know this capability, so skip it */ + ptr = strchr(ptr, ' '); + } + + return GIT_SUCCESS; +} + /* * Since this is a network connection, we need to parse and store the * pkt-lines at this stage and keep them there. @@ -242,6 +273,10 @@ static int git_connect(git_transport *transport, int direction) t->parent.connected = 1; error = store_refs(t); + if (error < GIT_SUCCESS) + return error; + + error = detect_caps(t); cleanup: if (error < GIT_SUCCESS) { @@ -280,7 +315,7 @@ static int git_send_wants(git_transport *transport, git_headarray *array) { transport_git *t = (transport_git *) transport; - return git_pkt_send_wants(array, t->socket); + return git_pkt_send_wants(array, &t->caps, t->socket); } static int git_send_have(git_transport *transport, git_oid *oid)