summaryrefslogtreecommitdiff
path: root/src/libgit2/transports/smart_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libgit2/transports/smart_protocol.c')
-rw-r--r--src/libgit2/transports/smart_protocol.c135
1 files changed, 122 insertions, 13 deletions
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
index 0d47acafe..488ef07c0 100644
--- a/src/libgit2/transports/smart_protocol.c
+++ b/src/libgit2/transports/smart_protocol.c
@@ -243,6 +243,12 @@ int git_smart__detect_caps(
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_SHALLOW)) {
+ caps->common = caps->shallow = 1;
+ ptr += strlen(GIT_CAP_SHALLOW);
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
@@ -250,13 +256,20 @@ int git_smart__detect_caps(
return 0;
}
-static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf)
+static int recv_pkt(
+ git_pkt **out_pkt,
+ git_pkt_type *out_type,
+ transport_smart *t,
+ gitno_buffer *buf)
{
const char *ptr = buf->data, *line_end = ptr;
git_pkt *pkt = NULL;
git_pkt_parse_data pkt_parse_data = { 0 };
int error = 0, ret;
+ pkt_parse_data.oid_type = t->owner->repo->oid_type;
+ pkt_parse_data.seen_capabilities = 1;
+
do {
if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset, &pkt_parse_data);
@@ -297,7 +310,7 @@ static int store_common(transport_smart *t)
int error;
do {
- if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
+ if ((error = recv_pkt(&pkt, NULL, t, buf)) < 0)
return error;
if (pkt->type != GIT_PKT_ACK) {
@@ -314,7 +327,7 @@ static int store_common(transport_smart *t)
return 0;
}
-static int wait_while_ack(gitno_buffer *buf)
+static int wait_while_ack(transport_smart *t, gitno_buffer *buf)
{
int error;
git_pkt *pkt = NULL;
@@ -323,7 +336,7 @@ static int wait_while_ack(gitno_buffer *buf)
while (1) {
git_pkt_free(pkt);
- if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
+ if ((error = recv_pkt(&pkt, NULL, t, buf)) < 0)
return error;
if (pkt->type == GIT_PKT_NAK)
@@ -344,7 +357,48 @@ static int wait_while_ack(gitno_buffer *buf)
return 0;
}
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
+static int cap_not_sup_err(const char *cap_name)
+{
+ git_error_set(GIT_ERROR_NET, "server doesn't support %s", cap_name);
+ return GIT_EINVALID;
+}
+
+/* Disables server capabilities we're not interested in */
+static int setup_caps(
+ transport_smart_caps *caps,
+ const git_fetch_negotiation *wants)
+{
+ if (wants->depth > 0) {
+ if (!caps->shallow)
+ return cap_not_sup_err(GIT_CAP_SHALLOW);
+ } else {
+ caps->shallow = 0;
+ }
+
+ return 0;
+}
+
+static int setup_shallow_roots(
+ git_array_oid_t *out,
+ const git_fetch_negotiation *wants)
+{
+ git_array_clear(*out);
+
+ if (wants->shallow_roots_len > 0) {
+ git_array_init_to_size(*out, wants->shallow_roots_len);
+ GIT_ERROR_CHECK_ALLOC(out->ptr);
+
+ memcpy(out->ptr, wants->shallow_roots,
+ sizeof(git_oid) * wants->shallow_roots_len);
+ }
+
+ return 0;
+}
+
+int git_smart__negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *wants)
{
transport_smart *t = (transport_smart *)transport;
git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
@@ -356,7 +410,11 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
unsigned int i;
git_oid oid;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = setup_caps(&t->caps, wants)) < 0 ||
+ (error = setup_shallow_roots(&t->shallow_roots, wants)) < 0)
+ return error;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
return error;
if ((error = git_revwalk_new(&walk, repo)) < 0)
@@ -366,6 +424,37 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = git_revwalk__push_glob(walk, "refs/*", &opts)) < 0)
goto on_error;
+ if (wants->depth > 0) {
+ git_pkt_shallow *pkt;
+
+ if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+ goto on_error;
+
+ while ((error = recv_pkt((git_pkt **)&pkt, NULL, t, buf)) == 0) {
+ bool complete = false;
+
+ if (pkt->type == GIT_PKT_SHALLOW) {
+ error = git_oidarray__add(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_UNSHALLOW) {
+ git_oidarray__remove(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* Server is done, stop processing shallow oids */
+ complete = true;
+ } else {
+ git_error_set(GIT_ERROR_NET, "unexpected packet type");
+ error = -1;
+ }
+
+ git_pkt_free((git_pkt *) pkt);
+
+ if (complete || error < 0)
+ break;
+ }
+
+ if (error < 0)
+ goto on_error;
+ }
+
/*
* Our support for ACK extensions is simply to parse them. On
* the first ACK we will accept that as enough common
@@ -406,7 +495,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
if ((error = store_common(t)) < 0)
goto on_error;
} else {
- if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
+ if ((error = recv_pkt(NULL, &pkt_type, t, buf)) < 0)
goto on_error;
if (pkt_type == GIT_PKT_ACK) {
@@ -428,7 +517,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int j;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, j, pkt) {
@@ -448,7 +537,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int j;
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, j, pkt) {
@@ -466,10 +555,11 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
goto on_error;
if (t->cancelled.val) {
- git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
+ git_error_set(GIT_ERROR_NET, "the fetch was cancelled");
error = GIT_EUSER;
goto on_error;
}
+
if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
goto on_error;
@@ -478,7 +568,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
/* Now let's eat up whatever the server gives us */
if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
- if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
+ if ((error = recv_pkt(NULL, &pkt_type, t, buf)) < 0)
return error;
if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
@@ -486,7 +576,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return -1;
}
} else {
- error = wait_while_ack(buf);
+ error = wait_while_ack(t, buf);
}
return error;
@@ -497,6 +587,25 @@ on_error:
return error;
}
+int git_smart__shallow_roots(git_oidarray *out, git_transport *transport)
+{
+ transport_smart *t = (transport_smart *)transport;
+ size_t len;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&len, t->shallow_roots.size, sizeof(git_oid));
+
+ out->count = t->shallow_roots.size;
+
+ if (len) {
+ out->ids = git__malloc(len);
+ memcpy(out->ids, t->shallow_roots.ptr, len);
+ } else {
+ out->ids = NULL;
+ }
+
+ return 0;
+}
+
static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_indexer_progress *stats)
{
int recvd;
@@ -602,7 +711,7 @@ int git_smart__download_pack(
goto done;
}
- if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
+ if ((error = recv_pkt(&pkt, NULL, t, buf)) >= 0) {
/* Check cancellation after network call */
if (t->cancelled.val) {
git_error_clear();