Skip to content

Commit

Permalink
submodule: provide a wrapper for simple submodule clone steps
Browse files Browse the repository at this point in the history
  • Loading branch information
tiennou authored and pks-t committed Oct 17, 2019
1 parent 0298e0a commit 3c5d78b
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 58 deletions.
19 changes: 18 additions & 1 deletion include/git2/submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -285,6 +286,22 @@ GIT_EXTERN(int) git_submodule_add_setup(
const char *path,
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.
*
Expand Down
25 changes: 22 additions & 3 deletions src/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,12 @@ int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t loc
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 4 additions & 0 deletions src/clone.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
59 changes: 59 additions & 0 deletions src/submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "path.h"
#include "index.h"
#include "worktree.h"
#include "clone.h"

#define GIT_MODULES_FILE ".gitmodules"

Expand Down Expand Up @@ -815,6 +816,64 @@ int git_submodule_repo_init(
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;
Expand Down
54 changes: 0 additions & 54 deletions tests/clone/nonetwork.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "clar_libgit2.h"

#include "git2/clone.h"
#include "git2/sys/commit.h"
#include "../submodule/submodule_helpers.h"
#include "remote.h"
#include "futils.h"
Expand Down Expand Up @@ -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");
}
81 changes: 81 additions & 0 deletions tests/submodule/add.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -183,3 +184,83 @@ void test_submodule_add__file_exists_in_index(void)
git_submodule_free(sm);
git_buf_dispose(&name);
}

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_submodule_add__homemade_clone(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");
}

void test_submodule_add__submodule_clone(void)
{
git_index *index;
git_oid tree_id, commit_id;
git_submodule *sm;
git_signature *sig;

cl_git_pass(git_repository_init(&g_repo, "willaddsubmodule-add", 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-add", true));
cl_git_pass(git_submodule_clone(NULL, sm, NULL));
cl_git_pass(git_submodule_add_finalize(sm));
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-add");
}

0 comments on commit 3c5d78b

Please sign in to comment.