Initial commit

This commit is contained in:
jiapengyu
2026-07-27 13:37:48 +08:00
commit ecba71e2a5
39123 changed files with 5989154 additions and 0 deletions
+251
View File
@@ -0,0 +1,251 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#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";
void test_submodule_add__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void assert_submodule_url(const char* name, const char *url)
{
git_buf key = GIT_BUF_INIT;
cl_git_pass(git_buf_printf(&key, "submodule.%s.url", name));
assert_config_entry_value(g_repo, git_buf_cstr(&key), url);
git_buf_dispose(&key);
}
void test_submodule_add__url_absolute(void)
{
git_submodule *sm;
git_repository *repo;
git_buf dot_git_content = GIT_BUF_INIT;
g_repo = setup_fixture_submod2();
/* re-add existing submodule */
cl_git_fail_with(
GIT_EEXISTS,
git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1));
/* add a submodule using a gitlink */
cl_git_pass(
git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2", 1)
);
git_submodule_free(sm);
cl_assert(git_path_isfile("submod2/" "sm_libgit2" "/.git"));
cl_assert(git_path_isdir("submod2/.git/modules"));
cl_assert(git_path_isdir("submod2/.git/modules/" "sm_libgit2"));
cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD"));
assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git");
cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2"));
/* Verify worktree path is relative */
assert_config_entry_value(repo, "core.worktree", "../../../sm_libgit2/");
/* Verify gitdir path is relative */
cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git"));
cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr);
git_repository_free(repo);
git_buf_dispose(&dot_git_content);
/* add a submodule not using a gitlink */
cl_git_pass(
git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2b", 0)
);
git_submodule_free(sm);
cl_assert(git_path_isdir("submod2/" "sm_libgit2b" "/.git"));
cl_assert(git_path_isfile("submod2/" "sm_libgit2b" "/.git/HEAD"));
cl_assert(!git_path_exists("submod2/.git/modules/" "sm_libgit2b"));
assert_submodule_url("sm_libgit2b", "https://github.com/libgit2/libgit2.git");
}
void test_submodule_add__url_relative(void)
{
git_submodule *sm;
git_remote *remote;
git_strarray problems = {0};
/* default remote url is https://github.com/libgit2/false.git */
g_repo = cl_git_sandbox_init("testrepo2");
/* make sure we don't default to origin - rename origin -> test_remote */
cl_git_pass(git_remote_rename(&problems, g_repo, "origin", "test_remote"));
cl_assert_equal_i(0, problems.count);
git_strarray_free(&problems);
cl_git_fail(git_remote_lookup(&remote, g_repo, "origin"));
cl_git_pass(
git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1)
);
git_submodule_free(sm);
assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository");
}
void test_submodule_add__url_relative_to_origin(void)
{
git_submodule *sm;
/* default remote url is https://github.com/libgit2/false.git */
g_repo = cl_git_sandbox_init("testrepo2");
cl_git_pass(
git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1)
);
git_submodule_free(sm);
assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository");
}
void test_submodule_add__url_relative_to_workdir(void)
{
git_submodule *sm;
/* In this repo, HEAD (master) has no remote tracking branc h*/
g_repo = cl_git_sandbox_init("testrepo");
cl_git_pass(
git_submodule_add_setup(&sm, g_repo, "./", "TestGitRepository", 1)
);
git_submodule_free(sm);
assert_submodule_url("TestGitRepository", git_repository_workdir(g_repo));
}
static void test_add_entry(
git_index *index,
const char *idstr,
const char *path,
git_filemode_t mode)
{
git_index_entry entry = {{0}};
cl_git_pass(git_oid_fromstr(&entry.id, idstr));
entry.path = path;
entry.mode = mode;
cl_git_pass(git_index_add(index, &entry));
}
void test_submodule_add__path_exists_in_index(void)
{
git_index *index;
git_submodule *sm;
git_buf filename = GIT_BUF_INIT;
g_repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_buf_joinpath(&filename, "subdirectory", "test.txt"));
cl_git_pass(git_repository_index__weakptr(&index, g_repo));
test_add_entry(index, valid_blob_id, filename.ptr, GIT_FILEMODE_BLOB);
cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
git_submodule_free(sm);
git_buf_dispose(&filename);
}
void test_submodule_add__file_exists_in_index(void)
{
git_index *index;
git_submodule *sm;
git_buf name = GIT_BUF_INIT;
g_repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_repository_index__weakptr(&index, g_repo));
test_add_entry(index, valid_blob_id, "subdirectory", GIT_FILEMODE_BLOB);
cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
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);
}
+98
View File
@@ -0,0 +1,98 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "futils.h"
#include "repository.h"
static git_repository *g_repo = NULL;
void test_submodule_escape__cleanup(void)
{
cl_git_sandbox_cleanup();
}
#define EVIL_SM_NAME "../../modules/evil"
#define EVIL_SM_NAME_WINDOWS "..\\\\..\\\\modules\\\\evil"
#define EVIL_SM_NAME_WINDOWS_UNESC "..\\..\\modules\\evil"
static int find_evil(git_submodule *sm, const char *name, void *payload)
{
int *foundit = (int *) payload;
GIT_UNUSED(sm);
if (!git__strcmp(EVIL_SM_NAME, name) ||
!git__strcmp(EVIL_SM_NAME_WINDOWS_UNESC, name))
*foundit = true;
return 0;
}
void test_submodule_escape__from_gitdir(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
unsigned int sm_location;
g_repo = setup_fixture_submodule_simple();
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"" EVIL_SM_NAME "\"]\n"
" path = testrepo\n"
" url = ../testrepo.git\n");
git_buf_dispose(&buf);
/* Find it all the different ways we know about it */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit));
cl_assert_equal_i(0, foundit);
cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME));
/*
* We do know about this as it's in the index and HEAD, but the data is
* incomplete as there is no configured data for it (we pretend it
* doesn't exist). This leaves us with an odd situation but it's
* consistent with what we would do if we did add a submodule with no
* configuration.
*/
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_location(&sm_location, sm));
cl_assert_equal_i(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_HEAD, sm_location);
git_submodule_free(sm);
}
void test_submodule_escape__from_gitdir_windows(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
unsigned int sm_location;
g_repo = setup_fixture_submodule_simple();
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"" EVIL_SM_NAME_WINDOWS "\"]\n"
" path = testrepo\n"
" url = ../testrepo.git\n");
git_buf_dispose(&buf);
/* Find it all the different ways we know about it */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit));
cl_assert_equal_i(0, foundit);
cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME_WINDOWS_UNESC));
/*
* We do know about this as it's in the index and HEAD, but the data is
* incomplete as there is no configured data for it (we pretend it
* doesn't exist). This leaves us with an odd situation but it's
* consistent with what we would do if we did add a submodule with no
* configuration.
*/
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_location(&sm_location, sm));
cl_assert_equal_i(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_HEAD, sm_location);
git_submodule_free(sm);
}
+115
View File
@@ -0,0 +1,115 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "futils.h"
static git_repository *g_repo = NULL;
void test_submodule_init__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_submodule_init__absolute_url(void)
{
git_submodule *sm;
git_config *cfg;
git_buf absolute_url = GIT_BUF_INIT;
const char *config_url;
g_repo = setup_fixture_submodule_simple();
cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
/* write the absolute url to the .gitmodules file*/
cl_git_pass(git_submodule_set_url(g_repo, "testrepo", absolute_url.ptr));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify that the .gitmodules is set with an absolute path*/
cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm));
/* init and verify that absolute path is written to .git/config */
cl_git_pass(git_submodule_init(sm, false));
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
cl_assert_equal_s(absolute_url.ptr, config_url);
git_buf_dispose(&absolute_url);
git_config_free(cfg);
git_submodule_free(sm);
}
void test_submodule_init__relative_url(void)
{
git_submodule *sm;
git_config *cfg;
git_buf absolute_url = GIT_BUF_INIT;
const char *config_url;
g_repo = setup_fixture_submodule_simple();
cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify that the .gitmodules is set with an absolute path*/
cl_assert_equal_s("../testrepo.git", git_submodule_url(sm));
/* init and verify that absolute path is written to .git/config */
cl_git_pass(git_submodule_init(sm, false));
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
cl_assert_equal_s(absolute_url.ptr, config_url);
git_buf_dispose(&absolute_url);
git_config_free(cfg);
git_submodule_free(sm);
}
void test_submodule_init__relative_url_detached_head(void)
{
git_submodule *sm;
git_config *cfg;
git_buf absolute_url = GIT_BUF_INIT;
const char *config_url;
git_reference *head_ref = NULL;
git_object *head_commit = NULL;
g_repo = setup_fixture_submodule_simple();
/* Put the parent repository into a detached head state. */
cl_git_pass(git_repository_head(&head_ref, g_repo));
cl_git_pass(git_reference_peel(&head_commit, head_ref, GIT_OBJECT_COMMIT));
cl_git_pass(git_repository_set_head_detached(g_repo, git_commit_id((git_commit *)head_commit)));
cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify that the .gitmodules is set with an absolute path*/
cl_assert_equal_s("../testrepo.git", git_submodule_url(sm));
/* init and verify that absolute path is written to .git/config */
cl_git_pass(git_submodule_init(sm, false));
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
cl_assert_equal_s(absolute_url.ptr, config_url);
git_buf_dispose(&absolute_url);
git_config_free(cfg);
git_object_free(head_commit);
git_reference_free(head_ref);
git_submodule_free(sm);
}
+80
View File
@@ -0,0 +1,80 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "futils.h"
#include "repository.h"
static git_repository *g_repo = NULL;
void test_submodule_inject_option__initialize(void)
{
g_repo = setup_fixture_submodule_simple();
}
void test_submodule_inject_option__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static int find_naughty(git_submodule *sm, const char *name, void *payload)
{
int *foundit = (int *) payload;
GIT_UNUSED(sm);
if (!git__strcmp("naughty", name))
*foundit = true;
return 0;
}
void test_submodule_inject_option__url(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = testrepo\n"
" url = -u./payload\n");
git_buf_dispose(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("testrepo", git_submodule_path(sm));
cl_assert_equal_p(NULL, git_submodule_url(sm));
git_submodule_free(sm);
}
void test_submodule_inject_option__path(void)
{
int foundit;
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
cl_git_rewritefile(buf.ptr,
"[submodule \"naughty\"]\n"
" path = --something\n"
" url = blah.git\n");
git_buf_dispose(&buf);
/* We do want to find it, but with the appropriate field empty */
foundit = 0;
cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
cl_assert_equal_i(1, foundit);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
cl_assert_equal_s("naughty", git_submodule_path(sm));
cl_assert_equal_s("blah.git", git_submodule_url(sm));
git_submodule_free(sm);
}
+476
View File
@@ -0,0 +1,476 @@
#include "clar_libgit2.h"
#include "submodule_helpers.h"
#include "git2/sys/repository.h"
#include "repository.h"
#include "futils.h"
static git_repository *g_repo = NULL;
void test_submodule_lookup__initialize(void)
{
g_repo = setup_fixture_submod2();
}
void test_submodule_lookup__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_submodule_lookup__simple_lookup(void)
{
assert_submodule_exists(g_repo, "sm_unchanged");
/* lookup pending change in .gitmodules that is not in HEAD */
assert_submodule_exists(g_repo, "sm_added_and_uncommited");
/* lookup pending change in .gitmodules that is not in HEAD nor index */
assert_submodule_exists(g_repo, "sm_gitmodules_only");
/* lookup git repo subdir that is not added as submodule */
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
/* lookup existing directory that is not a submodule */
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
/* lookup existing file that is not a submodule */
refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
/* lookup non-existent item */
refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
/* lookup a submodule by path with a trailing slash */
assert_submodule_exists(g_repo, "sm_added_and_uncommited/");
}
void test_submodule_lookup__accessors(void)
{
git_submodule *sm;
const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_assert(git_submodule_owner(sm) == g_repo);
cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm),
"3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
cl_assert(git_submodule_head_id(sm) == NULL);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm),
"5e4963595a9774b90524d35a807169049de8ccad") == 0);
git_submodule_free(sm);
}
typedef struct {
int count;
} sm_lookup_data;
static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
{
sm_lookup_data *data = payload;
data->count += 1;
cl_assert_equal_s(git_submodule_name(sm), name);
return 0;
}
void test_submodule_lookup__foreach(void)
{
git_config *cfg;
sm_lookup_data data;
memset(&data, 0, sizeof(data));
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(8, data.count);
memset(&data, 0, sizeof(data));
/* Change the path for a submodule so it doesn't match the name */
cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));
git_config_free(cfg);
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(8, data.count);
}
static int foreach_cb(git_submodule *sm, const char *name, void *payload)
{
GIT_UNUSED(sm);
GIT_UNUSED(name);
GIT_UNUSED(payload);
return 0;
}
void test_submodule_lookup__duplicated_path(void)
{
cl_git_rewritefile("submod2/.gitmodules",
"[submodule \"sm1\"]\n"
" path = duplicated-path\n"
" url = sm1\n"
"[submodule \"sm2\"]\n"
" path = duplicated-path\n"
" url = sm2\n");
cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
}
void test_submodule_lookup__lookup_even_with_unborn_head(void)
{
git_reference *head;
/* put us on an unborn branch */
cl_git_pass(git_reference_symbolic_create(
&head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL));
git_reference_free(head);
test_submodule_lookup__simple_lookup(); /* baseline should still pass */
}
void test_submodule_lookup__lookup_even_with_missing_index(void)
{
git_index *idx;
/* give the repo an empty index */
cl_git_pass(git_index_new(&idx));
git_repository_set_index(g_repo, idx);
git_index_free(idx);
test_submodule_lookup__simple_lookup(); /* baseline should still pass */
}
void test_submodule_lookup__backslashes(void)
{
git_config *cfg;
git_submodule *sm;
git_repository *subrepo;
git_buf buf = GIT_BUF_INIT;
const char *backslashed_path = "..\\submod2_target";
cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.url", backslashed_path));
git_config_free(cfg);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_assert_equal_s(backslashed_path, git_submodule_url(sm));
cl_git_pass(git_submodule_open(&subrepo, sm));
cl_git_pass(git_submodule_resolve_url(&buf, g_repo, backslashed_path));
git_buf_dispose(&buf);
git_submodule_free(sm);
git_repository_free(subrepo);
}
static void baseline_tests(void)
{
/* small baseline that should work even if we change the index or make
* commits from the index
*/
assert_submodule_exists(g_repo, "sm_unchanged");
assert_submodule_exists(g_repo, "sm_gitmodules_only");
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
}
static void add_submodule_with_commit(const char *name)
{
git_submodule *sm;
git_repository *smrepo;
git_index *idx;
git_buf p = GIT_BUF_INIT;
cl_git_pass(git_submodule_add_setup(&sm, g_repo,
"https://github.com/libgit2/libgit2.git", name, 1));
assert_submodule_exists(g_repo, name);
cl_git_pass(git_submodule_open(&smrepo, sm));
cl_git_pass(git_repository_index(&idx, smrepo));
cl_git_pass(git_buf_joinpath(&p, git_repository_workdir(smrepo), "file"));
cl_git_mkfile(p.ptr, "new file");
git_buf_dispose(&p);
cl_git_pass(git_index_add_bypath(idx, "file"));
cl_git_pass(git_index_write(idx));
git_index_free(idx);
cl_repo_commit_from_index(NULL, smrepo, NULL, 0, "initial commit");
git_repository_free(smrepo);
cl_git_pass(git_submodule_add_finalize(sm));
git_submodule_free(sm);
}
void test_submodule_lookup__just_added(void)
{
git_submodule *sm;
git_buf snap1 = GIT_BUF_INIT, snap2 = GIT_BUF_INIT;
git_reference *original_head = NULL;
refute_submodule_exists(g_repo, "sm_just_added", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "sm_just_added_2", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "sm_just_added_head", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
baseline_tests();
cl_git_pass(git_futils_readbuffer(&snap1, "submod2/.gitmodules"));
cl_git_pass(git_repository_head(&original_head, g_repo));
cl_git_pass(git_submodule_add_setup(&sm, g_repo,
"https://github.com/libgit2/libgit2.git", "sm_just_added", 1));
git_submodule_free(sm);
assert_submodule_exists(g_repo, "sm_just_added");
cl_git_pass(git_submodule_add_setup(&sm, g_repo,
"https://github.com/libgit2/libgit2.git", "sm_just_added_2", 1));
assert_submodule_exists(g_repo, "sm_just_added_2");
cl_git_fail(git_submodule_add_finalize(sm)); /* fails if no HEAD */
git_submodule_free(sm);
add_submodule_with_commit("sm_just_added_head");
cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit new sm to head");
assert_submodule_exists(g_repo, "sm_just_added_head");
add_submodule_with_commit("sm_just_added_idx");
assert_submodule_exists(g_repo, "sm_just_added_idx");
cl_git_pass(git_futils_readbuffer(&snap2, "submod2/.gitmodules"));
cl_git_append2file(
"submod2/.gitmodules",
"\n[submodule \"mismatch_name\"]\n"
"\tpath = mismatch_path\n"
"\turl = https://example.com/example.git\n\n");
assert_submodule_exists(g_repo, "mismatch_name");
assert_submodule_exists(g_repo, "mismatch_path");
assert_submodule_exists(g_repo, "sm_just_added");
assert_submodule_exists(g_repo, "sm_just_added_2");
assert_submodule_exists(g_repo, "sm_just_added_idx");
assert_submodule_exists(g_repo, "sm_just_added_head");
baseline_tests();
cl_git_rewritefile("submod2/.gitmodules", snap2.ptr);
git_buf_dispose(&snap2);
refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
assert_submodule_exists(g_repo, "sm_just_added");
assert_submodule_exists(g_repo, "sm_just_added_2");
assert_submodule_exists(g_repo, "sm_just_added_idx");
assert_submodule_exists(g_repo, "sm_just_added_head");
baseline_tests();
cl_git_rewritefile("submod2/.gitmodules", snap1.ptr);
git_buf_dispose(&snap1);
refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
/* note error code change, because add_setup made a repo in the workdir */
refute_submodule_exists(g_repo, "sm_just_added", GIT_EEXISTS);
refute_submodule_exists(g_repo, "sm_just_added_2", GIT_EEXISTS);
/* these still exist in index and head respectively */
assert_submodule_exists(g_repo, "sm_just_added_idx");
assert_submodule_exists(g_repo, "sm_just_added_head");
baseline_tests();
{
git_index *idx;
cl_git_pass(git_repository_index(&idx, g_repo));
cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_idx"));
cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_head"));
cl_git_pass(git_index_write(idx));
git_index_free(idx);
}
refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_EEXISTS);
assert_submodule_exists(g_repo, "sm_just_added_head");
{
cl_git_pass(git_reference_create(NULL, g_repo, "refs/heads/master", git_reference_target(original_head), 1, "move head back"));
git_reference_free(original_head);
}
refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS);
}
/* Test_App and Test_App2 are fairly similar names, make sure we load the right one */
void test_submodule_lookup__prefix_name(void)
{
git_submodule *sm;
cl_git_rewritefile("submod2/.gitmodules",
"[submodule \"Test_App\"]\n"
" path = Test_App\n"
" url = ../Test_App\n"
"[submodule \"Test_App2\"]\n"
" path = Test_App2\n"
" url = ../Test_App\n");
cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App"));
cl_assert_equal_s("Test_App", git_submodule_name(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App2"));
cl_assert_equal_s("Test_App2", git_submodule_name(sm));
git_submodule_free(sm);
}
void test_submodule_lookup__renamed(void)
{
const char *newpath = "sm_actually_changed";
git_index *idx;
sm_lookup_data data;
cl_git_pass(git_repository_index__weakptr(&idx, g_repo));
/* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */
cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed"));
/* Change the path in .gitmodules and stage it*/
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.path", newpath));
git_config_free(cfg);
cl_git_pass(git_index_add_bypath(idx, ".gitmodules"));
}
/* Change the worktree info in the submodule's config */
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.git/modules/sm_unchanged/config"));
cl_git_pass(git_config_set_string(cfg, "core.worktree", "../../../sm_actually_changed"));
git_config_free(cfg);
}
/* Rename the entry in the index */
{
const git_index_entry *e;
git_index_entry entry = {{ 0 }};
e = git_index_get_bypath(idx, "sm_unchanged", 0);
cl_assert(e);
cl_assert_equal_i(GIT_FILEMODE_COMMIT, e->mode);
entry.path = newpath;
entry.mode = GIT_FILEMODE_COMMIT;
git_oid_cpy(&entry.id, &e->id);
cl_git_pass(git_index_remove(idx, "sm_unchanged", 0));
cl_git_pass(git_index_add(idx, &entry));
cl_git_pass(git_index_write(idx));
}
memset(&data, 0, sizeof(data));
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(8, data.count);
}
void test_submodule_lookup__cached(void)
{
git_submodule *sm;
git_submodule *sm2;
/* See that the simple tests still pass. */
git_repository_submodule_cache_all(g_repo);
test_submodule_lookup__simple_lookup();
git_repository_submodule_cache_clear(g_repo);
test_submodule_lookup__simple_lookup();
/* Check that subsequent calls return different objects when cached. */
git_repository_submodule_cache_all(g_repo);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
cl_assert_equal_p(sm, sm2);
git_submodule_free(sm2);
/* and that we get new objects again after clearing the cache. */
git_repository_submodule_cache_clear(g_repo);
cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
cl_assert(sm != sm2);
git_submodule_free(sm);
git_submodule_free(sm2);
}
void test_submodule_lookup__lookup_in_bare_repository_fails(void)
{
git_submodule *sm;
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("submodules.git");
cl_git_fail(git_submodule_lookup(&sm, g_repo, "nonexisting"));
}
void test_submodule_lookup__foreach_in_bare_repository_fails(void)
{
cl_git_sandbox_cleanup();
g_repo = cl_git_sandbox_init("submodules.git");
cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
}
void test_submodule_lookup__fail_invalid_gitmodules(void)
{
git_submodule *sm;
sm_lookup_data data;
memset(&data, 0, sizeof(data));
cl_git_rewritefile("submod2/.gitmodules",
"[submodule \"Test_App\"\n"
" path = Test_App\n"
" url = ../Test_App\n");
cl_git_fail(git_submodule_lookup(&sm, g_repo, "Test_App"));
cl_git_fail(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
}
+233
View File
@@ -0,0 +1,233 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "config/config_helpers.h"
static git_repository *g_repo = NULL;
#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
#define SM_LIBGIT2_BRANCH "github-branch"
#define SM_LIBGIT2 "sm_libgit2"
void test_submodule_modify__initialize(void)
{
g_repo = setup_fixture_submod2();
}
static int delete_one_config(const git_config_entry *entry, void *payload)
{
git_config *cfg = payload;
return git_config_delete_entry(cfg, entry->name);
}
static int init_one_submodule(
git_submodule *sm, const char *name, void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(payload);
return git_submodule_init(sm, false);
}
void test_submodule_modify__init(void)
{
git_config *cfg;
const char *str;
/* erase submodule data from .git/config */
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(
git_config_foreach_match(cfg, "submodule\\..*", delete_one_config, cfg));
git_config_free(cfg);
/* confirm no submodule data in config */
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
git_config_free(cfg);
/* call init and see that settings are copied */
cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
/* confirm submodule data in config */
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
git_config_free(cfg);
}
static int sync_one_submodule(
git_submodule *sm, const char *name, void *payload)
{
GIT_UNUSED(name);
GIT_UNUSED(payload);
return git_submodule_sync(sm);
}
static void assert_submodule_url_is_synced(
git_submodule *sm, const char *parent_key, const char *child_key)
{
git_repository *smrepo;
assert_config_entry_value(g_repo, parent_key, git_submodule_url(sm));
cl_git_pass(git_submodule_open(&smrepo, sm));
assert_config_entry_value(smrepo, child_key, git_submodule_url(sm));
git_repository_free(smrepo);
}
void test_submodule_modify__sync(void)
{
git_submodule *sm1, *sm2, *sm3;
git_config *cfg;
const char *str;
#define SM1 "sm_unchanged"
#define SM2 "sm_changed_head"
#define SM3 "sm_added_and_uncommited"
/* look up some submodules */
cl_git_pass(git_submodule_lookup(&sm1, g_repo, SM1));
cl_git_pass(git_submodule_lookup(&sm2, g_repo, SM2));
cl_git_pass(git_submodule_lookup(&sm3, g_repo, SM3));
/* At this point, the .git/config URLs for the submodules have
* not be rewritten with the absolute paths (although the
* .gitmodules have. Let's confirm that they DO NOT match
* yet, then we can do a sync to make them match...
*/
/* check submodule info does not match before sync */
cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
cl_assert(strcmp(git_submodule_url(sm1), str) != 0);
cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
cl_assert(strcmp(git_submodule_url(sm2), str) != 0);
cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
cl_assert(strcmp(git_submodule_url(sm3), str) != 0);
git_config_free(cfg);
/* sync all the submodules */
cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL));
/* check that submodule config is updated */
assert_submodule_url_is_synced(
sm1, "submodule."SM1".url", "remote.origin.url");
assert_submodule_url_is_synced(
sm2, "submodule."SM2".url", "remote.origin.url");
assert_submodule_url_is_synced(
sm3, "submodule."SM3".url", "remote.origin.url");
git_submodule_free(sm1);
git_submodule_free(sm2);
git_submodule_free(sm3);
}
void assert_ignore_change(git_submodule_ignore_t ignore)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", ignore));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(ignore, git_submodule_ignore(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_ignore(void)
{
assert_ignore_change(GIT_SUBMODULE_IGNORE_UNTRACKED);
assert_ignore_change(GIT_SUBMODULE_IGNORE_NONE);
assert_ignore_change(GIT_SUBMODULE_IGNORE_ALL);
}
void assert_update_change(git_submodule_update_t update)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", update));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(update, git_submodule_update_strategy(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_update(void)
{
assert_update_change(GIT_SUBMODULE_UPDATE_REBASE);
assert_update_change(GIT_SUBMODULE_UPDATE_NONE);
assert_update_change(GIT_SUBMODULE_UPDATE_CHECKOUT);
}
void assert_recurse_change(git_submodule_recurse_t recurse)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", recurse));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_i(recurse, git_submodule_fetch_recurse_submodules(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_fetch_recurse_submodules(void)
{
assert_recurse_change(GIT_SUBMODULE_RECURSE_YES);
assert_recurse_change(GIT_SUBMODULE_RECURSE_NO);
assert_recurse_change(GIT_SUBMODULE_RECURSE_ONDEMAND);
}
void test_submodule_modify__set_branch(void)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert(git_submodule_branch(sm) == NULL);
git_submodule_free(sm);
cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", SM_LIBGIT2_BRANCH));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", NULL));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert(git_submodule_branch(sm) == NULL);
git_submodule_free(sm);
}
void test_submodule_modify__set_url(void)
{
git_submodule *sm;
cl_git_pass(git_submodule_set_url(g_repo, "sm_changed_head", SM_LIBGIT2_URL));
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm));
git_submodule_free(sm);
}
void test_submodule_modify__set_relative_url(void)
{
git_buf path = GIT_BUF_INIT;
git_repository *repo;
git_submodule *sm;
cl_git_pass(git_submodule_set_url(g_repo, SM1, "../relative-url"));
cl_git_pass(git_submodule_lookup(&sm, g_repo, SM1));
cl_git_pass(git_submodule_sync(sm));
cl_git_pass(git_submodule_open(&repo, sm));
cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), "relative-url"));
assert_config_entry_value(g_repo, "submodule."SM1".url", path.ptr);
assert_config_entry_value(repo, "remote.origin.url", path.ptr);
git_repository_free(repo);
git_submodule_free(sm);
git_buf_dispose(&path);
}
+130
View File
@@ -0,0 +1,130 @@
/* test the submodule APIs on repositories where there are no submodules */
#include "clar_libgit2.h"
#include "posix.h"
#include "futils.h"
void test_submodule_nosubs__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_submodule_nosubs__lookup(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_submodule *sm = NULL;
p_mkdir("status/subrepo", 0777);
cl_git_mkfile("status/subrepo/.git", "gitdir: ../.git");
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
}
static int fake_submod_cb(git_submodule *sm, const char *n, void *p)
{
GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p);
return 0;
}
void test_submodule_nosubs__foreach(void)
{
git_repository *repo = cl_git_sandbox_init("status");
cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL));
}
void test_submodule_nosubs__add(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_submodule *sm, *sm2;
cl_git_pass(git_submodule_add_setup(&sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1));
cl_git_pass(git_submodule_lookup(&sm2, repo, "submodules/libgit2"));
git_submodule_free(sm2);
cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL));
git_submodule_free(sm);
}
void test_submodule_nosubs__bad_gitmodules(void)
{
git_repository *repo = cl_git_sandbox_init("status");
cl_git_mkfile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=flooble\n\n");
cl_git_rewritefile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=rebase\n\n");
cl_git_pass(git_submodule_lookup(NULL, repo, "foobar"));
cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir"));
}
void test_submodule_nosubs__add_and_delete(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_submodule *sm;
git_buf buf = GIT_BUF_INIT;
cl_git_fail(git_submodule_lookup(NULL, repo, "libgit2"));
cl_git_fail(git_submodule_lookup(NULL, repo, "submodules/libgit2"));
/* create */
cl_git_pass(git_submodule_add_setup(
&sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1));
cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
git_submodule_free(sm);
cl_git_pass(git_futils_readbuffer(&buf, "status/.gitmodules"));
cl_assert(strstr(buf.ptr, "[submodule \"submodules/libgit2\"]") != NULL);
cl_assert(strstr(buf.ptr, "path = submodules/libgit2") != NULL);
git_buf_dispose(&buf);
/* lookup */
cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
git_submodule_free(sm);
/* update name */
cl_git_rewritefile(
"status/.gitmodules",
"[submodule \"libgit2\"]\n"
" path = submodules/libgit2\n"
" url = https://github.com/libgit2/libgit2.git\n");
cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2"));
cl_assert_equal_s("libgit2", git_submodule_name(sm));
cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
git_submodule_free(sm);
/* revert name update */
cl_git_rewritefile(
"status/.gitmodules",
"[submodule \"submodules/libgit2\"]\n"
" path = submodules/libgit2\n"
" url = https://github.com/libgit2/libgit2.git\n");
cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
git_submodule_free(sm);
/* remove completely */
cl_must_pass(p_unlink("status/.gitmodules"));
cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
cl_git_fail(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
}
+90
View File
@@ -0,0 +1,90 @@
#include "clar_libgit2.h"
#include "submodule_helpers.h"
#include "path.h"
static git_repository *g_parent;
static git_repository *g_child;
static git_submodule *g_module;
void test_submodule_open__initialize(void)
{
g_parent = setup_fixture_submod2();
}
void test_submodule_open__cleanup(void)
{
git_submodule_free(g_module);
git_repository_free(g_child);
cl_git_sandbox_cleanup();
g_parent = NULL;
g_child = NULL;
g_module = NULL;
}
static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name)
{
git_buf expected = GIT_BUF_INIT, actual = GIT_BUF_INIT;
/* assert working directory */
cl_git_pass(git_buf_joinpath(&expected, git_repository_workdir(parent), sm_name));
cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
cl_git_pass(git_buf_sets(&actual, git_repository_workdir(child)));
cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
cl_assert_equal_s(expected.ptr, actual.ptr);
git_buf_clear(&expected);
git_buf_clear(&actual);
/* assert common directory */
cl_git_pass(git_buf_joinpath(&expected, git_repository_commondir(parent), "modules"));
cl_git_pass(git_buf_joinpath(&expected, expected.ptr, sm_name));
cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL));
cl_git_pass(git_buf_sets(&actual, git_repository_commondir(child)));
cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
cl_assert_equal_s(expected.ptr, actual.ptr);
/* assert git directory */
cl_git_pass(git_buf_sets(&actual, git_repository_path(child)));
cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL));
cl_assert_equal_s(expected.ptr, actual.ptr);
git_buf_dispose(&expected);
git_buf_dispose(&actual);
}
void test_submodule_open__opening_via_lookup_succeeds(void)
{
cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged"));
cl_git_pass(git_submodule_open(&g_child, g_module));
assert_sm_valid(g_parent, g_child, "sm_unchanged");
}
void test_submodule_open__direct_open_succeeds(void)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
cl_git_pass(git_repository_open(&g_child, path.ptr));
assert_sm_valid(g_parent, g_child, "sm_unchanged");
git_buf_dispose(&path);
}
void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void)
{
git_buf path = GIT_BUF_INIT;
/*
* This is actually not a valid submodule, but we
* encountered at least one occasion where the gitdir
* file existed inside of a submodule's gitdir. As we are
* now able to open these submodules correctly, we still
* add a test for this.
*/
cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git");
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
cl_git_pass(git_repository_open(&g_child, path.ptr));
assert_sm_valid(g_parent, g_child, "sm_unchanged");
git_buf_dispose(&path);
}
+38
View File
@@ -0,0 +1,38 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "config/config_helpers.h"
#include "futils.h"
static git_repository *g_repo = NULL;
void test_submodule_repository_init__basic(void)
{
git_submodule *sm;
git_repository *repo;
git_buf dot_git_content = GIT_BUF_INIT;
g_repo = setup_fixture_submod2();
cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
cl_git_pass(git_submodule_init(sm, 0));
cl_git_pass(git_submodule_repo_init(&repo, sm, 1));
/* Verify worktree */
assert_config_entry_value(repo, "core.worktree", "../../../sm_gitmodules_only/");
/* Verify gitlink */
cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git"));
cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr);
cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git"));
cl_assert(git_path_isdir("submod2/.git/modules"));
cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only"));
cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD"));
git_submodule_free(sm);
git_repository_free(repo);
git_buf_dispose(&dot_git_content);
}
+354
View File
@@ -0,0 +1,354 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "futils.h"
#include "iterator.h"
static git_repository *g_repo = NULL;
void test_submodule_status__initialize(void)
{
g_repo = setup_fixture_submod2();
}
void test_submodule_status__cleanup(void)
{
}
void test_submodule_status__unchanged(void)
{
unsigned int status = get_submodule_status(g_repo, "sm_unchanged");
unsigned int expected =
GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD;
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_assert(expected == status);
}
static void rm_submodule(const char *name)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), name));
cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
git_buf_dispose(&path);
}
static void add_submodule_to_index(const char *name)
{
git_submodule *sm;
cl_git_pass(git_submodule_lookup(&sm, g_repo, name));
cl_git_pass(git_submodule_add_to_index(sm, true));
git_submodule_free(sm);
}
static void rm_submodule_from_index(const char *name)
{
git_index *index;
size_t pos;
cl_git_pass(git_repository_index(&index, g_repo));
cl_assert(!git_index_find(&pos, index, name));
cl_git_pass(git_index_remove(index, name, 0));
cl_git_pass(git_index_write(index));
git_index_free(index);
}
/* 4 values of GIT_SUBMODULE_IGNORE to check */
void test_submodule_status__ignore_none(void)
{
unsigned int status;
rm_submodule("sm_unchanged");
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
status = get_submodule_status(g_repo, "sm_changed_index");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_head");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_file");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_changed_untracked_file");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
status = get_submodule_status(g_repo, "sm_missing_commits");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
status = get_submodule_status(g_repo, "sm_added_and_uncommited");
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */
status = get_submodule_status(g_repo, "sm_unchanged");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
status = get_submodule_status(g_repo, "sm_unchanged");
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
status = get_submodule_status(g_repo, "sm_changed_head");
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
/* remove sm_changed_head from index */
rm_submodule_from_index("sm_changed_head");
status = get_submodule_status(g_repo, "sm_changed_head");
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
}
void test_submodule_status__ignore_untracked(void)
{
unsigned int status;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
rm_submodule("sm_unchanged");
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
}
void test_submodule_status__ignore_dirty(void)
{
unsigned int status;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
rm_submodule("sm_unchanged");
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
/* removed sm_unchanged for deleted workdir */
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
}
void test_submodule_status__ignore_all(void)
{
unsigned int status;
git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
rm_submodule("sm_unchanged");
refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* removed sm_unchanged for deleted workdir */
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* now mkdir sm_unchanged to test uninitialized */
cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
/* update sm_changed_head in index */
add_submodule_to_index("sm_changed_head");
cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
}
typedef struct {
size_t counter;
const char **paths;
int *statuses;
} submodule_expectations;
static int confirm_submodule_status(
const char *path, unsigned int status_flags, void *payload)
{
submodule_expectations *exp = payload;
while (exp->statuses[exp->counter] < 0)
exp->counter++;
cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags);
cl_assert_equal_s(exp->paths[exp->counter++], path);
GIT_UNUSED(status_flags);
return 0;
}
void test_submodule_status__iterator(void)
{
git_iterator *iter;
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
const git_index_entry *entry;
size_t i;
static const char *expected[] = {
".gitmodules",
"just_a_dir/",
"just_a_dir/contents",
"just_a_file",
"not-submodule/",
"not-submodule/README.txt",
"not/",
"not/README.txt",
"README.txt",
"sm_added_and_uncommited",
"sm_changed_file",
"sm_changed_head",
"sm_changed_index",
"sm_changed_untracked_file",
"sm_missing_commits",
"sm_unchanged",
NULL
};
static int expected_flags[] = {
GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */
-1, /* "just_a_dir/" will be skipped */
GIT_STATUS_CURRENT, /* "just_a_dir/contents" */
GIT_STATUS_CURRENT, /* "just_a_file" */
GIT_STATUS_WT_NEW, /* "not-submodule/" untracked item */
-1, /* "not-submodule/README.txt" */
GIT_STATUS_WT_NEW, /* "not/" untracked item */
-1, /* "not/README.txt" */
GIT_STATUS_CURRENT, /* "README.txt */
GIT_STATUS_INDEX_NEW, /* "sm_added_and_uncommited" */
GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */
GIT_STATUS_WT_MODIFIED, /* "sm_changed_head" */
GIT_STATUS_WT_MODIFIED, /* "sm_changed_index" */
GIT_STATUS_WT_MODIFIED, /* "sm_changed_untracked_file" */
GIT_STATUS_WT_MODIFIED, /* "sm_missing_commits" */
GIT_STATUS_CURRENT, /* "sm_unchanged" */
0
};
submodule_expectations exp = { 0, expected, expected_flags };
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
git_index *index;
iter_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
cl_git_pass(git_repository_index(&index, g_repo));
cl_git_pass(git_iterator_for_workdir(&iter, g_repo, index, NULL, &iter_opts));
for (i = 0; !git_iterator_advance(&entry, iter); ++i)
cl_assert_equal_s(expected[i], entry->path);
git_iterator_free(iter);
git_index_free(index);
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
cl_git_pass(git_status_foreach_ext(
g_repo, &opts, confirm_submodule_status, &exp));
}
void test_submodule_status__untracked_dirs_containing_ignored_files(void)
{
unsigned int status, expected;
cl_git_append2file(
"submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n");
cl_git_pass(
git_futils_mkdir_relative("sm_unchanged/directory", "submod2", 0755, 0, NULL));
cl_git_mkfile(
"submod2/sm_unchanged/directory/i_am.ignored",
"ignore this file, please\n");
status = get_submodule_status(g_repo, "sm_unchanged");
cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
expected = GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD;
cl_assert(status == expected);
}
+246
View File
@@ -0,0 +1,246 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "path.h"
#include "util.h"
#include "posix.h"
#include "submodule_helpers.h"
#include "git2/sys/repository.h"
/* rewrite gitmodules -> .gitmodules
* rewrite the empty or relative urls inside each module
* rename the .gitted directory inside any submodule to .git
*/
void rewrite_gitmodules(const char *workdir)
{
git_buf in_f = GIT_BUF_INIT, out_f = GIT_BUF_INIT, path = GIT_BUF_INIT;
FILE *in, *out;
char line[256];
cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules"));
cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules"));
cl_assert((in = fopen(in_f.ptr, "rb")) != NULL);
cl_assert((out = fopen(out_f.ptr, "wb")) != NULL);
while (fgets(line, sizeof(line), in) != NULL) {
char *scan = line;
while (*scan == ' ' || *scan == '\t') scan++;
/* rename .gitted -> .git in submodule directories */
if (git__prefixcmp(scan, "path =") == 0) {
scan += strlen("path =");
while (*scan == ' ') scan++;
git_buf_joinpath(&path, workdir, scan);
git_buf_rtrim(&path);
git_buf_joinpath(&path, path.ptr, ".gitted");
if (!git_buf_oom(&path) && p_access(path.ptr, F_OK) == 0) {
git_buf_joinpath(&out_f, workdir, scan);
git_buf_rtrim(&out_f);
git_buf_joinpath(&out_f, out_f.ptr, ".git");
if (!git_buf_oom(&out_f))
p_rename(path.ptr, out_f.ptr);
}
}
/* copy non-"url =" lines verbatim */
if (git__prefixcmp(scan, "url =") != 0) {
fputs(line, out);
continue;
}
/* convert relative URLs in "url =" lines */
scan += strlen("url =");
while (*scan == ' ') scan++;
if (*scan == '.') {
git_buf_joinpath(&path, workdir, scan);
git_buf_rtrim(&path);
} else if (!*scan || *scan == '\n') {
git_buf_joinpath(&path, workdir, "../testrepo.git");
} else {
fputs(line, out);
continue;
}
git_path_prettify(&path, path.ptr, NULL);
git_buf_putc(&path, '\n');
cl_assert(!git_buf_oom(&path));
fwrite(line, scan - line, sizeof(char), out);
fputs(path.ptr, out);
}
fclose(in);
fclose(out);
cl_must_pass(p_unlink(in_f.ptr));
git_buf_dispose(&in_f);
git_buf_dispose(&out_f);
git_buf_dispose(&path);
}
static void cleanup_fixture_submodules(void *payload)
{
cl_git_sandbox_cleanup(); /* either "submodules" or "submod2" */
if (payload)
cl_fixture_cleanup(payload);
}
git_repository *setup_fixture_submodules(void)
{
git_repository *repo = cl_git_sandbox_init("submodules");
cl_fixture_sandbox("testrepo.git");
rewrite_gitmodules(git_repository_workdir(repo));
p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
git_repository *setup_fixture_submod2(void)
{
git_repository *repo = cl_git_sandbox_init("submod2");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
rewrite_gitmodules(git_repository_workdir(repo));
p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
p_rename("submod2/not/.gitted", "submod2/not/.git");
cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
git_repository *setup_fixture_submod3(void)
{
git_repository *repo = cl_git_sandbox_init("submod3");
cl_fixture_sandbox("submod2_target");
p_rename("submod2_target/.gitted", "submod2_target/.git");
rewrite_gitmodules(git_repository_workdir(repo));
p_rename("submod3/One/.gitted", "submod3/One/.git");
p_rename("submod3/TWO/.gitted", "submod3/TWO/.git");
p_rename("submod3/three/.gitted", "submod3/three/.git");
p_rename("submod3/FoUr/.gitted", "submod3/FoUr/.git");
p_rename("submod3/Five/.gitted", "submod3/Five/.git");
p_rename("submod3/six/.gitted", "submod3/six/.git");
p_rename("submod3/sEvEn/.gitted", "submod3/sEvEn/.git");
p_rename("submod3/EIGHT/.gitted", "submod3/EIGHT/.git");
p_rename("submod3/nine/.gitted", "submod3/nine/.git");
p_rename("submod3/TEN/.gitted", "submod3/TEN/.git");
cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
git_repository *setup_fixture_super(void)
{
git_repository *repo = cl_git_sandbox_init("super");
cl_fixture_sandbox("sub.git");
p_mkdir("super/sub", 0777);
rewrite_gitmodules(git_repository_workdir(repo));
cl_set_cleanup(cleanup_fixture_submodules, "sub.git");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
git_repository *setup_fixture_submodule_simple(void)
{
git_repository *repo = cl_git_sandbox_init("submodule_simple");
cl_fixture_sandbox("testrepo.git");
p_mkdir("submodule_simple/testrepo", 0777);
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
git_repository *setup_fixture_submodule_with_path(void)
{
git_repository *repo = cl_git_sandbox_init("submodule_with_path");
cl_fixture_sandbox("testrepo.git");
p_mkdir("submodule_with_path/lib", 0777);
p_mkdir("submodule_with_path/lib/testrepo", 0777);
cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
cl_git_pass(git_repository_reinit_filesystem(repo, 1));
return repo;
}
void assert__submodule_exists(
git_repository *repo, const char *name,
const char *msg, const char *file, int line)
{
git_submodule *sm;
int error = git_submodule_lookup(&sm, repo, name);
if (error)
cl_git_report_failure(error, 0, file, line, msg);
cl_assert_at_line(sm != NULL, file, line);
git_submodule_free(sm);
}
void refute__submodule_exists(
git_repository *repo, const char *name, int expected_error,
const char *msg, const char *file, int line)
{
clar__assert_equal(
file, line, msg, 1, "%i",
expected_error, (int)(git_submodule_lookup(NULL, repo, name)));
}
unsigned int get_submodule_status(git_repository *repo, const char *name)
{
unsigned int status = 0;
assert(repo && name);
cl_git_pass(git_submodule_status(&status, repo, name, GIT_SUBMODULE_IGNORE_UNSPECIFIED));
return status;
}
static int print_submodules(git_submodule *sm, const char *name, void *p)
{
unsigned int loc = 0;
GIT_UNUSED(p);
git_submodule_location(&loc, sm);
fprintf(stderr, "# submodule %s (at %s) flags %x\n",
name, git_submodule_path(sm), loc);
return 0;
}
void dump_submodules(git_repository *repo)
{
git_submodule_foreach(repo, print_submodules, NULL);
}
@@ -0,0 +1,25 @@
extern void rewrite_gitmodules(const char *workdir);
/* these will automatically set a cleanup callback */
extern git_repository *setup_fixture_submodules(void);
extern git_repository *setup_fixture_submod2(void);
extern git_repository *setup_fixture_submod3(void);
extern git_repository *setup_fixture_submodule_simple(void);
extern git_repository *setup_fixture_super(void);
extern git_repository *setup_fixture_submodule_with_path(void);
extern unsigned int get_submodule_status(git_repository *, const char *);
extern void assert__submodule_exists(
git_repository *, const char *, const char *, const char *, int);
#define assert_submodule_exists(repo,name) \
assert__submodule_exists(repo, name, "git_submodule_lookup(" #name ") failed", __FILE__, __LINE__)
extern void refute__submodule_exists(
git_repository *, const char *, int err, const char *, const char *, int);
#define refute_submodule_exists(repo,name,code) \
refute__submodule_exists(repo, name, code, "expected git_submodule_lookup(" #name ") to fail with error " #code, __FILE__, __LINE__)
extern void dump_submodules(git_repository *repo);
+440
View File
@@ -0,0 +1,440 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
#include "futils.h"
static git_repository *g_repo = NULL;
void test_submodule_update__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_submodule_update__unitialized_submodule_no_init(void)
{
git_submodule *sm;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
g_repo = setup_fixture_submodule_simple();
/* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* updating an unitialized repository throws */
cl_git_fail_with(
GIT_ERROR,
git_submodule_update(sm, 0, &update_options));
git_submodule_free(sm);
}
struct update_submodule_cb_payload {
int update_tips_called;
int checkout_progress_called;
int checkout_notify_called;
};
static void checkout_progress_cb(
const char *path,
size_t completed_steps,
size_t total_steps,
void *payload)
{
struct update_submodule_cb_payload *update_payload = payload;
GIT_UNUSED(path);
GIT_UNUSED(completed_steps);
GIT_UNUSED(total_steps);
update_payload->checkout_progress_called = 1;
}
static int checkout_notify_cb(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload)
{
struct update_submodule_cb_payload *update_payload = payload;
GIT_UNUSED(why);
GIT_UNUSED(path);
GIT_UNUSED(baseline);
GIT_UNUSED(target);
GIT_UNUSED(workdir);
update_payload->checkout_notify_called = 1;
return 0;
}
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
struct update_submodule_cb_payload *update_payload = data;
GIT_UNUSED(refname);
GIT_UNUSED(a);
GIT_UNUSED(b);
update_payload->update_tips_called = 1;
return 1;
}
void test_submodule_update__update_submodule(void)
{
git_submodule *sm;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
struct update_submodule_cb_payload update_payload = { 0 };
g_repo = setup_fixture_submodule_simple();
update_options.checkout_opts.progress_cb = checkout_progress_cb;
update_options.checkout_opts.progress_payload = &update_payload;
update_options.fetch_opts.callbacks.update_tips = update_tips;
update_options.fetch_opts.callbacks.payload = &update_payload;
/* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify the initial state of the submodule */
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
/* initialize and update the submodule */
cl_git_pass(git_submodule_init(sm, 0));
cl_git_pass(git_submodule_update(sm, 0, &update_options));
/* verify state */
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD);
cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
/* verify that the expected callbacks have been called. */
cl_assert_equal_i(1, update_payload.checkout_progress_called);
cl_assert_equal_i(1, update_payload.update_tips_called);
git_submodule_free(sm);
}
void test_submodule_update__update_submodule_with_path(void)
{
git_submodule *sm;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
struct update_submodule_cb_payload update_payload = { 0 };
g_repo = setup_fixture_submodule_with_path();
update_options.checkout_opts.progress_cb = checkout_progress_cb;
update_options.checkout_opts.progress_payload = &update_payload;
update_options.fetch_opts.callbacks.update_tips = update_tips;
update_options.fetch_opts.callbacks.payload = &update_payload;
/* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
/* verify the initial state of the submodule */
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
/* initialize and update the submodule */
cl_git_pass(git_submodule_init(sm, 0));
cl_git_pass(git_submodule_update(sm, 0, &update_options));
/* verify state */
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD);
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
/* verify that the expected callbacks have been called. */
cl_assert_equal_i(1, update_payload.checkout_progress_called);
cl_assert_equal_i(1, update_payload.update_tips_called);
git_submodule_free(sm);
}
void test_submodule_update__update_and_init_submodule(void)
{
git_submodule *sm;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
g_repo = setup_fixture_submodule_simple();
/* get the submodule */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
/* update (with option to initialize sub repo) */
cl_git_pass(git_submodule_update(sm, 1, &update_options));
/* verify expected state */
cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
git_submodule_free(sm);
}
void test_submodule_update__update_already_checked_out_submodule(void)
{
git_submodule *sm = NULL;
git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
git_reference *branch_reference = NULL;
git_object *branch_commit = NULL;
struct update_submodule_cb_payload update_payload = { 0 };
g_repo = setup_fixture_submodule_simple();
update_options.checkout_opts.progress_cb = checkout_progress_cb;
update_options.checkout_opts.progress_payload = &update_payload;
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
cl_git_pass(git_submodule_update(sm, 1, &update_options));
/* verify expected state */
cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
/* checkout the alternate_1 branch */
checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
/*
* Verify state after checkout of parent repository. The submodule ID in the
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS_WD_MODIFIED);
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
/*
* Update the submodule and verify the state.
* Now, the HEAD, index, and Workdir commits should all be updated to
* the new commit.
*/
cl_git_pass(git_submodule_update(sm, 0, &update_options));
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
/* verify that the expected callbacks have been called. */
cl_assert_equal_i(1, update_payload.checkout_progress_called);
git_submodule_free(sm);
git_object_free(branch_commit);
git_reference_free(branch_reference);
}
void test_submodule_update__update_blocks_on_dirty_wd(void)
{
git_submodule *sm = NULL;
git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
git_reference *branch_reference = NULL;
git_object *branch_commit = NULL;
struct update_submodule_cb_payload update_payload = { 0 };
g_repo = setup_fixture_submodule_simple();
update_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
update_options.checkout_opts.notify_cb = checkout_notify_cb;
update_options.checkout_opts.notify_payload = &update_payload;
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
cl_git_pass(git_submodule_update(sm, 1, &update_options));
/* verify expected state */
cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
/* checkout the alternate_1 branch */
checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
/*
* Verify state after checkout of parent repository. The submodule ID in the
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS_WD_MODIFIED);
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
/*
* Create a conflicting edit in the subrepository to verify that
* the submodule update action is blocked.
*/
cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
O_WRONLY | O_CREAT | O_TRUNC, 0755);
cl_git_fail(git_submodule_update(sm, 0, &update_options));
/* verify that the expected callbacks have been called. */
cl_assert_equal_i(1, update_payload.checkout_notify_called);
/* verify that the submodule state has not changed. */
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
git_submodule_free(sm);
git_object_free(branch_commit);
git_reference_free(branch_reference);
}
void test_submodule_update__can_force_update(void)
{
git_submodule *sm = NULL;
git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
unsigned int submodule_status = 0;
git_reference *branch_reference = NULL;
git_object *branch_commit = NULL;
g_repo = setup_fixture_submodule_simple();
/* Initialize and update the sub repository */
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
cl_git_pass(git_submodule_update(sm, 1, &update_options));
/* verify expected state */
cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
/* checkout the alternate_1 branch */
checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
/*
* Verify state after checkout of parent repository. The submodule ID in the
* HEAD commit and index should be updated, but not the workdir.
*/
cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
git_submodule_free(sm);
cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS_IN_CONFIG |
GIT_SUBMODULE_STATUS_IN_WD |
GIT_SUBMODULE_STATUS_WD_MODIFIED);
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
/*
* Create a conflicting edit in the subrepository to verify that
* the submodule update action is blocked.
*/
cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
O_WRONLY | O_CREAT | O_TRUNC, 0777);
/* forcefully checkout and verify the submodule state was updated. */
update_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_submodule_update(sm, 0, &update_options));
cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
git_submodule_free(sm);
git_object_free(branch_commit);
git_reference_free(branch_reference);
}