Actually implement object negotiation Only signal that we need a pack if we do need it and don't send a want just because it's the first. If we don't need to download the pack, then we can skip all of the negotiation and just return success. Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
diff --git a/src/fetch.c b/src/fetch.c
index 03febe2..0dce875 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -75,6 +75,8 @@ static int filter_wants(git_remote *remote)
/* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(repo->db, &head->oid))
head->local = 1;
+ else
+ remote->need_pack = 1;
error = git_vector_insert(&list, head);
if (error < GIT_SUCCESS)
@@ -108,11 +110,13 @@ int git_fetch_negotiate(git_remote *remote)
/* Don't try to negotiate when we don't want anything */
if (list->len == 0)
return GIT_SUCCESS;
+ if (!remote->need_pack)
+ return GIT_SUCCESS;
+
/*
* Now we have everything set up so we can start tell the server
* what we want and what we have.
*/
- remote->need_pack = 1;
error = git_transport_send_wants(remote->transport, list);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to send want list");
diff --git a/src/netops.c b/src/netops.c
index 55cb7e4..8126bce 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -55,7 +55,7 @@ int gitno_recv(gitno_buffer *buf)
ret = recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
if (ret < 0)
- return git__throw(GIT_EOSERR, "Failed to receive data");
+ return git__throw(GIT_EOSERR, "Failed to receive data: %s", strerror(errno));
if (ret == 0) /* Orderly shutdown, so exit */
return GIT_SUCCESS;
diff --git a/src/pkt.c b/src/pkt.c
index 419fb9c..5370111 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -299,8 +299,8 @@ static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps,
int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd)
{
- unsigned int i;
- int ret = GIT_SUCCESS;
+ unsigned int i = 0;
+ int error = GIT_SUCCESS;
char buf[STRLEN(WANT_PREFIX) + GIT_OID_HEXSZ + 2];
git_remote_head *head;
@@ -308,26 +308,35 @@ int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd)
buf[sizeof(buf) - 2] = '\n';
buf[sizeof(buf) - 1] = '\0';
- if (refs->len > 0 && caps->common) {
- /* Some capabilities are active, so we need to send what we support */
- send_want_with_caps(refs->heads[0], caps, fd);
- i = 1;
- } else {
- i = 0;
+ /* If there are common caps, find the first one */
+ if (caps->common) {
+ for (; i < refs->len; ++i) {
+ head = refs->heads[i];
+ if (head->local)
+ continue;
+ else
+ break;
+ }
+
+ error = send_want_with_caps(refs->heads[i], caps, fd);
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to send want pkt with caps");
+ /* Increase it here so it's correct whether we run this or not */
+ i++;
}
+ /* Continue from where we left off */
for (; i < refs->len; ++i) {
head = refs->heads[i];
if (head->local)
continue;
git_oid_fmt(buf + STRLEN(WANT_PREFIX), &head->oid);
- gitno_send(fd, buf, STRLEN(buf), 0);
+ error = gitno_send(fd, buf, STRLEN(buf), 0);
+ return git__rethrow(error, "Failed to send want pkt");
}
- git_pkt_send_flush(fd);
-
- return ret;
+ return git_pkt_send_flush(fd);
}
/*
diff --git a/src/transport_git.c b/src/transport_git.c
index bb759a1..0e50a08 100644
--- a/src/transport_git.c
+++ b/src/transport_git.c
@@ -327,7 +327,7 @@ 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)
+static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *GIT_UNUSED(list))
{
transport_git *t = (transport_git *) transport;
git_revwalk *walk;
@@ -336,6 +336,11 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g
git_oid oid;
int error;
unsigned int i;
+ char buff[128];
+ gitno_buffer buf;
+ GIT_UNUSED_ARG(list);
+
+ gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);
error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
if (error < GIT_ERROR)
@@ -349,12 +354,18 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g
git_revwalk_sorting(walk, GIT_SORT_TIME);
for (i = 0; i < refs.count; ++i) {
+ /* No tags */
+ if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
+ continue;
+
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;
}
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
+ continue;
error = git_revwalk_push(walk, git_reference_oid(ref));
if (error < GIT_ERROR) {
error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
@@ -372,17 +383,42 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g
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 (i % 20 == 0) {
+ const char *ptr = buf.data, *line_end;
+ git_pkt *pkt;
+ git_pkt_send_flush(t->socket);
+ while (1) {
+ error = gitno_recv(&buf);
+ if (error < GIT_SUCCESS) {
+ error = git__rethrow(error, "Error receiving data");
+ goto cleanup;
+ }
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
+ if (error == GIT_ESHORTBUFFER)
+ continue;
+ if (error < GIT_SUCCESS) {
+ error = git__rethrow(error, "Failed to get answer");
+ goto cleanup;
+ }
+
+ gitno_consume(&buf, line_end);
+
+ if (pkt->type == GIT_PKT_ACK) {
+ error = GIT_SUCCESS;
+ goto done;
+ } else if (pkt->type == GIT_PKT_NAK) {
+ break;
+ } else {
+ error = git__throw(GIT_ERROR, "Got unexpected pkt type");
+ goto cleanup;
+ }
+ }
+ }
}
if (error == GIT_EREVWALKOVER)
error = GIT_SUCCESS;
+done:
git_pkt_send_flush(t->socket);
git_pkt_send_done(t->socket);
@@ -426,17 +462,17 @@ static int store_pack(char **out, gitno_buffer *buf, git_repository *repo)
goto cleanup;
while (1) {
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS)
- goto cleanup;
- if (error == 0) /* Orderly shutdown */
- break;
-
+ /* Part of the packfile has been received, don't loose it */
error = git_filebuf_write(&file, buf->data, buf->offset);
if (error < GIT_SUCCESS)
goto cleanup;
gitno_consume_n(buf, buf->offset);
+ error = gitno_recv(buf);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+ if (error == 0) /* Orderly shutdown */
+ break;
}
*out = git__strdup(file.path_lock);
@@ -485,9 +521,9 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
if (error < GIT_SUCCESS)
return error;
- if (pkt->type == GIT_PKT_PACK) {
+ if (pkt->type == GIT_PKT_PACK)
return store_pack(out, &buf, repo);
- }
+
/* For now we don't care about anything */
free(pkt);
gitno_consume(&buf, line_end);