Commit da2902204b12e4ba2ad218590f0826a8b8c1badc

Carlos Martín Nieto 2011-07-31T02:40:43

Download pack Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/src/fetch.c b/src/fetch.c
index 61bcc15..522625e 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -131,22 +131,6 @@ cleanup:
 	return error;
 }
 
-/* Push any (OID) ref it gets into the walker */
-static int push_stuff(const char *name, void *data)
-{
-	git_revwalk *walk = (git_revwalk *) data;
-	git_reference *ref;
-	git_repository *repo;
-	int error;
-
-	repo = git_revwalk_repository(walk);
-	error = git_reference_lookup(&ref, repo, name);
-	if (error < GIT_SUCCESS)
-		return error;
-
-	return git_revwalk_push(walk, git_reference_oid(ref));
-}
-
 /*
  * 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
@@ -157,12 +141,14 @@ int git_fetch_negotiate(git_headarray *list, git_repository *repo, git_remote *r
 	git_revwalk *walk;
 	int error;
 	unsigned int i;
-	char local[1024];
-	git_refspec *spec;
 	git_reference *ref;
 	git_strarray refs;
 	git_oid oid;
 
+	/* Don't try to negotiate when we don't want anything */
+	if (list->len == 0)
+		return GIT_EINVALIDARGS;
+
 	/*
 	 * Now we have everything set up so we can start tell the server
 	 * what we want and what we have.
@@ -201,17 +187,15 @@ int git_fetch_negotiate(git_headarray *list, git_repository *repo, git_remote *r
 		error = GIT_SUCCESS;
 
 	/* TODO: git_pkt_send_flush(fd), or git_transport_flush() */
-	printf("Wound send 0000\n");
+	git_transport_send_flush(remote->transport);
+	git_transport_send_done(remote->transport);
 
 cleanup:
 	git_revwalk_free(walk);
 	return error;
 }
 
-int git_fetch_download_pack(git_remote *remote)
+int git_fetch_download_pack(git_remote *remote, git_repository *repo)
 {
-	/*
-	 * First, we ignore any ACKs we receive and wait for a NACK
-	 */
-	return GIT_ENOTIMPLEMENTED;
+	return git_transport_download_pack(remote->transport, repo);
 }
diff --git a/src/pkt.c b/src/pkt.c
index 61c011c..fa0ee77 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -52,9 +52,12 @@ static int flush_pkt(git_pkt **out)
 	return GIT_SUCCESS;
 }
 
-static int ack_pkt(git_pkt **out, const char *line, size_t len)
+/* the rest of the line will be useful for multi_ack */
+static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len))
 {
 	git_pkt *pkt;
+	GIT_UNUSED_ARG(line);
+	GIT_UNUSED_ARG(len);
 
 	pkt = git__malloc(sizeof(git_pkt));
 	if (pkt == NULL)
@@ -66,7 +69,7 @@ static int ack_pkt(git_pkt **out, const char *line, size_t len)
 	return GIT_SUCCESS;
 }
 
-static int nack_pkt(git_pkt **out)
+static int nak_pkt(git_pkt **out)
 {
 	git_pkt *pkt;
 
@@ -74,7 +77,21 @@ static int nack_pkt(git_pkt **out)
 	if (pkt == NULL)
 		return GIT_ENOMEM;
 
-	pkt->type = GIT_PKT_NACK;
+	pkt->type = GIT_PKT_NAK;
+	*out = pkt;
+
+	return GIT_SUCCESS;
+}
+
+static int pack_pkt(git_pkt **out)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	if (pkt == NULL)
+		return GIT_ENOMEM;
+
+	pkt->type = GIT_PKT_PACK;
 	*out = pkt;
 
 	return GIT_SUCCESS;
