Skip to content

Commit 3c5d78b

Browse files
tiennoupks-t
authored andcommitted
submodule: provide a wrapper for simple submodule clone steps
1 parent 0298e0a commit 3c5d78b

File tree

6 files changed

+184
-58
lines changed

6 files changed

+184
-58
lines changed

include/git2/submodule.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ GIT_EXTERN(int) git_submodule_foreach(
263263
* from the working directory to the new repo.
264264
*
265265
* To fully emulate "git submodule add" call this function, then open the
266-
* submodule repo and perform the clone step as needed. Lastly, call
266+
* submodule repo and perform the clone step as needed (if you don't need
267+
* anything custom see `git_submodule_add_clone()`). Lastly, call
267268
* `git_submodule_add_finalize()` to wrap up adding the new submodule and
268269
* .gitmodules to the index to be ready to commit.
269270
*
@@ -285,6 +286,22 @@ GIT_EXTERN(int) git_submodule_add_setup(
285286
const char *path,
286287
int use_gitlink);
287288

289+
/**
290+
* Perform the clone step for a newly created submodule.
291+
*
292+
* This performs the necessary `git_clone` to setup a newly-created submodule.
293+
*
294+
* @param out The newly created repository object. Optional.
295+
* @param submodule The submodule currently waiting for its clone.
296+
* @param opts The options to use.
297+
*
298+
* @return 0 on success, -1 on other errors (see git_clone).
299+
*/
300+
GIT_EXTERN(int) git_submodule_clone(
301+
git_repository **out,
302+
git_submodule *submodule,
303+
const git_submodule_update_options *opts);
304+
288305
/**
289306
* Resolve the setup of a new git submodule.
290307
*

src/clone.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,11 +382,12 @@ int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t loc
382382
return is_local;
383383
}
384384

385-
int git_clone(
385+
static int git__clone(
386386
git_repository **out,
387387
const char *url,
388388
const char *local_path,
389-
const git_clone_options *_options)
389+
const git_clone_options *_options,
390+
int use_existing)
390391
{
391392
int error = 0;
392393
git_repository *repo = NULL;
@@ -403,7 +404,7 @@ int git_clone(
403404
GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
404405

405406
/* Only clone to a new directory or an empty directory */
406-
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
407+
if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) {
407408
git_error_set(GIT_ERROR_INVALID,
408409
"'%s' exists and is not an empty directory", local_path);
409410
return GIT_EEXISTS;
@@ -455,6 +456,24 @@ int git_clone(
455456
return error;
456457
}
457458

459+
int git_clone(
460+
git_repository **out,
461+
const char *url,
462+
const char *local_path,
463+
const git_clone_options *_options)
464+
{
465+
return git__clone(out, url, local_path, _options, 0);
466+
}
467+
468+
int git_clone__submodule(
469+
git_repository **out,
470+
const char *url,
471+
const char *local_path,
472+
const git_clone_options *_options)
473+
{
474+
return git__clone(out, url, local_path, _options, 1);
475+
}
476+
458477
int git_clone_options_init(git_clone_options *opts, unsigned int version)
459478
{
460479
GIT_INIT_STRUCTURE_FROM_TEMPLATE(

src/clone.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
#include "git2/clone.h"
1313

14+
extern int git_clone__submodule(git_repository **out,
15+
const char *url, const char *local_path,
16+
const git_clone_options *_options);
17+
1418
extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
1519

1620
#endif

src/submodule.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "path.h"
2424
#include "index.h"
2525
#include "worktree.h"
26+
#include "clone.h"
2627

2728
#define GIT_MODULES_FILE ".gitmodules"
2829

@@ -815,6 +816,64 @@ int git_submodule_repo_init(
815816
return error;
816817
}
817818

819+
static int clone_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
820+
{
821+
GIT_UNUSED(url);
822+
GIT_UNUSED(payload);
823+
return git_remote_lookup(out, repo, name);
824+
}
825+
826+
static int clone_return_repo(git_repository **out, const char *path, int bare, void *payload)
827+
{
828+
git_submodule *sm = payload;
829+
830+
GIT_UNUSED(path);
831+
GIT_UNUSED(bare);
832+
return git_submodule_open(out, sm);
833+
}
834+
835+
int git_submodule_clone(git_repository **out, git_submodule *submodule, const git_submodule_update_options *given_opts)
836+
{
837+
int error;
838+
git_repository *clone;
839+
git_buf rel_path = GIT_BUF_INIT;
840+
git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
841+
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
842+
843+
assert(submodule);
844+
845+
if (given_opts)
846+
memcpy(&sub_opts, given_opts, sizeof(sub_opts));
847+
848+
GIT_ERROR_CHECK_VERSION(&sub_opts, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
849+
850+
memcpy(&opts.checkout_opts, &sub_opts.checkout_opts, sizeof(sub_opts.checkout_opts));
851+
memcpy(&opts.fetch_opts, &sub_opts.fetch_opts, sizeof(sub_opts.fetch_opts));
852+
opts.repository_cb = clone_return_repo;
853+
opts.repository_cb_payload = submodule;
854+
opts.remote_cb = clone_return_origin;
855+
opts.remote_cb_payload = submodule;
856+
857+
git_buf_puts(&rel_path, git_repository_workdir(git_submodule_owner(submodule)));
858+
git_buf_joinpath(&rel_path, git_buf_cstr(&rel_path), git_submodule_path(submodule));
859+
860+
GIT_ERROR_CHECK_ALLOC_BUF(&rel_path);
861+
862+
error = git_clone__submodule(&clone, git_submodule_url(submodule), git_buf_cstr(&rel_path), &opts);
863+
if (error < 0)
864+
goto cleanup;
865+
866+
if (!out)
867+
git_repository_free(clone);
868+
else
869+
*out = clone;
870+
871+
cleanup:
872+
git_buf_dispose(&rel_path);
873+
874+
return error;
875+
}
876+
818877
int git_submodule_add_finalize(git_submodule *sm)
819878
{
820879
int error;

tests/clone/nonetwork.c

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#include "clar_libgit2.h"
22

33
#include "git2/clone.h"
4-
#include "git2/sys/commit.h"
54
#include "../submodule/submodule_helpers.h"
65
#include "remote.h"
76
#include "futils.h"
@@ -352,56 +351,3 @@ void test_clone_nonetwork__clone_from_empty_sets_upstream(void)
352351
git_repository_free(repo);
353352
cl_fixture_cleanup("./repowithunborn");
354353
}
355-
356-
static int just_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
357-
{
358-
GIT_UNUSED(url); GIT_UNUSED(payload);
359-
360-
return git_remote_lookup(out, repo, name);
361-
}
362-
363-
static int just_return_repo(git_repository **out, const char *path, int bare, void *payload)
364-
{
365-
git_submodule *sm = payload;
366-
367-
GIT_UNUSED(path); GIT_UNUSED(bare);
368-
369-
return git_submodule_open(out, sm);
370-
}
371-
372-
void test_clone_nonetwork__clone_submodule(void)
373-
{
374-
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
375-
git_index *index;
376-
git_oid tree_id, commit_id;
377-
git_submodule *sm;
378-
git_signature *sig;
379-
git_repository *sm_repo;
380-
381-
cl_git_pass(git_repository_init(&g_repo, "willaddsubmodule", false));
382-
383-
384-
/* Create the submodule structure, clone into it and finalize */
385-
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo", true));
386-
387-
clone_opts.repository_cb = just_return_repo;
388-
clone_opts.repository_cb_payload = sm;
389-
clone_opts.remote_cb = just_return_origin;
390-
clone_opts.remote_cb_payload = sm;
391-
cl_git_pass(git_clone(&sm_repo, cl_fixture("testrepo.git"), "testrepo", &clone_opts));
392-
cl_git_pass(git_submodule_add_finalize(sm));
393-
git_repository_free(sm_repo);
394-
git_submodule_free(sm);
395-
396-
cl_git_pass(git_repository_index(&index, g_repo));
397-
cl_git_pass(git_index_write_tree(&tree_id, index));
398-
git_index_free(index);
399-
400-
cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
401-
cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
402-
&tree_id, 0, NULL));
403-
404-
git_signature_free(sig);
405-
406-
assert_submodule_exists(g_repo, "testrepo");
407-
}

tests/submodule/add.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "config/config_helpers.h"
66
#include "futils.h"
77
#include "repository.h"
8+
#include "git2/sys/commit.h"
89

910
static git_repository *g_repo = NULL;
1011
static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
@@ -183,3 +184,83 @@ void test_submodule_add__file_exists_in_index(void)
183184
git_submodule_free(sm);
184185
git_buf_dispose(&name);
185186
}
187+
188+
static int just_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
189+
{
190+
GIT_UNUSED(url); GIT_UNUSED(payload);
191+
192+
return git_remote_lookup(out, repo, name);
193+
}
194+
195+
static int just_return_repo(git_repository **out, const char *path, int bare, void *payload)
196+
{
197+
git_submodule *sm = payload;
198+
199+
GIT_UNUSED(path); GIT_UNUSED(bare);
200+
201+
return git_submodule_open(out, sm);
202+
}
203+
204+
void test_submodule_add__homemade_clone(void)
205+
{
206+
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
207+
git_index *index;
208+
git_oid tree_id, commit_id;
209+
git_submodule *sm;
210+
git_signature *sig;
211+
git_repository *sm_repo;
212+
213+
cl_git_pass(git_repository_init(&g_repo, "willaddsubmodule", false));
214+
215+
/* Create the submodule structure, clone into it and finalize */
216+
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo", true));
217+
218+
clone_opts.repository_cb = just_return_repo;
219+
clone_opts.repository_cb_payload = sm;
220+
clone_opts.remote_cb = just_return_origin;
221+
clone_opts.remote_cb_payload = sm;
222+
cl_git_pass(git_clone(&sm_repo, cl_fixture("testrepo.git"), "testrepo", &clone_opts));
223+
cl_git_pass(git_submodule_add_finalize(sm));
224+
git_repository_free(sm_repo);
225+
git_submodule_free(sm);
226+
227+
cl_git_pass(git_repository_index(&index, g_repo));
228+
cl_git_pass(git_index_write_tree(&tree_id, index));
229+
git_index_free(index);
230+
231+
cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
232+
cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
233+
&tree_id, 0, NULL));
234+
235+
git_signature_free(sig);
236+
237+
assert_submodule_exists(g_repo, "testrepo");
238+
}
239+
240+
void test_submodule_add__submodule_clone(void)
241+
{
242+
git_index *index;
243+
git_oid tree_id, commit_id;
244+
git_submodule *sm;
245+
git_signature *sig;
246+
247+
cl_git_pass(git_repository_init(&g_repo, "willaddsubmodule-add", false));
248+
249+
/* Create the submodule structure, clone into it and finalize */
250+
cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo-add", true));
251+
cl_git_pass(git_submodule_clone(NULL, sm, NULL));
252+
cl_git_pass(git_submodule_add_finalize(sm));
253+
git_submodule_free(sm);
254+
255+
cl_git_pass(git_repository_index(&index, g_repo));
256+
cl_git_pass(git_index_write_tree(&tree_id, index));
257+
git_index_free(index);
258+
259+
cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
260+
cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
261+
&tree_id, 0, NULL));
262+
263+
git_signature_free(sig);
264+
265+
assert_submodule_exists(g_repo, "testrepo-add");
266+
}

0 commit comments

Comments
 (0)