Commit 7a520f5d8af2aedd5693bf7314527d76d9af2ef4

Carlos Martín Nieto 2012-04-13T23:19:38

fetch: use the streaming indexer when downloading a pack This changes the git_remote_download() API, but the existing one is silly, so you don't get to complain. The new API allows to know how much data has been downloaded, how many objects we expect in total and how many we've processed.

diff --git a/include/git2/remote.h b/include/git2/remote.h
index e6537ec..576f584 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -150,7 +150,7 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void 
  * @param filename where to store the temproray filename
  * @return GIT_SUCCESS or an error code
  */
-GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote);
+GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
 
 /**
  * Check whether the remote is connected
diff --git a/src/fetch.c b/src/fetch.c
index 57a6d02..8da4fd8 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -9,6 +9,7 @@
 #include "git2/oid.h"
 #include "git2/refs.h"
 #include "git2/revwalk.h"
+#include "git2/indexer.h"
 
 #include "common.h"
 #include "transport.h"
@@ -101,30 +102,27 @@ int git_fetch_negotiate(git_remote *remote)
 	return t->negotiate_fetch(t, remote->repo, &remote->refs);
 }
 
-int git_fetch_download_pack(char **out, git_remote *remote)
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
 {
-	if(!remote->need_pack) {
-		*out = NULL;
+	if(!remote->need_pack)
 		return 0;
-	}
 
-	return remote->transport->download_pack(out, remote->transport, remote->repo);
+	return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
 }
 
 /* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
 int git_fetch__download_pack(
-	char **out,
 	const char *buffered,
 	size_t buffered_size,
 	GIT_SOCKET fd,
-	git_repository *repo)
+	git_repository *repo,
+	git_off_t *bytes,
+	git_indexer_stats *stats)
 {
-	git_filebuf file = GIT_FILEBUF_INIT;
-	int error;
+	int recvd;
 	char buff[1024];
-	git_buf path = GIT_BUF_INIT;
-	static const char suff[] = "/objects/pack/pack-received";
 	gitno_buffer buf;
+	git_indexer_stream *idx;
 
 	gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
 
@@ -133,41 +131,33 @@ int git_fetch__download_pack(
 		return -1;
 	}
 
-	if (git_buf_joinpath(&path, repo->path_repository, suff) < 0)
-		goto on_error;
+	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+		return -1;
 
-	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY) < 0)
+	memset(stats, 0, sizeof(git_indexer_stats));
+	if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
 		goto on_error;
 
-	/* Part of the packfile has been received, don't loose it */
-	if (git_filebuf_write(&file, buffered, buffered_size) < 0)
-		goto on_error;
+	*bytes = buffered_size;
 
-	while (1) {
-		if (git_filebuf_write(&file, buf.data, buf.offset) < 0)
+	do {
+		if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
 			goto on_error;
 
 		gitno_consume_n(&buf, buf.offset);
-		error = gitno_recv(&buf);
-		if (error < GIT_SUCCESS)
+		if ((recvd = gitno_recv(&buf)) < 0)
 			goto on_error;
-		if (error == 0) /* Orderly shutdown */
-			break;
-	}
 
-	*out = git__strdup(file.path_lock);
-	if (*out == NULL)
-		goto on_error;
+		*bytes += recvd;
+	} while(recvd > 0);
 
-	/* A bit dodgy, but we need to keep the pack at the temporary path */
-	if (git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE) < 0)
+	if (git_indexer_stream_finalize(idx, stats))
 		goto on_error;
 
-	git_buf_free(&path);
-
+	git_indexer_stream_free(idx);
 	return 0;
+
 on_error:
-	git_buf_free(&path);
-	git_filebuf_cleanup(&file);
+	git_indexer_stream_free(idx);
 	return -1;
 }
diff --git a/src/fetch.h b/src/fetch.h
index c1ab840..03767be 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -10,9 +10,9 @@
 #include "netops.h"
 
 int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(char **out, git_remote *remote);
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
 
-int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
-                             GIT_SOCKET fd, git_repository *repo);
+int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
+			     git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
 
 #endif
diff --git a/src/remote.c b/src/remote.c
index b48a233..bbb491d 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -297,16 +297,16 @@ int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
 	return remote->transport->ls(remote->transport, list_cb, payload);
 }
 
-int git_remote_download(char **filename, git_remote *remote)
+int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
 {
 	int error;
 
-	assert(filename && remote);
+	assert(remote && bytes && stats);
 
 	if ((error = git_fetch_negotiate(remote)) < 0)
 		return error;
 
-	return git_fetch_download_pack(filename, remote);
+	return git_fetch_download_pack(remote, bytes, stats);
 }
 
 int git_remote_update_tips(git_remote *remote)
