Blame tests/checkout/typechange.c

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
}