1 #include "clar_libgit2.h"
3 #include "status_helpers.h"
4 #include "../submodule/submodule_helpers.h"
6 static git_repository
*g_repo
= NULL
;
8 void test_status_submodules__initialize(void)
12 void test_status_submodules__cleanup(void)
16 void test_status_submodules__api(void)
20 g_repo
= setup_fixture_submodules();
22 cl_assert(git_submodule_lookup(NULL
, g_repo
, "nonexistent") == GIT_ENOTFOUND
);
24 cl_assert(git_submodule_lookup(NULL
, g_repo
, "modified") == GIT_ENOTFOUND
);
26 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "testrepo"));
27 cl_assert(sm
!= NULL
);
28 cl_assert_equal_s("testrepo", git_submodule_name(sm
));
29 cl_assert_equal_s("testrepo", git_submodule_path(sm
));
30 git_submodule_free(sm
);
33 void test_status_submodules__0(void)
37 g_repo
= setup_fixture_submodules();
39 cl_assert(git_path_isdir("submodules/.git"));
40 cl_assert(git_path_isdir("submodules/testrepo/.git"));
41 cl_assert(git_path_isfile("submodules/.gitmodules"));
44 git_status_foreach(g_repo
, cb_status__count
, &counts
)
47 cl_assert_equal_i(6, counts
);
50 static const char *expected_files
[] = {
59 static unsigned int expected_status
[] = {
60 GIT_STATUS_WT_MODIFIED
,
62 GIT_STATUS_INDEX_DELETED
,
64 GIT_STATUS_WT_MODIFIED
,
68 static int cb_status__match(const char *p
, unsigned int s
, void *payload
)
70 status_entry_counts
*counts
= payload
;
71 int idx
= counts
->entry_count
++;
74 counts
->file
, counts
->func
, counts
->line
,
75 "Status path mismatch", 1,
76 "%s", counts
->expected_paths
[idx
], p
);
79 counts
->file
, counts
->func
, counts
->line
,
80 "Status code mismatch", 1,
81 "%o", counts
->expected_statuses
[idx
], s
);
86 void test_status_submodules__1(void)
88 status_entry_counts counts
;
90 g_repo
= setup_fixture_submodules();
92 cl_assert(git_path_isdir("submodules/.git"));
93 cl_assert(git_path_isdir("submodules/testrepo/.git"));
94 cl_assert(git_path_isfile("submodules/.gitmodules"));
96 status_counts_init(counts
, expected_files
, expected_status
);
98 cl_git_pass( git_status_foreach(g_repo
, cb_status__match
, &counts
) );
100 cl_assert_equal_i(6, counts
.entry_count
);
103 void test_status_submodules__single_file(void)
105 unsigned int status
= 0;
106 g_repo
= setup_fixture_submodules();
107 cl_git_pass( git_status_file(&status
, g_repo
, "testrepo") );
111 void test_status_submodules__moved_head(void)
114 git_repository
*smrepo
;
116 git_status_options opts
= GIT_STATUS_OPTIONS_INIT
;
117 status_entry_counts counts
;
118 static const char *expected_files_with_sub
[] = {
127 static unsigned int expected_status_with_sub
[] = {
128 GIT_STATUS_WT_MODIFIED
,
129 GIT_STATUS_INDEX_NEW
,
130 GIT_STATUS_INDEX_DELETED
,
132 GIT_STATUS_WT_MODIFIED
,
133 GIT_STATUS_WT_MODIFIED
,
137 g_repo
= setup_fixture_submodules();
139 cl_git_pass(git_submodule_lookup(&sm
, g_repo
, "testrepo"));
140 cl_git_pass(git_submodule_open(&smrepo
, sm
));
141 git_submodule_free(sm
);
143 /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
145 git_oid_fromstr(&oid
, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
146 cl_git_pass(git_repository_set_head_detached(smrepo
, &oid
));
148 /* first do a normal status, which should now include the submodule */
150 opts
.flags
= GIT_STATUS_OPT_DEFAULTS
;
153 counts
, expected_files_with_sub
, expected_status_with_sub
);
155 git_status_foreach_ext(g_repo
, &opts
, cb_status__match
, &counts
));
156 cl_assert_equal_i(7, counts
.entry_count
);
158 /* try again with EXCLUDE_SUBMODULES which should skip it */
160 opts
.flags
= GIT_STATUS_OPT_DEFAULTS
| GIT_STATUS_OPT_EXCLUDE_SUBMODULES
;
162 status_counts_init(counts
, expected_files
, expected_status
);
164 git_status_foreach_ext(g_repo
, &opts
, cb_status__match
, &counts
));
165 cl_assert_equal_i(6, counts
.entry_count
);
167 git_repository_free(smrepo
);
170 void test_status_submodules__dirty_workdir_only(void)
172 git_status_options opts
= GIT_STATUS_OPTIONS_INIT
;
173 status_entry_counts counts
;
174 static const char *expected_files_with_sub
[] = {
183 static unsigned int expected_status_with_sub
[] = {
184 GIT_STATUS_WT_MODIFIED
,
185 GIT_STATUS_INDEX_NEW
,
186 GIT_STATUS_INDEX_DELETED
,
188 GIT_STATUS_WT_MODIFIED
,
189 GIT_STATUS_WT_MODIFIED
,
193 g_repo
= setup_fixture_submodules();
195 cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
196 cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
198 /* first do a normal status, which should now include the submodule */
200 opts
.flags
= GIT_STATUS_OPT_DEFAULTS
;
203 counts
, expected_files_with_sub
, expected_status_with_sub
);
205 git_status_foreach_ext(g_repo
, &opts
, cb_status__match
, &counts
));
206 cl_assert_equal_i(7, counts
.entry_count
);
208 /* try again with EXCLUDE_SUBMODULES which should skip it */
210 opts
.flags
= GIT_STATUS_OPT_DEFAULTS
| GIT_STATUS_OPT_EXCLUDE_SUBMODULES
;
212 status_counts_init(counts
, expected_files
, expected_status
);
214 git_status_foreach_ext(g_repo
, &opts
, cb_status__match
, &counts
));
215 cl_assert_equal_i(6, counts
.entry_count
);
218 void test_status_submodules__uninitialized(void)
220 git_repository
*cloned_repo
;
221 git_status_list
*statuslist
;
223 g_repo
= cl_git_sandbox_init("submod2");
225 cl_git_pass(git_clone(&cloned_repo
, "submod2", "submod2-clone", NULL
));
227 cl_git_pass(git_status_list_new(&statuslist
, cloned_repo
, NULL
));
228 cl_assert_equal_i(0, git_status_list_entrycount(statuslist
));
230 git_status_list_free(statuslist
);
231 git_repository_free(cloned_repo
);
232 cl_git_sandbox_cleanup();
235 void test_status_submodules__contained_untracked_repo(void)
237 git_status_options opts
= GIT_STATUS_OPTIONS_INIT
;
238 status_entry_counts counts
;
239 git_repository
*contained
;
240 static const char *expected_files_not_ignored
[] = {
247 static unsigned int expected_status_not_ignored
[] = {
248 GIT_STATUS_WT_MODIFIED
,
249 GIT_STATUS_INDEX_NEW
,
250 GIT_STATUS_INDEX_DELETED
,
251 GIT_STATUS_WT_MODIFIED
,
254 static const char *expected_files_with_untracked
[] = {
262 static const char *expected_files_with_untracked_dir
[] = {
270 static unsigned int expected_status_with_untracked
[] = {
271 GIT_STATUS_WT_MODIFIED
,
272 GIT_STATUS_INDEX_NEW
,
273 GIT_STATUS_INDEX_DELETED
,
275 GIT_STATUS_WT_MODIFIED
,
279 g_repo
= setup_fixture_submodules();
281 /* skip empty directory */
283 cl_must_pass(p_mkdir("submodules/dir", 0777));
284 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
;
287 counts
, expected_files_not_ignored
, expected_status_not_ignored
);
288 cl_git_pass(git_status_foreach_ext(
289 g_repo
, &opts
, cb_status__match
, &counts
));
290 cl_assert_equal_i(5, counts
.entry_count
);
292 /* still skipping because empty == ignored */
294 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
295 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
298 counts
, expected_files_not_ignored
, expected_status_not_ignored
);
299 cl_git_pass(git_status_foreach_ext(
300 g_repo
, &opts
, cb_status__match
, &counts
));
301 cl_assert_equal_i(5, counts
.entry_count
);
303 /* find non-ignored contents of directory */
305 cl_git_mkfile("submodules/dir/file.md", "hello");
306 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
307 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
310 counts
, expected_files_with_untracked
, expected_status_with_untracked
);
311 cl_git_pass(git_status_foreach_ext(
312 g_repo
, &opts
, cb_status__match
, &counts
));
313 cl_assert_equal_i(6, counts
.entry_count
);
315 /* but skip if all content is ignored */
317 cl_git_append2file("submodules/.git/info/exclude", "\n*.md\n\n");
318 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
319 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
322 counts
, expected_files_not_ignored
, expected_status_not_ignored
);
323 cl_git_pass(git_status_foreach_ext(
324 g_repo
, &opts
, cb_status__match
, &counts
));
325 cl_assert_equal_i(5, counts
.entry_count
);
327 /* same is true if it contains a git link */
329 cl_git_mkfile("submodules/dir/.git", "gitlink: ../.git");
330 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
331 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
334 counts
, expected_files_not_ignored
, expected_status_not_ignored
);
335 cl_git_pass(git_status_foreach_ext(
336 g_repo
, &opts
, cb_status__match
, &counts
));
337 cl_assert_equal_i(5, counts
.entry_count
);
339 /* but if it contains tracked files, it should just show up as a
340 * directory and exclude the files in it
343 cl_git_mkfile("submodules/dir/another_file", "hello");
344 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
345 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
348 counts
, expected_files_with_untracked_dir
,
349 expected_status_with_untracked
);
350 cl_git_pass(git_status_foreach_ext(
351 g_repo
, &opts
, cb_status__match
, &counts
));
352 cl_assert_equal_i(6, counts
.entry_count
);
354 /* that applies to a git repo with a .git directory too */
356 cl_must_pass(p_unlink("submodules/dir/.git"));
357 cl_git_pass(git_repository_init(&contained
, "submodules/dir", false));
358 git_repository_free(contained
);
359 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
360 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
;
363 counts
, expected_files_with_untracked_dir
,
364 expected_status_with_untracked
);
365 cl_git_pass(git_status_foreach_ext(
366 g_repo
, &opts
, cb_status__match
, &counts
));
367 cl_assert_equal_i(6, counts
.entry_count
);
369 /* same result even if we don't recurse into subdirectories */
371 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
;
374 counts
, expected_files_with_untracked_dir
,
375 expected_status_with_untracked
);
376 cl_git_pass(git_status_foreach_ext(
377 g_repo
, &opts
, cb_status__match
, &counts
));
378 cl_assert_equal_i(6, counts
.entry_count
);
380 /* and if we remove the untracked file, it goes back to ignored */
382 cl_must_pass(p_unlink("submodules/dir/another_file"));
385 counts
, expected_files_not_ignored
, expected_status_not_ignored
);
386 cl_git_pass(git_status_foreach_ext(
387 g_repo
, &opts
, cb_status__match
, &counts
));
388 cl_assert_equal_i(5, counts
.entry_count
);
391 void test_status_submodules__broken_stuff_that_git_allows(void)
393 git_status_options opts
= GIT_STATUS_OPTIONS_INIT
;
394 status_entry_counts counts
;
395 git_repository
*contained
;
396 static const char *expected_files_with_broken
[] = {
405 static unsigned int expected_status_with_broken
[] = {
406 GIT_STATUS_WT_MODIFIED
,
407 GIT_STATUS_INDEX_NEW
,
408 GIT_STATUS_INDEX_NEW
,
409 GIT_STATUS_INDEX_DELETED
,
411 GIT_STATUS_WT_MODIFIED
,
415 g_repo
= setup_fixture_submodules();
417 opts
.flags
= GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
418 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
|
419 GIT_STATUS_OPT_INCLUDE_IGNORED
;
421 /* make a directory and stick a tracked item into the index */
424 cl_must_pass(p_mkdir("submodules/broken", 0777));
425 cl_git_mkfile("submodules/broken/tracked", "tracked content");
426 cl_git_pass(git_repository_index(&idx
, g_repo
));
427 cl_git_pass(git_index_add_bypath(idx
, "broken/tracked"));
428 cl_git_pass(git_index_write(idx
));
433 counts
, expected_files_with_broken
, expected_status_with_broken
);
434 cl_git_pass(git_status_foreach_ext(
435 g_repo
, &opts
, cb_status__match
, &counts
));
436 cl_assert_equal_i(7, counts
.entry_count
);
438 /* directory with tracked items that looks a little bit like a repo */
440 cl_must_pass(p_mkdir("submodules/broken/.git", 0777));
441 cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777));
442 cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus");
445 counts
, expected_files_with_broken
, expected_status_with_broken
);
446 cl_git_pass(git_status_foreach_ext(
447 g_repo
, &opts
, cb_status__match
, &counts
));
448 cl_assert_equal_i(7, counts
.entry_count
);
450 /* directory with tracked items that is a repo */
452 cl_git_pass(git_futils_rmdir_r(
453 "submodules/broken/.git", NULL
, GIT_RMDIR_REMOVE_FILES
));
454 cl_git_pass(git_repository_init(&contained
, "submodules/broken", false));
455 git_repository_free(contained
);
458 counts
, expected_files_with_broken
, expected_status_with_broken
);
459 cl_git_pass(git_status_foreach_ext(
460 g_repo
, &opts
, cb_status__match
, &counts
));
461 cl_assert_equal_i(7, counts
.entry_count
);
463 /* directory with tracked items that claims to be a submodule but is not */
465 cl_git_pass(git_futils_rmdir_r(
466 "submodules/broken/.git", NULL
, GIT_RMDIR_REMOVE_FILES
));
467 cl_git_append2file("submodules/.gitmodules",
468 "\n[submodule \"broken\"]\n"
470 "\turl = https://github.com/not/used\n\n");
473 counts
, expected_files_with_broken
, expected_status_with_broken
);
474 cl_git_pass(git_status_foreach_ext(
475 g_repo
, &opts
, cb_status__match
, &counts
));
476 cl_assert_equal_i(7, counts
.entry_count
);
479 void test_status_submodules__entry_but_dir_tracked(void)
481 git_repository
*repo
;
482 git_status_list
*status
;
487 cl_git_pass(git_repository_init(&repo
, "mixed-submodule", 0));
488 cl_git_mkfile("mixed-submodule/.gitmodules", "[submodule \"sub\"]\n path = sub\n url = ../foo\n");
489 cl_git_pass(p_mkdir("mixed-submodule/sub", 0777));
490 cl_git_mkfile("mixed-submodule/sub/file", "");
492 /* Create the commit with sub/file as a file, and an entry for sub in the modules list */
494 git_oid tree_id
, commit_id
;
498 cl_git_pass(git_repository_index(&index
, repo
));
499 cl_git_pass(git_index_add_bypath(index
, ".gitmodules"));
500 cl_git_pass(git_index_add_bypath(index
, "sub/file"));
501 cl_git_pass(git_index_write(index
));
502 cl_git_pass(git_index_write_tree(&tree_id
, index
));
503 cl_git_pass(git_signature_now(&sig
, "Sloppy Submoduler", "sloppy@example.com"));
504 cl_git_pass(git_tree_lookup(&tree
, repo
, &tree_id
));
505 cl_git_pass(git_commit_create(&commit_id
, repo
, NULL
, sig
, sig
, NULL
, "message", tree
, 0, NULL
));
506 cl_git_pass(git_reference_create(&ref
, repo
, "refs/heads/master", &commit_id
, 1, "commit: foo"));
507 git_reference_free(ref
);
508 git_signature_free(sig
);
511 cl_git_pass(git_diff_tree_to_index(&diff
, repo
, tree
, index
, NULL
));
512 cl_assert_equal_i(0, git_diff_num_deltas(diff
));
515 cl_git_pass(git_diff_index_to_workdir(&diff
, repo
, index
, NULL
));
516 cl_assert_equal_i(0, git_diff_num_deltas(diff
));
519 cl_git_pass(git_status_list_new(&status
, repo
, NULL
));
520 cl_assert_equal_i(0, git_status_list_entrycount(status
));
522 git_status_list_free(status
);
523 git_index_free(index
);
525 git_repository_free(repo
);
528 void test_status_submodules__mixed_case(void)
530 git_status_list
*status
;
531 git_status_options status_opts
= GIT_STATUS_OPTIONS_INIT
;
532 const git_status_entry
*s
;
536 GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
537 GIT_STATUS_OPT_INCLUDE_IGNORED
|
538 GIT_STATUS_OPT_INCLUDE_UNMODIFIED
|
539 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
|
540 GIT_STATUS_OPT_RECURSE_IGNORED_DIRS
|
541 GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
|
542 GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR
|
543 GIT_STATUS_OPT_RENAMES_FROM_REWRITES
|
544 GIT_STATUS_OPT_INCLUDE_UNREADABLE
|
545 GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED
;
547 g_repo
= setup_fixture_submod3();
549 cl_git_pass(git_status_list_new(&status
, g_repo
, &status_opts
));
551 for (i
= 0; i
< git_status_list_entrycount(status
); i
++) {
552 s
= git_status_byindex(status
, i
);
554 if (s
->head_to_index
&&
555 strcmp(s
->head_to_index
->old_file
.path
, ".gitmodules") == 0)
558 cl_assert_equal_i(0, s
->status
);
561 git_status_list_free(status
);