]> git.proxmox.com Git - libgit2.git/blobdiff - tests/repo/init.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / tests / repo / init.c
index 56fd6b8b67a212339707a7b722b0cf96687db719..ba00d2918eb0d45181ea1a2608cc95eb74e56e1e 100644 (file)
@@ -1,8 +1,10 @@
 #include "clar_libgit2.h"
-#include "fileops.h"
+#include "futils.h"
 #include "repository.h"
 #include "config.h"
 #include "path.h"
+#include "config/config_helpers.h"
+#include "repo/repo_helpers.h"
 
 enum repo_mode {
        STANDARD_REPOSITORY = 0,
@@ -10,17 +12,23 @@ enum repo_mode {
 };
 
 static git_repository *_repo = NULL;
-static mode_t g_umask = 0;
+static git_buf _global_path = GIT_BUF_INIT;
 
 void test_repo_init__initialize(void)
 {
        _repo = NULL;
 
-       /* load umask if not already loaded */
-       if (!g_umask) {
-               g_umask = p_umask(022);
-               (void)p_umask(g_umask);
-       }
+       git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+                        &_global_path);
+}
+
+void test_repo_init__cleanup(void)
+{
+       git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+               _global_path.ptr);
+       git_buf_dispose(&_global_path);
+
+       cl_fixture_cleanup("tmp_global_path");
 }
 
 static void cleanup_repository(void *path)
@@ -98,7 +106,7 @@ void test_repo_init__bare_repo_escaping_current_workdir(void)
        cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
 
        cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
-       cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
+       cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), GIT_DIR_MODE));
 
        /* Change the current working directory */
        cl_git_pass(chdir(git_buf_cstr(&path_repository)));
@@ -114,8 +122,8 @@ void test_repo_init__bare_repo_escaping_current_workdir(void)
 
        cl_git_pass(chdir(git_buf_cstr(&path_current_workdir)));
 
-       git_buf_free(&path_current_workdir);
-       git_buf_free(&path_repository);
+       git_buf_dispose(&path_current_workdir);
+       git_buf_dispose(&path_repository);
 
        cleanup_repository("a");
 }
@@ -176,7 +184,7 @@ void test_repo_init__additional_templates(void)
        cl_assert(git_path_isdir(git_buf_cstr(&path)));
        /* won't confirm specific contents of hooks dir since it may vary */
 
-       git_buf_free(&path);
+       git_buf_dispose(&path);
 }
 
 static void assert_config_entry_on_init_bytype(
@@ -230,6 +238,68 @@ void test_repo_init__detect_ignorecase(void)
                "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
 }
 
