{"thread":{"id":"12811","subject":"[PATCH 0/7] Case-insensitive filesystem support, take 1","startedAt":"2008-03-22T17:21:05Z","lastAt":"2008-03-26T03:37:18Z","messageCount":29,"participants":["Linus Torvalds","Johannes Schindelin","Steffen Prohaska","Junio C Hamano","Dmitry Potapov","Derek Fawcus","Jan Hudec"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"72680","messageId":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":null,"subject":"[PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:21:05Z","receivedAt":"2008-03-22T17:21:05Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nOk, so I said it wasn't a high priority, but I had already done all the \nreally core support for this in that we have the name hashes now that I \nwanted to use for looking up names case-insensitively, so I took it as a \nchallenge to do this cleanly. I already knew how I wanted to do it, so how \nhard could it be?\n\nFirst, a few caveats:\n\n - I've tested this series, both on a case-sensitive one (using hardlinks \n   to test corner cases) and on a vfat filesystem under Linux (which is \n   case-insensitive and *really* odd wrt case preservation - it remembers \n   the name of removed files, so it preserves case even across removal and \n   re-creation!)\n\n - HOWEVER. The testing has been very targeted, and I only convered a few \n   cases to really care. Things like case-renaming, for example, will be \n   trivial to do, but I didn't do it. So if you want to do a \n\n\tgit mv Abc abc\n\n   on a case-insensitive filesystem, you currently still have to do it as \n   the sequence\n\n\tgit mv Abc xyz\n\tgit mv xyz abc\n\n   because I did *not* make git-mv know about case-insensitivity.\n\n - The only two operations that care about case-insensitivity after this \n   series of seven patches are\n\n    (a) \"git status\" and friends (like \"git add\") that use the directory \n        traversal code will know to ignore files that have a case- \n        insensitive version in the index. So if you have messed up the \n        case in the working tree (or the filesystem isn't even case- \n        preserving), then \"git add\" and \"git status\" won't show the \n        case-different file as being \"unknown\".\n\n    (b) merging trees (git read-tree) knows about unexpectedly found files\n        that are due to case-insensitive filesystems, and knows to ignore \n        them. This means that switching branches where the case of a file \n        changes works, and it means that going a merge across that case \n        also works.\n\n - I made this all conditional on\n\n\t[core]\n\t\tignorecase = true\n\n   because obviously I'm not at all interested in penalizing sane \n   filesystems. That said, I also worked at trying to make sure that it's \n   safe and possible to do this on a case-sensitive filesystem in case you \n   are working on a project that doesn't like case-sensitivity, so the \n   \"git status\" and \"git add .\" kind of operations won't add aliases\n\n - Finally: the \"case independence\" rules could be anything, but right now \n   I *only* do the standard US-ASCII versions. This will *not* help with \n   the insane OS X cases of UTF-8 normalization: to actually get that you \n   need to make sure that the hash-function for names and the comparison \n   functions work correctly with those more complex cases.\n\n   So _conceptually_ this should all work for UTF-8 normalization 'case' \n   insensitivity too or on just generally utf-8 cases, but I didn't \n   actually do that more complex case. That's a separate area, and will \n   not affect the core logic.\n\nAnd then one final caveat: I think the patch-series is fairly clean, and \neach patch in itself is pretty simple, but my testing has been limited, \nand not only haven't I extended the case-insensitivity to all operations, \nbut I would suggest some care from people who test this.\n\nBut if you care about the crazy OS X UTF-8 normalization (which apparently \ncan happen even on otherwise case-sensitive filesystems), or if you care \nabout the more regular case insensitivity of HFS+ or Windows, you need to \ntest this - even if you tests right now should be limited to US-ASCII \nonly. Because I'll happily fix issues, but I'm not using those crap \nfilesystems myself, nor will I be doing any more testing on VFAT unless \npeople actually point out issues to me.\n\nSo it's up to you users of crap OS's to test the cases and make good \nreports, and I'll care just because I think it's a somewhat interesting \nproblem (it's why I did this series), but I'll never do anything about \nthis without prodding and good reports. Ok?\n\nJunio: I think this is safe, if only because it's all so very \nstraight-forward, and I tried very hard to make each change trivial and \nlimited and fairly easy to understand. Some of the patches are pure \ncleanups that I hit when looking at the code and wanted to fix before I \neven made any other changes.\n\n\t\t\tLinus\n"},{"id":"72681","messageId":"alpine.LFD.1.00.0803221021220.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","subject":"[PATCH 1/7] Make unpack_trees_options bit flags actual bitfields","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:22:39Z","receivedAt":"2008-03-22T17:22:39Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Fri, 21 Mar 2008 13:14:47 -0700\n\nInstead of wasting space with whole integers for a single bit.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nThis is really unrelated to the rest of the series, except for the fact \nthat it irritated me when I was thinking about the required changes to \nunpack-trees.\n\n unpack-trees.h |   20 ++++++++++----------\n 1 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 50453ed..ad8cc65 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -9,16 +9,16 @@ typedef int (*merge_fn_t)(struct cache_entry **src,\n \t\tstruct unpack_trees_options *options);\n \n struct unpack_trees_options {\n-\tint reset;\n-\tint merge;\n-\tint update;\n-\tint index_only;\n-\tint nontrivial_merge;\n-\tint trivial_merges_only;\n-\tint verbose_update;\n-\tint aggressive;\n-\tint skip_unmerged;\n-\tint gently;\n+\tunsigned int reset:1,\n+\t\t     merge:1,\n+\t\t     update:1,\n+\t\t     index_only:1,\n+\t\t     nontrivial_merge:1,\n+\t\t     trivial_merges_only:1,\n+\t\t     verbose_update:1,\n+\t\t     aggressive:1,\n+\t\t     skip_unmerged:1,\n+\t\t     gently:1;\n \tconst char *prefix;\n \tint pos;\n \tstruct dir_struct *dir;\n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72682","messageId":"alpine.LFD.1.00.0803221022480.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221021220.3020@woody.linux-foundation.org","subject":"[PATCH 2/7] Move name hashing functions into a file of its own","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:25:34Z","receivedAt":"2008-03-22T17:25:34Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Fri, 21 Mar 2008 13:16:24 -0700\n\nIt's really totally separate functionality, and if we want to start\ndoing case-insensitive hash lookups, I'd rather do it when it's\nseparated out.\n\nIt also renames \"remove_index_entry()\" to \"remove_name_hash()\", because \nthat really describes the thing better. It doesn't actually remove the \nindex entry, that's done by \"remove_index_entry_at()\", which is something \nvery different, despite the similarity in names.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nThis makes no code changes what-so-ever apart from the movement/renaming, \njust moves things around a bit, and makes a function that used to be \nstatic (add_name_hash()) be exported because it's now in a different file.\n\n Makefile            |    1 +\n builtin-read-tree.c |    2 +-\n cache.h             |   31 ++++++++++++----------\n name-hash.c         |   73 +++++++++++++++++++++++++++++++++++++++++++++++++++\n read-cache.c        |   65 ++-------------------------------------------\n 5 files changed, 95 insertions(+), 77 deletions(-)\n create mode 100644 name-hash.c\n\ndiff --git a/Makefile b/Makefile\nindex 7c70b00..6d35662 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -421,6 +421,7 @@ LIB_OBJS += log-tree.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += merge-file.o\n+LIB_OBJS += name-hash.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex e9cfd2b..7ac3088 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -40,7 +40,7 @@ static int read_cache_unmerged(void)\n \tfor (i = 0; i < active_nr; i++) {\n \t\tstruct cache_entry *ce = active_cache[i];\n \t\tif (ce_stage(ce)) {\n-\t\t\tremove_index_entry(ce);\n+\t\t\tremove_name_hash(ce);\n \t\t\tif (last && !strcmp(ce->name, last->name))\n \t\t\t\tcontinue;\n \t\t\tcache_tree_invalidate_path(active_cache_tree, ce->name);\ndiff --git a/cache.h b/cache.h\nindex 2a1e7ec..2afc788 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -153,20 +153,6 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry\n \tdst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;\n }\n \n-/*\n- * We don't actually *remove* it, we can just mark it invalid so that\n- * we won't find it in lookups.\n- *\n- * Not only would we have to search the lists (simple enough), but\n- * we'd also have to rehash other hash buckets in case this makes the\n- * hash bucket empty (common). So it's much better to just mark\n- * it.\n- */\n-static inline void remove_index_entry(struct cache_entry *ce)\n-{\n-\tce->ce_flags |= CE_UNHASHED;\n-}\n-\n static inline unsigned create_ce_flags(size_t len, unsigned stage)\n {\n \tif (len >= CE_NAMEMASK)\n@@ -241,6 +227,23 @@ struct index_state {\n \n extern struct index_state the_index;\n \n+/* Name hashing */\n+extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);\n+/*\n+ * We don't actually *remove* it, we can just mark it invalid so that\n+ * we won't find it in lookups.\n+ *\n+ * Not only would we have to search the lists (simple enough), but\n+ * we'd also have to rehash other hash buckets in case this makes the\n+ * hash bucket empty (common). So it's much better to just mark\n+ * it.\n+ */\n+static inline void remove_name_hash(struct cache_entry *ce)\n+{\n+\tce->ce_flags |= CE_UNHASHED;\n+}\n+\n+\n #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS\n #define active_cache (the_index.cache)\n #define active_nr (the_index.cache_nr)\ndiff --git a/name-hash.c b/name-hash.c\nnew file mode 100644\nindex 0000000..e56eb16\n--- /dev/null\n+++ b/name-hash.c\n@@ -0,0 +1,73 @@\n+/*\n+ * name-hash.c\n+ *\n+ * Hashing names in the index state\n+ *\n+ * Copyright (C) 2008 Linus Torvalds\n+ */\n+#define NO_THE_INDEX_COMPATIBILITY_MACROS\n+#include \"cache.h\"\n+\n+static unsigned int hash_name(const char *name, int namelen)\n+{\n+\tunsigned int hash = 0x123;\n+\n+\tdo {\n+\t\tunsigned char c = *name++;\n+\t\thash = hash*101 + c;\n+\t} while (--namelen);\n+\treturn hash;\n+}\n+\n+static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n+{\n+\tvoid **pos;\n+\tunsigned int hash;\n+\n+\tif (ce->ce_flags & CE_HASHED)\n+\t\treturn;\n+\tce->ce_flags |= CE_HASHED;\n+\tce->next = NULL;\n+\thash = hash_name(ce->name, ce_namelen(ce));\n+\tpos = insert_hash(hash, ce, &istate->name_hash);\n+\tif (pos) {\n+\t\tce->next = *pos;\n+\t\t*pos = ce;\n+\t}\n+}\n+\n+static void lazy_init_name_hash(struct index_state *istate)\n+{\n+\tint nr;\n+\n+\tif (istate->name_hash_initialized)\n+\t\treturn;\n+\tfor (nr = 0; nr < istate->cache_nr; nr++)\n+\t\thash_index_entry(istate, istate->cache[nr]);\n+\tistate->name_hash_initialized = 1;\n+}\n+\n+void add_name_hash(struct index_state *istate, struct cache_entry *ce)\n+{\n+\tce->ce_flags &= ~CE_UNHASHED;\n+\tif (istate->name_hash_initialized)\n+\t\thash_index_entry(istate, ce);\n+}\n+\n+int index_name_exists(struct index_state *istate, const char *name, int namelen)\n+{\n+\tunsigned int hash = hash_name(name, namelen);\n+\tstruct cache_entry *ce;\n+\n+\tlazy_init_name_hash(istate);\n+\tce = lookup_hash(hash, &istate->name_hash);\n+\n+\twhile (ce) {\n+\t\tif (!(ce->ce_flags & CE_UNHASHED)) {\n+\t\t\tif (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))\n+\t\t\t\treturn 1;\n+\t\t}\n+\t\tce = ce->next;\n+\t}\n+\treturn 0;\n+}\ndiff --git a/read-cache.c b/read-cache.c\nindex a92b25b..5dc998d 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -23,80 +23,21 @@\n \n struct index_state the_index;\n \n-static unsigned int hash_name(const char *name, int namelen)\n-{\n-\tunsigned int hash = 0x123;\n-\n-\tdo {\n-\t\tunsigned char c = *name++;\n-\t\thash = hash*101 + c;\n-\t} while (--namelen);\n-\treturn hash;\n-}\n-\n-static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)\n-{\n-\tvoid **pos;\n-\tunsigned int hash;\n-\n-\tif (ce->ce_flags & CE_HASHED)\n-\t\treturn;\n-\tce->ce_flags |= CE_HASHED;\n-\tce->next = NULL;\n-\thash = hash_name(ce->name, ce_namelen(ce));\n-\tpos = insert_hash(hash, ce, &istate->name_hash);\n-\tif (pos) {\n-\t\tce->next = *pos;\n-\t\t*pos = ce;\n-\t}\n-}\n-\n-static void lazy_init_name_hash(struct index_state *istate)\n-{\n-\tint nr;\n-\n-\tif (istate->name_hash_initialized)\n-\t\treturn;\n-\tfor (nr = 0; nr < istate->cache_nr; nr++)\n-\t\thash_index_entry(istate, istate->cache[nr]);\n-\tistate->name_hash_initialized = 1;\n-}\n-\n static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)\n {\n-\tce->ce_flags &= ~CE_UNHASHED;\n \tistate->cache[nr] = ce;\n-\tif (istate->name_hash_initialized)\n-\t\thash_index_entry(istate, ce);\n+\tadd_name_hash(istate, ce);\n }\n \n static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)\n {\n \tstruct cache_entry *old = istate->cache[nr];\n \n-\tremove_index_entry(old);\n+\tremove_name_hash(old);\n \tset_index_entry(istate, nr, ce);\n \tistate->cache_changed = 1;\n }\n \n-int index_name_exists(struct index_state *istate, const char *name, int namelen)\n-{\n-\tunsigned int hash = hash_name(name, namelen);\n-\tstruct cache_entry *ce;\n-\n-\tlazy_init_name_hash(istate);\n-\tce = lookup_hash(hash, &istate->name_hash);\n-\n-\twhile (ce) {\n-\t\tif (!(ce->ce_flags & CE_UNHASHED)) {\n-\t\t\tif (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))\n-\t\t\t\treturn 1;\n-\t\t}\n-\t\tce = ce->next;\n-\t}\n-\treturn 0;\n-}\n-\n /*\n  * This only updates the \"non-critical\" parts of the directory\n  * cache, ie the parts that aren't tracked by GIT, and only used\n@@ -438,7 +379,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)\n {\n \tstruct cache_entry *ce = istate->cache[pos];\n \n-\tremove_index_entry(ce);\n+\tremove_name_hash(ce);\n \tistate->cache_changed = 1;\n \tistate->cache_nr--;\n \tif (pos >= istate->cache_nr)\n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72683","messageId":"alpine.LFD.1.00.0803221025410.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221022480.3020@woody.linux-foundation.org","subject":"[PATCH 3/7] Make \"index_name_exists()\" return the cache_entry it found","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:28:07Z","receivedAt":"2008-03-22T17:28:07Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Fri, 21 Mar 2008 15:53:00 -0700\n\nThis allows verify_absent() in unpack_trees() to use the hash chains\nrather than looking it up using the binary search.\n\nPerhaps more imporantly, it's also going to be useful for the next phase, \nwhere we actually start looking at the cache entry when we do \ncase-insensitive lookups and checking the result.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\nNo real change, except verify_absent() can now use the name hashing rather \nthan the binary search. But it's all still very much case-sensitive.\n\n cache.h        |    2 +-\n name-hash.c    |    6 +++---\n unpack-trees.c |    8 ++++----\n 3 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 2afc788..76d95d2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -353,7 +353,7 @@ extern int write_index(const struct index_state *, int newfd);\n extern int discard_index(struct index_state *);\n extern int unmerged_index(const struct index_state *);\n extern int verify_path(const char *path);\n-extern int index_name_exists(struct index_state *istate, const char *name, int namelen);\n+extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen);\n extern int index_name_pos(const struct index_state *, const char *name, int namelen);\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\ndiff --git a/name-hash.c b/name-hash.c\nindex e56eb16..2678148 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -54,7 +54,7 @@ void add_name_hash(struct index_state *istate, struct cache_entry *ce)\n \t\thash_index_entry(istate, ce);\n }\n \n-int index_name_exists(struct index_state *istate, const char *name, int namelen)\n+struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen)\n {\n \tunsigned int hash = hash_name(name, namelen);\n \tstruct cache_entry *ce;\n@@ -65,9 +65,9 @@ int index_name_exists(struct index_state *istate, const char *name, int namelen)\n \twhile (ce) {\n \t\tif (!(ce->ce_flags & CE_UNHASHED)) {\n \t\t\tif (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))\n-\t\t\t\treturn 1;\n+\t\t\t\treturn ce;\n \t\t}\n \t\tce = ce->next;\n \t}\n-\treturn 0;\n+\treturn NULL;\n }\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex a59f475..ca4c845 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -538,6 +538,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \tif (!lstat(ce->name, &st)) {\n \t\tint cnt;\n \t\tint dtype = ce_to_dtype(ce);\n+\t\tstruct cache_entry *result;\n \n \t\tif (o->dir && excluded(o->dir, ce->name, &dtype))\n \t\t\t/*\n@@ -581,10 +582,9 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \t\t * delete this path, which is in a subdirectory that\n \t\t * is being replaced with a blob.\n \t\t */\n-\t\tcnt = index_name_pos(&o->result, ce->name, strlen(ce->name));\n-\t\tif (0 <= cnt) {\n-\t\t\tstruct cache_entry *ce = o->result.cache[cnt];\n-\t\t\tif (ce->ce_flags & CE_REMOVE)\n+\t\tresult = index_name_exists(&o->result, ce->name, ce_namelen(ce));\n+\t\tif (result) {\n+\t\t\tif (result->ce_flags & CE_REMOVE)\n \t\t\t\treturn 0;\n \t\t}\n \n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72684","messageId":"alpine.LFD.1.00.0803221028170.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221025410.3020@woody.linux-foundation.org","subject":"[PATCH 4/7] Make hash_name_lookup able to do case-independent lookups","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:30:31Z","receivedAt":"2008-03-22T17:30:31Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Fri, 21 Mar 2008 15:55:19 -0700\n\nRight now nobody uses it, but \"index_name_exists()\" gets a flag so\nyou can enable it on a case-by-case basis.\n\nSigned-of-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nOooh.. We actually have some (admittedly stupid) case insensitivity code \nstarting to appear. So we now hash the names insensitively, and we have \nthe _capability_ to do case-insensitive lookups, but nobody actually uses \nthat insensitive lookup capability yet.\n\nBut things are now starting to get interesting.\n\n cache.h        |    4 ++--\n dir.c          |    2 +-\n name-hash.c    |   50 ++++++++++++++++++++++++++++++++++++++++++++++++--\n unpack-trees.c |    2 +-\n 4 files changed, 52 insertions(+), 6 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 76d95d2..a9ddaa1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -264,7 +264,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)\n #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))\n #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))\n-#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))\n+#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))\n #endif\n \n enum object_type {\n@@ -353,7 +353,7 @@ extern int write_index(const struct index_state *, int newfd);\n extern int discard_index(struct index_state *);\n extern int unmerged_index(const struct index_state *);\n extern int verify_path(const char *path);\n-extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen);\n+extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);\n extern int index_name_pos(const struct index_state *, const char *name, int namelen);\n #define ADD_CACHE_OK_TO_ADD 1\t\t/* Ok to add */\n #define ADD_CACHE_OK_TO_REPLACE 2\t/* Ok to replace file/directory */\ndiff --git a/dir.c b/dir.c\nindex edc458e..7362e83 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -371,7 +371,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)\n \n struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)\n {\n-\tif (cache_name_exists(pathname, len))\n+\tif (cache_name_exists(pathname, len, 0))\n \t\treturn NULL;\n \n \tALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);\ndiff --git a/name-hash.c b/name-hash.c\nindex 2678148..2253870 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -8,12 +8,25 @@\n #define NO_THE_INDEX_COMPATIBILITY_MACROS\n #include \"cache.h\"\n \n+/*\n+ * This removes bit 5 if bit 6 is set.\n+ *\n+ * That will make US-ASCII characters hash to their upper-case\n+ * equivalent. We could easily do this one whole word at a time,\n+ * but that's for future worries.\n+ */\n+static inline unsigned char icase_hash(unsigned char c)\n+{\n+\treturn c & ~((c & 0x40) >> 1);\n+}\n+\n static unsigned int hash_name(const char *name, int namelen)\n {\n \tunsigned int hash = 0x123;\n \n \tdo {\n \t\tunsigned char c = *name++;\n+\t\tc = icase_hash(c);\n \t\thash = hash*101 + c;\n \t} while (--namelen);\n \treturn hash;\n@@ -54,7 +67,40 @@ void add_name_hash(struct index_state *istate, struct cache_entry *ce)\n \t\thash_index_entry(istate, ce);\n }\n \n-struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen)\n+static int slow_same_name(const char *name1, int len1, const char *name2, int len2)\n+{\n+\tif (len1 != len2)\n+\t\treturn 0;\n+\n+\twhile (len1) {\n+\t\tunsigned char c1 = *name1++;\n+\t\tunsigned char c2 = *name2++;\n+\t\tlen1--;\n+\t\tif (c1 != c2) {\n+\t\t\tc1 = toupper(c1);\n+\t\t\tc2 = toupper(c2);\n+\t\t\tif (c1 != c2)\n+\t\t\t\treturn 0;\n+\t\t}\n+\t}\n+\treturn 1;\n+}\n+\n+static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase)\n+{\n+\tint len = ce_namelen(ce);\n+\n+\t/*\n+\t * Always fo exact compare (even if we want a case-ignoring comparison\n+\t * we do the quick exact one first, because it will be the common case).\n+\t */\n+\tif (len == namelen && !cache_name_compare(name, namelen, ce->name, len))\n+\t\treturn 1;\n+\n+\treturn icase && slow_same_name(name, namelen, ce->name, len);\n+}\n+\n+struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)\n {\n \tunsigned int hash = hash_name(name, namelen);\n \tstruct cache_entry *ce;\n@@ -64,7 +110,7 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na\n \n \twhile (ce) {\n \t\tif (!(ce->ce_flags & CE_UNHASHED)) {\n-\t\t\tif (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))\n+\t\t\tif (same_name(ce, name, namelen, icase))\n \t\t\t\treturn ce;\n \t\t}\n \t\tce = ce->next;\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex ca4c845..bf7d8f6 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -582,7 +582,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \t\t * delete this path, which is in a subdirectory that\n \t\t * is being replaced with a blob.\n \t\t */\n-\t\tresult = index_name_exists(&o->result, ce->name, ce_namelen(ce));\n+\t\tresult = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);\n \t\tif (result) {\n \t\t\tif (result->ce_flags & CE_REMOVE)\n \t\t\t\treturn 0;\n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72685","messageId":"alpine.LFD.1.00.0803221030380.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221028170.3020@woody.linux-foundation.org","subject":"[PATCH 5/7] Add 'core.ignorecase' option","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:33:37Z","receivedAt":"2008-03-22T17:33:37Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Fri, 21 Mar 2008 16:52:46 -0700\n\n..and start using it for directory entry traversal (ie \"git status\" will\nnot consider entries that match an existing entry case-insensitively to\nbe a new file)\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nBasically all of this patch is just setting up the \"ignore_case\" variable \n(default to false), but this one-liner in \"dir_add_name()\" is the actual \nmeat of it all:\n\n\t-\tif (cache_name_exists(pathname, len, 0))\n\t+\tif (cache_name_exists(pathname, len, ignore_case))\n\nbecause it now means that we will ignore unknown directory entries that \nmatch already-known files if they match case-insensitively and we have \ncore.ignorecase being set.\n\nSo this actually starts using the case insensitivity logic, but it's for a \nreally quite trivial and not very interesting case. \n\n cache.h       |    1 +\n config.c      |    5 +++++\n dir.c         |    2 +-\n environment.c |    1 +\n 4 files changed, 8 insertions(+), 1 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex a9ddaa1..9bce723 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -407,6 +407,7 @@ extern int delete_ref(const char *, const unsigned char *sha1);\n extern int trust_executable_bit;\n extern int quote_path_fully;\n extern int has_symlinks;\n+extern int ignore_case;\n extern int assume_unchanged;\n extern int prefer_symlink_refs;\n extern int log_all_ref_updates;\ndiff --git a/config.c b/config.c\nindex 0624494..3d51868 100644\n--- a/config.c\n+++ b/config.c\n@@ -342,6 +342,11 @@ int git_default_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.ignorecase\")) {\n+\t\tignore_case = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.bare\")) {\n \t\tis_bare_repository_cfg = git_config_bool(var, value);\n \t\treturn 0;\ndiff --git a/dir.c b/dir.c\nindex 7362e83..b5bfbca 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -371,7 +371,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)\n \n struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)\n {\n-\tif (cache_name_exists(pathname, len, 0))\n+\tif (cache_name_exists(pathname, len, ignore_case))\n \t\treturn NULL;\n \n \tALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);\ndiff --git a/environment.c b/environment.c\nindex 6739a3f..5fcd5b2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];\n int trust_executable_bit = 1;\n int quote_path_fully = 1;\n int has_symlinks = 1;\n+int ignore_case = 0;\n int assume_unchanged;\n int prefer_symlink_refs;\n int is_bare_repository_cfg = -1; /* unspecified */\n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72686","messageId":"alpine.LSU.1.00.0803221835240.4353@racer.site","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221021220.3020@woody.linux-foundation.org","subject":"Re: [PATCH 1/7] Make unpack_trees_options bit flags actual bitfields","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-22T17:36:09Z","receivedAt":"2008-03-22T17:36:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 22 Mar 2008, Linus Torvalds wrote:\n\n> From: Linus Torvalds <torvalds@woody.linux-foundation.org>\n> Date: Fri, 21 Mar 2008 13:14:47 -0700\n\nAs this patch is really unrelated to the series, this comment is really \nunrelated to the content of the patch ;-)\n\nAny point in doing the \"From:\" and \"Date:\" inside the mail body?\n\nCiao,\nDscho\n"},{"id":"72687","messageId":"alpine.LFD.1.00.0803221033430.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221030380.3020@woody.linux-foundation.org","subject":"[PATCH 6/7] Make branch merging aware of underlying case-insensitive filsystems","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:38:25Z","receivedAt":"2008-03-22T17:38:25Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Sat, 22 Mar 2008 09:35:59 -0700\n\nIf we find an unexpected file, see if that filename perhaps exists in a\ncase-insensitive way in the index, and whether the file matches that. If\nso, ignore it as a known pre-existing file of a different name.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nAll right, this is *it*. This is the actual core code that does something \ninteresting.\n\nI've tried to explain the behaviour in the comment, and let's face it, the \npatch is really really simple (yeah, 26 new lines but they are all really \ntrivial and over half of them of them are actually the comments about \nwhat is going on).\n\nThe core of the code itself is just two lines, really, but it's all \nwrapped in a helper function and I tried to make it be really really \nobvious what is going on!\n\nThe reason why \"src_index\" had to become non-const is stupid: it's not \nbecause we actually do anything that really writes to the index, but the \nlazy index name hashing code means that even just a name lookup will \npossibly create the name hash in the index.\n\nOh well.\n\n unpack-trees.c |   26 ++++++++++++++++++++++++++\n unpack-trees.h |    2 +-\n 2 files changed, 27 insertions(+), 1 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex bf7d8f6..95d3413 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -521,6 +521,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n }\n \n /*\n+ * This gets called when there was no index entry for the tree entry 'dst',\n+ * but we found a file in the working tree that 'lstat()' said was fine,\n+ * and we're on a case-insensitive filesystem.\n+ *\n+ * See if we can find a case-insensitive match in the index that also\n+ * matches the stat information, and assume it's that other file!\n+ */\n+static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)\n+{\n+\tstruct cache_entry *src;\n+\n+\tsrc = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);\n+\treturn src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);\n+}\n+\n+/*\n  * We do not want to remove or overwrite a working tree file that\n  * is not tracked, unless it is ignored.\n  */\n@@ -540,6 +556,16 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \t\tint dtype = ce_to_dtype(ce);\n \t\tstruct cache_entry *result;\n \n+\t\t/*\n+\t\t * It may be that the 'lstat()' succeeded even though\n+\t\t * target 'ce' was absent, because there is an old\n+\t\t * entry that is different only in case..\n+\t\t *\n+\t\t * Ignore that lstat() if it matches.\n+\t\t */\n+\t\tif (ignore_case && icase_exists(o, ce, &st))\n+\t\t\treturn 0;\n+\n \t\tif (o->dir && excluded(o->dir, ce->name, &dtype))\n \t\t\t/*\n \t\t\t * ce->name is explicitly excluded, so it is Ok to\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex ad8cc65..d436d6c 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -31,7 +31,7 @@ struct unpack_trees_options {\n \tvoid *unpack_data;\n \n \tstruct index_state *dst_index;\n-\tconst struct index_state *src_index;\n+\tstruct index_state *src_index;\n \tstruct index_state result;\n };\n \n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72688","messageId":"alpine.LFD.1.00.0803221038320.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221033430.3020@woody.linux-foundation.org","subject":"[PATCH 7/7] Make unpack-tree update removed files before any updated files","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:45:05Z","receivedAt":"2008-03-22T17:45:05Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Sat, 22 Mar 2008 09:48:41 -0700\n\nThis is immaterial on sane filesystems, but if you have a broken (aka\ncase-insensitive) filesystem, and the objective is to remove the file\n'abc' and replace it with the file 'Abc', then we must make sure to do\nthe removal first.\n\nOtherwise, you'd first update the file 'Abc' - which would just\noverwrite the file 'abc' due to the broken case-insensitive filesystem -\nand then remove file 'abc' - which would now brokenly remove the just\nupdated file 'Abc' on that broken filesystem.\n\nBy doing removals first, this won't happen.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nOk, this one looks - and is, really - trivial, but it's actually the only \none in the whole series that I'm even remotely nervous about. First off, \nit actually does what it does regardless of that \"core.ignorecase\" \nvariable, but that wouldn't worry me if it wasn't for the fact that I \ndon't remember/understand what the heck that \"last_symlink\" logic was \nthere for.\n\nI *think* the logic is only for removal, and that splitting up the single \nloop to be two loops is totally safe and actually cleans things up, but I \nreally want somebody to take a look at this. \n\nThis patch is important, because without it you can't reliably switch \nbetween branches with case-aliases on a case-insensitive filesystem, and \nstrictly speaking I should have put it before the previous patch, but I \nput it last because of this worry of mine. Patches 1-6 I think are totally \nobvious and ready to go at any point. This one I really want Junio to \ndouble-check.\n\nThe patch itself is really really trivial. We used to do both removal and \nfile updates in one phase, we just split it into two. So I don't worry \nabout the code, I only worry about that \"last_symlink\" thing.\n\nAnyway, this concludes the series. It's not _complete_ - the \ncase-independent compare function is a joke and only gets US-ASCII \ncorrect, and there are other cases we will want to fix up. But in the end, \nI think this series of seven trivial patches really does make git somewhat \naware of broken filesystems at a very core level.\n\n unpack-trees.c |    9 +++++++--\n 1 files changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 95d3413..feae846 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -79,16 +79,21 @@ static int check_updates(struct unpack_trees_options *o)\n \tfor (i = 0; i < index->cache_nr; i++) {\n \t\tstruct cache_entry *ce = index->cache[i];\n \n-\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE))\n-\t\t\tdisplay_progress(progress, ++cnt);\n \t\tif (ce->ce_flags & CE_REMOVE) {\n+\t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tif (o->update)\n \t\t\t\tunlink_entry(ce->name, last_symlink);\n \t\t\tremove_index_entry_at(&o->result, i);\n \t\t\ti--;\n \t\t\tcontinue;\n \t\t}\n+\t}\n+\n+\tfor (i = 0; i < index->cache_nr; i++) {\n+\t\tstruct cache_entry *ce = index->cache[i];\n+\n \t\tif (ce->ce_flags & CE_UPDATE) {\n+\t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tce->ce_flags &= ~CE_UPDATE;\n \t\t\tif (o->update) {\n \t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n-- \n1.5.5.rc0.28.g61a0.dirty\n"},{"id":"72689","messageId":"alpine.LFD.1.00.0803221045280.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LSU.1.00.0803221835240.4353@racer.site","subject":"Re: [PATCH 1/7] Make unpack_trees_options bit flags actual bitfields","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T17:47:59Z","receivedAt":"2008-03-22T17:47:59Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Mar 2008, Johannes Schindelin wrote:\n> \n> Any point in doing the \"From:\" and \"Date:\" inside the mail body?\n\nI encourage people to do that for the kernel (\"From:\" in particular), \nbecause while git-am will pick them up from the email headers, that is \nonly true if the emails don't get passed around and commented upon by \nothers.\n\nNow, in git, the chain-of-command is very short (everybody -> Junio), so \nit really doesn't matter, but in the kernel, when people send out patches \nlike this, the patches may be forwarded by others (who add their sign-off \nlines etc), and then it's really good to have that \"From:\" line in \nparticular in the body of the email, because it's more likely that it will \nremain there (otherwise we depend on the person who signs off and forwards \nit to add it!)\n\n\t\t\tLinus\n"},{"id":"72690","messageId":"alpine.LSU.1.00.0803221857360.4353@racer.site","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221045280.3020@woody.linux-foundation.org","subject":"Re: [PATCH 1/7] Make unpack_trees_options bit flags actual bitfields","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-22T17:57:52Z","receivedAt":"2008-03-22T17:57:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 22 Mar 2008, Linus Torvalds wrote:\n\n> On Sat, 22 Mar 2008, Johannes Schindelin wrote:\n> \n> > Any point in doing the \"From:\" and \"Date:\" inside the mail body?\n> \n> I encourage people to do that for the kernel (\"From:\" in particular), \n> because while git-am will pick them up from the email headers, that is \n> only true if the emails don't get passed around and commented upon by \n> others.\n\nOkay.\n\nThanks,\nDscho\n"},{"id":"72691","messageId":"alpine.LFD.1.00.0803221049090.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221038320.3020@woody.linux-foundation.org","subject":"[PATCH 0/7] Final words","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T18:06:20Z","receivedAt":"2008-03-22T18:06:20Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nSo the whole patch series looks like this:\n\n\t Makefile            |    1 +\n\t builtin-read-tree.c |    2 +-\n\t cache.h             |   36 +++++++++-------\n\t config.c            |    5 ++\n\t dir.c               |    2 +-\n\t environment.c       |    1 +\n\t name-hash.c         |  119 +++++++++++++++++++++++++++++++++++++++++++++++++++\n\t read-cache.c        |   65 +--------------------------\n\t unpack-trees.c      |   43 ++++++++++++++++---\n\t unpack-trees.h      |   22 +++++-----\n\t 10 files changed, 199 insertions(+), 97 deletions(-)\n\t create mode 100644 name-hash.c\n\nand clearly does add more lines than it deletes, but it all really is \npretty simple, and none of this is rocket science or even very intrusive. \nWhat took me longest to do was not the actual code itself, but to get \n_just_ the right approach so that the end result would be as simple and \nnonintrusive as possible. That core patch 6/7 was redone at least ten \ntimes before I was happy with it.\n\nAnyway, perhaps exactly because I tried very hard to make it all make \nsense, I'm actually very very happy with the patch. I suspect it's too \nlate for v1.5.5 even if I think all the patches are really simple, but I'm \nhoping it can go into at least \"pu\" and have people actually *test* it.\n\nTalking about testing, the kind of safety I wanted to get with this patch \nis perhaps best described by the tests I did not on case-insensitive \nfilesystems, but on regular *good* filesystems together with setting the \n\"core.ignorecase\" config variable.\n\nHere's an example of how that patch 6/7 works and tries to be really \ncareful even on a case-sensitive filesystem:\n\n\tmkdir test-case\n\tcd test-case\n\tgit init\n\tgit config core.ignorecase true\n\n\techo \"File\" > File\n\tgit add File\n\tgit commit -m \"Create 'File'\"\n\n\tgit checkout -b other\n\tgit rm File\n\techo \"file\" > file\n\tgit add file\n\tgit commit -m \"Create 'file'\"\n\n\techo \"File\" > File\n\tgit checkout master\n\nand now it complains about\n\n\terror: Untracked working tree file 'File' would be overwritten by merge.\n\nwhich is correct, because while it is doing its case-insensitivity checks, \nit also noticed that \"File\" did *not* match the stat information for \n'file', so it really _is_ an untracked working tree file.\n\nSo it's actually trying to be a lot more careful than just saying \"ok, we \nalready know about 'File'\". See what happens next:\n\n\trm File\n\tln file File\n\tgit checkout master\n\nand now it very happily did the switch to master, even though 'File' got \noverwritten, because now it again found that untracked file 'File', but \nnow it could match it up *exactly* against the case-insensitive file \n'file', so git was happy that it wasn't actually throwing away any info, \nand the fact that it overwrite 'File' was ok, because it considered it the \nsame file as 'file'.\n\nSo the whole thing is not only able to handle these name aliases, it \nactually handles them by checking that it's safe.\n\nFinal note: I also did notice that I didn't fix the 'git add\" case like I \nthought I did, it currently only fixes \"git status\". So I still want to \nfix \"git add\" and \"git mv\" to do the right thing when there are case- \ninsensitive aliases, but that's a separate issue from this particular \nseries..\n\n\t\tLinus\n"},{"id":"72692","messageId":"alpine.LFD.1.00.0803221121520.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221049090.3020@woody.linux-foundation.org","subject":"Re: [PATCH 0/7] Final words","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T18:28:00Z","receivedAt":"2008-03-22T18:28:00Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Mar 2008, Linus Torvalds wrote:\n> \n> Final note: I also did notice that I didn't fix the 'git add\" case like I \n> thought I did, it currently only fixes \"git status\". So I still want to \n> fix \"git add\" and \"git mv\" to do the right thing when there are case- \n> insensitive aliases, but that's a separate issue from this particular \n> series..\n\ngit-add will want more than this, but this is an example of what we should \ndo - if 'ignore_case' is set, we probably should disallow adding the same \ncase-insensitive name twice to the index.\n\nThis does *not* guarantee that the index never would have aliases when \ncore.ignorecase is set, since the index might have been populated from a \ntree that was generated on a sane filesystem, and we still allow that, but \nthings like this are probably good things to do for projects that want to \nwork case-insensitively.\n\nSo even if you have a case-sensitive filesystem, the goal (I think) should \nbe that you can set core.ignorecase to true, and that should help you work \nwith other people who may be stuck on case-insensitive crud.\n\nAnyway, the reason \"git add\" didn't actually work with the simple change \nto dir_add_name() is that \"git add\" doesn't load the index until *after* \nit has done the directory traversal (because it actually *wants* to see \nfiles that are already in the index). \n\nSomething like this at least disallows the dual add if the case has \nchanged.\n\n\t\tLinus\n\n----\n read-cache.c |    7 +++++++\n 1 files changed, 7 insertions(+), 0 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 5dc998d..6aee6e0 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -476,6 +476,13 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \t\treturn 0;\n \t}\n \n+\tif (ignore_case) {\n+\t\tstruct cache_entry *alias;\n+\t\talias = index_name_exists(istate, ce->name, ce_namelen(ce), 1);\n+\t\tif (alias)\n+\t\t\tdie(\"Will not add file alias '%s' ('%s' already exists in index)\", ce->name, alias->name);\n+\t}\n+\n \tif (index_path(ce->sha1, path, &st, 1))\n \t\tdie(\"unable to index file %s\", path);\n \tif (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))\n"},{"id":"72704","messageId":"alpine.LFD.1.00.0803221417430.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221038320.3020@woody.linux-foundation.org","subject":"[PATCH 8/7] When adding files to the index, add support for case-independent matches","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T21:19:30Z","receivedAt":"2008-03-22T21:19:30Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nDate: Sat, 22 Mar 2008 13:19:49 -0700\n\nThis simplifies the matching case of \"I already have this file and it is \nup-to-date\" and makes it do the right thing in the face of \ncase-insensitive aliases.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nThis is patch 1/2 to make \"git add\" act better. Discard the previous \nsimplistic and overly die()-eager patch that I hadn't signed off on.\n\n read-cache.c |   12 +++++-------\n 1 files changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex 5dc998d..8c57adf 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -431,9 +431,9 @@ static int index_name_pos_also_unmerged(struct index_state *istate,\n \n int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n {\n-\tint size, namelen, pos;\n+\tint size, namelen;\n \tstruct stat st;\n-\tstruct cache_entry *ce;\n+\tstruct cache_entry *ce, *alias;\n \tunsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;\n \n \tif (lstat(path, &st))\n@@ -466,13 +466,11 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \t\tce->ce_mode = ce_mode_from_stat(ent, st.st_mode);\n \t}\n \n-\tpos = index_name_pos(istate, ce->name, namelen);\n-\tif (0 <= pos &&\n-\t    !ce_stage(istate->cache[pos]) &&\n-\t    !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {\n+\talias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);\n+\tif (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, &st, ce_option)) {\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n-\t\tce_mark_uptodate(istate->cache[pos]);\n+\t\tce_mark_uptodate(alias);\n \t\treturn 0;\n \t}\n \n-- \n1.5.5.rc0.31.gdcfd.dirty\n"},{"id":"72705","messageId":"alpine.LFD.1.00.0803221419400.3020@woody.linux-foundation.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221417430.3020@woody.linux-foundation.org","subject":"[PATCH 9/7] Make git-add behave more sensibly in a case-insensitive environment","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-22T21:22:44Z","receivedAt":"2008-03-22T21:22:44Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nFrom: Linus Torvalds <torvalds@woody.linux-foundation.org>\nSubject: [PATCH 2/2] Make git-add behave more sensibly in a case-insensitive environment\n\nThis expands on the previous patch, and allows \"git add\" to sanely handle \na filename that has changed case, keeping the case in the index constant, \nand avoiding aliases.\n\nIn particular, if you have an index entry called \"File\", but the\nchecked-out tree is case-corrupted and has an entry called \"file\"\ninstead, doing a\n\n\tgit add .\n\n(or naming \"file\" explicitly) will automatically notice that we have an\nalias, and will replace the name \"file\" with the existing index\ncapitalization (ie \"File\").\n\nHowever, if we actually have *both* a file called \"File\" and one called\n\"file\", and they don't have the same lstat() information (ie we're on a\ncase-sensitive filesystem but have the \"core.ignorecase\" flag set), we\nwill error out if we try to add them both.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n\nThe previous patch handled the \"nothing changed\" case, this one actually \nhandles the case of the data needing to be updated.\n\nThe CE_ADDED flag is an in-memory flag that just protects a single \"git \nadd\" invocation from changing the same alias twice. That would not be \nright, but if you do separate\n\n\tgit add File\n\tgit add file\n\ncommands, the second one will happily update the information that the \nfirst one added even if it was different - but\n\n\tgit add File file\n\nwould be an error if they don't have the same stat() information.\n\n cache.h      |    1 +\n read-cache.c |   37 ++++++++++++++++++++++++++++++++++++-\n 2 files changed, 37 insertions(+), 1 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 9bce723..81727e4 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -133,6 +133,7 @@ struct cache_entry {\n #define CE_UPDATE    (0x10000)\n #define CE_REMOVE    (0x20000)\n #define CE_UPTODATE  (0x40000)\n+#define CE_ADDED     (0x80000)\n \n #define CE_HASHED    (0x100000)\n #define CE_UNHASHED  (0x200000)\ndiff --git a/read-cache.c b/read-cache.c\nindex 8c57adf..26ed644 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -429,6 +429,38 @@ static int index_name_pos_also_unmerged(struct index_state *istate,\n \treturn pos;\n }\n \n+static int different_name(struct cache_entry *ce, struct cache_entry *alias)\n+{\n+\tint len = ce_namelen(ce);\n+\treturn ce_namelen(alias) != len || memcmp(ce->name, alias->name, len);\n+}\n+\n+/*\n+ * If we add a filename that aliases in the cache, we will use the\n+ * name that we already have - but we don't want to update the same\n+ * alias twice, because that implies that there were actually two\n+ * different files with aliasing names!\n+ *\n+ * So we use the CE_ADDED flag to verify that the alias was an old\n+ * one before we accept it as \n+ */\n+static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)\n+{\n+\tint len;\n+\tstruct cache_entry *new;\n+\n+\tif (alias->ce_flags & CE_ADDED)\n+\t\tdie(\"Will not add file alias '%s' ('%s' already exists in index)\", ce->name, alias->name);\n+\n+\t/* Ok, create the new entry using the name of the existing alias */\n+\tlen = ce_namelen(alias);\n+\tnew = xcalloc(1, cache_entry_size(len));\n+\tmemcpy(new->name, alias->name, len);\n+\tcopy_cache_entry(new, ce);\n+\tfree(ce);\n+\treturn new;\n+}\n+\n int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n {\n \tint size, namelen;\n@@ -471,11 +503,14 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)\n \t\t/* Nothing changed, really */\n \t\tfree(ce);\n \t\tce_mark_uptodate(alias);\n+\t\talias->ce_flags |= CE_ADDED;\n \t\treturn 0;\n \t}\n-\n \tif (index_path(ce->sha1, path, &st, 1))\n \t\tdie(\"unable to index file %s\", path);\n+\tif (ignore_case && alias && different_name(ce, alias))\n+\t\tce = create_alias_ce(ce, alias);\n+\tce->ce_flags |= CE_ADDED;\n \tif (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))\n \t\tdie(\"unable to add %s to index\",path);\n \tif (verbose)\n-- \n1.5.5.rc0.31.gdcfd.dirty\n"},{"id":"72710","messageId":"alpine.OSX.1.00.0803222250330.21118@cougar","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","subject":"[PATCH] t0050: Set core.ignorecase case to activate case insensitivity","fromName":"Steffen Prohaska","fromEmail":"prohaska@zib.de","sentAt":"2008-03-22T22:01:39Z","receivedAt":"2008-03-22T22:01:39Z","isPatch":true,"sender":{"key":"prohaska@zib.de","avatar":"https://avatars.githubusercontent.com/u/217580?v=4"},"body":"Case insensitive file handling is only activated by\ncore.ignorecase = true.  Hence, we need to set it to give the tests\nin t0050 a chance to succeed.\n\nSigned-off-by: Steffen Prohaska <prohaska@zib.de>\n---\n t/t0050-filesystem.sh |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\nOn Sat, 22 Mar 2008, Linus Torvalds wrote:\n\n>  - I made this all conditional on\n> \n> \t[core]\n> \t\tignorecase = true\n> \n>    because obviously I'm not at all interested in penalizing sane \n>    filesystems. That said, I also worked at trying to make sure that it's \n>    safe and possible to do this on a case-sensitive filesystem in case you \n>    are working on a project that doesn't like case-sensitivity, so the \n>    \"git status\" and \"git add .\" kind of operations won't add aliases\n\nWith this commit applied test 2 of t0050 passes.  This is the minimal\nchange to make t0050 useful.  Eventually test_expect_failure should be\nreplaced with test_expect_success.\n\n    Steffen\n\ndiff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh\nindex 3fbad77..cb109ff 100755\n--- a/t/t0050-filesystem.sh\n+++ b/t/t0050-filesystem.sh\n@@ -34,6 +34,7 @@ test_expect_success 'see if we expect ' '\n \n test_expect_success \"setup case tests\" '\n \n+\tgit config core.ignorecase true &&\n \ttouch camelcase &&\n \tgit add camelcase &&\n \tgit commit -m \"initial\" &&\n-- \n1.5.4.4.613.gaa46e5\n"},{"id":"72720","messageId":"7vbq56f0qm.fsf@gitster.siamese.dyndns.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221038320.3020@woody.linux-foundation.org","subject":"Re: [PATCH 7/7] Make unpack-tree update removed files before any updated files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-23T05:49:53Z","receivedAt":"2008-03-23T05:49:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> Ok, this one looks - and is, really - trivial, but it's actually the only \n> one in the whole series that I'm even remotely nervous about. First off, \n> it actually does what it does regardless of that \"core.ignorecase\" \n> variable, but that wouldn't worry me if it wasn't for the fact that I \n> don't remember/understand what the heck that \"last_symlink\" logic was \n> there for.\n\nlast_symlink is just a cached information used by underlying\nhas_symlink_leading_path() function for optimization.\n\nThe motivation behind has_symlink_leading_path() is reasonably well\ndescribed in\n\n - f859c84: Add has_symlink_leading_path() function., 2007-05-11,\n\n - 64cab59: apply: do not get confused by symlinks in the middle,\n   2007-05-11, and\n\n - 16a4c61: read-tree -m -u: avoid getting confused by intermediate\n   symlinks., 2007-05-10\n\nThe short version is that:\n\n - sometimes we want to make sure a path a/b/c/d exists (or does not\n   exist) in the work tree;\n\n - however, !lstat(\"a/b/c/d\") is not quite it.  if a/b is a symlink in the\n   work tree that points at somewhere that happens to have c/d underneath,\n   !lstat() says \"yeah, there is\", but that one is _different_ from what\n   checking out a cache entry a/b/c/d would produce (because in that case\n   we will remove a/b symlink, create a/b/ directory and deposit blob b\n   there).\n\n - So we often need to see if a given path has symlink component in the\n   leading part in the work tree (e.g. given \"a/b/c/d\", we would need to\n   check if any of \"a\", \"a/b\", \"a/b/c\" is a symlink).\n\nThe function has_symlink_leading_path() answers that question, and its\nsecond argument is a buffer to cache \"the last work tree path found to be\na symlink\", so if you call it with \"a/b/c/d\" and then with \"a/b/c/e\" in\nthe above example situation, the second call can re-use the information\nthe first call found out, which is \"a/b is a symlink\".\n\n\nI do not think your patch breaks the passing around of last_symlink cached\ninformation.  Although the three commits I quoted above are all backed by\nreal-world breakage cases that they did fix, the issues they deal with are\nindeed tricky cases.  Although your patch (the change in 7/7) should not\nmake any difference to the issues, thinking about them is already making\nme feel nervous.\n"},{"id":"72721","messageId":"7v7ifueznu.fsf@gitster.siamese.dyndns.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803221033430.3020@woody.linux-foundation.org","subject":"Re: [PATCH 6/7] Make branch merging aware of underlying case-insensitive filsystems","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-03-23T06:13:09Z","receivedAt":"2008-03-23T06:13:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> @@ -540,6 +556,16 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n>  \t\tint dtype = ce_to_dtype(ce);\n>  \t\tstruct cache_entry *result;\n>  \n> +\t\t/*\n> +\t\t * It may be that the 'lstat()' succeeded even though\n> +\t\t * target 'ce' was absent, because there is an old\n> +\t\t * entry that is different only in case..\n> +\t\t *\n> +\t\t * Ignore that lstat() if it matches.\n> +\t\t */\n> +\t\tif (ignore_case && icase_exists(o, ce, &st))\n> +\t\t\treturn 0;\n> +\n\nIt may well be the case that this lstat() returning success was caused by\na ghost match with a file with different case, and I think it is the right\nthing to say \"no, it does not exist\" if that is the case.\n\nI wonder what happens when the file with the same case does exist that we\nare trying to make sure is missing?\n\nAs far as I can tell, icase_exists() does not ask \"does a file with this\nname in different case exist, and a file with this exact case doesn't?\"\nbut asks \"does a file with this name, or another name that is different\nonly in case, exist?\".\n"},{"id":"72770","messageId":"alpine.LFD.1.00.0803230829001.16824@woody.linux-foundation.org","threadId":"12811","inReplyTo":"7v7ifueznu.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 6/7] Make branch merging aware of underlying case-insensitive filsystems","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-23T15:41:06Z","receivedAt":"2008-03-23T15:41:06Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sat, 22 Mar 2008, Junio C Hamano wrote:\n> \n> I wonder what happens when the file with the same case does exist that we\n> are trying to make sure is missing?\n\nCan't happen. This whole code-path only triggers if the entry didn't exist \nin the index when we merge a tree.\n\nSo we know a priori that the source index didn't contain the thing.\n\n> As far as I can tell, icase_exists() does not ask \"does a file with this\n> name in different case exist, and a file with this exact case doesn't?\"\n> but asks \"does a file with this name, or another name that is different\n> only in case, exist?\".\n\nCorrect. But see the call chain - this thing is only called if index is \nNULL, ie \"there was no entry in the index\".\n\nSo in this case, the other comment (above \"icase_exists()\") talks about \nthat:\n\n\tThis gets called when there was no index entry for the tree entry \n\t'dst', but we found a file in the working tree that 'lstat()' said \n\twas fine, [...]\n\nand you can verify that \"verify_absent()\" only gets called by things where \n\"index\" was NULL (only three callers, and two of them are expressly inside \na \"if (!old)\" case, and the third one is right after a \"if (index) return\"\nstatement.\n\n[ There's _one_ special case: the \"index\" thing may have been NULL not \n  because there was no path in the source index, but because we didn't \n  even look at the index in the first place! So strictly speaking, we \n  should have a test for \"o->merge\" being set, but afaik that must always \n  be true if we have \"o->update\" set, and again, this logic only triggers \n  for that case.\n\n  So the only case that doesn't set \"o->merge\" to get the index is \n  \"builtin-read-tree.c\" when you do a plain tree-only merge, but that one \n  has\n\n\tif ((opts.update||opts.index_only) && !opts.merge)\n\t\tusage(read_tree_usage);\n\n  to make sure that you cannot update the working tree without taking the \n  index into account ]\n\nAnyway, I think it's all good. \n\n\t\t\tLinus\n"},{"id":"73005","messageId":"1206428273-15926-1-git-send-email-dpotapov@gmail.com","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","subject":"[PATCH] git-init: autodetect core.ignorecase","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2008-03-25T06:57:53Z","receivedAt":"2008-03-25T06:57:53Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"We already detect if symbolic links are supported by the filesystem.\nThis patch adds autodetect for case-insensitive filesystems, such\nas VFAT and others.\n\nSigned-off-by: Dmitry Potapov <dpotapov@gmail.com>\n---\n\nThis patch goes on top of lt/case-insensitive\n\n builtin-init-db.c |    8 +++++++-\n 1 files changed, 7 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 79eaf8d..62f7c08 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -254,8 +254,8 @@ static int create_default_files(const char *git_dir, const char *template_path)\n \t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \n-\t/* Check if symlink is supported in the work tree */\n \tif (!reinit) {\n+\t\t/* Check if symlink is supported in the work tree */\n \t\tpath[len] = 0;\n \t\tstrcpy(path + len, \"tXXXXXX\");\n \t\tif (!close(xmkstemp(path)) &&\n@@ -266,6 +266,12 @@ static int create_default_files(const char *git_dir, const char *template_path)\n \t\t\tunlink(path); /* good */\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\tgit_config_set(\"core.ignorecase\", \"true\");\n \t}\n \n \treturn reinit;\n-- \n1.5.5.rc1.10.g3948\n"},{"id":"73014","messageId":"20080325081409.GI25381@dpotapov.dyndns.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2008-03-25T08:14:09Z","receivedAt":"2008-03-25T08:14:09Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"On Sat, Mar 22, 2008 at 10:21:05AM -0700, Linus Torvalds wrote:\n> \n>  - I've tested this series, both on a case-sensitive one (using hardlinks \n>    to test corner cases) and on a vfat filesystem under Linux (which is \n>    case-insensitive and *really* odd wrt case preservation - it remembers \n>    the name of removed files, so it preserves case even across removal and \n>    re-creation!)\n\nI also have observed this problem with VFAT on Linux, but the effect was not\nstable. It looks like old information is preserved somewhere in caches...\n\nAnyway, I have tested this series of patches a bit on Windows and so far\nI have found the following:\n\n- merge different branches were two file names are only differ by case\n  will cause that the result branch has two file names that differ only\n  by case and one of them will be overwritten by the other and shown as\n  modified in the worktree by git status.\n\n- git status cares only about case-insensitivity only for files and not\n  for directories. Thus, if case of letters in a directory name is changed\n  then this directory will be shown as untracked.\n\n- pattern specified in .gitignore are match as case-sensitive despite\n  core.ignorecase set to true.\n\nPersonally, I don't care about any of the above issues much as I rarely\nwork on Windows and when I do, I always check that all filenames are in\nlow case except Makefile (and a few more exceptions). So, I have never\nhad any problem with using Git on case-insensitive system...\n\nDmitry\n"},{"id":"73020","messageId":"alpine.LSU.1.00.0803251056520.10660@wbgn129.biozentrum.uni-wuerzburg.de","threadId":"12811","inReplyTo":"1206428273-15926-1-git-send-email-dpotapov@gmail.com","subject":"Re: [PATCH] git-init: autodetect core.ignorecase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-25T09:59:00Z","receivedAt":"2008-03-25T09:59:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 25 Mar 2008, Dmitry Potapov wrote:\n\n> diff --git a/builtin-init-db.c b/builtin-init-db.c\n> index 79eaf8d..62f7c08 100644\n> --- a/builtin-init-db.c\n> +++ b/builtin-init-db.c\n> @@ -254,8 +254,8 @@ static int create_default_files(const char *git_dir, const char *template_path)\n>  \t\t\tgit_config_set(\"core.worktree\", work_tree);\n>  \t}\n>  \n> -\t/* Check if symlink is supported in the work tree */\n>  \tif (!reinit) {\n> +\t\t/* Check if symlink is supported in the work tree */\n>  \t\tpath[len] = 0;\n>  \t\tstrcpy(path + len, \"tXXXXXX\");\n>  \t\tif (!close(xmkstemp(path)) &&\n> @@ -266,6 +266,12 @@ static int create_default_files(const char *git_dir, const char *template_path)\n>  \t\t\tunlink(path); /* good */\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\tgit_config_set(\"core.ignorecase\", \"true\");\n\nClever!\n\nLast time I checked, the \"HEAD\" file on VFAT was converted to \"head\" when \nthe repository was initialised on Win32 (IIRC) and read on Linux (IIRC).  \nMaybe this problem has gone away, but if not, it should definitely be \nfixed (depending on core.ignorecase).\n\nCiao,\nDscho\n"},{"id":"73029","messageId":"20080325104931.GJ25381@dpotapov.dyndns.org","threadId":"12811","inReplyTo":"alpine.LSU.1.00.0803251056520.10660@wbgn129.biozentrum.uni-wuerzburg.de","subject":"[PATCH v2] git-init: autodetect core.ignorecase","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2008-03-25T10:49:31Z","receivedAt":"2008-03-25T10:49:31Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"We already detect if symbolic links are supported by the filesystem.\nThis patch adds autodetect for case-insensitive filesystems, such\nas VFAT and others.\n\nSigned-off-by: Dmitry Potapov <dpotapov@gmail.com>\n---\n\nThere was a stupid mistake in a previous patch...  (Inadvertantly,\nI sent the old version of my patch, which was incorrect, and did not\nrealize that until saw Johannes' reply to my email.)\n\n builtin-init-db.c |    8 +++++++-\n 1 files changed, 7 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 79eaf8d..62f7c08 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -254,8 +254,8 @@ static int create_default_files(const char *git_dir, const char *template_path)\n \t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \n-\t/* Check if symlink is supported in the work tree */\n \tif (!reinit) {\n+\t\t/* Check if symlink is supported in the work tree */\n \t\tpath[len] = 0;\n \t\tstrcpy(path + len, \"tXXXXXX\");\n \t\tif (!close(xmkstemp(path)) &&\n@@ -266,6 +266,12 @@ static int create_default_files(const char *git_dir, const char *template_path)\n \t\t\tunlink(path); /* good */\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\tgit_config_set(\"core.ignorecase\", \"true\");\n \t}\n \n \treturn reinit;\n-- \n1.5.5.rc1.10.g3948\n"},{"id":"73031","messageId":"20080325110329.GK25381@dpotapov.dyndns.org","threadId":"12811","inReplyTo":"alpine.LSU.1.00.0803251056520.10660@wbgn129.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git-init: autodetect core.ignorecase","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2008-03-25T11:03:29Z","receivedAt":"2008-03-25T11:03:29Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"On Tue, Mar 25, 2008 at 10:59:00AM +0100, Johannes Schindelin wrote:\n> \n> Last time I checked, the \"HEAD\" file on VFAT was converted to \"head\" when \n> the repository was initialised on Win32 (IIRC) and read on Linux (IIRC).  \n\nIt is result of mounting the VFAT disk on Linux with shortname=lower,\nwhich is the default :( Perhaps, it was a reasonable default for DOS\ndays, but now it only creates troubles. I usually mount VFAT disks with\nshortname=winnt on Linux, though some people prefer shortname=mixed.\n\nDmitry\n"},{"id":"73035","messageId":"20080325113956.GA7559@cisco.com","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803220955140.3020@woody.linux-foundation.org","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Derek Fawcus","fromEmail":"dfawcus@cisco.com","sentAt":"2008-03-25T11:39:56Z","receivedAt":"2008-03-25T11:39:56Z","isPatch":true,"sender":{"key":"dfawcus@cisco.com","avatar":null},"body":"On Sat, Mar 22, 2008 at 10:21:05AM -0700, Linus Torvalds wrote:\n>    ... and on a vfat filesystem under Linux (which is \n>    case-insensitive and *really* odd wrt case preservation - it remembers \n>    the name of removed files, so it preserves case even across removal and \n>    re-creation!)\n\nInteresting.\nThat sounds a bit like the claimed windows 95 properly of 'tunneling' renames.\nISTR that it was to catch a move via 'shortname' which then preserved the longname.\n\nHowever I'd have expected the Linux version to always use the long name...\n\nDF\n"},{"id":"73054","messageId":"20080325182635.GB4857@efreet.light.src","threadId":"12811","inReplyTo":"20080325113956.GA7559@cisco.com","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Jan Hudec","fromEmail":"bulb@ucw.cz","sentAt":"2008-03-25T18:26:35Z","receivedAt":"2008-03-25T18:26:35Z","isPatch":true,"sender":{"key":"bulb@ucw.cz","avatar":null},"body":"On Tue, Mar 25, 2008 at 11:39:56 +0000, Derek Fawcus wrote:\n> On Sat, Mar 22, 2008 at 10:21:05AM -0700, Linus Torvalds wrote:\n> >    ... and on a vfat filesystem under Linux (which is \n> >    case-insensitive and *really* odd wrt case preservation - it remembers \n> >    the name of removed files, so it preserves case even across removal and \n> >    re-creation!)\n> \n> Interesting.\n> That sounds a bit like the claimed windows 95 properly of 'tunneling' renames.\n> ISTR that it was to catch a move via 'shortname' which then preserved the longname.\n> \n> However I'd have expected the Linux version to always use the long name...\n\n... if it's there -- which it might not. Linux may create it always, but\nWindows will not if they think they don't need to.\n\n-- \n\t\t\t\t\t\t Jan 'Bulb' Hudec <bulb@ucw.cz>\n"},{"id":"73074","messageId":"alpine.LFD.1.00.0803251347400.2775@woody.linux-foundation.org","threadId":"12811","inReplyTo":"20080325081409.GI25381@dpotapov.dyndns.org","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-25T21:04:58Z","receivedAt":"2008-03-25T21:04:58Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 25 Mar 2008, Dmitry Potapov wrote:\n> \n> - merge different branches were two file names are only differ by case\n>   will cause that the result branch has two file names that differ only\n>   by case and one of them will be overwritten by the other and shown as\n>   modified in the worktree by git status.\n\nOk. So there's two issues here:\n\n - the git trees themselves had two different names\n\n   This is not something I'm *ever* planning on changing. All my \"case \n   insensitive\" patches were about the *working*tree*, not about core git \n   data structures themselves.\n\n   In other words: git itself is internally very much a case-sensitive \n   program, and the index and the trees are case-sensitive and will remain \n   so forever as far as I'm concerned. So when you do a tree-level merge \n   of two trees that have two different names that are equivalent in case, \n   git will create a result that has those two names. Because git itself \n   is not case-insensitive.\n\n - HOWEVER - when checking things out, we should probably notice that \n   we're now writing the two different files out and over-writing one of \n   them, and fail at that stage. I don't know what a good failure \n   behaviour would be, though. I'll have to think about it.\n\nIOW, all my case-insensitivity checking was very much designed to be about \nthe working tree, not about git internal representations. Put another way, \nthey should really only affect code that does \"lstat()\" to check whether \na file exists or code that does \"open()\" to open/create a file.\n\n> - git status cares only about case-insensitivity only for files and not\n>   for directories. Thus, if case of letters in a directory name is changed\n>   then this directory will be shown as untracked.\n\nAhh, yes. This is sad. It comes about because while we can look up whole \nnames in the index case-insensitively, we have no equivalent way to look \nup individual path components, so that still uses the \"index_name_pos()\" \nmodel and then looking aroung the failure point to see if we hit a \nsubdirectory. Remember: the index doesn't actually contain directories at \nall, just lists of files.\n\nThis will not be trivial to fix. \n\n> - pattern specified in .gitignore are match as case-sensitive despite\n>   core.ignorecase set to true.\n\nThis should probably be fairly straightforward. All the logic here is in \nthe function \"excluded_1()\" in dir.c - and largely it would be about \nchanging that \"strcmp()\" into a \"strcasecmp()\" and using the FNM_CASEFOLD \nflag to fnmatch().\n\nThe only half-way subtle issues here are\n\n - do we really want to use strcasecmp() (which may match differently than \n   our hashing matches!) or do we want to expand on our icase_cmp() or \n   similar in hash-name.c (I think the latter is the right thing, but it \n   requires a bit more work)\n\n - FNM_CASEFOLD has the same issue, but also adds the wrinkle of being a \n   GNU-only extension. Which is sad, since most systems that have glibc \n   would never need it in the first place. So then we get back to the \n   whole issue of maybe having to reimplement 'fnmatch()', or at least a \n   subset of it that git uses.\n\nSo that last issue is conceptually simple and straightforward to fix, but \nfixing it right would almost certainly be a few hundred lines of code \n(fnmatch() in particular is nasty if you want to do all the cases, but \nperhaps just '*' is ok?).\n\nThe first two issues are nontrivial.\n\n\t\t\tLinus\n"},{"id":"73098","messageId":"20080326024609.GL25381@dpotapov.dyndns.org","threadId":"12811","inReplyTo":"alpine.LFD.1.00.0803251347400.2775@woody.linux-foundation.org","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Dmitry Potapov","fromEmail":"dpotapov@gmail.com","sentAt":"2008-03-26T02:46:09Z","receivedAt":"2008-03-26T02:46:09Z","isPatch":true,"sender":{"key":"dpotapov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6568595?v=4"},"body":"On Tue, Mar 25, 2008 at 02:04:58PM -0700, Linus Torvalds wrote:\n> \n> IOW, all my case-insensitivity checking was very much designed to be about \n> the working tree, not about git internal representations. Put another way, \n> they should really only affect code that does \"lstat()\" to check whether \n> a file exists or code that does \"open()\" to open/create a file.\n\nOf course, case-insensitivity is about the working tree only. But when\nI merge another branch to the current one, git normally checks that it\nis not going to overwrite existing files in the *work tree* and refuses\nto do the merge if some files may be overwritten.\n\nSo if I work on a case-insensitive filesystem and have a file in a different\ncase and core.ignorecase=false, then the merge fails as expected!\n\nBut core.ignorecase=true, which is supposed to do a better job for case-\ninsensitive filesystems, actually causes the problem here.\n\nHere is my test script:\n\n====\nmkdir git-test\ncd git-test\ngit init\ngit config core.ignorecase true\necho foo > foo\ngit add foo\ngit commit -m 'initial commit'\n\ngit checkout -b other\n\necho file > file\ngit add file\ngit commit -m 'add file'\n\ngit checkout master\necho File > File\ngit add File\ngit commit -m 'add File'\n\n# I expect merge to fail here... and it does fail if core.ignorecase\n# is set to false, but with core.ignorecase = true, git will overwrite\n# 'File'.\n# git config core.ignorecase false\ngit merge other\n===\n\nDmitry\n"},{"id":"73102","messageId":"alpine.LFD.1.00.0803252035430.2775@woody.linux-foundation.org","threadId":"12811","inReplyTo":"20080326024609.GL25381@dpotapov.dyndns.org","subject":"Re: [PATCH 0/7] Case-insensitive filesystem support, take 1","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-03-26T03:37:18Z","receivedAt":"2008-03-26T03:37:18Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 26 Mar 2008, Dmitry Potapov wrote:\n> \n> Of course, case-insensitivity is about the working tree only. But when\n> I merge another branch to the current one, git normally checks that it\n> is not going to overwrite existing files in the *work tree* and refuses\n> to do the merge if some files may be overwritten.\n\nI do agree - but this is not really about the *merge*. It is about the \ncheckout.\n\nImagine that the merge had been done on a sane filesystem, and you're just \npulling it or cloning it. No merge on your machine, but the problem is the \nsame.\n\n\t\tLinus\n"}]}