Commit e4ac4000 by Edward Thomson

checkout tests: test symlinks based on support, not platform

When testing whether symlinks are correctly checked out, examine the
`core.symlinks` configuration option to determine if symlinks are
supported in a repository, don't simply assume that Windows means that
symbolic links are not supported.

Further, when testing the expected default behavior of `core.symlinks`,
test the filesystem's support to determine if symlinks are supported.

Finally, ensure that `core.symlinks=true` fails on a system where
symlinks are actually not supported.  This aligns with the behavior of
Git for Windows.
parent 7b6875f4
......@@ -2,6 +2,7 @@
#include "git2/repository.h"
#include "git2/sys/index.h"
#include "fileops.h"
#include "repository.h"
static git_repository *g_repo;
static git_index *g_index;
......@@ -184,28 +185,35 @@ static void ensure_workdir(const char *path, int mode, const char *oid_str)
ensure_workdir_oid(path, oid_str);
}
static void ensure_workdir_link(const char *path, const char *target)
static void ensure_workdir_link(
git_repository *repo,
const char *path,
const char *target)
{
#ifdef GIT_WIN32
ensure_workdir_contents(path, target);
#else
git_buf fullpath = GIT_BUF_INIT;
char actual[1024];
struct stat st;
int len;
int symlinks;
cl_git_pass(
git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
cl_git_pass(git_repository__cvar(&symlinks, repo, GIT_CVAR_SYMLINKS));
cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st));
cl_assert(S_ISLNK(st.st_mode));
if (!symlinks) {
ensure_workdir_contents(path, target);
} else {
git_buf fullpath = GIT_BUF_INIT;
char actual[1024];
struct stat st;
int len;
cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0);
actual[len] = '\0';
cl_assert(strcmp(actual, target) == 0);
cl_git_pass(
git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
git_buf_dispose(&fullpath);
#endif
cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st));
cl_assert(S_ISLNK(st.st_mode));
cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0);
actual[len] = '\0';
cl_assert(strcmp(actual, target) == 0);
git_buf_dispose(&fullpath);
}
}
void test_checkout_conflict__ignored(void)
......@@ -415,8 +423,8 @@ void test_checkout_conflict__links(void)
cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
/* Conflicts with links always keep the ours side (even with -Xtheirs) */
ensure_workdir_link("link-1", LINK_OURS_TARGET);
ensure_workdir_link("link-2", LINK_OURS_TARGET);
ensure_workdir_link(g_repo, "link-1", LINK_OURS_TARGET);
ensure_workdir_link(g_repo, "link-2", LINK_OURS_TARGET);
}
void test_checkout_conflict__add_add(void)
......@@ -684,7 +692,7 @@ void test_checkout_conflict__renames(void)
void test_checkout_conflict__rename_keep_ours(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
struct checkout_index_entry checkout_index_entries[] = {
{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
......@@ -728,122 +736,122 @@ void test_checkout_conflict__rename_keep_ours(void)
{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
};
struct checkout_name_entry checkout_name_entries[] = {
{
"3a-renamed-in-ours-deleted-in-theirs.txt",
"3a-newname-in-ours-deleted-in-theirs.txt",
""
},
{
"3b-renamed-in-theirs-deleted-in-ours.txt",
"",
"3b-newname-in-theirs-deleted-in-ours.txt"
},
{
"4a-renamed-in-ours-added-in-theirs.txt",
"4a-newname-in-ours-added-in-theirs.txt",
""
},
{
"4b-renamed-in-theirs-added-in-ours.txt",
"",
"4b-newname-in-theirs-added-in-ours.txt"
},
{
"5a-renamed-in-ours-added-in-theirs.txt",
"5a-newname-in-ours-added-in-theirs.txt",
"5a-renamed-in-ours-added-in-theirs.txt"
},
{
"5b-renamed-in-theirs-added-in-ours.txt",
"5b-renamed-in-theirs-added-in-ours.txt",
"5b-newname-in-theirs-added-in-ours.txt"
},
{
"6-both-renamed-1-to-2.txt",
"6-both-renamed-1-to-2-ours.txt",
"6-both-renamed-1-to-2-theirs.txt"
},
{
"7-both-renamed-side-1.txt",
"7-both-renamed.txt",
"7-both-renamed-side-1.txt"
},
{
"7-both-renamed-side-2.txt",
"7-both-renamed-side-2.txt",
"7-both-renamed.txt"
}
};
opts.checkout_strategy |= GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
create_index(checkout_index_entries, 41);
create_index_names(checkout_name_entries, 9);
cl_git_pass(git_index_write(g_index));
cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
ensure_workdir("0a-no-change.txt",
0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
ensure_workdir("0b-duplicated-in-ours.txt",
0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
ensure_workdir("0b-rewritten-in-ours.txt",
0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e");
ensure_workdir("0c-duplicated-in-theirs.txt",
0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
ensure_workdir("0c-rewritten-in-theirs.txt",
0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09");
ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
ensure_workdir("1a-newname-in-ours.txt",
0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
ensure_workdir("1b-newname-in-theirs.txt",
0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
ensure_workdir("2-newname-in-both.txt",
0100644, "178940b450f238a56c0d75b7955cb57b38191982");
ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
ensure_workdir("4a-newname-in-ours-added-in-theirs.txt",
0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
ensure_workdir("4b-newname-in-theirs-added-in-ours.txt",
0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
ensure_workdir("5a-newname-in-ours-added-in-theirs.txt",
0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
ensure_workdir("5b-newname-in-theirs-added-in-ours.txt",
0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
ensure_workdir("6-both-renamed-1-to-2-ours.txt",
0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
ensure_workdir("7-both-renamed.txt",
0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
}
......
......@@ -3,6 +3,7 @@
#include "git2/checkout.h"
#include "refs.h"
#include "path.h"
#include "repository.h"
#ifdef GIT_WIN32
# include <windows.h>
......@@ -91,6 +92,18 @@ static void assert_name_is(const char *expected)
free(actual);
}
static int symlink_or_fake(git_repository *repo, const char *a, const char *b)
{
int symlinks;
cl_git_pass(git_repository__cvar(&symlinks, repo, GIT_CVAR_SYMLINKS));
if (symlinks)
return p_symlink(a, b);
else
return git_futils_fake_symlink(a, b);
}
void test_checkout_icase__refuses_to_overwrite_files_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
......@@ -117,7 +130,7 @@ void test_checkout_icase__refuses_to_overwrite_links_for_files(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(p_symlink("../tmp", "testrepo/BRANCH_FILE.txt"));
cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/BRANCH_FILE.txt"));
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
......@@ -129,7 +142,7 @@ void test_checkout_icase__overwrites_links_for_files_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_must_pass(p_symlink("../tmp", "testrepo/NEW.txt"));
cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/NEW.txt"));
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
......@@ -205,7 +218,7 @@ void test_checkout_icase__refuses_to_overwrite_links_for_folders(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
cl_must_pass(p_symlink("..", "testrepo/A"));
cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
......@@ -217,7 +230,7 @@ void test_checkout_icase__overwrites_links_for_folders_when_forced(void)
{
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_must_pass(p_symlink("..", "testrepo/A"));
cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
......
......@@ -136,6 +136,25 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
#endif
}
static bool supports_symlinks(const char *dir)
{
git_buf path = GIT_BUF_INIT;
struct stat st;
bool supports_symlinks = 1;
cl_git_pass(git_buf_joinpath(&path, dir, "test"));
/* see if symlinks are supported in the "symlink" directory */
if (p_symlink("target", path.ptr) < 0 ||
p_lstat(path.ptr, &st) < 0 ||
! (S_ISLNK(st.st_mode)))
supports_symlinks = 0;
git_buf_dispose(&path);
return supports_symlinks;
}
void test_checkout_index__honor_coresymlinks_default(void)
{
git_repository *repo;
......@@ -162,10 +181,9 @@ void test_checkout_index__honor_coresymlinks_default(void)
git_object_free(target);
git_repository_free(repo);
#ifdef GIT_WIN32
check_file_contents("./symlink/link_to_new.txt", "new.txt");
#else
{
if (!supports_symlinks("symlink")) {
check_file_contents("./symlink/link_to_new.txt", "new.txt");
} else {
char link_data[1024];
size_t link_size = 1024;
......@@ -175,14 +193,33 @@ void test_checkout_index__honor_coresymlinks_default(void)
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./symlink/link_to_new.txt", "my new file\n");
}
#endif
cl_fixture_cleanup("symlink");
}
void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
if (supports_symlinks("testrepo")) {
cl_skip();
}
cl_repo_set_bool(g_repo, "core.symlinks", true);
opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
}
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
char link_data[GIT_PATH_MAX];
size_t link_size = GIT_PATH_MAX;
if (!supports_symlinks("testrepo")) {
cl_skip();
}
cl_repo_set_bool(g_repo, "core.symlinks", true);
......@@ -190,20 +227,11 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
#ifdef GIT_WIN32
check_file_contents("./testrepo/link_to_new.txt", "new.txt");
#else
{
char link_data[1024];
size_t link_size = 1024;
link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
}
#endif
link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
}
void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
......@@ -474,7 +502,7 @@ void test_checkout_index__can_overcome_name_clashes(void)
cl_assert(git_path_isfile("./testrepo/path0/file0"));
opts.checkout_strategy =
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_SAFE |
GIT_CHECKOUT_RECREATE_MISSING |
GIT_CHECKOUT_ALLOW_CONFLICTS;
cl_git_pass(git_checkout_index(g_repo, index, &opts));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment