Commit 7e1a94db11f38c87ae224821a8c570ffa1e11270

Carlos Martín Nieto 2011-07-31T01:16:47

Move have sending Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/src/fetch.c b/src/fetch.c
index c799c80..61bcc15 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -51,7 +51,7 @@ static int whn_cmp(const void *a, const void *b)
 int git_fetch_list_want(git_headarray *whn_list, git_repository *repo, git_remote *remote)
 {
 	git_vector list;
-	git_headarray refs, lrefs;
+	git_headarray refs;
 	git_transport *t = remote->transport;
 	const git_refspec *spec;
 	int error;
@@ -159,31 +159,49 @@ int git_fetch_negotiate(git_headarray *list, git_repository *repo, git_remote *r
 	unsigned int i;
 	char local[1024];
 	git_refspec *spec;
+	git_reference *ref;
+	git_strarray refs;
+	git_oid oid;
 
-	error = git_revwalk_new(&walk, repo);
-	if (error < GIT_SUCCESS)
-		return git__rethrow(error, "Failed to create walker");
+	/*
+	 * Now we have everything set up so we can start tell the server
+	 * what we want and what we have.
+	 */
+	git_transport_send_wants(remote->transport, list);
 
-	for (i = 0; i < list->len; ++i) {
-		git_reference *ref;
-		git_remote_head *head = list->heads[i];
+	error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
+	if (error < GIT_ERROR)
+		return git__rethrow(error, "Failed to list all references");
 
-		if (!head->local)
-			continue;
+	error = git_revwalk_new(&walk, repo);
+	if (error < GIT_ERROR) {
+		error = git__rethrow(error, "Failed to list all references");
+		goto cleanup;
+	}
 
-		error = git_revwalk_push(walk, &head->loid);
-		if (error < GIT_SUCCESS) {
-			error = git__rethrow(error, "Failed to push a local OID");
+	for (i = 0; i < refs.count; ++i) {
+		error = git_reference_lookup(&ref, repo, refs.strings[i]);
+		if (error < GIT_ERROR) {
+			error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
+			goto cleanup;
+		}
+
+		error = git_revwalk_push(walk, git_reference_oid(ref));
+		if (error < GIT_ERROR) {
+			error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
 			goto cleanup;
 		}
 	}
+	git_strarray_free(&refs);
 
-	/*
-	 * Now we have everything set up so we can start tell the server
-	 * what we want and what we have.
-	 */
-	git_transport_send_wants(remote->transport, list);
-	git_transport_send_haves(remote->transport, repo);
+	while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
+		git_transport_send_have(remote->transport, &oid);
+	}
+	if (error == GIT_EREVWALKOVER)
+		error = GIT_SUCCESS;
+
+	/* TODO: git_pkt_send_flush(fd), or git_transport_flush() */
+	printf("Wound send 0000\n");
 
 cleanup:
 	git_revwalk_free(walk);
@@ -192,5 +210,8 @@ cleanup:
 
 int git_fetch_download_pack(git_remote *remote)
 {
+	/*
+	 * First, we ignore any ACKs we receive and wait for a NACK
+	 */
 	return GIT_ENOTIMPLEMENTED;
 }
diff --git a/src/pkt.c b/src/pkt.c
index fbb5dfb..61c011c 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -52,10 +52,38 @@ static int flush_pkt(git_pkt **out)
 	return GIT_SUCCESS;
 }
 
+static int ack_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	if (pkt == NULL)
+		return GIT_ENOMEM;
+
+	pkt->type = GIT_PKT_ACK;
+	*out = pkt;
+
+	return GIT_SUCCESS;
+}
+
+static int nack_pkt(git_pkt **out)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	if (pkt == NULL)
+		return GIT_ENOMEM;
+
+	pkt->type = GIT_PKT_NACK;
+	*out = pkt;
+
+	return GIT_SUCCESS;
+}
+
 /*
  * Parse an other-ref line.
  */
-int ref_pkt(git_pkt **out, const char *line, size_t len)
+static int ref_pkt(git_pkt **out, const char *line, size_t len)
 {
 	git_pkt_ref *pkt;
 	int error, has_caps = 0;
@@ -185,11 +213,14 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
 
 	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
 
-	/*
-	 * For now, we're just going to assume we're parsing references
-	 */
+	/* Assming the minimal size is actually 4 */
+	if (!git__prefixcmp(line, "ACK"))
+		error = ack_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "NACK"))
+		error = nack_pkt(head);
+	else
+		error = ref_pkt(head, line, len);
 
-	error = ref_pkt(head, line, len);
 	*out = line + len;
 
 	return error;
@@ -239,62 +270,32 @@ int git_pkt_send_wants(git_headarray *refs, int fd)
 	}
 
 	/* TODO: git_pkt_send_flush(fd) */
-	printf("Wound send 0000\n");
+	printf("Would send 0000\n");
 
 	return ret;
 }
 
+/*
+ * TODO: this should be a more generic function, maybe to be used by
+ * git_pkt_send_wants, as it's not performance-critical
+ */
 #define HAVE_PREFIX "0032have "
 
