|
Packit Service |
20376f |
#include "clar_libgit2.h"
|
|
Packit Service |
20376f |
#include "git2/checkout.h"
|
|
Packit Service |
20376f |
#include "path.h"
|
|
Packit Service |
20376f |
#include "posix.h"
|
|
Packit Service |
20376f |
#include "fileops.h"
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static git_repository *g_repo = NULL;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/*
|
|
Packit Service |
20376f |
From the test repo used for this test:
|
|
Packit Service |
20376f |
--------------------------------------
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
This is a test repo for libgit2 where tree entries have type changes
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
The key types that could be found in tree entries are:
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
1 - GIT_FILEMODE_NEW = 0000000
|
|
Packit Service |
20376f |
2 - GIT_FILEMODE_TREE = 0040000
|
|
Packit Service |
20376f |
3 - GIT_FILEMODE_BLOB = 0100644
|
|
Packit Service |
20376f |
4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
|
|
Packit Service |
20376f |
5 - GIT_FILEMODE_LINK = 0120000
|
|
Packit Service |
20376f |
6 - GIT_FILEMODE_COMMIT = 0160000
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
I will try to have every type of transition somewhere in the history
|
|
Packit Service |
20376f |
of this repo.
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
Commits
|
|
Packit Service |
20376f |
-------
|
|
Packit Service |
20376f |
Initial commit - a(1) b(1) c(1) d(1) e(1)
|
|
Packit Service |
20376f |
Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
|
|
Packit Service |
20376f |
Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
|
|
Packit Service |
20376f |
Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
|
|
Packit Service |
20376f |
Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
|
|
Packit Service |
20376f |
Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
|
|
Packit Service |
20376f |
Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
*/
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static const char *g_typechange_oids[] = {
|
|
Packit Service |
20376f |
"79b9f23e85f55ea36a472a902e875bc1121a94cb",
|
|
Packit Service |
20376f |
"9bdb75b73836a99e3dbeea640a81de81031fdc29",
|
|
Packit Service |
20376f |
"0e7ed140b514b8cae23254cb8656fe1674403aff",
|
|
Packit Service |
20376f |
"9d0235c7a7edc0889a18f97a42ee6db9fe688447",
|
|
Packit Service |
20376f |
"9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
|
|
Packit Service |
20376f |
"1b63caae4a5ca96f78e8dfefc376c6a39a142475",
|
|
Packit Service |
20376f |
"6eae26c90e8ccc4d16208972119c40635489c6f0",
|
|
Packit Service |
20376f |
NULL
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static bool g_typechange_empty[] = {
|
|
Packit Service |
20376f |
true, false, false, false, false, false, true, true
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static const int g_typechange_expected_conflicts[] = {
|
|
Packit Service |
20376f |
1, 2, 3, 3, 2, 3, 2
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static const int g_typechange_expected_untracked[] = {
|
|
Packit Service |
20376f |
6, 4, 3, 2, 3, 2, 5
|
|
Packit Service |
20376f |
};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void test_checkout_typechange__initialize(void)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
g_repo = cl_git_sandbox_init("typechanges");
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_fixture_sandbox("submod2_target");
|
|
Packit Service |
20376f |
p_rename("submod2_target/.gitted", "submod2_target/.git");
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void test_checkout_typechange__cleanup(void)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
cl_git_sandbox_cleanup();
|
|
Packit Service |
20376f |
cl_fixture_cleanup("submod2_target");
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void assert_file_exists(const char *path)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
cl_assert_(git_path_isfile(path), path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void assert_dir_exists(const char *path)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
cl_assert_(git_path_isdir(path), path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void assert_workdir_matches_tree(
|
|
Packit Service |
20376f |
git_repository *repo, const git_oid *id, const char *root, bool recurse)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_object *obj;
|
|
Packit Service |
20376f |
git_tree *tree;
|
|
Packit Service |
20376f |
size_t i, max_i;
|
|
Packit Service |
20376f |
git_buf path = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!root)
|
|
Packit Service |
20376f |
root = git_repository_workdir(repo);
|
|
Packit Service |
20376f |
cl_assert(root);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJ_ANY));
|
|
Packit Service |
20376f |
cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJ_TREE));
|
|
Packit Service |
20376f |
git_object_free(obj);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
max_i = git_tree_entrycount(tree);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; i < max_i; ++i) {
|
|
Packit Service |
20376f |
const git_tree_entry *te = git_tree_entry_byindex(tree, i);
|
|
Packit Service |
20376f |
cl_assert(te);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(git_buf_joinpath(&path, root, git_tree_entry_name(te)));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
switch (git_tree_entry_type(te)) {
|
|
Packit Service |
20376f |
case GIT_OBJ_COMMIT:
|
|
Packit Service |
20376f |
assert_dir_exists(path.ptr);
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case GIT_OBJ_TREE:
|
|
Packit Service |
20376f |
assert_dir_exists(path.ptr);
|
|
Packit Service |
20376f |
if (recurse)
|
|
Packit Service |
20376f |
assert_workdir_matches_tree(
|
|
Packit Service |
20376f |
repo, git_tree_entry_id(te), path.ptr, true);
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case GIT_OBJ_BLOB:
|
|
Packit Service |
20376f |
switch (git_tree_entry_filemode(te)) {
|
|
Packit Service |
20376f |
case GIT_FILEMODE_BLOB:
|
|
Packit Service |
20376f |
case GIT_FILEMODE_BLOB_EXECUTABLE:
|
|
Packit Service |
20376f |
assert_file_exists(path.ptr);
|
|
Packit Service |
20376f |
/* because of cross-platform, don't confirm exec bit yet */
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
case GIT_FILEMODE_LINK:
|
|
Packit Service |
20376f |
cl_assert_(git_path_exists(path.ptr), path.ptr);
|
|
Packit Service |
20376f |
/* because of cross-platform, don't confirm link yet */
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
default:
|
|
Packit Service |
20376f |
cl_assert(false); /* really?! */
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
break;
|
|
Packit Service |
20376f |
default:
|
|
Packit Service |
20376f |
cl_assert(false); /* really?!! */
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_tree_free(tree);
|
|
Packit Service |
20376f |
git_buf_free(&path);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void test_checkout_typechange__checkout_typechanges_safe(void)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int i;
|
|
Packit Service |
20376f |
git_object *obj;
|
|
Packit Service |
20376f |
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
|
|
Packit Service |
20376f |
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(
|
|
Packit Service |
20376f |
git_repository_set_head_detached(g_repo, git_object_id(obj)));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_object_free(obj);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
if (!g_typechange_empty[i]) {
|
|
Packit Service |
20376f |
cl_assert(git_path_isdir("typechanges"));
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/a"));
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/b"));
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/c"));
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/d"));
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/e"));
|
|
Packit Service |
20376f |
} else {
|
|
Packit Service |
20376f |
cl_assert(git_path_isdir("typechanges"));
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/a"));
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/b"));
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/c"));
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/d"));
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/e"));
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
typedef struct {
|
|
Packit Service |
20376f |
int conflicts;
|
|
Packit Service |
20376f |
int dirty;
|
|
Packit Service |
20376f |
int updates;
|
|
Packit Service |
20376f |
int untracked;
|
|
Packit Service |
20376f |
int ignored;
|
|
Packit Service |
20376f |
} notify_counts;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int notify_counter(
|
|
Packit Service |
20376f |
git_checkout_notify_t why,
|
|
Packit Service |
20376f |
const char *path,
|
|
Packit Service |
20376f |
const git_diff_file *baseline,
|
|
Packit Service |
20376f |
const git_diff_file *target,
|
|
Packit Service |
20376f |
const git_diff_file *workdir,
|
|
Packit Service |
20376f |
void *payload)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
notify_counts *cts = payload;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GIT_UNUSED(path);
|
|
Packit Service |
20376f |
GIT_UNUSED(baseline);
|
|
Packit Service |
20376f |
GIT_UNUSED(target);
|
|
Packit Service |
20376f |
GIT_UNUSED(workdir);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
switch (why) {
|
|
Packit Service |
20376f |
case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break;
|
|
Packit Service |
20376f |
case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break;
|
|
Packit Service |
20376f |
case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break;
|
|
Packit Service |
20376f |
case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break;
|
|
Packit Service |
20376f |
case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break;
|
|
Packit Service |
20376f |
default: break;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static void force_create_file(const char *file)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int error = git_futils_rmdir_r(file, NULL,
|
|
Packit Service |
20376f |
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
|
|
Packit Service |
20376f |
cl_assert(!error || error == GIT_ENOTFOUND);
|
|
Packit Service |
20376f |
cl_git_pass(git_futils_mkpath2file(file, 0777));
|
|
Packit Service |
20376f |
cl_git_rewritefile(file, "yowza!!");
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
git_buf submodulepath = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
git_buf dirtypath = GIT_BUF_INIT;
|
|
Packit Service |
20376f |
git_repository *submodule_repo;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
GIT_UNUSED(name);
|
|
Packit Service |
20376f |
GIT_UNUSED(payload);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* remove submodule directory in preparation for init and repo_init */
|
|
Packit Service |
20376f |
cl_git_pass(git_buf_joinpath(
|
|
Packit Service |
20376f |
&submodulepath,
|
|
Packit Service |
20376f |
git_repository_workdir(g_repo),
|
|
Packit Service |
20376f |
git_submodule_path(sm)
|
|
Packit Service |
20376f |
));
|
|
Packit Service |
20376f |
git_futils_rmdir_r(git_buf_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* initialize submodule's repository */
|
|
Packit Service |
20376f |
cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
/* create a file in the submodule workdir to make it dirty */
|
|
Packit Service |
20376f |
cl_git_pass(
|
|
Packit Service |
20376f |
git_buf_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty"));
|
|
Packit Service |
20376f |
force_create_file(git_buf_cstr(&dirtypath));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_buf_free(&dirtypath);
|
|
Packit Service |
20376f |
git_buf_free(&submodulepath);
|
|
Packit Service |
20376f |
git_repository_free(submodule_repo);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
return 0;
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
void test_checkout_typechange__checkout_with_conflicts(void)
|
|
Packit Service |
20376f |
{
|
|
Packit Service |
20376f |
int i;
|
|
Packit Service |
20376f |
git_object *obj;
|
|
Packit Service |
20376f |
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
|
|
Packit Service |
20376f |
notify_counts cts = {0};
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
opts.notify_flags =
|
|
Packit Service |
20376f |
GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED;
|
|
Packit Service |
20376f |
opts.notify_cb = notify_counter;
|
|
Packit Service |
20376f |
opts.notify_payload = &cts;
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
for (i = 0; g_typechange_oids[i] != NULL; ++i) {
|
|
Packit Service |
20376f |
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
force_create_file("typechanges/a/blocker");
|
|
Packit Service |
20376f |
force_create_file("typechanges/b");
|
|
Packit Service |
20376f |
force_create_file("typechanges/c/sub/sub/file");
|
|
Packit Service |
20376f |
git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
|
|
Packit Service |
20376f |
p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
|
|
Packit Service |
20376f |
force_create_file("typechanges/untracked");
|
|
Packit Service |
20376f |
cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
|
Packit Service |
20376f |
memset(&cts, 0, sizeof(cts));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
|
|
Packit Service |
20376f |
cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]);
|
|
Packit Service |
20376f |
cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]);
|
|
Packit Service |
20376f |
cl_assert_equal_i(cts.dirty, 0);
|
|
Packit Service |
20376f |
cl_assert_equal_i(cts.updates, 0);
|
|
Packit Service |
20376f |
cl_assert_equal_i(cts.ignored, 0);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
opts.checkout_strategy =
|
|
Packit Service |
20376f |
GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
|
|
Packit Service |
20376f |
memset(&cts, 0, sizeof(cts));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_assert(git_path_exists("typechanges/untracked"));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
|
|
Packit Service |
20376f |
cl_assert_equal_i(0, cts.conflicts);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_assert(!git_path_exists("typechanges/untracked"));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
cl_git_pass(
|
|
Packit Service |
20376f |
git_repository_set_head_detached(g_repo, git_object_id(obj)));
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
|
|
Packit Service |
20376f |
|
|
Packit Service |
20376f |
git_object_free(obj);
|
|
Packit Service |
20376f |
}
|
|
Packit Service |
20376f |
}
|