Commit 114dc6e14c47ff574b4c97d4519782de3f9d28b2

Carlos Martín Nieto 2012-07-24T17:10:57

network: implement multi_ack for the git transport

diff --git a/src/fetch.c b/src/fetch.c
index 6a72641..4880772 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 e003b97..e60e30d 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 09afb0c..3ef36f4 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 4fcde2d..f5cdfe7 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;