Unverified Commit 4c0dea5a by Patrick Steinhardt Committed by GitHub

Merge pull request #4637 from tiennou/feature/submodule-easy-clone

Provide a wrapper for simple submodule clone steps
parents 0298e0a6 73e9535d
......@@ -263,7 +263,8 @@ GIT_EXTERN(int) git_submodule_foreach(
* from the working directory to the new repo.
*
* To fully emulate "git submodule add" call this function, then open the
* submodule repo and perform the clone step as needed. Lastly, call
* submodule repo and perform the clone step as needed (if you don't need
* anything custom see `git_submodule_add_clone()`). Lastly, call
* `git_submodule_add_finalize()` to wrap up adding the new submodule and
* .gitmodules to the index to be ready to commit.
*
......@@ -286,6 +287,22 @@ GIT_EXTERN(int) git_submodule_add_setup(
int use_gitlink);
/**
* Perform the clone step for a newly created submodule.
*
* This performs the necessary `git_clone` to setup a newly-created submodule.
*
* @param out The newly created repository object. Optional.
* @param submodule The submodule currently waiting for its clone.
* @param opts The options to use.
*
* @return 0 on success, -1 on other errors (see git_clone).
*/
GIT_EXTERN(int) git_submodule_clone(
git_repository **out,
git_submodule *submodule,
const git_submodule_update_options *opts);
/**
* Resolve the setup of a new git submodule.
*
* This should be called on a submodule once you have called add setup
......
......@@ -382,11 +382,12 @@ done:
return is_local;
}
int git_clone(
static int git__clone(
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *_options)
const git_clone_options *_options,
int use_existing)
{
int error = 0;
git_repository *repo = NULL;
......@@ -403,7 +404,7 @@ int git_clone(
GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
/* Only clone to a new directory or an empty directory */
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) {
git_error_set(GIT_ERROR_INVALID,
"'%s' exists and is not an empty directory", local_path);
return GIT_EEXISTS;
......@@ -455,6 +456,24 @@ int git_clone(
return error;
}
int git_clone(
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *_options)
{
return git__clone(out, url, local_path, _options, 0);
}
int git_clone__submodule(
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *_options)
{
return git__clone(out, url, local_path, _options, 1);
}
int git_clone_options_init(git_clone_options *opts, unsigned int version)
{
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
......
......@@ -11,6 +11,10 @@
#include "git2/clone.h"
extern int git_clone__submodule(git_repository **out,
const char *url, const char *local_path,
const git_clone_options *_options);
extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
#endif
......@@ -23,6 +23,7 @@
#include "path.h"
#include "index.h"
#include "worktree.h"
#include "clone.h"
#define GIT_MODULES_FILE ".gitmodules"
......@@ -815,6 +816,64 @@ done:
return error;
}
static int clone_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
{
GIT_UNUSED(url);
GIT_UNUSED(payload);
return git_remote_lookup(out, repo, name);
}
static int clone_return_repo(git_repository **out, const char *path, int bare, void *payload)
{
git_submodule *sm = payload;
GIT_UNUSED(path);
GIT_UNUSED(bare);
return git_submodule_open(out, sm);
}
int git_submodule_clone(git_repository **out, git_submodule *submodule, const git_submodule_update_options *given_opts)
{
int error;
git_repository *clone;
git_buf rel_path = GIT_BUF_INIT;
git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
assert(submodule);
if (given_opts)
memcpy(&sub_opts, given_opts, sizeof(sub_opts));
GIT_ERROR_CHECK_VERSION(&sub_opts, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
memcpy(&opts.checkout_opts, &sub_opts.checkout_opts, sizeof(sub_opts.checkout_opts));
memcpy(&opts.fetch_opts, &sub_opts.fetch_opts, sizeof(sub_opts.fetch_opts));
opts.repository_cb = clone_return_repo;
opts.repository_cb_payload = submodule;
opts.remote_cb = clone_return_origin;
opts.remote_cb_payload = submodule;
git_buf_puts(&rel_path, git_repository_workdir(git_submodule_owner(submodule)));
git_buf_joinpath(&rel_path, git_buf_cstr(&rel_path), git_submodule_path(submodule));
GIT_ERROR_CHECK_ALLOC_BUF(&rel_path);
error = git_clone__submodule(&clone, git_submodule_url(submodule), git_buf_cstr(&rel_path), &opts);
if (error < 0)
goto cleanup;
if (!out)
git_repository_free(clone);
else
*out = clone;
cleanup:
git_buf_dispose(&rel_path);
return error;
}
int git_submodule_add_finalize(git_submodule *sm)
{
int error;
......
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "git2/sys/commit.h"
#include "../submodule/submodule_helpers.h"
#include "remote.h"
#include "futils.h"
......@@ -352,56 +351,3 @@ void test_clone_nonetwork__clone_from_empty_sets_upstream(void)
git_repository_free(repo);
cl_fixture_cleanup("./repowithunborn");
}
static int just_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
{
GIT_UNUSED(url); GIT_UNUSED(payload);
return git_remote_lookup(out, repo, name);
}
static int just_return_repo(git_repository **out, const char *path, int bare, void *payload)
{
git_submodule *sm = payload;
GIT_UNUSED(path); GIT_UNUSED(bare);
return git_submodule_open(out, sm);
}
void test_clone_nonetwork__clone_submodule(void)
{
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
git_index *index;
git_oid tree_id, commit_id;
git_submodule *sm;
git_signature *sig;
git_repository *sm_repo;
cl_git_pass(git_repository_init(&g_repo, "willaddsubmodule", false));
/* Create the submodule structure, clone into it and finalize */
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo", true));
clone_opts.repository_cb = just_return_repo;
clone_opts.repository_cb_payload = sm;
clone_opts.remote_cb = just_return_origin;
clone_opts.remote_cb_payload = sm;
cl_git_pass(git_clone(&sm_repo, cl_fixture("testrepo.git"), "testrepo", &clone_opts));
cl_git_pass(git_submodule_add_finalize(sm));
git_repository_free(sm_repo);
git_submodule_free(sm);
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
git_index_free(index);
cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
&tree_id, 0, NULL));
git_signature_free(sig);
assert_submodule_exists(g_repo, "testrepo");
}
......@@ -5,6 +5,7 @@
#include "config/config_helpers.h"
#include "futils.h"
#include "repository.h"
#include "git2/sys/commit.h"
static git_repository *g_repo = NULL;
static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
......@@ -183,3 +184,68 @@ void test_submodule_add__file_exists_in_index(void)
git_submodule_free(sm);
git_buf_dispose(&name);
}
void test_submodule_add__submodule_clone(void)
{
git_oid tree_id, commit_id;
git_signature *sig;
git_submodule *sm;
git_index *index;
g_repo = cl_git_sandbox_init("empty_standard_repo");
/* Create the submodule structure, clone into it and finalize */
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo-add", true));
cl_git_pass(git_submodule_clone(NULL, sm, NULL));
cl_git_pass(git_submodule_add_finalize(sm));
/* Create the submodule commit */
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
&tree_id, 0, NULL));
assert_submodule_exists(g_repo, "testrepo-add");
git_signature_free(sig);
git_submodule_free(sm);
git_index_free(index);
}
void test_submodule_add__submodule_clone_into_nonempty_dir_succeeds(void)
{
git_submodule *sm;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(p_mkdir("empty_standard_repo/sm", 0777));
cl_git_mkfile("empty_standard_repo/sm/foobar", "");
/* Create the submodule structure, clone into it and finalize */
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "sm", true));
cl_git_pass(git_submodule_clone(NULL, sm, NULL));
cl_git_pass(git_submodule_add_finalize(sm));
cl_assert(git_path_exists("empty_standard_repo/sm/foobar"));
assert_submodule_exists(g_repo, "sm");
git_submodule_free(sm);
}
void test_submodule_add__submodule_clone_twice_fails(void)
{
git_submodule *sm;
g_repo = cl_git_sandbox_init("empty_standard_repo");
/* Create the submodule structure, clone into it and finalize */
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "sm", true));
cl_git_pass(git_submodule_clone(NULL, sm, NULL));
cl_git_pass(git_submodule_add_finalize(sm));
cl_git_fail(git_submodule_clone(NULL, sm, NULL));
git_submodule_free(sm);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment