diff options
-rw-r--r-- | src/fetch.c | 50 | ||||
-rw-r--r-- | src/fetch.h | 1 | ||||
-rw-r--r-- | src/transport.c | 5 | ||||
-rw-r--r-- | src/transport.h | 6 | ||||
-rw-r--r-- | src/transport_git.c | 67 |
5 files changed, 83 insertions, 46 deletions
diff --git a/src/fetch.c b/src/fetch.c index 044d4c9cc..03febe279 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -98,14 +98,8 @@ cleanup: */ int git_fetch_negotiate(git_remote *remote) { - git_revwalk *walk; int error; - unsigned int i; - git_reference *ref; - git_strarray refs; git_headarray *list = &remote->refs; - git_repository *repo = remote->repo; - git_oid oid; error = filter_wants(remote); if (error < GIT_SUCCESS) @@ -119,47 +113,11 @@ int git_fetch_negotiate(git_remote *remote) * what we want and what we have. */ remote->need_pack = 1; - git_transport_send_wants(remote->transport, list); - - error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); - if (error < GIT_ERROR) - return git__rethrow(error, "Failed to list all references"); - - error = git_revwalk_new(&walk, repo); - if (error < GIT_ERROR) { - error = git__rethrow(error, "Failed to list all references"); - goto cleanup; - } - git_revwalk_sorting(walk, GIT_SORT_TIME); - - 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); - - 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() */ - git_transport_send_flush(remote->transport); - git_transport_send_done(remote->transport); + error = git_transport_send_wants(remote->transport, list); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to send want list"); -cleanup: - git_revwalk_free(walk); - return error; + return git_transport_negotiate_fetch(remote->transport, remote->repo, &remote->refs); } int git_fetch_download_pack(char **out, git_remote *remote) diff --git a/src/fetch.h b/src/fetch.h index 2856f12ee..ad4451ffe 100644 --- a/src/fetch.h +++ b/src/fetch.h @@ -2,5 +2,6 @@ #define INCLUDE_fetch_h__ int git_fetch_negotiate(git_remote *remote); +int git_fetch_download_pack(char **out, git_remote *remote); #endif diff --git a/src/transport.c b/src/transport.c index 1bd0c4e9e..91723df73 100644 --- a/src/transport.c +++ b/src/transport.c @@ -90,6 +90,11 @@ int git_transport_send_have(struct git_transport *transport, git_oid *oid) return transport->send_have(transport, oid); } +int git_transport_negotiate_fetch(struct git_transport *transport, git_repository *repo, git_headarray *list) +{ + return transport->negotiate_fetch(transport, repo, list); +} + int git_transport_send_flush(struct git_transport *transport) { return transport->send_flush(transport); diff --git a/src/transport.h b/src/transport.h index b14684bcb..69bec4c66 100644 --- a/src/transport.h +++ b/src/transport.h @@ -73,6 +73,11 @@ struct git_transport { */ int (*send_done)(struct git_transport *transport); /** + * Negotiate the minimal amount of objects that need to be + * retrieved + */ + int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, git_headarray *list); + /** * Send a flush */ int (*send_flush)(struct git_transport *transport); @@ -99,6 +104,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_negotiate_fetch(struct git_transport *transport, git_repository *repo, 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); diff --git a/src/transport_git.c b/src/transport_git.c index 60958c9cc..bb759a1fe 100644 --- a/src/transport_git.c +++ b/src/transport_git.c @@ -27,6 +27,8 @@ #include "git2/common.h" #include "git2/types.h" #include "git2/errors.h" +#include "git2/net.h" +#include "git2/revwalk.h" #include "vector.h" #include "transport.h" @@ -325,6 +327,70 @@ static int git_send_have(git_transport *transport, git_oid *oid) return git_pkt_send_have(oid, t->socket); } +static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *list) +{ + transport_git *t = (transport_git *) transport; + git_revwalk *walk; + git_reference *ref; + git_strarray refs; + git_oid oid; + int error; + unsigned int i; + + error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); + if (error < GIT_ERROR) + return git__rethrow(error, "Failed to list all references"); + + error = git_revwalk_new(&walk, repo); + if (error < GIT_ERROR) { + error = git__rethrow(error, "Failed to list all references"); + goto cleanup; + } + git_revwalk_sorting(walk, GIT_SORT_TIME); + + 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); + + /* + * We don't support any kind of ACK extensions, so the negotiation + * boils down to sending what we have and listening for an ACK + * every once in a while. + */ + i = 0; + while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) { + error = git_pkt_send_have(&oid, t->socket); + i++; + /* + * This is a magic number so we don't flood the server. We + * should check every once in a while to see if the server has + * sent an ACK. + */ + if (i % 160 == 0) + break; + } + if (error == GIT_EREVWALKOVER) + error = GIT_SUCCESS; + + git_pkt_send_flush(t->socket); + git_pkt_send_done(t->socket); + +cleanup: + git_revwalk_free(walk); + return error; +} + static int git_send_flush(git_transport *transport) { transport_git *t = (transport_git *) transport; @@ -476,6 +542,7 @@ 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.negotiate_fetch = git_negotiate_fetch; t->parent.send_flush = git_send_flush; t->parent.send_done = git_send_done; t->parent.download_pack = git_download_pack; |