Commit df341926 by Russell Belfer

Merge pull request #2336 from libgit2/rb/unicode-branch-names

Pass unconverted Unicode path data when iconv doesn't like it
parents af567e88 8a2ef218
...@@ -799,8 +799,11 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen) ...@@ -799,8 +799,11 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
if (rv != (size_t)-1) if (rv != (size_t)-1)
break; break;
/* if we cannot convert the data (probably because iconv thinks
* it is not valid UTF-8 source data), then use original data
*/
if (errno != E2BIG) if (errno != E2BIG)
goto fail; return 0;
/* make space for 2x the remaining data to be converted /* make space for 2x the remaining data to be converted
* (with per retry overhead to avoid infinite loops) * (with per retry overhead to avoid infinite loops)
...@@ -823,6 +826,64 @@ fail: ...@@ -823,6 +826,64 @@ fail:
return -1; return -1;
} }
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";
/* 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.
*/
bool git_path_does_fs_decompose_unicode(const char *root)
{
git_buf path = GIT_BUF_INIT;
int fd;
bool found_decomposed = 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, root, nfc_file) < 0 ||
(fd = p_mkstemp(path.ptr)) < 0)
goto done;
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, root, nfd_file) < 0)
goto done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
found_decomposed = git_path_exists(path.ptr);
/* remove temporary file (using original precomposed path) */
if (git_buf_joinpath(&path, root, nfc_file) < 0)
goto done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
(void)p_unlink(path.ptr);
done:
git_buf_free(&path);
return found_decomposed;
}
#else
bool git_path_does_fs_decompose_unicode(const char *root)
{
GIT_UNUSED(root);
return false;
}
#endif #endif
#if defined(__sun) || defined(__GNU__) #if defined(__sun) || defined(__GNU__)
......
...@@ -436,4 +436,6 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen); ...@@ -436,4 +436,6 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
#endif /* GIT_USE_ICONV */ #endif /* GIT_USE_ICONV */
extern bool git_path_does_fs_decompose_unicode(const char *root);
#endif #endif
...@@ -889,60 +889,6 @@ static bool are_symlinks_supported(const char *wd_path) ...@@ -889,60 +889,6 @@ static bool are_symlinks_supported(const char *wd_path)
return symlinks_supported; return symlinks_supported;
} }
#ifdef GIT_USE_ICONV
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";
/* 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 does_fs_decompose_unicode_paths(const char *wd_path)
{
git_buf path = GIT_BUF_INIT;
int fd;
bool found_decomposed = 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 done;
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 done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
found_decomposed = git_path_exists(path.ptr);
/* remove temporary file (using original precomposed path) */
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
goto done;
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
(void)p_unlink(path.ptr);
done:
git_buf_free(&path);
return found_decomposed;
}
#endif
static int create_empty_file(const char *path, mode_t mode) static int create_empty_file(const char *path, mode_t mode)
{ {
int fd; int fd;
...@@ -1033,8 +979,9 @@ static int repo_init_fs_configs( ...@@ -1033,8 +979,9 @@ static int repo_init_fs_configs(
#ifdef GIT_USE_ICONV #ifdef GIT_USE_ICONV
if ((error = git_config_set_bool( if ((error = git_config_set_bool(
cfg, "core.precomposeunicode", cfg, "core.precomposeunicode",
does_fs_decompose_unicode_paths(work_dir))) < 0) git_path_does_fs_decompose_unicode(work_dir))) < 0)
return error; return error;
/* on non-iconv platforms, don't even set core.precomposeunicode */
#endif #endif
return 0; return 0;
......
...@@ -408,7 +408,8 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg) ...@@ -408,7 +408,8 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg)
int val = 0; int val = 0;
git_config *config; git_config *config;
cl_git_pass(git_repository_config(&config, repo)); cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_get_bool(&val, config, cfg));; if (git_config_get_bool(&val, config, cfg) < 0)
giterr_clear();
git_config_free(config); git_config_free(config);
return val; return val;
} }
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "refs.h" #include "refs.h"
#include "path.h"
static git_repository *repo; static git_repository *repo;
static git_commit *target; static git_commit *target;
...@@ -137,3 +138,58 @@ void test_refs_branches_create__default_reflog_message(void) ...@@ -137,3 +138,58 @@ void test_refs_branches_create__default_reflog_message(void)
git_reflog_free(log); git_reflog_free(log);
git_signature_free(sig); git_signature_free(sig);
} }
static void assert_branch_matches_name(
const char *expected, const char *lookup_as)
{
git_reference *ref;
git_buf b = GIT_BUF_INIT;
cl_git_pass(git_branch_lookup(&ref, repo, lookup_as, GIT_BRANCH_LOCAL));
cl_git_pass(git_buf_sets(&b, "refs/heads/"));
cl_git_pass(git_buf_puts(&b, expected));
cl_assert_equal_s(b.ptr, git_reference_name(ref));
cl_git_pass(
git_oid_cmp(git_reference_target(ref), git_commit_id(target)));
git_reference_free(ref);
git_buf_free(&b);
}
void test_refs_branches_create__can_create_branch_with_unicode(void)
{
const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
const char *emoji = "\xF0\x9F\x8D\xB7";
const char *names[] = { nfc, nfd, emoji };
const char *alt[] = { nfd, nfc, NULL };
const char *expected[] = { nfc, nfd, emoji };
unsigned int i;
bool fs_decompose_unicode =
git_path_does_fs_decompose_unicode(git_repository_path(repo));
retrieve_known_commit(&target, repo);
if (cl_repo_get_bool(repo, "core.precomposeunicode"))
expected[1] = nfc;
/* test decomp. because not all Mac filesystems decompose unicode */
else if (fs_decompose_unicode)
expected[0] = nfd;
for (i = 0; i < ARRAY_SIZE(names); ++i) {
cl_git_pass(git_branch_create(
&branch, repo, names[i], target, 0, NULL, NULL));
cl_git_pass(git_oid_cmp(
git_reference_target(branch), git_commit_id(target)));
assert_branch_matches_name(expected[i], names[i]);
if (fs_decompose_unicode && alt[i])
assert_branch_matches_name(expected[i], alt[i]);
cl_git_pass(git_branch_delete(branch));
git_reference_free(branch);
branch = NULL;
}
}
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