diff --git a/src/transport.h b/src/transport.h
index 4c12357..1cea32b 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -81,7 +81,7 @@ struct git_transport {
 	/**
 	 * Download the packfile
 	 */
-	int (*download_pack)(char **out, struct git_transport *transport, git_repository *repo);
+	int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
 	/**
 	 * Fetch the changes
 	 */
diff --git a/src/transports/git.c b/src/transports/git.c
index 825f072..62106de 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -382,7 +382,7 @@ static int git_send_done(git_transport *transport)
 	return git_pkt_send_done(t->socket);
 }
 
-static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
 {
 	transport_git *t = (transport_git *) transport;
 	int error = 0, read_bytes;
@@ -410,7 +410,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
 
 			if (pkt->type == GIT_PKT_PACK) {
 				git__free(pkt);
-				return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo);
+				return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
 			}
 
 			/* For now we don't care about anything */
diff --git a/src/transports/http.c b/src/transports/http.c
index 0938fef..e6a7094 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -529,7 +529,8 @@ cleanup:
 }
 
 typedef struct {
-	git_filebuf *file;
+	git_indexer_stream *idx;
+	git_indexer_stats *stats;
 	transport_http *transport;
 } download_pack_cbdata;
 
@@ -545,10 +546,10 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
 {
 	download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
 	transport_http *t = data->transport;
-	git_filebuf *file = data->file;
+	git_indexer_stream *idx = data->idx;
+	git_indexer_stats *stats = data->stats;
 
-
-	return t->error = git_filebuf_write(file, str, len);
+	return t->error = git_indexer_stream_add(idx, str, len, stats);
 }
 
 /*
@@ -557,80 +558,68 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
  * the simple downloader. Furthermore, we're using keep-alive
  * connections, so the simple downloader would just hang.
  */
-static int http_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
 {
 	transport_http *t = (transport_http *) transport;
 	git_buf *oldbuf = &t->buf;
-	int ret = 0;
+	int recvd;
 	http_parser_settings settings;
 	char buffer[1024];
 	gitno_buffer buf;
+	git_indexer_stream *idx = NULL;
 	download_pack_cbdata data;
-	git_filebuf file = GIT_FILEBUF_INIT;
-	git_buf path = GIT_BUF_INIT;
-	char suff[] = "/objects/pack/pack-received\0";
+
+	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+
+	if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
+		giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
+		return -1;
+	}
+
+	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+		return -1;
+
 
 	/*
 	 * This is part of the previous response, so we don't want to
 	 * re-init the parser, just set these two callbacks.
 	 */
-	data.file = &file;
+	memset(stats, 0, sizeof(git_indexer_stats));
+	data.stats = stats;
+	data.idx = idx;
 	data.transport = t;
 	t->parser.data = &data;
 	t->transfer_finished = 0;
 	memset(&settings, 0x0, sizeof(settings));
 	settings.on_message_complete = on_message_complete_download_pack;
 	settings.on_body = on_body_download_pack;
+	*bytes = oldbuf->size;
 
-	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
-
-	if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
-		giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
-		return -1;
-	}
-
-	if (git_buf_joinpath(&path, repo->path_repository, suff) < 0)
-		goto on_error;
-
-	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY) < 0)
-		goto on_error;
-
-	/* Part of the packfile has been received, don't loose it */
-	if (git_filebuf_write(&file, oldbuf->ptr, oldbuf->size) < 0)
+	if (git_indexer_stream_add(idx, oldbuf->ptr, oldbuf->size, stats) < 0)
 		goto on_error;
 
-	while(1) {
+	do {
 		size_t parsed;
 
-		ret = gitno_recv(&buf);
-		if (ret < 0)
+		if ((recvd = gitno_recv(&buf)) < 0)
 			goto on_error;
 
 		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
-		/* Both should happen at the same time */
 		if (parsed != buf.offset || t->error < 0)
-			return t->error;
+			goto on_error;
 
+		*bytes += recvd;
 		gitno_consume_n(&buf, parsed);
+	} while (recvd > 0 && !t->transfer_finished);
 
-		if (ret == 0 || t->transfer_finished) {
-			break;
-		}
-	}
-
-	*out = git__strdup(file.path_lock);
-	GITERR_CHECK_ALLOC(*out);
-
-	/* A bit dodgy, but we need to keep the pack at the temporary path */
-	ret = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
-
-	git_buf_free(&path);
+	if (git_indexer_stream_finalize(idx, stats) < 0)
+		goto on_error;
 
+	git_indexer_stream_free(idx);
 	return 0;
 
 on_error:
-	git_filebuf_cleanup(&file);
-	git_buf_free(&path);
+	git_indexer_stream_free(idx);
 	return -1;
 }