mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-04 13:33:41 +00:00

Untangle git_futils_mkdir from git_futils_mkdir_ext - the latter assumes that we own everything beneath the base, as if it were being called with a base of the repository or working directory, and is tailored towards checkout and ensuring that there is no bogosity beneath the base that must be cleaned up. This is (at best) slow and (at worst) unsafe in the larger context of a filesystem where we do not own things and cannot do things like unlink symlinks that are in our way.
355 lines
12 KiB
C
355 lines
12 KiB
C
#include "clar_libgit2.h"
|
|
#include "posix.h"
|
|
#include "path.h"
|
|
#include "submodule_helpers.h"
|
|
#include "fileops.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_free(&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);
|
|
}
|