summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fetch.c50
-rw-r--r--src/fetch.h1
-rw-r--r--src/transport.c5
-rw-r--r--src/transport.h6
-rw-r--r--src/transport_git.c67
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;