diff --git a/src/repository.c b/src/repository.c index 0d7c09484..64f13978d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -843,10 +843,6 @@ fail: static bool is_chmod_supported(const char *file_path) { struct stat st1, st2; - static int _is_supported = -1; - - if (_is_supported > -1) - return _is_supported; if (p_stat(file_path, &st1) < 0) return false; @@ -857,27 +853,19 @@ static bool is_chmod_supported(const char *file_path) if (p_stat(file_path, &st2) < 0) return false; - _is_supported = (st1.st_mode != st2.st_mode); - - return _is_supported; + return (st1.st_mode != st2.st_mode); } static bool is_filesystem_case_insensitive(const char *gitdir_path) { git_buf path = GIT_BUF_INIT; - static int _is_insensitive = -1; + int is_insensitive = -1; - if (_is_insensitive > -1) - return _is_insensitive; + if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg")) + is_insensitive = git_path_exists(git_buf_cstr(&path)); - if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0) - goto cleanup; - - _is_insensitive = git_path_exists(git_buf_cstr(&path)); - -cleanup: git_buf_free(&path); - return _is_insensitive; + return is_insensitive; } static bool are_symlinks_supported(const char *wd_path) @@ -885,24 +873,69 @@ static bool are_symlinks_supported(const char *wd_path) git_buf path = GIT_BUF_INIT; int fd; struct stat st; - static int _symlinks_supported = -1; - - if (_symlinks_supported > -1) - return _symlinks_supported; + int symlinks_supported = -1; if ((fd = git_futils_mktmp(&path, wd_path)) < 0 || p_close(fd) < 0 || p_unlink(path.ptr) < 0 || p_symlink("testing", path.ptr) < 0 || p_lstat(path.ptr, &st) < 0) - _symlinks_supported = false; + symlinks_supported = false; else - _symlinks_supported = (S_ISLNK(st.st_mode) != 0); + symlinks_supported = (S_ISLNK(st.st_mode) != 0); (void)p_unlink(path.ptr); git_buf_free(&path); - return _symlinks_supported; + return symlinks_supported; +} + +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. + */ +static bool should_precompose_unicode_paths(const char *wd_path) +{ + git_buf path = GIT_BUF_INIT; + int fd; + bool need_precompose = false; + char tmp[6]; + + /* Create a file using a precomposed path and then try to find it + * using the decomposed name. If the lookup fails, then we will mark + * that we should precompose unicode for this repository. + */ + if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 || + (fd = p_mkstemp(path.ptr)) < 0) + goto fail; + p_close(fd); + + /* record trailing digits generated by mkstemp */ + memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); + + /* try to look up as NFD path */ + if (git_buf_joinpath(&path, wd_path, nfd_file) < 0) + goto fail; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + need_precompose = !git_path_exists(path.ptr); + + /* remove temporary file */ + if (git_buf_joinpath(&path, wd_path, nfc_file) < 0) + goto fail; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + (void)p_unlink(path.ptr); + +fail: + git_buf_free(&path); + return need_precompose; } static int create_empty_file(const char *path, mode_t mode) @@ -930,6 +963,7 @@ static int repo_init_config( int error = 0; git_buf cfg_path = GIT_BUF_INIT; git_config *config = NULL; + bool is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0); #define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \ @@ -954,17 +988,23 @@ static int repo_init_config( goto cleanup; SET_REPO_CONFIG( - bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0); + bool, "core.bare", is_bare); SET_REPO_CONFIG( int32, "core.repositoryformatversion", GIT_REPO_VERSION); SET_REPO_CONFIG( bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path))); - if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) { - SET_REPO_CONFIG(bool, "core.logallrefupdates", true); +#if __APPLE__ + SET_REPO_CONFIG( + bool, "core.precomposeunicode", + should_precompose_unicode_paths(is_bare ? repo_dir : work_dir)); +#endif - if (!are_symlinks_supported(work_dir)) - SET_REPO_CONFIG(bool, "core.symlinks", false); + if (!are_symlinks_supported(is_bare ? repo_dir : work_dir)) + SET_REPO_CONFIG(bool, "core.symlinks", false); + + if (!is_bare) { + SET_REPO_CONFIG(bool, "core.logallrefupdates", true); if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { SET_REPO_CONFIG(string, "core.worktree", work_dir); @@ -973,9 +1013,6 @@ static int repo_init_config( if (git_config_delete_entry(config, "core.worktree") < 0) giterr_clear(); } - } else { - if (!are_symlinks_supported(repo_dir)) - SET_REPO_CONFIG(bool, "core.symlinks", false); } if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) && diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c index 392be205b..6f3c84175 100644 --- a/tests-clar/repo/init.c +++ b/tests-clar/repo/init.c @@ -241,6 +241,16 @@ void test_repo_init__detect_ignorecase(void) #endif } +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); +#else + assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND); +#endif +} + void test_repo_init__reinit_doesnot_overwrite_ignorecase(void) { git_config *config;