summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryuangli <yuangli@mathworks.com>2022-07-04 17:08:04 +0100
committeryuangli <yuangli@mathworks.com>2022-07-04 17:08:04 +0100
commitafa79ca05862c24baa0360324ab7b63c0df90689 (patch)
treefbaa0ddf5f5176e5fb8d83ed7e0db72209473c87
parent70a332a51b6364507d19bd1821b22fabbd1f2e08 (diff)
downloadlibgit2-afa79ca05862c24baa0360324ab7b63c0df90689.tar.gz
Merge branch 'pr/tiennou/4747' into transportPR
-rw-r--r--include/git2/remote.h7
-rw-r--r--include/git2/repository.h11
-rw-r--r--include/git2/sys/transport.h17
-rw-r--r--src/array.h4
-rw-r--r--src/fetch.c22
-rw-r--r--src/object.c21
-rw-r--r--src/object.h6
-rw-r--r--src/remote.h1
-rw-r--r--src/repository.c89
-rw-r--r--src/repository.h3
-rw-r--r--src/transports/local.c6
-rw-r--r--src/transports/smart.c28
-rw-r--r--src/transports/smart.h21
-rw-r--r--src/transports/smart_pkt.c90
-rw-r--r--src/transports/smart_protocol.c63
-rw-r--r--tests/clone/shallow.c55
-rw-r--r--tests/repo/grafts.c119
-rw-r--r--tests/repo/shallow.c125
18 files changed, 659 insertions, 29 deletions
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 4d57eaaf7..e577f8a7b 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -738,11 +738,16 @@ typedef struct {
* Extra headers for this fetch operation
*/
git_strarray custom_headers;
+
+ /**
+ * Depth of the fetch to perform
+ */
+ int depth;
} git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION 1
#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \
- GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT }
+ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT, { NULL }, -1 }
/**
* Initialize git_fetch_options structure
diff --git a/include/git2/repository.h b/include/git2/repository.h
index ec8adfb29..bbf3ae4cd 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -921,6 +921,17 @@ GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo);
/**
+ * Determine the shallow roots of the repository
+ *
+ * This oidarray is owned by the library. Do not free it.
+ *
+ * @param out An array of shallow oids.
+ * @param repo The repository
+ * @return 0 on success, an error otherwise.
+ */
+GIT_EXTERN(int) git_repository_shallow_roots(git_oidarray *out, git_repository *repo);
+
+/**
* Retrieve the configured identity to use for reflogs
*
* The memory is owned by the repository and must not be freed by the
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index fee34544f..ea8bcb64b 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -33,6 +33,15 @@ typedef enum {
GIT_TRANSPORTFLAGS_NONE = 0,
} git_transport_flags_t;
+typedef struct git_shallowarray git_shallowarray;
+
+typedef struct {
+ const git_remote_head * const *refs;
+ size_t count;
+ git_shallowarray *shallow_roots;
+ int depth;
+} git_fetch_negotiation;
+
struct git_transport {
unsigned int version; /**< The struct version */
@@ -87,8 +96,7 @@ struct git_transport {
int GIT_CALLBACK(negotiate_fetch)(
git_transport *transport,
git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
+ const git_fetch_negotiation *fetch_data);
/**
* Start downloading the packfile from the remote repository.
@@ -435,6 +443,11 @@ GIT_EXTERN(int) git_smart_subtransport_ssh(
git_transport *owner,
void *param);
+GIT_EXTERN(size_t) git_shallowarray_count(git_shallowarray *array);
+GIT_EXTERN(const git_oid *) git_shallowarray_get(git_shallowarray *array, size_t idx);
+GIT_EXTERN(int) git_shallowarray_add(git_shallowarray *array, git_oid *oid);
+GIT_EXTERN(int) git_shallowarray_remove(git_shallowarray *array, git_oid *oid);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/src/array.h b/src/array.h
index e97688b36..3d6c9113c 100644
--- a/src/array.h
+++ b/src/array.h
@@ -85,12 +85,14 @@ on_oom:
#define git_array_foreach(a, i, element) \
for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
+typedef int (*git_array_compare_cb)(const void *, const void *);
+
GIT_INLINE(int) git_array__search(
size_t *out,
void *array_ptr,
size_t item_size,
size_t array_len,
- int (*compare)(const void *, const void *),
+ git_array_compare_cb compare,
const void *key)
{
size_t lim;
diff --git a/src/fetch.c b/src/fetch.c
index dedbb54fa..e08671db3 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -18,6 +18,7 @@
#include "netops.h"
#include "repository.h"
#include "refs.h"
+#include "transports/smart.h"
static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
{
@@ -128,10 +129,18 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
* Now we have everything set up so we can start tell the
* server what we want and what we have.
*/
+ remote->nego.refs = (const git_remote_head * const *)remote->refs.contents;
+ remote->nego.count = remote->refs.length;
+ remote->nego.depth = opts->depth;
+ remote->nego.shallow_roots = git__malloc(sizeof(git_shallowarray));
+
+ git_array_init(remote->nego.shallow_roots->array);
+
+ git_repository__shallow_roots(&remote->nego.shallow_roots->array, remote->repo);
+
return t->negotiate_fetch(t,
remote->repo,
- (const git_remote_head * const *)remote->refs.contents,
- remote->refs.length);
+ &remote->nego);
}
int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks)
@@ -139,6 +148,7 @@ int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *call
git_transport *t = remote->transport;
git_indexer_progress_cb progress = NULL;
void *payload = NULL;
+ int error;
if (!remote->need_pack)
return 0;
@@ -148,7 +158,13 @@ int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *call
payload = callbacks->payload;
}
- return t->download_pack(t, remote->repo, &remote->stats, progress, payload);
+ if ((error = t->download_pack(t, remote->repo, &remote->stats, progress, payload)) < 0)
+ return error;
+
+ if ((error = git_repository__shallow_roots_write(remote->repo, remote->nego.shallow_roots->array)) < 0)
+ return error;
+
+ return 0;
}
int git_fetch_options_init(git_fetch_options *opts, unsigned int version)
diff --git a/src/object.c b/src/object.c
index 42e1e46bc..b58f01a34 100644
--- a/src/object.c
+++ b/src/object.c
@@ -104,15 +104,13 @@ int git_object__from_raw(
return 0;
}
-int git_object__from_odb_object(
+int git_object__init_from_odb_object(
git_object **object_out,
git_repository *repo,
git_odb_object *odb_obj,
git_object_t type)
{
- int error;
size_t object_size;
- git_object_def *def;
git_object *object = NULL;
GIT_ASSERT_ARG(object_out);
@@ -139,6 +137,23 @@ int git_object__from_odb_object(
object->cached.size = odb_obj->cached.size;
object->repo = repo;
+ *object_out = object;
+ return 0;
+}
+
+int git_object__from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type)
+{
+ int error;
+ git_object_def *def;
+ git_object *object = NULL;
+
+ if ((error = git_object__init_from_odb_object(&object, repo, odb_obj, type)) < 0)
+ return error;
+
/* Parse raw object data */
def = &git_objects_table[odb_obj->cached.type];
GIT_ASSERT(def->free && def->parse);
diff --git a/src/object.h b/src/object.h
index 4b6793612..71a966a92 100644
--- a/src/object.h
+++ b/src/object.h
@@ -35,6 +35,12 @@ int git_object__from_raw(
size_t size,
git_object_t type);
+int git_object__init_from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type);
+
int git_object__from_odb_object(
git_object **object_out,
git_repository *repo,
diff --git a/src/remote.h b/src/remote.h
index ce92db76a..8297af197 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -35,6 +35,7 @@ struct git_remote {
git_remote_autotag_option_t download_tags;
int prune_refs;
int passed_refspecs;
+ git_fetch_negotiation nego;
};
typedef struct git_remote_connection_opts {
diff --git a/src/repository.c b/src/repository.c
index ab5753795..537622273 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -3192,6 +3192,95 @@ int git_repository_state_cleanup(git_repository *repo)
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
+int git_repository__shallow_roots(git_array_oid_t *out, git_repository *repo)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_buf contents = GIT_BUF_INIT;
+ int error, updated, line_num = 1;
+ char *line;
+ char *buffer;
+
+ assert(out && repo);
+
+ if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0)
+ return error;
+
+ error = git_futils_readbuffer_updated(&contents, git_buf_cstr(&path), &repo->shallow_checksum, &updated);
+ git_buf_dispose(&path);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+
+ /* cancel out GIT_ENOTFOUND */
+ git_error_clear();
+ error = 0;
+
+ if (!updated) {
+ *out = repo->shallow_oids;
+ goto cleanup;
+ }
+
+ git_array_clear(repo->shallow_oids);
+
+ buffer = contents.ptr;
+ while ((line = git__strsep(&buffer, "\n")) != NULL) {
+ git_oid *oid = git_array_alloc(repo->shallow_oids);
+
+ error = git_oid_fromstr(oid, line);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_REPOSITORY, "Invalid OID at line %d", line_num);
+ git_array_clear(repo->shallow_oids);
+ error = -1;
+ goto cleanup;
+ }
+ ++line_num;
+ }
+
+ if (*buffer) {
+ git_error_set(GIT_ERROR_REPOSITORY, "No EOL at line %d", line_num);
+ git_array_clear(repo->shallow_oids);
+ error = -1;
+ goto cleanup;
+ }
+
+ *out = repo->shallow_oids;
+
+cleanup:
+ git_buf_dispose(&contents);
+
+ return error;
+}
+
+int git_repository__shallow_roots_write(git_repository *repo, git_array_oid_t roots)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_buf path = GIT_BUF_INIT;
+ int error = 0;
+ size_t idx;
+ git_oid *oid;
+
+ assert(repo);
+
+ if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0)
+ return error;
+
+ if ((error = git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_HASH_CONTENTS, 0666)) < 0)
+ return error;
+
+ git_array_foreach(roots, idx, oid) {
+ git_filebuf_write(&file, git_oid_tostr_s(oid), GIT_OID_HEXSZ);
+ git_filebuf_write(&file, "\n", 1);
+ }
+
+ git_filebuf_commit(&file);
+
+ /* WIP: reload shallow */
+ if (load_shallow(repo) < 0)
+ return -1;
+
+ return 0;
+}
+
int git_repository_is_shallow(git_repository *repo)
{
git_buf path = GIT_BUF_INIT;
diff --git a/src/repository.h b/src/repository.h
index 8da65652d..4b6004bea 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -242,6 +242,9 @@ extern size_t git_repository__reserved_names_posix_len;
bool git_repository__reserved_names(
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
+int git_repository__shallow_roots(git_array_oid_t *out, git_repository *repo);
+int git_repository__shallow_roots_write(git_repository *repo, git_array_oid_t roots);
+
/*
* The default branch for the repository; the `init.defaultBranch`
* configuration option, if set, or `master` if it is not.
diff --git a/src/transports/local.c b/src/transports/local.c
index bb31b1345..17905d222 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -268,15 +268,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/transports/smart.c b/src/transports/smart.c
index 587f14358..a26bf79ec 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -558,3 +558,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/transports/smart.h b/src/transports/smart.h
index a05d4c9e3..6e49237ea 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -14,6 +14,7 @@
#include "netops.h"
#include "buffer.h"
#include "push.h"
+#include "oidarray.h"
#include "git2/sys/transport.h"
#define GIT_SIDE_BAND_DATA 1
@@ -30,6 +31,7 @@
#define GIT_CAP_REPORT_STATUS "report-status"
#define GIT_CAP_THIN_PACK "thin-pack"
#define GIT_CAP_SYMREF "symref"
+#define GIT_CAP_SHALLOW "shallow"
extern bool git_smart__ofs_delta_enabled;
@@ -47,6 +49,8 @@ typedef enum {
GIT_PKT_OK,
GIT_PKT_NG,
GIT_PKT_UNPACK,
+ GIT_PKT_SHALLOW,
+ GIT_PKT_UNSHALLOW,
} git_pkt_type;
/* Used for multi_ack and multi_ack_detailed */
@@ -118,6 +122,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 {
int common:1,
ofs_delta:1,
@@ -128,7 +137,8 @@ typedef struct transport_smart_caps {
include_tag:1,
delete_refs:1,
report_status:1,
- thin_pack:1;
+ thin_pack:1,
+ shallow:1;
} transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload);
@@ -171,8 +181,7 @@ int git_smart__push(git_transport *transport, git_push *push, const git_remote_c
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,
@@ -192,8 +201,12 @@ int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, si
int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf);
-int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf);
+int git_pkt_buffer_wants(const git_fetch_negotiation *wants, transport_smart_caps *caps, git_buf *buf);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
void git_pkt_free(git_pkt *pkt);
+struct git_shallowarray {
+ git_array_oid_t array;
+};
+
#endif
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 56b680d28..6a1e842ee 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -363,6 +363,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];
@@ -489,6 +533,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);
@@ -554,6 +602,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (caps->ofs_delta)
git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
+ if (caps->shallow)
+ git_buf_puts(&str, GIT_CAP_SHALLOW " ");
+
if (git_buf_oom(&str))
return -1;
@@ -583,8 +634,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_buf *buf)
{
@@ -592,22 +642,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_HEXSZ];
- head = refs[i];
+ head = wants->refs[i];
if (head->local)
continue;
@@ -619,6 +669,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/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 91de163e9..df1931191 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -205,6 +205,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
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, ' ');
}
@@ -305,7 +311,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;
@@ -317,7 +342,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)
@@ -327,6 +355,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
@@ -389,7 +444,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) {
@@ -409,7 +464,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) {
diff --git a/tests/clone/shallow.c b/tests/clone/shallow.c
new file mode 100644
index 000000000..d05272065
--- /dev/null
+++ b/tests/clone/shallow.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+void test_clone_shallow__initialize(void)
+{
+
+}
+
+void test_clone_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+#define CLONE_DEPTH 5
+
+void test_clone_shallow__clone_depth(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid oid;
+ git_oidarray roots;
+ size_t depth = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = CLONE_DEPTH;
+
+ git_buf_joinpath(&path, clar_sandbox_path(), "shallowclone");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_buf_cstr(&path), &clone_opts));
+
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository_shallow_roots(&roots, repo));
+ cl_assert_equal_i(1, roots.count);
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&roots.ids[0]));
+
+ git_revwalk_new(&walk, repo);
+
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ if (depth + 1 > CLONE_DEPTH)
+ cl_fail("expected depth mismatch");
+ depth++;
+ }
+
+ cl_git_pass(error);
+
+ git_buf_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
diff --git a/tests/repo/grafts.c b/tests/repo/grafts.c
new file mode 100644
index 000000000..82bd0ad7a
--- /dev/null
+++ b/tests/repo/grafts.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "grafts.h"
+
+static git_repository *g_repo;
+
+void test_repo_grafts__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("grafted.git");
+}
+
+void test_repo_grafts__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_grafts__graft_register(void)
+{
+ git_oid oid_src;
+ git_commit_graft *graft;
+ git_graftmap *grafts = git_oidmap_alloc();
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+
+ git_oid *oid1 = git_array_alloc(parents);
+ cl_git_pass(git_oid_fromstr(&oid_src, "2f3053cbff8a4ca2f0666de364ddb734a28a31a9"));
+ git_oid_cpy(oid1, &oid_src);
+
+ git_oid_fromstr(&oid_src, "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git__graft_register(grafts, &oid_src, parents));
+ git_array_clear(parents);
+
+ cl_assert_equal_i(1, git_oidmap_size(grafts));
+ cl_git_pass(git__graft_for_oid(&graft, grafts, &oid_src));
+ cl_assert_equal_s("f503807ffa920e407a600cfaee96b7152259acc7", git_oid_tostr_s(&graft->oid));
+ cl_assert_equal_i(1, git_array_size(graft->parents));
+ cl_assert_equal_s("2f3053cbff8a4ca2f0666de364ddb734a28a31a9", git_oid_tostr_s(git_array_get(graft->parents, 0)));
+
+ git__graft_clear(grafts);
+ git_oidmap_free(grafts);
+}
+
+void test_repo_grafts__grafted_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+ git_commit *commit;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/branch"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[0]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[2]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oids[0]));
+
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+ git_revwalk_free(w);
+}
+
+void test_repo_grafts__grafted_objects(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ cl_git_pass(git_oid_fromstr(&oid, "f503807ffa920e407a600cfaee96b7152259acc7"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid_fromstr(&oid, "0512adebd3782157f0d5c9b22b043f87b4aaff9e"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid_fromstr(&oid, "66cc22a015f6ca75b34c82d28f78ba663876bade"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(4, git_commit_parentcount(commit));
+ git_commit_free(commit);
+}
+
+void test_repo_grafts__grafted_merge_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/bottom"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "66cc22a015f6ca75b34c82d28f78ba663876bade");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "e414f42f4e6bc6934563a2349a8600f0ab68618e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "1c18e80a276611bb9b146590616bbc5aebdf2945");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "d7224d49d6d5aff6ade596ed74f4bcd4f77b29e2");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "0512adebd3782157f0d5c9b22b043f87b4aaff9e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ git_revwalk_free(w);
+}
diff --git a/tests/repo/shallow.c b/tests/repo/shallow.c
new file mode 100644
index 000000000..a73dfc013
--- /dev/null
+++ b/tests/repo/shallow.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static git_repository *g_repo;
+static git_oid g_shallow_oid;
+
+void test_repo_shallow__initialize(void)
+{
+ cl_git_pass(git_oid_fromstr(&g_shallow_oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+}
+
+void test_repo_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_shallow__no_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__empty_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_mkfile("testrepo.git/shallow", "");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__shallow_repo(void)
+{
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_assert_equal_i(1, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__clears_errors(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+ cl_assert_equal_p(NULL, git_error_last());
+}
+
+void test_repo_shallow__shallow_oids(void)
+{
+ git_oidarray oids, oids2;
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_repository_shallow_roots(&oids, g_repo));
+ cl_assert_equal_i(1, oids.count);
+ cl_assert_equal_oid(&g_shallow_oid, &oids.ids[0]);
+
+ cl_git_pass(git_repository_shallow_roots(&oids2, g_repo));
+ cl_assert_equal_p(oids.ids, oids2.ids);
+}
+
+void test_repo_shallow__cache_clearing(void)
+{
+ git_oidarray oids, oids2;
+ git_oid tmp_oid;
+
+ git_oid_fromstr(&tmp_oid, "0000000000000000000000000000000000000000");
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_repository_shallow_roots(&oids, g_repo));
+ cl_assert_equal_i(1, oids.count);
+ cl_assert_equal_oid(&g_shallow_oid, &oids.ids[0]);
+
+ cl_git_mkfile("shallow.git/shallow",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644\n"
+ "0000000000000000000000000000000000000000\n"
+ );
+
+ cl_git_pass(git_repository_shallow_roots(&oids2, g_repo));
+ cl_assert_equal_i(2, oids2.count);
+ cl_assert_equal_oid(&g_shallow_oid, &oids2.ids[0]);
+ cl_assert_equal_oid(&tmp_oid, &oids2.ids[1]);
+}
+
+void test_repo_shallow__errors_on_borked(void)
+{
+ git_oidarray oids;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_mkfile("shallow.git/shallow", "lolno");
+
+ cl_git_fail_with(-1, git_repository_shallow_roots(&oids, g_repo));
+
+ cl_git_mkfile("shallow.git/shallow", "lolno\n");
+
+ cl_git_fail_with(-1, git_repository_shallow_roots(&oids, g_repo));
+}
+
+void test_repo_shallow__revwalk_behavior(void)
+{
+ git_revwalk *w;
+ git_oid oid_1, oid_2, oid_3;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_head(w));
+
+ cl_git_pass(git_revwalk_next(&oid_1, w)); // a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ cl_git_pass(git_revwalk_next(&oid_2, w)); // be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid_3, w));
+
+ cl_assert_equal_s(git_oid_tostr_s(&oid_1), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_assert_equal_s(git_oid_tostr_s(&oid_2), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ git_revwalk_free(w);
+}
+
+void test_repo_shallow__grafted_object(void)
+{
+ git_commit *commit;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &g_shallow_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+}