summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fetch.c32
-rw-r--r--src/pkt.c51
-rw-r--r--src/pkt.h4
-rw-r--r--src/transport.c11
-rw-r--r--src/transport.h14
-rw-r--r--src/transport_git.c93
6 files changed, 165 insertions, 40 deletions
diff --git a/src/fetch.c b/src/fetch.c
index 61bcc1542..522625ef0 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 61c011c6c..fa0ee7701 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 b5d2a17da..3a8fac5e1 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 6098989a8..b05833433 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 097b9ac0a..ed07b782d 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 4b183ed52..b8b1fdd54 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;