-int git_pkt_send_haves(git_repository *repo, int fd)
+int git_pkt_send_have(git_oid *oid, int fd)
 {
-	unsigned int i;
 	int ret = GIT_SUCCESS;
-	char buf[STRLEN(HAVE_PREFIX) + GIT_OID_HEXSZ + 2];
-	git_oid oid;
-	git_revwalk *walk;
-	git_strarray refs;
-	git_reference *ref;
-	git_remote_head *head;
-
-	memcpy(buf, HAVE_PREFIX, STRLEN(HAVE_PREFIX));
-	buf[sizeof(buf) - 2] = '\n';
-	buf[sizeof(buf) - 1] = '\0';
+	char buf[] = "0032have 0000000000000000000000000000000000000000\n";
 
-	ret = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
-	if (ret < GIT_ERROR)
-		return git__rethrow(ret, "Failed to list all references");
+	git_oid_fmt(buf + STRLEN(HAVE_PREFIX), oid);
+	printf("would send %s", buf);
 
-	ret = git_revwalk_new(&walk, repo);
-	if (ret < GIT_ERROR) {
-		ret = git__rethrow(ret, "Failed to list all references");
-		goto cleanup;
-	}
-
-	for (i = 0; i < refs.count; ++i) {
-		ret = git_reference_lookup(&ref, repo, refs.strings[i]);
-		if (ret < GIT_ERROR) {
-			ret = git__rethrow(ret, "Failed to lookup %s", refs.strings[i]);
-			goto cleanup;
-		}
-
-		ret = git_revwalk_push(walk, git_reference_oid(ref));
-		if (ret < GIT_ERROR) {
-			ret = git__rethrow(ret, "Failed to push %s", refs.strings[i]);
-			goto cleanup;
-		}
-	}
-
-	while ((ret = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
-		git_oid_fmt(buf + STRLEN(HAVE_PREFIX), &oid);
-		printf("would send %s", buf);
-	}
+	return ret;
+}
 
-	/* TODO: git_pkt_send_flush(fd) */
-	printf("Wound send 0000\n");
+int git_pkt_send_have(int fd)
+{
+	char buf[] = "0009done\n";
+	printf("Would send %s", buf);
 
-cleanup:
-	git_revwalk_free(walk);
-	git_strarray_free(&refs);
-	return ret;
+	return GIT_SUCCESS;
 }
diff --git a/src/pkt.h b/src/pkt.h
index 4340703..b5d2a17 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -34,6 +34,16 @@ enum git_pkt_type {
 	GIT_PKT_FLUSH,
 	GIT_PKT_REF,
 	GIT_PKT_HAVE,
+	GIT_PKT_ACK,
+	GIT_PKT_NACK,
+};
+
+/* Used for multi-ack */
+enum git_ack_status {
+	GIT_ACK_NONE,
+	GIT_ACK_CONTINUE,
+	GIT_ACK_COMMON,
+	GIT_ACK_READY
 };
 
 /* This would be a flush pkt */
@@ -55,10 +65,17 @@ typedef struct {
 	char *capabilities;
 } git_pkt_ref;
 
+/* Useful later */
+typedef struct {
+	enum git_pkt_type type;
+	git_oid oid;
+	enum git_ack_status status;
+} git_pkt_ack;
+
 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_haves(git_repository *repo, int fd);
 int git_pkt_send_wants(git_headarray *refs, int fd);
+int git_pkt_send_have(git_oid *oid, int fd);
 void git_pkt_free(git_pkt *pkt);
 
 #endif
diff --git a/src/transport.c b/src/transport.c
index 53caf9e..6098989 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -85,11 +85,15 @@ int git_transport_send_wants(struct git_transport *transport, git_headarray *arr
 	return transport->send_wants(transport, array);
 }
 
-int git_transport_send_haves(struct git_transport *transport, git_repository *repo)
+int git_transport_send_have(struct git_transport *transport, git_oid *oid)
 {
-	return transport->send_haves(transport, repo);
+	return transport->send_have(transport, oid);
 }
 
+int git_transport_send_done(struct git_transport *transport)
+{
+	return transport->send_done(transport);
+}
 int git_transport_close(git_transport *transport)
 {
 	return transport->close(transport);
diff --git a/src/transport.h b/src/transport.h
index 6e3501b..097b9ac 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -63,7 +63,7 @@ struct git_transport {
 	/**
 	 * Send the list of 'have' refs
 	 */
-	int (*send_haves)(struct git_transport *transport, git_repository *repo);
+	int (*send_have)(struct git_transport *transport, git_oid *oid);
 	/**
 	 * Fetch the changes
 	 */
@@ -83,5 +83,7 @@ int git_transport_git(struct git_transport **transport);
 int git_transport_dummy(struct git_transport **transport);
 
 int git_transport_send_wants(struct git_transport *transport, git_headarray *array);
+int git_transport_send_have(struct git_transport *transport, git_oid *oid);
+int git_transport_send_done(struct git_transport *transport);
 
 #endif
diff --git a/src/transport_git.c b/src/transport_git.c
index 0453ca0..4b183ed 100644
--- a/src/transport_git.c
+++ b/src/transport_git.c
@@ -281,11 +281,18 @@ static int git_send_wants(git_transport *transport, git_headarray *array)
 	return git_pkt_send_wants(array, t->socket);
 }
 
-static int git_send_haves(git_transport *transport, git_repository *repo)
+static int git_send_have(git_transport *transport, git_oid *oid)
 {
 	transport_git *t = (transport_git *) transport;
 
-	return git_pkt_send_haves(repo, t->socket);
+	return git_pkt_send_have(oid, t->socket);
+}
+
+static int git_send_done(git_transport *transport)
+{
+	transport_git *t = (transport_git *) transport;
+
+	return git_pkt_send_done(t->socket);
 }
 
 static int git_close(git_transport *transport)
@@ -333,7 +340,8 @@ int git_transport_git(git_transport **out)
 	t->parent.connect = git_connect;
 	t->parent.ls = git_ls;
 	t->parent.send_wants = git_send_wants;
-	t->parent.send_haves = git_send_haves;
+	t->parent.send_have = git_send_have;
+	t->parent.send_done = git_send_done;
 	t->parent.close = git_close;
 	t->parent.free = git_free;