Update repo init with fewer platform assumptions The repo init code was assuming Windows == no filemode, and Mac or Windows == no case sensitivity. Those assumptions are not consistently true depending on the mounted file system. This is a first step to removing those assumptions. It focuses on the repo init code and the tests of that code. There are still many other tests that are broken when those assumptions don't hold true, but this clears up one area of the code. Also, this moves the core.precomposeunicode logic to be closer to the current logic in core Git where it will be set to true on any filesystem where composed unicode is decomposed when read back.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
diff --git a/src/repository.c b/src/repository.c
index 51cc76d..f609e57 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -895,18 +895,20 @@ static bool are_symlinks_supported(const char *wd_path)
static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
-/* On Mac, HDFS always stores files using decomposed unicode, but when
- * writing to VFAT or SAMBA file systems, filenames may be kept as
- * precomposed unicode, but will be converted to decomposed form when
- * reading the directory entries. This can cause file name mismatches.
- * The solution is to convert directory entries to precomposed form if we
- * cannot look up the file from the decomposed path.
+/* Check if the platform is decomposing unicode data for us. We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
*/
-static bool should_precompose_unicode_paths(const char *wd_path)
+static bool does_fs_decompose_unicode_paths(const char *wd_path)
{
git_buf path = GIT_BUF_INIT;
int fd;
- bool need_precompose = false;
+ bool found_decomposed = false;
char tmp[6];
/* Create a file using a precomposed path and then try to find it
@@ -915,7 +917,7 @@ static bool should_precompose_unicode_paths(const char *wd_path)
*/
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
(fd = p_mkstemp(path.ptr)) < 0)
- goto fail;
+ goto done;
p_close(fd);
/* record trailing digits generated by mkstemp */
@@ -923,21 +925,21 @@ static bool should_precompose_unicode_paths(const char *wd_path)
/* try to look up as NFD path */
if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
- goto fail;
+ goto done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
- need_precompose = !git_path_exists(path.ptr);
+ found_decomposed = git_path_exists(path.ptr);
- /* remove temporary file */
+ /* remove temporary file (using original precomposed path) */
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
- goto fail;
+ goto done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
(void)p_unlink(path.ptr);
-fail:
+done:
git_buf_free(&path);
- return need_precompose;
+ return found_decomposed;
}
#endif
@@ -1001,7 +1003,7 @@ static int repo_init_config(
#ifdef GIT_USE_ICONV
SET_REPO_CONFIG(
bool, "core.precomposeunicode",
- should_precompose_unicode_paths(is_bare ? repo_dir : work_dir));
+ does_fs_decompose_unicode_paths(is_bare ? repo_dir : work_dir));
#endif
if (!are_symlinks_supported(is_bare ? repo_dir : work_dir))
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
index 6f3c841..061e444 100644
--- a/tests-clar/repo/init.c
+++ b/tests-clar/repo/init.c
@@ -179,41 +179,32 @@ void test_repo_init__additional_templates(void)
git_buf_free(&path);
}
-static void assert_config_entry_on_init_bytype(const char *config_key, int expected_value, bool is_bare)
+static void assert_config_entry_on_init_bytype(
+ const char *config_key, int expected_value, bool is_bare)
{
git_config *config;
- int current_value;
- git_buf repo_path = GIT_BUF_INIT;
+ int error, current_value;
+ const char *repo_path = is_bare ?
+ "config_entry/test.bare.git" : "config_entry/test.non.bare.git";
cl_set_cleanup(&cleanup_repository, "config_entry");
- cl_git_pass(git_buf_puts(&repo_path, "config_entry/test."));
-
- if (!is_bare)
- cl_git_pass(git_buf_puts(&repo_path, "non."));
-
- cl_git_pass(git_buf_puts(&repo_path, "bare.git"));
-
- cl_git_pass(git_repository_init(&_repo, git_buf_cstr(&repo_path), is_bare));
-
- git_buf_free(&repo_path);
+ cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
- git_repository_config(&config, _repo);
+ cl_git_pass(git_repository_config(&config, _repo));
+ error = git_config_get_bool(¤t_value, config, config_key);
+ git_config_free(config);
if (expected_value >= 0) {
- cl_git_pass(git_config_get_bool(¤t_value, config, config_key));
-
+ cl_assert_equal_i(0, error);
cl_assert_equal_i(expected_value, current_value);
} else {
- int error = git_config_get_bool(¤t_value, config, config_key);
-
cl_assert_equal_i(expected_value, error);
}
-
- git_config_free(config);
}
-static void assert_config_entry_on_init(const char *config_key, int expected_value)
+static void assert_config_entry_on_init(
+ const char *config_key, int expected_value)
{
assert_config_entry_on_init_bytype(config_key, expected_value, true);
git_repository_free(_repo);
@@ -221,31 +212,47 @@ static void assert_config_entry_on_init(const char *config_key, int expected_val
assert_config_entry_on_init_bytype(config_key, expected_value, false);
}
-void test_repo_init__detect_filemode(void)
+static int expect_filemode_support(void)
{
-#ifdef GIT_WIN32
- assert_config_entry_on_init("core.filemode", false);
-#else
- assert_config_entry_on_init("core.filemode", true);
-#endif
+ struct stat st;
+
+ cl_git_write2file("testmode", "whatever\n", 0, O_CREAT | O_WRONLY, 0767);
+ cl_must_pass(p_stat("testmode", &st));
+ cl_must_pass(p_unlink("testmode"));
+
+ return (st.st_mode & 0111) == 0101;
}
-#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
+void test_repo_init__detect_filemode(void)
+{
+ assert_config_entry_on_init("core.filemode", expect_filemode_support());
+}
void test_repo_init__detect_ignorecase(void)
{
-#if CASE_INSENSITIVE_FILESYSTEM
- assert_config_entry_on_init("core.ignorecase", true);
-#else
- assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
-#endif
+ struct stat st;
+ bool found_without_match;
+
+ cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_without_match = (p_stat("Testcaps", &st) == 0);
+ cl_must_pass(p_unlink("testCAPS"));
+
+ assert_config_entry_on_init(
+ "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
}
void test_repo_init__detect_precompose_unicode_required(void)
{
-#ifdef __APPLE__
- /* hard to test "true" case without SAMBA or VFAT file system available */
- assert_config_entry_on_init("core.precomposeunicode", false);
+ char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn";
+ struct stat st;
+ bool found_with_nfd;
+
+ cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_with_nfd = (p_stat(decomposed, &st) == 0);
+ cl_must_pass(p_unlink(composed));
+
+#ifdef GIT_USE_ICONV
+ assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
#else
assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
#endif
@@ -280,13 +287,7 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
void test_repo_init__reinit_overwrites_filemode(void)
{
- int expected, current_value;
-
-#ifdef GIT_WIN32
- expected = false;
-#else
- expected = true;
-#endif
+ int expected = expect_filemode_support(), current_value;
/* Init a new repo */
cl_set_cleanup(&cleanup_repository, "overwrite.git");
@@ -358,7 +359,10 @@ void test_repo_init__extended_1(void)
cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
cl_assert(S_ISDIR(st.st_mode));
- cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+ if (expect_filemode_support())
+ cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+ else
+ 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);