@@ -184,6 +201,15 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
 
 	error = parse_len(line);
 	if (error < GIT_SUCCESS) {
+		/*
+		 * If we fail to parse the length, it might be because the
+		 * server is trying to send us the packfile already.
+		 */
+		if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
+			*out = line;
+			return pack_pkt(head);
+		}
+
 		return git__throw(error, "Failed to parse pkt length");
 	}
 
@@ -216,8 +242,8 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
 	/* 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 if (!git__prefixcmp(line, "NAK"))
+		error = nak_pkt(head);
 	else
 		error = ref_pkt(head, line, len);
 
@@ -266,11 +292,10 @@ int git_pkt_send_wants(git_headarray *refs, int fd)
 			continue;
 
 		git_oid_fmt(buf + STRLEN(WANT_PREFIX), &head->oid);
-		printf("would send %s", buf);
+		gitno_send(fd, buf, STRLEN(buf), 0);
 	}
 
-	/* TODO: git_pkt_send_flush(fd) */
-	printf("Would send 0000\n");
+	git_pkt_send_flush(fd);
 
 	return ret;
 }
@@ -283,19 +308,15 @@ int git_pkt_send_wants(git_headarray *refs, int fd)
 
 int git_pkt_send_have(git_oid *oid, int fd)
 {
-	int ret = GIT_SUCCESS;
 	char buf[] = "0032have 0000000000000000000000000000000000000000\n";
 
 	git_oid_fmt(buf + STRLEN(HAVE_PREFIX), oid);
-	printf("would send %s", buf);
-
-	return ret;
+	return gitno_send(fd, buf, STRLEN(buf), 0);
 }
 
-int git_pkt_send_have(int fd)
+int git_pkt_send_done(int fd)
 {
 	char buf[] = "0009done\n";
-	printf("Would send %s", buf);
 
-	return GIT_SUCCESS;
+	return gitno_send(fd, buf, STRLEN(buf), 0);
 }
diff --git a/src/pkt.h b/src/pkt.h
index b5d2a17..3a8fac5 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -35,7 +35,8 @@ enum git_pkt_type {
 	GIT_PKT_REF,
 	GIT_PKT_HAVE,
 	GIT_PKT_ACK,
-	GIT_PKT_NACK,
+	GIT_PKT_NAK,
+	GIT_PKT_PACK,
 };
 
 /* Used for multi-ack */
@@ -74,6 +75,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_have(git_oid *oid, int fd);
 void git_pkt_free(git_pkt *pkt);
diff --git a/src/transport.c b/src/transport.c
index 6098989..b058334 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -90,10 +90,21 @@ int git_transport_send_have(struct git_transport *transport, git_oid *oid)
 	return transport->send_have(transport, oid);
 }
 
+int git_transport_send_flush(struct git_transport *transport)
+{
+	return transport->send_flush(transport);
+}
+
 int git_transport_send_done(struct git_transport *transport)
 {
 	return transport->send_done(transport);
 }
