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 @@ -64,6 +64,18 @@ struct git_transport { * Send the list of 'have' refs */ 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 */ @@ -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;