summaryrefslogtreecommitdiff
path: root/src/libgit2/transports
diff options
context:
space:
mode:
Diffstat (limited to 'src/libgit2/transports')
-rw-r--r--src/libgit2/transports/local.c6
-rw-r--r--src/libgit2/transports/smart.c28
-rw-r--r--src/libgit2/transports/smart.h23
-rw-r--r--src/libgit2/transports/smart_pkt.c90
-rw-r--r--src/libgit2/transports/smart_protocol.c62
5 files changed, 189 insertions, 20 deletions
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
index 6c754a034..24f49cc65 100644
--- a/src/libgit2/transports/local.c
+++ b/src/libgit2/transports/local.c
@@ -284,15 +284,13 @@ static int local_ls(const git_remote_head ***out, size_t *size, git_transport *t
static int local_negotiate_fetch(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count)
+ const git_fetch_negotiation *wants)
{
transport_local *t = (transport_local*)transport;
git_remote_head *rhead;
unsigned int i;
- GIT_UNUSED(refs);
- GIT_UNUSED(count);
+ GIT_UNUSED(wants);
/* Fill in the loids */
git_vector_foreach(&t->refs, i, rhead) {
diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c
index 7f57dba2a..9d1afeb05 100644
--- a/src/libgit2/transports/smart.c
+++ b/src/libgit2/transports/smart.c
@@ -482,3 +482,31 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
*out = (git_transport *) t;
return 0;
}
+
+size_t git_shallowarray_count(git_shallowarray *array)
+{
+ return git_array_size(array->array);
+}
+
+const git_oid * git_shallowarray_get(git_shallowarray *array, size_t idx)
+{
+ return git_array_get(array->array, idx);
+}
+
+int git_shallowarray_add(git_shallowarray *array, git_oid *oid)
+{
+ size_t oid_index;
+ if (git_array_search(&oid_index, array->array, (git_array_compare_cb)git_oid_cmp, &oid) < 0) {
+ git_oid *tmp = git_array_alloc(array->array);
+ git_oid_cpy(tmp, oid);
+ }
+ return 0;
+}
+
+int git_shallowarray_remove(git_shallowarray *array, git_oid *oid)
+{
+ GIT_UNUSED(array);
+ GIT_UNUSED(oid);
+ /* no git_array_remove… meh */
+ return -1;
+}
diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h
index 9323d6c44..bc072d2fe 100644
--- a/src/libgit2/transports/smart.h
+++ b/src/libgit2/transports/smart.h
@@ -14,6 +14,7 @@
#include "netops.h"
#include "push.h"
#include "str.h"
+#include "oidarray.h"
#include "git2/sys/transport.h"
#define GIT_SIDE_BAND_DATA 1
@@ -32,6 +33,7 @@
#define GIT_CAP_SYMREF "symref"
#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
+#define GIT_CAP_SHALLOW "shallow"
extern bool git_smart__ofs_delta_enabled;
@@ -48,7 +50,9 @@ typedef enum {
GIT_PKT_PROGRESS,
GIT_PKT_OK,
GIT_PKT_NG,
- GIT_PKT_UNPACK
+ GIT_PKT_UNPACK,
+ GIT_PKT_SHALLOW,
+ GIT_PKT_UNSHALLOW,
} git_pkt_type;
/* Used for multi_ack and multi_ack_detailed */
@@ -120,6 +124,11 @@ typedef struct {
int unpack_ok;
} git_pkt_unpack;
+typedef struct {
+ git_pkt_type type;
+ git_oid oid;
+} git_pkt_shallow;
+
typedef struct transport_smart_caps {
unsigned int common:1,
ofs_delta:1,
@@ -132,7 +141,8 @@ typedef struct transport_smart_caps {
report_status:1,
thin_pack:1,
want_tip_sha1:1,
- want_reachable_sha1:1;
+ want_reachable_sha1:1,
+ shallow:1;
} transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload);
@@ -167,8 +177,7 @@ int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
+ const git_fetch_negotiation *wants);
int git_smart__download_pack(
git_transport *transport,
@@ -186,8 +195,12 @@ int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, si
int git_pkt_buffer_flush(git_str *buf);
int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_str *buf);
-int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_str *buf);
+int git_pkt_buffer_wants(const git_fetch_negotiation *wants, transport_smart_caps *caps, git_str *buf);
int git_pkt_buffer_have(git_oid *oid, git_str *buf);
void git_pkt_free(git_pkt *pkt);
+struct git_shallowarray {
+ git_array_oid_t array;
+};
+
#endif
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
index e679819fa..e368cb7e2 100644
--- a/src/libgit2/transports/smart_pkt.c
+++ b/src/libgit2/transports/smart_pkt.c
@@ -366,6 +366,50 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len)
return 0;
}
+static int shallow_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_shallow *pkt;
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_SHALLOW;
+ line += 7;
+ len -= 7;
+
+ if (len >= GIT_OID_HEXSZ) {
+ git_oid_fromstr(&pkt->oid, line + 1);
+ line += GIT_OID_HEXSZ + 1;
+ len -= GIT_OID_HEXSZ + 1;
+ }
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
+static int unshallow_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_shallow *pkt;
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_UNSHALLOW;
+ line += 9;
+ len -= 9;
+
+ if (len >= GIT_OID_HEXSZ) {
+ git_oid_fromstr(&pkt->oid, line + 1);
+ line += GIT_OID_HEXSZ + 1;
+ len -= GIT_OID_HEXSZ + 1;
+ }
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
static int parse_len(size_t *out, const char *line, size_t linelen)
{
char num[PKT_LEN_SIZE + 1];
@@ -492,6 +536,10 @@ int git_pkt_parse_line(
error = ng_pkt(pkt, line, len);
else if (!git__prefixncmp(line, len, "unpack"))
error = unpack_pkt(pkt, line, len);
+ else if (!git__prefixcmp(line, "shallow"))
+ error = shallow_pkt(pkt, line, len);
+ else if (!git__prefixcmp(line, "unshallow"))
+ error = unshallow_pkt(pkt, line, len);
else
error = ref_pkt(pkt, line, len);
@@ -557,6 +605,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (caps->ofs_delta)
git_str_puts(&str, GIT_CAP_OFS_DELTA " ");
+ if (caps->shallow)
+ git_str_puts(&str, GIT_CAP_SHALLOW " ");
+
if (git_str_oom(&str))
return -1;
@@ -586,8 +637,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
*/
int git_pkt_buffer_wants(
- const git_remote_head * const *refs,
- size_t count,
+ const git_fetch_negotiation *wants,
transport_smart_caps *caps,
git_str *buf)
{
@@ -595,22 +645,22 @@ int git_pkt_buffer_wants(
const git_remote_head *head;
if (caps->common) {
- for (; i < count; ++i) {
- head = refs[i];
+ for (; i < wants->count; ++i) {
+ head = wants->refs[i];
if (!head->local)
break;
}
- if (buffer_want_with_caps(refs[i], caps, buf) < 0)
+ if (buffer_want_with_caps(wants->refs[i], caps, buf) < 0)
return -1;
i++;
}
- for (; i < count; ++i) {
+ for (; i < wants->count; ++i) {
char oid[GIT_OID_SHA1_HEXSIZE];
- head = refs[i];
+ head = wants->refs[i];
if (head->local)
continue;
@@ -622,6 +672,32 @@ int git_pkt_buffer_wants(
return -1;
}
+ /* Tell the server about our shallow objects */
+ for (i = 0; i < git_shallowarray_count(wants->shallow_roots); i++) {
+ char oid[GIT_OID_HEXSZ];
+ git_buf shallow_buf = GIT_BUF_INIT;
+
+ git_oid_fmt(oid, git_shallowarray_get(wants->shallow_roots, i));
+ git_buf_puts(&shallow_buf, "shallow ");
+ git_buf_put(&shallow_buf, oid, GIT_OID_HEXSZ);
+ git_buf_putc(&shallow_buf, '\n');
+
+ git_buf_printf(buf, "%04x%s", (unsigned int)git_buf_len(&shallow_buf) + 4, git_buf_cstr(&shallow_buf));
+
+ if (git_buf_oom(buf))
+ return -1;
+ }
+
+ if (wants->depth > 0) {
+ git_buf deepen_buf = GIT_BUF_INIT;
+
+ git_buf_printf(&deepen_buf, "deepen %d\n", wants->depth);
+ git_buf_printf(buf,"%04x%s", (unsigned int)git_buf_len(&deepen_buf) + 4, git_buf_cstr(&deepen_buf));
+
+ if (git_buf_oom(buf))
+ return -1;
+ }
+
return git_pkt_buffer_flush(buf);
}
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
index 09778b335..cd90db43f 100644
--- a/src/libgit2/transports/smart_protocol.c
+++ b/src/libgit2/transports/smart_protocol.c
@@ -214,6 +214,11 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
if (!git__prefixcmp(ptr, GIT_CAP_WANT_REACHABLE_SHA1)) {
caps->common = caps->want_reachable_sha1 = 1;
ptr += strlen(GIT_CAP_DELETE_REFS);
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_SHALLOW)) {
+ caps->common = caps->shallow = 1;
+ ptr += strlen(GIT_CAP_SHALLOW);
continue;
}
@@ -317,7 +322,26 @@ 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) {
+ if (!caps->shallow)
+ return cap_not_sup_err(GIT_CAP_SHALLOW);
+ } else {
+ caps->shallow = 0;
+ }
+
+ 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;
@@ -329,7 +353,10 @@ 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)
+ return error;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
return error;
if ((error = git_revwalk_new(&walk, repo)) < 0)
@@ -339,6 +366,33 @@ 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, buf)) == 0) {
+
+ if (pkt->type == GIT_PKT_SHALLOW) {
+ printf("shallow %s\n", git_oid_tostr_s(&pkt->oid));
+ git_shallowarray_add(wants->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_UNSHALLOW) {
+ printf("unshallow %s\n", git_oid_tostr_s(&pkt->oid));
+ git_shallowarray_remove(wants->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* Server is done, stop processing shallow oids */
+ break;
+ } else {
+ git_error_set(GIT_ERROR_NET, "Unexpected pkt type");
+ goto on_error;
+ }
+ }
+
+ 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
@@ -401,7 +455,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) {
@@ -421,7 +475,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) {