From df5eb3239b1419b3f4382d7bcca9a5d85611d7d3 Mon Sep 17 00:00:00 2001 From: yuangli Date: Tue, 9 Aug 2022 19:24:57 +0100 Subject: support fetch unshallow option on shallow repos --- include/git2/remote.h | 9 ++++- src/libgit2/clone.c | 7 +--- src/libgit2/fetch.c | 9 +++-- src/libgit2/repository.c | 19 ++++++---- src/libgit2/transports/smart.c | 26 +++++++++++--- tests/libgit2/clone/shallow.c | 41 +++++++++++++++++++++ tests/libgit2/transports/smart/shallowarray.c | 52 +++++++++++++++++++++++++++ 7 files changed, 144 insertions(+), 19 deletions(-) create mode 100644 tests/libgit2/transports/smart/shallowarray.c diff --git a/include/git2/remote.h b/include/git2/remote.h index f3415d843..240e5aa4e 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -750,6 +750,13 @@ typedef struct { */ int depth; + /** + * Unshallow flag of the fetch to perform. + * + * The default is 0, which means the flag is off. + */ + int unshallow; + /** * Whether to allow off-site redirects. If this is not * specified, the `http.followRedirects` configuration setting @@ -765,7 +772,7 @@ typedef struct { #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, -1 } + GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT, -1, 0 } /** * Initialize git_fetch_options structure diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c index 5e07dc733..6f34cb7ca 100644 --- a/src/libgit2/clone.c +++ b/src/libgit2/clone.c @@ -389,11 +389,6 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c return error; } -static int git_fetch_is_shallow(const git_fetch_options *opts) -{ - return opts->depth > 0; -} - static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch) { int error; @@ -415,7 +410,7 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch memcpy(&fetch_opts, opts, sizeof(git_fetch_options)); fetch_opts.update_fetchhead = 0; - if (!git_fetch_is_shallow(opts)) + if (opts->depth <= 0) fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c index b90ce2ee8..a015cfbd3 100644 --- a/src/libgit2/fetch.c +++ b/src/libgit2/fetch.c @@ -61,7 +61,7 @@ static int mark_local(git_remote *remote) git_vector_foreach(&remote->refs, i, head) { /* If we have the object, mark it so we don't ask for it */ - if (git_odb_exists(odb, &head->oid)) + if (remote->nego.depth != INT_MAX && git_odb_exists(odb, &head->oid)) head->local = 1; else remote->need_pack = 1; @@ -173,6 +173,7 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) git_transport *t = remote->transport; remote->need_pack = 0; + remote->nego.depth = opts->unshallow ? INT_MAX : opts->depth; if (filter_wants(remote, opts) < 0) return -1; @@ -181,13 +182,17 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) if (!remote->need_pack) return 0; + if (opts->unshallow && opts->depth > 0) { + git_error_set(GIT_ERROR_INVALID, "options '--depth' and '--unshallow' cannot be used together"); + return -1; + } + /* * 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); diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c index 0d149a626..13559ef07 100644 --- a/src/libgit2/repository.c +++ b/src/libgit2/repository.c @@ -3366,10 +3366,10 @@ int git_repository__shallow_roots_write(git_repository *repo, git_array_oid_t ro assert(repo); if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0) - return error; + goto on_error; if ((error = git_filebuf_open(&file, git_str_cstr(&path), GIT_FILEBUF_HASH_CONTENTS, 0666)) < 0) - return error; + goto on_error; git_array_foreach(roots, idx, oid) { git_filebuf_write(&file, git_oid_tostr_s(oid), GIT_OID_HEXSZ); @@ -3378,12 +3378,19 @@ int git_repository__shallow_roots_write(git_repository *repo, git_array_oid_t ro git_filebuf_commit(&file); - git_str_dispose(&path); + if ((error = load_grafts(repo)) < 0) { + error = -1; + goto on_error; + } - if (load_grafts(repo) < 0) - return -1; + if (git_array_size(roots) == 0) { + remove(path.ptr); + } - return 0; +on_error: + git_str_dispose(&path); + + return error; } int git_repository_shallow_roots(git_oidarray *out, git_repository *repo) diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c index 9d1afeb05..b0925c8bb 100644 --- a/src/libgit2/transports/smart.c +++ b/src/libgit2/transports/smart.c @@ -496,17 +496,35 @@ const git_oid * git_shallowarray_get(git_shallowarray *array, size_t 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_ERROR_CHECK_ALLOC(tmp); + 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; + git_array_oid_t new_array = GIT_ARRAY_INIT; + git_oid *element; + git_oid *tmp; + size_t i; + + git_array_foreach(array->array, i, element) { + if (git_oid_cmp(oid, element)) { + tmp = git_array_alloc(new_array); + GIT_ERROR_CHECK_ALLOC(tmp); + + git_oid_cpy(tmp, element); + } + } + + git_array_clear(array->array); + array->array = new_array; + + return 0; } diff --git a/tests/libgit2/clone/shallow.c b/tests/libgit2/clone/shallow.c index 7fb056f91..2a88d5d05 100644 --- a/tests/libgit2/clone/shallow.c +++ b/tests/libgit2/clone/shallow.c @@ -131,3 +131,44 @@ void test_clone_shallow__clone_depth_five(void) git_revwalk_free(walk); git_repository_free(repo); } + +void test_clone_shallow__unshallow(void) +{ + git_str path = GIT_STR_INIT; + git_repository *repo; + git_revwalk *walk; + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; + git_remote *origin = NULL; + git_oid oid; + size_t num_commits = 0; + int error = 0; + + clone_opts.fetch_opts.depth = 5; + clone_opts.remote_cb = remote_single_branch; + + git_str_joinpath(&path, clar_sandbox_path(), "unshallow"); + cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts)); + cl_assert_equal_b(true, git_repository_is_shallow(repo)); + + fetch_opts.unshallow = 1; + cl_git_pass(git_remote_lookup(&origin, repo, "origin")); + + cl_git_pass(git_remote_fetch(origin, NULL, &fetch_opts, NULL)); + cl_assert_equal_b(false, git_repository_is_shallow(repo)); + + git_revwalk_new(&walk, repo); + git_revwalk_push_head(walk); + + while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) { + num_commits++; + } + + cl_assert_equal_i(num_commits, 21); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_remote_free(origin); + git_str_dispose(&path); + git_revwalk_free(walk); + git_repository_free(repo); +} diff --git a/tests/libgit2/transports/smart/shallowarray.c b/tests/libgit2/transports/smart/shallowarray.c new file mode 100644 index 000000000..c51e62713 --- /dev/null +++ b/tests/libgit2/transports/smart/shallowarray.c @@ -0,0 +1,52 @@ +#include "clar_libgit2.h" + +#include "git2/oid.h" +#include "git2/transport.h" + +#include "common.h" +#include "transports/smart.h" +#include "oid.h" + +#include + +#define oid_0 "c070ad8c08840c8116da865b2d65593a6bb9cd2a" +#define oid_1 "0966a434eb1a025db6b71485ab63a3bfbea520b6" +#define oid_2 "83834a7afdaa1a1260568567f6ad90020389f664" + +void test_transports_smart_shallowarray__add_and_remove_oid_from_shallowarray(void) +{ + git_oid oid_0_obj, oid_1_obj, oid_2_obj; + git_shallowarray *shallow_roots = git__malloc(sizeof(git_shallowarray)); + git_array_init(shallow_roots->array); + + git_oid_fromstr(&oid_0_obj, oid_0); + git_oid_fromstr(&oid_1_obj, oid_1); + git_oid_fromstr(&oid_2_obj, oid_2); + + git_shallowarray_add(shallow_roots, &oid_0_obj); + git_shallowarray_add(shallow_roots, &oid_1_obj); + git_shallowarray_add(shallow_roots, &oid_2_obj); + + cl_assert_equal_i(3, shallow_roots->array.size); + cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&shallow_roots->array.ptr[0])); + cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&shallow_roots->array.ptr[1])); + cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&shallow_roots->array.ptr[2])); + + git_shallowarray_remove(shallow_roots, &oid_2_obj); + + cl_assert_equal_i(2, shallow_roots->array.size); + cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&shallow_roots->array.ptr[0])); + cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&shallow_roots->array.ptr[1])); + + git_shallowarray_remove(shallow_roots, &oid_1_obj); + + cl_assert_equal_i(1, shallow_roots->array.size); + cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&shallow_roots->array.ptr[0])); + + git_shallowarray_remove(shallow_roots, &oid_0_obj); + + cl_assert_equal_i(0, shallow_roots->array.size); + + git_array_clear(shallow_roots->array); + git__free(shallow_roots); +} -- cgit v1.2.1