{"thread":{"id":"35779","subject":"[PATCH 1/2] init-db.c: honor case on case preserving fs","startedAt":"2014-02-01T09:14:26Z","lastAt":"2014-02-02T18:08:46Z","messageCount":6,"participants":["Reuben Hawkins","Torsten Bögershausen","Dmitry Potapov","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"234017","messageId":"1391246067-30499-1-git-send-email-reubenhwk@gmail.com","threadId":"35779","inReplyTo":null,"subject":"[PATCH 1/2] init-db.c: honor case on case preserving fs","fromName":"Reuben Hawkins","fromEmail":"reubenhwk@gmail.com","sentAt":"2014-02-01T09:14:26Z","receivedAt":"2014-02-01T09:14:26Z","isPatch":true,"sender":{"key":"reubenhwk@gmail.com","avatar":"https://avatars.githubusercontent.com/u/201595?v=4"},"body":"Most case-insensitive filesystems are case-preserving. In these\nfilesystems (such as HFS+ on OS X) you can name a file Filename.txt,\nthen rename the file to FileName.txt.  That file will be accessible\nby both filenames, but the case is otherwise honored.  We don't want\nto have git ignore case on these case-preserving filesystem\nimplementations.\n\nThis change adds an additional check in init-db.c before\nautomatically setting core.ignorecase to true.\n\nThis fixes a problem where if you import an hg repository, using\ngit-remote-hg, on two OSX systems, one with a case-sensitive fs and\nthe other with case-insensitive fs, the sha1 commit ids of the\nrepositories diverge on commits where a file was renamed, but only\nthe case in the filename changes (for example renaming\nFilename.cpp -> FileName.cpp).\n\nThe alternative solutions are\n* to set ignore_case to 0 in fast-import.c at runtime\n* explicitly use strcmp, rather than strcmp_icase (also in\n  fast-import.c)\n* completely rework ignorecase into something that can handle more\n  options (true, false, sometimes, maybe, partially, etc...)\n\nSigned-off-by: Reuben Hawkins <reubenhwk@gmail.com>\n---\n builtin/init-db.c |   43 +++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 41 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 78aa387..34f09d8 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -288,8 +288,47 @@ static int create_default_files(const char *template_path)\n \t\t/* Check if the filesystem is case-insensitive */\n \t\tpath[len] = 0;\n \t\tstrcpy(path + len, \"CoNfIg\");\n-\t\tif (!access(path, F_OK))\n-\t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n+\t\tif (!access(path, F_OK)) {\n+\t\t\t/*\n+\t\t\t * This filesystem is at least partially case-insensitive.  Let's\n+\t\t\t * find out if this filesystem is completely case-insensitive.\n+\t\t\t *\n+\t\t\t * Create a CamelCase file here, make sure readdir reads a\n+\t\t\t * CamelCase file below.\n+\t\t\t */\n+\t\t\tint completely_insensitive_fs = 1;\n+\t\t\tchar const * const case_check_filename = \".CaseCheck\";\n+\t\t\tstruct dirent *dirent;\n+\t\t\tFILE *case_file;\n+\t\t\tDIR *dir;\n+\n+\t\t\tpath[len] = 0;\n+\t\t\tstrcpy(path + len, case_check_filename);\n+\t\t\tcase_file = fopen(path, \"w\");\n+\t\t\tif (!case_file)\n+\t\t\t\tdie_errno(_(\"cannot open '%s'\"), path);\n+\t\t\tfclose(case_file);\n+\n+\t\t\tpath[len] = 0;\n+\t\t\tdir = opendir(path);\n+\t\t\tif (!dir)\n+\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), path);\n+\n+\t\t\twhile ( (dirent = readdir(dir)) ) {\n+\t\t\t\tif (0 == strcmp(case_check_filename, dirent->d_name)) {\n+\t\t\t\t\tcompletely_insensitive_fs = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tclosedir(dir);\n+\t\t\tpath[len] = 0;\n+\t\t\tstrcpy(path + len, case_check_filename);\n+\t\t\tunlink(path);\n+\n+\t\t\tif (completely_insensitive_fs)\n+\t\t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n+\t\t}\n \t\tprobe_utf8_pathname_composition(path, len);\n \t}\n \n-- \n1.7.9.5\n"},{"id":"234018","messageId":"1391246067-30499-2-git-send-email-reubenhwk@gmail.com","threadId":"35779","inReplyTo":"1391246067-30499-1-git-send-email-reubenhwk@gmail.com","subject":"[PATCH 2/2] init-db.c: factor out probe_case_sensitivity func","fromName":"Reuben Hawkins","fromEmail":"reubenhwk@gmail.com","sentAt":"2014-02-01T09:14:27Z","receivedAt":"2014-02-01T09:14:27Z","isPatch":true,"sender":{"key":"reubenhwk@gmail.com","avatar":"https://avatars.githubusercontent.com/u/201595?v=4"},"body":"create_default_files was getting a bit long and indented.  This\nparticular bit of code got long enough to be its own function.\n\nSigned-off-by: Reuben Hawkins <reubenhwk@gmail.com>\n---\n builtin/init-db.c |   97 +++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 34f09d8..bcf7f4c 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -177,6 +177,58 @@ static int git_init_db_config(const char *k, const char *v, void *cb)\n \treturn 0;\n }\n \n+static void probe_case_sensitivity(char *path, size_t len)\n+{\n+\tint completely_insensitive_fs = 1;\n+\tchar const * const case_check_filename = \".CaseCheck\";\n+\tstruct dirent *dirent;\n+\tFILE *case_file;\n+\tDIR *dir;\n+\n+\t/*\n+\t * If we can't access \"config\" by the name \"CoNfIg\", this fs is\n+\t * case sensitive...so do nothing and return.\n+\t */\n+\tpath[len] = 0;\n+\tstrcpy(path + len, \"CoNfIg\");\n+\tif (access(path, F_OK))\n+\t\treturn;\n+\n+\t/*\n+\t * This filesystem is at least partially case-insensitive.  Let's\n+\t * find out if this filesystem is completely case-insensitive.\n+\t *\n+\t * Create a CamelCase file here, make sure readdir reads a\n+\t * CamelCase file below.\n+\t */\n+\tpath[len] = 0;\n+\tstrcpy(path + len, case_check_filename);\n+\tcase_file = fopen(path, \"w\");\n+\tif (!case_file)\n+\t\tdie_errno(_(\"cannot open '%s'\"), path);\n+\tfclose(case_file);\n+\n+\tpath[len] = 0;\n+\tdir = opendir(path);\n+\tif (!dir)\n+\t\tdie_errno(_(\"cannot opendir '%s'\"), path);\n+\n+\twhile ( (dirent = readdir(dir)) ) {\n+\t\tif (0 == strcmp(case_check_filename, dirent->d_name)) {\n+\t\t\tcompletely_insensitive_fs = 0;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tclosedir(dir);\n+\tpath[len] = 0;\n+\tstrcpy(path + len, case_check_filename);\n+\tunlink(path);\n+\n+\tif (completely_insensitive_fs)\n+\t\tgit_config_set(\"core.ignorecase\", \"true\");\n+}\n+\n static int create_default_files(const char *template_path)\n {\n \tconst char *git_dir = get_git_dir();\n@@ -285,50 +337,7 @@ static int create_default_files(const char *template_path)\n \t\telse\n \t\t\tgit_config_set(\"core.symlinks\", \"false\");\n \n-\t\t/* Check if the filesystem is case-insensitive */\n-\t\tpath[len] = 0;\n-\t\tstrcpy(path + len, \"CoNfIg\");\n-\t\tif (!access(path, F_OK)) {\n-\t\t\t/*\n-\t\t\t * This filesystem is at least partially case-insensitive.  Let's\n-\t\t\t * find out if this filesystem is completely case-insensitive.\n-\t\t\t *\n-\t\t\t * Create a CamelCase file here, make sure readdir reads a\n-\t\t\t * CamelCase file below.\n-\t\t\t */\n-\t\t\tint completely_insensitive_fs = 1;\n-\t\t\tchar const * const case_check_filename = \".CaseCheck\";\n-\t\t\tstruct dirent *dirent;\n-\t\t\tFILE *case_file;\n-\t\t\tDIR *dir;\n-\n-\t\t\tpath[len] = 0;\n-\t\t\tstrcpy(path + len, case_check_filename);\n-\t\t\tcase_file = fopen(path, \"w\");\n-\t\t\tif (!case_file)\n-\t\t\t\tdie_errno(_(\"cannot open '%s'\"), path);\n-\t\t\tfclose(case_file);\n-\n-\t\t\tpath[len] = 0;\n-\t\t\tdir = opendir(path);\n-\t\t\tif (!dir)\n-\t\t\t\tdie_errno(_(\"cannot opendir '%s'\"), path);\n-\n-\t\t\twhile ( (dirent = readdir(dir)) ) {\n-\t\t\t\tif (0 == strcmp(case_check_filename, dirent->d_name)) {\n-\t\t\t\t\tcompletely_insensitive_fs = 0;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tclosedir(dir);\n-\t\t\tpath[len] = 0;\n-\t\t\tstrcpy(path + len, case_check_filename);\n-\t\t\tunlink(path);\n-\n-\t\t\tif (completely_insensitive_fs)\n-\t\t\t\tgit_config_set(\"core.ignorecase\", \"true\");\n-\t\t}\n+\t\tprobe_case_sensitivity(path, len);\n \t\tprobe_utf8_pathname_composition(path, len);\n \t}\n \n-- \n1.7.9.5\n"},{"id":"234020","messageId":"52ECE5EB.6090304@web.de","threadId":"35779","inReplyTo":"1391246067-30499-1-git-send-email-reubenhwk@gmail.com","subject":"Re: [PATCH 1/2] init-db.c: honor case on case preserving fs","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2014-02-01T12:17:47Z","receivedAt":"2014-02-01T12:17:47Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On 2014-02-01 10.14, Reuben Hawkins wrote:\n> Most case-insensitive filesystems are case-preserving. In these\n> filesystems (such as HFS+ on OS X) you can name a file Filename.txt,\n> then rename the file to FileName.txt.  That file will be accessible\n> by both filenames, but the case is otherwise honored.  We don't want\n> to have git ignore case on these case-preserving filesystem\n> implementations.\n\nYes, we want.\nBecause the file system will treat \"Filename.txt\" and \"FileName.txt\"\nthe same.\nWhatever is on disc, the OS will not distinguish them.\n(On a case-insensitive HFS+ partition).\n\nAnd when core.ignorecase == true, Git does the same what the OS does,\nignore the case.\n\nCould you describe the problems more in detail ?\n\nCould you supply a test case, (or a short script) which shows\nthe problem and makes it reproducable for others?\n\nWhich problems does your patch solve, which can not be solved\nby setting core.ignorecase==false manually?\n \n/Torsten\n"},{"id":"234021","messageId":"CAHkcotgdEKkZZ756LHxD0fum0_z43cerQE_yZR0VSnPZOKtKpA@mail.gmail.com","threadId":"35779","inReplyTo":"CAD_8n+RWNZkGO31XveDuSy2aXv5uAMy087AUUu2+wXtO=MngAg@mail.gmail.com","subject":"Re: [PATCH 1/2] init-db.c: honor case on case preserving fs","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2014-02-01T23:47:44Z","receivedAt":"2014-02-01T23:47:44Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"On Sat, Feb 1, 2014 at 1:14 PM, Reuben Hawkins <reubenhwk@gmail.com> wrote:\n> Most case-insensitive filesystems are case-preserving. In these\n> filesystems (such as HFS+ on OS X) you can name a file Filename.txt,\n> then rename the file to FileName.txt.  That file will be accessible\n> by both filenames, but the case is otherwise honored.  We don't want\n> to have git ignore case on these case-preserving filesystem\n> implementations.\n\nActually, we want to ignore case on ANY case-insensitive file system whether\nit is a case-preserving or not.\n\nLet's suppose we have a central repository where Filename.txt is renamed\nas FileName.txt. Now a user pulls changes from that repository on Windows.\n\nWith core.ignorecase = true, everything works perfectly:\n\n$ git pull\nremote: Counting objects: 3, done.\nremote: Total 2 (delta 0), reused 0 (delta 0)\nUnpacking objects: 100% (2/2), done.\nFrom z:/shared/test2/\n   3d885e6..ce5deef  master     -> origin/master\nUpdating 3d885e6..ce5deef\nFast-forward\n Filename.txt => FileName.txt | 0\n 1 file changed, 0 insertions(+), 0 deletions(-)\n rename Filename.txt => FileName.txt (100%)\n\n$ ls\nFileName.txt\n\nNow, if you try to do the same thing with core.ignorecase = false,\nyou get this:\n\n$ git pull\nremote: Counting objects: 3, done.\nremote: Total 2 (delta 0), reused 0 (delta 0)\nUnpacking objects: 100% (2/2), done.\nFrom z:/shared/test2/\n   3d885e6..ce5deef  master     -> origin/master\nUpdating 3d885e6..ce5deef\nerror: The following untracked working tree files would be overwritten by merge:\n        FileName.txt\nPlease move or remove them before you can merge.\nAborting\n\nI have tested this with git version 1.8.3.msysgit.0, but I don't think\nit is any different with the latest version.\n\n> This fixes a problem where if you import an hg repository, using\n> git-remote-hg,\n\nPerhaps, it fixes that case, but it breaks a far more common case.\nSo I believe that git-remote-hg should be corrected and not init-db.c\n\n\nDmitry\n"},{"id":"234029","messageId":"CAHkcoth+4d1PsSaoUEFcLVbE-q7bhVBWut1WRyEKCOxSJ0tTwQ@mail.gmail.com","threadId":"35779","inReplyTo":"CAD_8n+TQ4i2Z5zePXCTqpdF8mpRrzzKjDUB-NxZ1PJAwek-y1w@mail.gmail.com","subject":"Re: [PATCH 1/2] init-db.c: honor case on case preserving fs","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2014-02-02T05:46:55Z","receivedAt":"2014-02-02T05:46:55Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"On 2/2/14, Reuben Hawkins <reubenhwk@gmail.com> wrote:\n>>\n> This is a separate issue which core.ignorecase is sweeping under the rug.\n>  When you get this error message, do an 'ls' and you'll see there isn't an\n> untracked file called 'FileName.txt'.  There is, however, a tracked file\n> called 'Filename.txt'.  Because there aren't really any untracked files,\n> the merge should be able to complete without overwriting anything.\n\nI am afraid but it is exactly the same issue, because it never happens\non any case-sensitive system. IIRC, Git stats 'FileName.txt' to verify\nwhether it exists or not, and the file system tells that this file\nexists, because it ignores case. So the option is called 'ignorecase'.\n\nNo one meant this option to be about whether the file system preserves\ncase or not, because Git does not care about it and assumes that on\nany case-insensitive filesystem, the right case is in the index, which is\nusually better, because otherwise people can often change filenames\ninadvertently.\n\nI don't know much about importers, but I have looked at the source code\nof fast-import.c and I do not see ignore_case there, though it may affect\nit indirectly through some functions that it uses. AFAIK, fast-import does\nnot rely on the filesystem, it should always work with ignorecase=false.\n\n\nDmitry\n"},{"id":"234056","messageId":"xmqq7g9dgzf5.fsf@gitster.dls.corp.google.com","threadId":"35779","inReplyTo":"52ECE5EB.6090304@web.de","subject":"Re: [PATCH 1/2] init-db.c: honor case on case preserving fs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-02-02T18:08:46Z","receivedAt":"2014-02-02T18:08:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Torsten Bögershausen <tboegi@web.de> writes:\n\n> On 2014-02-01 10.14, Reuben Hawkins wrote:\n>> Most case-insensitive filesystems are case-preserving. In these\n>> filesystems (such as HFS+ on OS X) you can name a file Filename.txt,\n>> then rename the file to FileName.txt.  That file will be accessible\n>> by both filenames, but the case is otherwise honored.  We don't want\n>> to have git ignore case on these case-preserving filesystem\n>> implementations.\n>\n> Yes, we want.\n> Because the file system will treat \"Filename.txt\" and \"FileName.txt\"\n> the same.\n\nAnother important thing to remember is that we cannot have these two\nfiles at the same time on such a filesystem.\n\nSomebody may have Filename.txt in the commit at the tip of the\nhistory, you clone/fetch and check it out, and you will have\nFilename.txt with the original contents.  We do not try to corrupt\nthe filename on core.ignorecase filesystem by any canonicalization.\n\nBut then you may edit that file, and you either deliberately or\nwithout knowing (because some of your tools do this behind your\nback) may end up saving the result as FileName.txt.  What happens?\n\nWhen we ask \"what is the contents of Filename.txt now?\" (using the\noriginal name still in the index) to the underlying system, we will\nbe given what you placed in FileName.txt.  We won't see \"You do not\nhave Filename.txt, but you now have FileName.txt\".\n\nAnd that is the behaviour the end users (of not Git, but of a\nplatform with such a filesystem) do expect from their tools.  They\ndo not want to see \"You no longer have Filename.txt, and you have a\nnew file FileName.txt\".\n\nNow think what \"git add Filename.txt\" should do at that point?  It\nshould not say \"I was told to add Filename.txt, but there is no such\nfile, so I'll add nothing\".  If you run \"git add -u Filename.txt\",\nit should not say \"I was told to add Filename.txt, but there is no\nsuch file, so I'll remove existing Filename.txt from the index\".\n\nIt must pick up the updated contents from your new FileName.txt,\nupdate the index entry \"Filename.txt\", and the next \"git commit\"\nmust record it as an update to the same file.\n\nIf you are on the other hand trying to correct an earlier mistake of\nhaving named the file \"Filename.txt\" but you now want to rename it\n\"FileName.txt\", the above behaviour by core.ignorecase may make it a\nbit cumbersome to do.  You can first remove it from the index and\nthen re-add it, I would think, as a workaround.  Having to do a\n\"workaround\" is unfortunate but it is an unavoidable consequence of\nhaving to choose between the two and having to pick one.  Most of\nthe time you do not want such a rename (or rather, the loss of the\nfile Filename.txt and the creation of the unrelated FileName.txt)\nand a change from Filename.txt to FileName.txt is most likely to be\na mistake in the platform tool that mucked with the files on your\nfilesystem, so we choose to make it easy for the user not to be\ndisturbed by such a change.\n"}]}