+
+int git_transport_download_pack(git_transport *transport, git_repository *repo)
+{
+	return transport->download_pack(transport, repo);
+}
+
 int git_transport_close(git_transport *transport)
 {
 	return transport->close(transport);
diff --git a/src/transport.h b/src/transport.h
index 097b9ac..ed07b78 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -65,6 +65,18 @@ struct git_transport {
 	 */
 	int (*send_have)(struct git_transport *transport, git_oid *oid);
 	/**
+	 * Send a 'done' message
+	 */
+	int (*send_done)(struct git_transport *transport);
+	/**
+	 * Send a flush
+	 */
+	int (*send_flush)(struct git_transport *transport);
+	/**
+	 * Download the packfile
+	 */
+	int (*download_pack)(struct git_transport *transport, git_repository *repo);
+	/**
 	 * Fetch the changes
 	 */
 	int (*fetch)(struct git_transport *transport);
@@ -85,5 +97,7 @@ 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);
+int git_transport_send_flush(struct git_transport *transport);
+int git_transport_download_pack(git_transport *transport, git_repository *repo);
 
 #endif
diff --git a/src/transport_git.c b/src/transport_git.c
index 4b183ed..b8b1fdd 100644
--- a/src/transport_git.c
+++ b/src/transport_git.c
@@ -33,6 +33,8 @@
 #include "pkt.h"
 #include "common.h"
 #include "netops.h"
+#include "filebuf.h"
+#include "repository.h"
 
 typedef struct {
 	git_transport parent;
@@ -288,6 +290,13 @@ static int git_send_have(git_transport *transport, git_oid *oid)
 	return git_pkt_send_have(oid, t->socket);
 }
 
+static int git_send_flush(git_transport *transport)
+{
+	transport_git *t = (transport_git *) transport;
+
+	return git_pkt_send_flush(t->socket);
+}
+
 static int git_send_done(git_transport *transport)
 {
 	transport_git *t = (transport_git *) transport;
@@ -295,6 +304,88 @@ static int git_send_done(git_transport *transport)
 	return git_pkt_send_done(t->socket);
 }
 
+static int store_pack(gitno_buffer *buf, git_repository *repo)
+{
+	git_filebuf file;
+	int error;
+	char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-XXXX.pack\0";
+	off_t off = 0;
+
+	memcpy(path, repo->path_repository, GIT_PATH_MAX - off);
+	off += strlen(repo->path_repository);
+	memcpy(path + off, suff, GIT_PATH_MAX - off - STRLEN(suff));
+
+	error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+	while (1) {
+		if (buf->offset == 0)
+			break;
+
+		error = git_filebuf_write(&file, buf->data, buf->offset);
+		if (error < GIT_SUCCESS)
+			goto cleanup;
+
+		gitno_consume_n(buf, buf->offset);
+	}
+
+cleanup:
+	if (error < GIT_SUCCESS)
+		git_filebuf_cleanup(&file);
+	return error;
+}
+
+static int git_download_pack(git_transport *transport, git_repository *repo)
+{
+	transport_git *t = (transport_git *) transport;
+	int s = t->socket, error = GIT_SUCCESS, pack = 0;
+	gitno_buffer buf;
+	char buffer[1024];
+	git_pkt *pkt;
+	const char *line_end, *ptr;
+
+	gitno_buffer_setup(&buf, buffer, sizeof(buffer), s);
+	/*
+	 * First, we ignore any ACKs and wait for a NACK
+	 */
+	while (1) {
+		error = gitno_recv(&buf);
+		if (error < GIT_SUCCESS)
+			return git__rethrow(GIT_EOSERR, "Failed to receive data");
+		if (error < GIT_SUCCESS) /* Orderly shutdown */
+			return GIT_SUCCESS;
+
+		ptr = buf.data;
+		/* Whilst we're searching for the pack */
+		while (!pack) {
+			if (buf.offset == 0)
+				break;
+			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
+			if (error == GIT_ESHORTBUFFER)
+				break;
+			if (error < GIT_SUCCESS)
+				return error;
+
+			gitno_consume(&buf, line_end);
+			if (pkt->type == GIT_PKT_PACK)
+				pack = 1;
+			/* For now we don't care about anything */
+			free(pkt);
+		}
+
+		/*
+		 * No we have the packet, let's just put anything we get now
+		 * into a packfile
+		 */
+
+		return store_pack(&buf, repo);
+	}
+
+	return error;
+}
+
+
 static int git_close(git_transport *transport)
 {
 	transport_git *t = (transport_git*) transport;
@@ -341,7 +432,9 @@ int git_transport_git(git_transport **out)
 	t->parent.ls = git_ls;
 	t->parent.send_wants = git_send_wants;
 	t->parent.send_have = git_send_have;
+	t->parent.send_flush = git_send_flush;
 	t->parent.send_done = git_send_done;
+	t->parent.download_pack = git_download_pack;
 	t->parent.close = git_close;
 	t->parent.free = git_free;