Commit 0437d991bfb67ffd2d3bb3d5a2cf21261ea42029

Carlos Martín Nieto 2011-08-05T15:45:05

Use common capabilities Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/src/pkt.c b/src/pkt.c
index fa0ee77..12f1478 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 3a8fac5..1c6a206 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 0e08697..e809734 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 46678a2..4a10f17 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)