1 #include "clar_libgit2.h"
2 #include "submodule_helpers.h"
3 #include "git2/sys/repository.h"
4 #include "repository.h"
7 static git_repository
*g_repo
= NULL
;
9 void test_submodule_lookup__initialize(void)
11 g_repo
= setup_fixture_submod2();
14 void test_submodule_lookup__cleanup(void)
16 cl_git_sandbox_cleanup();
19 void test_submodule_lookup__simple_lookup(void)
21 assert_submodule_exists(g_repo
, "sm_unchanged");
23 /* lookup pending change in .gitmodules that is not in HEAD */
24 assert_submodule_exists(g_repo
, "sm_added_and_uncommited");
26 /* lookup pending change in .gitmodules that is not in HEAD nor index */
27 assert_submodule_exists(g_repo
, "sm_gitmodules_only");
29 /* lookup git repo subdir that is not added as submodule */
30 refute_submodule_exists(g_repo
, "not-submodule", GIT_EEXISTS
);
32 /* lookup existing directory that is not a submodule */
33 refute_submodule_exists(g_repo
, "just_a_dir", GIT_ENOTFOUND
);
35 /* lookup existing file that is not a submodule */
36 refute_submodule_exists(g_repo
, "just_a_file", GIT_ENOTFOUND
);
38 /* lookup non-existent item */
39 refute_submodule_exists(g_repo
, "no_such_file", GIT_ENOTFOUND
);
41 /* lookup a submodule by path with a trailing slash */
42 assert_submodule_exists(g_repo
, "sm_added_and_uncommited/");
45 void test_submodule_lookup__can_be_dupped(void)
48 git_submodule
*sm_duplicate
;
49 const char *oid
= "480095882d281ed676fe5b863569520e54a7d5c0";
52 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_unchanged"));
53 cl_assert(git_submodule_owner(sm
) == g_repo
);
54 cl_assert_equal_s("sm_unchanged", git_submodule_name(sm
));
55 cl_assert(git__suffixcmp(git_submodule_path(sm
), "sm_unchanged") == 0);
56 cl_assert(git__suffixcmp(git_submodule_url(sm
), "/submod2_target") == 0);
58 cl_assert(git_oid_streq(git_submodule_index_id(sm
), oid
) == 0);
59 cl_assert(git_oid_streq(git_submodule_head_id(sm
), oid
) == 0);
60 cl_assert(git_oid_streq(git_submodule_wd_id(sm
), oid
) == 0);
62 cl_assert(git_submodule_ignore(sm
) == GIT_SUBMODULE_IGNORE_NONE
);
63 cl_assert(git_submodule_update_strategy(sm
) == GIT_SUBMODULE_UPDATE_CHECKOUT
);
65 /* Duplicate and free original */
66 cl_assert(git_submodule_dup(&sm_duplicate
, sm
) == 0);
67 git_submodule_free(sm
);
70 cl_assert(git_submodule_owner(sm_duplicate
) == g_repo
);
71 cl_assert_equal_s("sm_unchanged", git_submodule_name(sm_duplicate
));
72 cl_assert(git__suffixcmp(git_submodule_path(sm_duplicate
), "sm_unchanged") == 0);
73 cl_assert(git__suffixcmp(git_submodule_url(sm_duplicate
), "/submod2_target") == 0);
75 cl_assert(git_oid_streq(git_submodule_index_id(sm_duplicate
), oid
) == 0);
76 cl_assert(git_oid_streq(git_submodule_head_id(sm_duplicate
), oid
) == 0);
77 cl_assert(git_oid_streq(git_submodule_wd_id(sm_duplicate
), oid
) == 0);
79 cl_assert(git_submodule_ignore(sm_duplicate
) == GIT_SUBMODULE_IGNORE_NONE
);
80 cl_assert(git_submodule_update_strategy(sm_duplicate
) == GIT_SUBMODULE_UPDATE_CHECKOUT
);
82 git_submodule_free(sm_duplicate
);
85 void test_submodule_lookup__accessors(void)
88 const char *oid
= "480095882d281ed676fe5b863569520e54a7d5c0";
90 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_unchanged"));
91 cl_assert(git_submodule_owner(sm
) == g_repo
);
92 cl_assert_equal_s("sm_unchanged", git_submodule_name(sm
));
93 cl_assert(git__suffixcmp(git_submodule_path(sm
), "sm_unchanged") == 0);
94 cl_assert(git__suffixcmp(git_submodule_url(sm
), "/submod2_target") == 0);
96 cl_assert(git_oid_streq(git_submodule_index_id(sm
), oid
) == 0);
97 cl_assert(git_oid_streq(git_submodule_head_id(sm
), oid
) == 0);
98 cl_assert(git_oid_streq(git_submodule_wd_id(sm
), oid
) == 0);
100 cl_assert(git_submodule_ignore(sm
) == GIT_SUBMODULE_IGNORE_NONE
);
101 cl_assert(git_submodule_update_strategy(sm
) == GIT_SUBMODULE_UPDATE_CHECKOUT
);
103 git_submodule_free(sm
);
106 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_changed_head"));
107 cl_assert_equal_s("sm_changed_head", git_submodule_name(sm
));
109 cl_assert(git_oid_streq(git_submodule_index_id(sm
), oid
) == 0);
110 cl_assert(git_oid_streq(git_submodule_head_id(sm
), oid
) == 0);
111 cl_assert(git_oid_streq(git_submodule_wd_id(sm
),
112 "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
114 git_submodule_free(sm
);
117 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_added_and_uncommited"));
118 cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm
));
120 cl_assert(git_oid_streq(git_submodule_index_id(sm
), oid
) == 0);
121 cl_assert(git_submodule_head_id(sm
) == NULL
);
122 cl_assert(git_oid_streq(git_submodule_wd_id(sm
), oid
) == 0);
124 git_submodule_free(sm
);
127 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_missing_commits"));
128 cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm
));
130 cl_assert(git_oid_streq(git_submodule_index_id(sm
), oid
) == 0);
131 cl_assert(git_oid_streq(git_submodule_head_id(sm
), oid
) == 0);
132 cl_assert(git_oid_streq(git_submodule_wd_id(sm
),
133 "5e4963595a9774b90524d35a807169049de8ccad") == 0);
135 git_submodule_free(sm
);
142 static int sm_lookup_cb(git_submodule
*sm
, const char *name
, void *payload
)
144 sm_lookup_data
*data
= payload
;
146 cl_assert_equal_s(git_submodule_name(sm
), name
);
150 void test_submodule_lookup__foreach(void)
155 memset(&data
, 0, sizeof(data
));
156 cl_git_pass(git_submodule_foreach(g_repo
, sm_lookup_cb
, &data
));
157 cl_assert_equal_i(8, data
.count
);
159 memset(&data
, 0, sizeof(data
));
161 /* Change the path for a submodule so it doesn't match the name */
162 cl_git_pass(git_config_open_ondisk(&cfg
, "submod2/.gitmodules"));
164 cl_git_pass(git_config_set_string(cfg
, "submodule.smchangedindex.path", "sm_changed_index"));
165 cl_git_pass(git_config_set_string(cfg
, "submodule.smchangedindex.url", "../submod2_target"));
166 cl_git_pass(git_config_delete_entry(cfg
, "submodule.sm_changed_index.path"));
167 cl_git_pass(git_config_delete_entry(cfg
, "submodule.sm_changed_index.url"));
169 git_config_free(cfg
);
171 cl_git_pass(git_submodule_foreach(g_repo
, sm_lookup_cb
, &data
));
172 cl_assert_equal_i(8, data
.count
);
175 static int foreach_cb(git_submodule
*sm
, const char *name
, void *payload
)
183 void test_submodule_lookup__duplicated_path(void)
185 cl_git_rewritefile("submod2/.gitmodules",
186 "[submodule \"sm1\"]\n"
187 " path = duplicated-path\n"
189 "[submodule \"sm2\"]\n"
190 " path = duplicated-path\n"
193 cl_git_fail(git_submodule_foreach(g_repo
, foreach_cb
, NULL
));
196 void test_submodule_lookup__lookup_even_with_unborn_head(void)
200 /* put us on an unborn branch */
201 cl_git_pass(git_reference_symbolic_create(
202 &head
, g_repo
, "HEAD", "refs/heads/garbage", 1, NULL
));
203 git_reference_free(head
);
205 test_submodule_lookup__simple_lookup(); /* baseline should still pass */
208 void test_submodule_lookup__lookup_even_with_missing_index(void)
212 /* give the repo an empty index */
213 cl_git_pass(git_index_new(&idx
));
214 git_repository_set_index(g_repo
, idx
);
217 test_submodule_lookup__simple_lookup(); /* baseline should still pass */
220 void test_submodule_lookup__backslashes(void)
224 git_repository
*subrepo
;
225 git_buf buf
= GIT_BUF_INIT
;
226 const char *backslashed_path
= "..\\submod2_target";
228 cl_git_pass(git_config_open_ondisk(&cfg
, "submod2/.gitmodules"));
229 cl_git_pass(git_config_set_string(cfg
, "submodule.sm_unchanged.url", backslashed_path
));
230 git_config_free(cfg
);
232 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_unchanged"));
233 cl_assert_equal_s(backslashed_path
, git_submodule_url(sm
));
234 cl_git_pass(git_submodule_open(&subrepo
, sm
));
236 cl_git_pass(git_submodule_resolve_url(&buf
, g_repo
, backslashed_path
));
238 git_buf_dispose(&buf
);
239 git_submodule_free(sm
);
240 git_repository_free(subrepo
);
243 static void baseline_tests(void)
245 /* small baseline that should work even if we change the index or make
246 * commits from the index
248 assert_submodule_exists(g_repo
, "sm_unchanged");
249 assert_submodule_exists(g_repo
, "sm_gitmodules_only");
250 refute_submodule_exists(g_repo
, "not-submodule", GIT_EEXISTS
);
253 static void add_submodule_with_commit(const char *name
)
256 git_repository
*smrepo
;
258 git_str p
= GIT_STR_INIT
;
260 cl_git_pass(git_submodule_add_setup(&sm
, g_repo
,
261 "https://github.com/libgit2/libgit2.git", name
, 1));
263 assert_submodule_exists(g_repo
, name
);
265 cl_git_pass(git_submodule_open(&smrepo
, sm
));
266 cl_git_pass(git_repository_index(&idx
, smrepo
));
268 cl_git_pass(git_str_joinpath(&p
, git_repository_workdir(smrepo
), "file"));
269 cl_git_mkfile(p
.ptr
, "new file");
272 cl_git_pass(git_index_add_bypath(idx
, "file"));
273 cl_git_pass(git_index_write(idx
));
276 cl_repo_commit_from_index(NULL
, smrepo
, NULL
, 0, "initial commit");
277 git_repository_free(smrepo
);
279 cl_git_pass(git_submodule_add_finalize(sm
));
281 git_submodule_free(sm
);
284 void test_submodule_lookup__just_added(void)
287 git_str snap1
= GIT_STR_INIT
, snap2
= GIT_STR_INIT
;
288 git_reference
*original_head
= NULL
;
290 refute_submodule_exists(g_repo
, "sm_just_added", GIT_ENOTFOUND
);
291 refute_submodule_exists(g_repo
, "sm_just_added_2", GIT_ENOTFOUND
);
292 refute_submodule_exists(g_repo
, "sm_just_added_idx", GIT_ENOTFOUND
);
293 refute_submodule_exists(g_repo
, "sm_just_added_head", GIT_ENOTFOUND
);
294 refute_submodule_exists(g_repo
, "mismatch_name", GIT_ENOTFOUND
);
295 refute_submodule_exists(g_repo
, "mismatch_path", GIT_ENOTFOUND
);
298 cl_git_pass(git_futils_readbuffer(&snap1
, "submod2/.gitmodules"));
299 cl_git_pass(git_repository_head(&original_head
, g_repo
));
301 cl_git_pass(git_submodule_add_setup(&sm
, g_repo
,
302 "https://github.com/libgit2/libgit2.git", "sm_just_added", 1));
303 git_submodule_free(sm
);
304 assert_submodule_exists(g_repo
, "sm_just_added");
306 cl_git_pass(git_submodule_add_setup(&sm
, g_repo
,
307 "https://github.com/libgit2/libgit2.git", "sm_just_added_2", 1));
308 assert_submodule_exists(g_repo
, "sm_just_added_2");
309 cl_git_fail(git_submodule_add_finalize(sm
)); /* fails if no HEAD */
310 git_submodule_free(sm
);
312 add_submodule_with_commit("sm_just_added_head");
313 cl_repo_commit_from_index(NULL
, g_repo
, NULL
, 0, "commit new sm to head");
314 assert_submodule_exists(g_repo
, "sm_just_added_head");
316 add_submodule_with_commit("sm_just_added_idx");
317 assert_submodule_exists(g_repo
, "sm_just_added_idx");
319 cl_git_pass(git_futils_readbuffer(&snap2
, "submod2/.gitmodules"));
322 "submod2/.gitmodules",
323 "\n[submodule \"mismatch_name\"]\n"
324 "\tpath = mismatch_path\n"
325 "\turl = https://example.com/example.git\n\n");
327 assert_submodule_exists(g_repo
, "mismatch_name");
328 assert_submodule_exists(g_repo
, "mismatch_path");
329 assert_submodule_exists(g_repo
, "sm_just_added");
330 assert_submodule_exists(g_repo
, "sm_just_added_2");
331 assert_submodule_exists(g_repo
, "sm_just_added_idx");
332 assert_submodule_exists(g_repo
, "sm_just_added_head");
335 cl_git_rewritefile("submod2/.gitmodules", snap2
.ptr
);
336 git_str_dispose(&snap2
);
338 refute_submodule_exists(g_repo
, "mismatch_name", GIT_ENOTFOUND
);
339 refute_submodule_exists(g_repo
, "mismatch_path", GIT_ENOTFOUND
);
340 assert_submodule_exists(g_repo
, "sm_just_added");
341 assert_submodule_exists(g_repo
, "sm_just_added_2");
342 assert_submodule_exists(g_repo
, "sm_just_added_idx");
343 assert_submodule_exists(g_repo
, "sm_just_added_head");
346 cl_git_rewritefile("submod2/.gitmodules", snap1
.ptr
);
347 git_str_dispose(&snap1
);
349 refute_submodule_exists(g_repo
, "mismatch_name", GIT_ENOTFOUND
);
350 refute_submodule_exists(g_repo
, "mismatch_path", GIT_ENOTFOUND
);
351 /* note error code change, because add_setup made a repo in the workdir */
352 refute_submodule_exists(g_repo
, "sm_just_added", GIT_EEXISTS
);
353 refute_submodule_exists(g_repo
, "sm_just_added_2", GIT_EEXISTS
);
354 /* these still exist in index and head respectively */
355 assert_submodule_exists(g_repo
, "sm_just_added_idx");
356 assert_submodule_exists(g_repo
, "sm_just_added_head");
361 cl_git_pass(git_repository_index(&idx
, g_repo
));
362 cl_git_pass(git_index_remove_bypath(idx
, "sm_just_added_idx"));
363 cl_git_pass(git_index_remove_bypath(idx
, "sm_just_added_head"));
364 cl_git_pass(git_index_write(idx
));
368 refute_submodule_exists(g_repo
, "sm_just_added_idx", GIT_EEXISTS
);
369 assert_submodule_exists(g_repo
, "sm_just_added_head");
372 cl_git_pass(git_reference_create(NULL
, g_repo
, "refs/heads/master", git_reference_target(original_head
), 1, "move head back"));
373 git_reference_free(original_head
);
376 refute_submodule_exists(g_repo
, "sm_just_added_head", GIT_EEXISTS
);
379 /* Test_App and Test_App2 are fairly similar names, make sure we load the right one */
380 void test_submodule_lookup__prefix_name(void)
384 cl_git_rewritefile("submod2/.gitmodules",
385 "[submodule \"Test_App\"]\n"
387 " url = ../Test_App\n"
388 "[submodule \"Test_App2\"]\n"
389 " path = Test_App2\n"
390 " url = ../Test_App\n");
392 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "Test_App"));
393 cl_assert_equal_s("Test_App", git_submodule_name(sm
));
395 git_submodule_free(sm
);
397 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "Test_App2"));
398 cl_assert_equal_s("Test_App2", git_submodule_name(sm
));
400 git_submodule_free(sm
);
403 void test_submodule_lookup__renamed(void)
405 const char *newpath
= "sm_actually_changed";
409 cl_git_pass(git_repository_index__weakptr(&idx
, g_repo
));
411 /* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */
413 cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed"));
415 /* Change the path in .gitmodules and stage it*/
419 cl_git_pass(git_config_open_ondisk(&cfg
, "submod2/.gitmodules"));
420 cl_git_pass(git_config_set_string(cfg
, "submodule.sm_unchanged.path", newpath
));
421 git_config_free(cfg
);
423 cl_git_pass(git_index_add_bypath(idx
, ".gitmodules"));
426 /* Change the worktree info in the submodule's config */
430 cl_git_pass(git_config_open_ondisk(&cfg
, "submod2/.git/modules/sm_unchanged/config"));
431 cl_git_pass(git_config_set_string(cfg
, "core.worktree", "../../../sm_actually_changed"));
432 git_config_free(cfg
);
435 /* Rename the entry in the index */
437 const git_index_entry
*e
;
438 git_index_entry entry
= {{ 0 }};
440 e
= git_index_get_bypath(idx
, "sm_unchanged", 0);
442 cl_assert_equal_i(GIT_FILEMODE_COMMIT
, e
->mode
);
444 entry
.path
= newpath
;
445 entry
.mode
= GIT_FILEMODE_COMMIT
;
446 git_oid_cpy(&entry
.id
, &e
->id
);
448 cl_git_pass(git_index_remove(idx
, "sm_unchanged", 0));
449 cl_git_pass(git_index_add(idx
, &entry
));
450 cl_git_pass(git_index_write(idx
));
453 memset(&data
, 0, sizeof(data
));
454 cl_git_pass(git_submodule_foreach(g_repo
, sm_lookup_cb
, &data
));
455 cl_assert_equal_i(8, data
.count
);
458 void test_submodule_lookup__cached(void)
462 /* See that the simple tests still pass. */
464 git_repository_submodule_cache_all(g_repo
);
465 test_submodule_lookup__simple_lookup();
466 git_repository_submodule_cache_clear(g_repo
);
467 test_submodule_lookup__simple_lookup();
469 /* Check that subsequent calls return different objects when cached. */
470 git_repository_submodule_cache_all(g_repo
);
471 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "sm_unchanged"));
472 cl_git_pass(git_submodule_lookup(&sm2
, g_repo
, "sm_unchanged"));
473 cl_assert_equal_p(sm
, sm2
);
474 git_submodule_free(sm2
);
476 /* and that we get new objects again after clearing the cache. */
477 git_repository_submodule_cache_clear(g_repo
);
478 cl_git_pass(git_submodule_lookup(&sm2
, g_repo
, "sm_unchanged"));
479 cl_assert(sm
!= sm2
);
480 git_submodule_free(sm
);
481 git_submodule_free(sm2
);
484 void test_submodule_lookup__lookup_in_bare_repository_fails(void)
488 cl_git_sandbox_cleanup();
489 g_repo
= cl_git_sandbox_init("submodules.git");
491 cl_git_fail(git_submodule_lookup(&sm
, g_repo
, "nonexisting"));
494 void test_submodule_lookup__foreach_in_bare_repository_fails(void)
496 cl_git_sandbox_cleanup();
497 g_repo
= cl_git_sandbox_init("submodules.git");
499 cl_git_fail(git_submodule_foreach(g_repo
, foreach_cb
, NULL
));
502 void test_submodule_lookup__fail_invalid_gitmodules(void)
506 memset(&data
, 0, sizeof(data
));
508 cl_git_rewritefile("submod2/.gitmodules",
509 "[submodule \"Test_App\"\n"
511 " url = ../Test_App\n");
513 cl_git_fail(git_submodule_lookup(&sm
, g_repo
, "Test_App"));
515 cl_git_fail(git_submodule_foreach(g_repo
, sm_lookup_cb
, &data
));