+/*
+ * Windows: if the filesystem supports symlinks (because we're running
+ * as administrator, or because the user has opted into it for normal
+ * users) then we can also opt-in explicitly by settings `core.symlinks`
+ * in the global config.  Symlinks remain off by default.
+ */
+
+void test_repo_init__symlinks_win32_enabled_by_global_config(void)
+{
+#ifndef GIT_WIN32
+       cl_skip();
+#else
+       git_config *config, *repo_config;
+       int val;
+
+       if (!git_path_supports_symlinks("link"))
+               cl_skip();
+
+       create_tmp_global_config("tmp_global_config", "core.symlinks", "true");
+
+       /*
+        * Create a new repository (can't use `assert_config_on_init` since we
+        * want to examine configuration levels with more granularity.)
+        */
+       cl_git_pass(git_repository_init(&_repo, "config_entry/test.non.bare.git", false));
+
+       /* Ensure that core.symlinks remains set (via the global config). */
+       cl_git_pass(git_repository_config(&config, _repo));
+       cl_git_pass(git_config_get_bool(&val, config, "core.symlinks"));
+       cl_assert_equal_i(1, val);
+
+       /*
+        * Ensure that the repository config does not set core.symlinks.
+        * It should remain inherited.
+        */
+       cl_git_pass(git_config_open_level(&repo_config, config, GIT_CONFIG_LEVEL_LOCAL));
+       cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&val, repo_config, "core.symlinks"));
+       git_config_free(repo_config);
+
+       git_config_free(config);
+#endif
+}
+
+void test_repo_init__symlinks_win32_off_by_default(void)
+{
+#ifndef GIT_WIN32
+       cl_skip();
+#else
+       assert_config_entry_on_init("core.symlinks", false);
+#endif
+}
+
+void test_repo_init__symlinks_posix_detected(void)
+{
+#ifdef GIT_WIN32
+       cl_skip();
+#else
+       assert_config_entry_on_init(
+           "core.symlinks", git_path_supports_symlinks("link") ? GIT_ENOTFOUND : false);
+#endif
+}
+
 void test_repo_init__detect_precompose_unicode_required(void)
 {
 #ifdef GIT_USE_ICONV
@@ -311,7 +381,7 @@ void test_repo_init__extended_0(void)
        cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
 
        /* make the directory first, then it should succeed */
-       cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+       cl_git_pass(git_futils_mkdir("extended", 0775, 0));
        cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
 
        cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
@@ -354,7 +424,7 @@ void test_repo_init__extended_1(void)
                cl_assert((S_ISGID & st.st_mode) == 0);
 
        cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
-       cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
+       cl_assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
        cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
        git_reference_free(ref);
 
@@ -370,8 +440,6 @@ void test_repo_init__extended_1(void)
 void test_repo_init__relative_gitdir(void)
 {
        git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-       git_config *cfg;
-       const char *worktree_path;
        git_buf dot_git_content = GIT_BUF_INIT;
 
        opts.workdir_path = "../c_wd";
@@ -391,24 +459,19 @@ void test_repo_init__relative_gitdir(void)
        /* Verify that the gitlink and worktree entries are relative */
 
        /* Verify worktree */
-       cl_git_pass(git_repository_config(&cfg, _repo));
-       cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
-       cl_assert_equal_s("../c_wd/", worktree_path);
+       assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
 
        /* Verify gitlink */
        cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
        cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
 
-       git_buf_free(&dot_git_content);
-       git_config_free(cfg);
+       git_buf_dispose(&dot_git_content);
        cleanup_repository("root");
 }
 
 void test_repo_init__relative_gitdir_2(void)
 {
        git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-       git_config *cfg;
-       const char *worktree_path;
        git_buf dot_git_content = GIT_BUF_INIT;
        git_buf full_path = GIT_BUF_INIT;
 
@@ -423,7 +486,7 @@ void test_repo_init__relative_gitdir_2(void)
 
        /* make the directory first, then it should succeed */
        cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
-       git_buf_free(&full_path);
+       git_buf_dispose(&full_path);
 
        cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
        cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
@@ -433,183 +496,23 @@ void test_repo_init__relative_gitdir_2(void)
        /* Verify that the gitlink and worktree entries are relative */
 
        /* Verify worktree */
-       cl_git_pass(git_repository_config(&cfg, _repo));
-       cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
-       cl_assert_equal_s("../c_wd/", worktree_path);
+       assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
 
        /* Verify gitlink */
        cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
        cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
 
-       git_buf_free(&dot_git_content);
-       git_config_free(cfg);
+       git_buf_dispose(&dot_git_content);
        cleanup_repository("root");
 }
 
-#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
-
-static void assert_hooks_match(
-       const char *template_dir,
-       const char *repo_dir,
-       const char *hook_path,
-       bool core_filemode)
-{
-       git_buf expected = GIT_BUF_INIT;
-       git_buf actual = GIT_BUF_INIT;
-       struct stat expected_st, st;
-
-       cl_git_pass(git_buf_joinpath(&expected, template_dir, hook_path));
-       cl_git_pass(git_path_lstat(expected.ptr, &expected_st));
-
-       cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path));
-       cl_git_pass(git_path_lstat(actual.ptr, &st));
-
-       cl_assert(expected_st.st_size == st.st_size);
-
-       if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
-               mode_t expected_mode =
-                       GIT_MODE_TYPE(expected_st.st_mode) |
-                       (GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);
-
-               if (!core_filemode) {
-                       CLEAR_FOR_CORE_FILEMODE(expected_mode);
-                       CLEAR_FOR_CORE_FILEMODE(st.st_mode);
-               }
-
-               cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
-       }
-
-       git_buf_free(&expected);
-       git_buf_free(&actual);
-}
-
-static void assert_mode_seems_okay(
-       const char *base, const char *path,
-       git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
-{
-       git_buf full = GIT_BUF_INIT;
-       struct stat st;
-
-       cl_git_pass(git_buf_joinpath(&full, base, path));
-       cl_git_pass(git_path_lstat(full.ptr, &st));
-       git_buf_free(&full);
-
-       if (!core_filemode) {
-               CLEAR_FOR_CORE_FILEMODE(expect_mode);
-               CLEAR_FOR_CORE_FILEMODE(st.st_mode);
-               expect_setgid = false;
-       }
-
-       if (S_ISGID != 0)
-               cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);
-
-       cl_assert_equal_b(
-               GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));
-
-       cl_assert_equal_i_fmt(
-               GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
-}
-
-void test_repo_init__extended_with_template(void)
-{
-       git_buf expected = GIT_BUF_INIT;
-       git_buf actual = GIT_BUF_INIT;
-       git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-       int filemode;
-
-       cl_set_cleanup(&cleanup_repository, "templated.git");
-
-       opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
-               GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
-       opts.template_path = cl_fixture("template");
-
-       cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));
-
-       cl_assert(git_repository_is_bare(_repo));
-
-       cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));
-
-       cl_git_pass(git_futils_readbuffer(
-               &expected, cl_fixture("template/description")));
-       cl_git_pass(git_futils_readbuffer(
-               &actual, "templated.git/description"));
-
-       cl_assert_equal_s(expected.ptr, actual.ptr);
-
-       git_buf_free(&expected);
-       git_buf_free(&actual);
-
-       filemode = cl_repo_get_bool(_repo, "core.filemode");
-
-       assert_hooks_match(
-               cl_fixture("template"), git_repository_path(_repo),
-               "hooks/update.sample", filemode);
-
-       assert_hooks_match(
-               cl_fixture("template"), git_repository_path(_repo),
-               "hooks/link.sample", filemode);
-}
-
-void test_repo_init__extended_with_template_and_shared_mode(void)
-{
-       git_buf expected = GIT_BUF_INIT;
-       git_buf actual = GIT_BUF_INIT;
-       git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-       int filemode = true;
-       const char *repo_path = NULL;
-
-       cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl");
-
-       opts.flags = GIT_REPOSITORY_INIT_MKPATH |
-               GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
-       opts.template_path = cl_fixture("template");
-       opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
-
-       cl_git_pass(git_repository_init_ext(&_repo, "init_shared_from_tpl", &opts));
-
-       cl_assert(!git_repository_is_bare(_repo));
-       cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/"));
-
-       filemode = cl_repo_get_bool(_repo, "core.filemode");
-
-       cl_git_pass(git_futils_readbuffer(
-               &expected, cl_fixture("template/description")));
-       cl_git_pass(git_futils_readbuffer(
-               &actual, "init_shared_from_tpl/.git/description"));
-
-       cl_assert_equal_s(expected.ptr, actual.ptr);
-
-       git_buf_free(&expected);
-       git_buf_free(&actual);
-
-       repo_path = git_repository_path(_repo);
-       assert_mode_seems_okay(repo_path, "hooks",
-               GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
-       assert_mode_seems_okay(repo_path, "info",
-               GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
-       assert_mode_seems_okay(repo_path, "description",
-               GIT_FILEMODE_BLOB, false, filemode);
-
-       /* for a non-symlinked hook, it should have shared permissions now */
-       assert_hooks_match(
-               cl_fixture("template"), git_repository_path(_repo),
-               "hooks/update.sample", filemode);
-
-       /* for a symlinked hook, the permissions still should match the
-        * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value
-        */
-       assert_hooks_match(
-               cl_fixture("template"), git_repository_path(_repo),
-               "hooks/link.sample", filemode);
-}
-
 void test_repo_init__can_reinit_an_initialized_repository(void)
 {
        git_repository *reinit;
 
        cl_set_cleanup(&cleanup_repository, "extended");
 
-       cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+       cl_git_pass(git_futils_mkdir("extended", 0775, 0));
        cl_git_pass(git_repository_init(&_repo, "extended", false));
 
        cl_git_pass(git_repository_init(&reinit, "extended", false));
@@ -628,7 +531,7 @@ void test_repo_init__init_with_initial_commit(void)
        /* Initialize the repository */
        cl_git_pass(git_repository_init(&_repo, "committed", 0));
 
-       /* Init will be automatically created when requested for a new repo */
+       /* Index will be automatically created when requested for a new repo */
        cl_git_pass(git_repository_index(&index, _repo));
 
        /* Create a file so we can commit it
@@ -683,3 +586,98 @@ void test_repo_init__init_with_initial_commit(void)
 
        git_index_free(index);
 }
+
+void test_repo_init__at_filesystem_root(void)
+{
+       git_repository *repo;
+       const char *sandbox = clar_sandbox_path();
+       git_buf root = GIT_BUF_INIT;
+       int root_len;
+
+       if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
+               cl_skip();
+
+       root_len = git_path_root(sandbox);
+       cl_assert(root_len >= 0);
+
+       git_buf_put(&root, sandbox, root_len+1);
+       git_buf_joinpath(&root, root.ptr, "libgit2_test_dir");
+
+       cl_assert(!git_path_exists(root.ptr));
+
+       cl_git_pass(git_repository_init(&repo, root.ptr, 0));
+       cl_assert(git_path_isdir(root.ptr));
+       cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+
+       git_buf_dispose(&root);
+       git_repository_free(repo);
+}
+
+void test_repo_init__nonexisting_directory(void)
+{
+       git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+       git_repository *repo;
+
+       /*
+        * If creating a repo with non-existing parent directories, then libgit2
+        * will by default create the complete directory hierarchy if using
+        * `git_repository_init`. Thus, let's use the extended version and not
+        * set the `GIT_REPOSITORY_INIT_MKPATH` flag.
+        */
+       cl_git_fail(git_repository_init_ext(&repo, "nonexisting/path", &opts));
+}
+
+void test_repo_init__nonexisting_root(void)
+{
+#ifdef GIT_WIN32
+       git_repository *repo;
+
+       /*
+        * This really only depends on the nonexistence of the Q: drive. We
+        * cannot implement the equivalent test on Unix systems, as there is
+        * fundamentally no path that is disconnected from the root directory.
+        */
+       cl_git_fail(git_repository_init(&repo, "Q:/non/existent/path", 0));
+       cl_git_fail(git_repository_init(&repo, "Q:\\non\\existent\\path", 0));
+#else
+       clar__skip();
+#endif
+}
+
+void test_repo_init__unwriteable_directory(void)
+{
+#ifndef GIT_WIN32
+       git_repository *repo;
+
+       if (geteuid() == 0)
+               clar__skip();
+
+       /*
+        * Create a non-writeable directory so that we cannot create directories
+        * inside of it. The root user has CAP_DAC_OVERRIDE, so he doesn't care
+        * for the directory permissions and thus we need to skip the test if
+        * run as root user.
+        */
+       cl_must_pass(p_mkdir("unwriteable", 0444));
+       cl_git_fail(git_repository_init(&repo, "unwriteable/repo", 0));
+       cl_must_pass(p_rmdir("unwriteable"));
+#else
+       clar__skip();
+#endif
+}
+
+void test_repo_init__defaultbranch_config(void)
+{
+       git_reference *head;
+
+       cl_set_cleanup(&cleanup_repository, "repo");
+
+       create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
+
+       cl_git_pass(git_repository_init(&_repo, "repo", 0));
+       cl_git_pass(git_reference_lookup(&head, _repo, "HEAD"));
+
+       cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));
+
+       git_reference_free(head);
+}