git/list[1] front-page[2] threads[3] people[4] search[5] about
 

On pathnames

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 24, 2008, 21:02 UTC
Message-ID
<7vprvr7x8h.fsf@gitster.siamese.dyndns.org>
In-Reply-To
<alpine.LFD.1.00.0801230930390.1741@woody.linux-foundation.org>

One of Linus's recent patch introduces an index hashtable so that we can later hash "equivalent" names into the same bucket to allow us non-byte-by-byte comparison.

Before going further, I needed to formalize what we are trying to achieve. I learned a few things from the long flamewar thread, but it is very inefficient to go back to the thread to pick only the useful pieces. The whole flamewar simply did not fit a small Panda brain.

That was the reason for this write-up.

Design constraints. In the following, I'll use two names $A and $B as an example. They are a pair of names that are considered equivalent in some contexts, such as:

        A=xt_connmark.c  B=xt_CONNMARK.c
 (1) Some filesystems prevent you from having these two
     (confusing) paths in a directory at the same time.  Some do
     not implement this confusion prevention, and allows both
     names to exist at the same time.
     Let's call the former "case insensitive", and the latter
     "case sensitive".
 (2) readdir(3) on some "case insensitive" filesystems returns
     $A, after a successful creat(2) of $B.  Others remember
     which one of the two "equivalent" names were used in
     creat(2).
     Let's call the former "case folding", and the latter "case
     preserving".
     We assume open(2) or lstat(2) of $A or $B will succeed
     after allowing creat(2) of $B if a case folding filesystem
     returns $A from readdir(3).
 (3) Among the "case folding" ones, some filesystems fold the
     pathname to a form that is less interoperable with other
     systems, and/or the form that is likely to be different
     from what the end-user usually enters.
     Such filesystems are "inconveniently case folding".

The last one is not quite apparent with the "xt_connmark.c" example, but if you replace $A and $B in the above description with:

        A=Ma"rchen       B=Märchen
it would hopefully become more clear.

For example, vfat is generally "case preserving". In that long flamewar thread, I think we learned that HFS+ is in general "inconveniently case folding" with respect to Unicode, by always folding to $A but the keyboard/IM input is more likely to come as $B, which happens to be the more interoperable form with other systems.

Issues with case insensitive filesystems ----------------------------------------

At the data structure level, a pathname to git is a sequence of bytes terminated with NUL. This will _not_ change.

By the way, at the data structure level, a tree entry in git can represent a blob that is a symbolic link. A tree entry in git can also represent a blob that is a regular file, and in that case, it can represent if it is executable or not. These will also not change.

Now, let's think about how we allow use of git on a filesystem that is incapable of symbolic links, and/or a filesystem that does not have trustable executable bit.

We do not say "Symlinks are evil and not supported everywhere, so let's introduce a project configuration to disallow addition of symlinks". We do not say that to the executable bit, either.

Instead, we have fallback methods to allow manipulating symlinks and executable bit on such a filesystem that is incapable of handling them natively.

We should be able to do the same for this "case sensitivity" issue. A tree that has xt_connmark.c and xt_CONNMARK.c at the same time cannot be checked out on a case insensitive filesystem.

The filesystem is simply incapable of it (please just calmly rephrase it in your head as "does not allow such confusing craziness" instead of starting another flamewar, if you feel the expression "incapable of" insults your favorite filesystem).

That may mean the project should avoid such equivalent names in its trees (and having a project wide configuration could be a technical means to help enforcing that policy), but it does not mean the core level of git should prevent them to be created on such systems. It just means that there should be a way, that could (and sometimes has to) be different from the "natural" way, to manipulate such tree entries even on a case insensitive filesystem.

For example, if I find that RelNotes symlink incorrectly points at Documentation/RelNotes-1.5.44.txt and want to fix it and push it out immediately, but if I am on the road and the only environment I can borrow is a git installation on a filesystem that is symlink-challenged, I can still do the fix. On such a filesystem, a symlink is checked out as a regular file but is still marked as a symlink in the index. The only thing I need to do is to edit the file (making sure not to add an extra LF at the end) and add it to the index. That's certainly different from the "natural" way to do that on a filesystem with symlinks, which is "ln -fs Documentation/RelNotse-1.5.4.txt RelNotes", but the point is that we make it possible.

The same thing should apply to two files that cannot be checked out at the same time on case insensitive filesystems. Perhaps we could have something like:

	$ git show :xt_CONNMARK.c >xt_connmark-1.c
        $ edit xt_connmark-1.c
	$ git add --as xt_CONNMARK.c xt_connmark-1.c

Issues with case folding filesystems ------------------------------------

In addition to the above, case folding filesystems additionally have an issue even when there is no "confusing" names in the tree. The project may want to have "Märchen" (but not "Ma"rchen"), but a checkout (which is creat(2) of "Märchen" -- because that is the byte sequence recorded in tree objects and the index) will result in "Ma"rchen" and no "Märchen" (hence readdir(3) returns "Ma"rchen").

Linus's patch to use a hashtable that links "equivalent" names together is a step in the right direction to address this. The tree (and the index) has name $B, we check out and the filesystem folds it to $A. When we get the name $A back from the filesystem (via readdir(3)), we hash the name using a hash function that would drop names $A and $B into the same bucket, and compare that name $A with each hash entry using a comparison that considers $A and $B are equivalent. If we find one, then we keep the name $B we have already.

If it is a new file, we won't find any name that is equivalent to $A in the index, and we use the name $A obtained from readdir(3).

BUT with a twist.

If the filesystem is known to be inconveniently case folding, we are better off registering $B instead of $A (assuming we can convert from $A to $B).

One bad issue during development is that we cannot sanely emulate case folding behaviour on non case-folding filesystems without wrapping open(2), lstat(2), and friends, because of the assumption we made above in (2) where we defined the term "case folding". This means that the codepath to deal with case folding filesystems inevitably are harder to debug.

Tasks -----

 - Identify which case folding filesystems need to be supported,
   and make sure somebody understands its folding logic;
 - For each supported case folding logic, these are needed:
   - a hash function that throws "equivalent" names in the same
     bucket, to be used in Linus's patch;
   - a compare function to determine equivalent names;
   - a convert function that takes a possibly inconvenient form
     of equivalent name (i.e. $A above) as input and returns
     more convenient form (i.e. $B above)
 - Identify places that we use the names obtained from places
   other than the index and tree.  From these places, we would
   need to call the convert function to (de)mangle the name
   before they hit the index.
   Because we may be getting driven by something like:
	$ find | xargs git-foo
   handling readdir(3) we do ourselves any specially does not
   make much sense.  Any path from the user is suspect.
 - Identify places that we look for a name in the index, and
   perform equivalent comparison instead of memcmp(3) we
   traditionally did.  Linus's patch gives scaffolding for this.
Previous: Linus TorvaldsNext: Nicolas Pitre
Message 231 of 260 in “git on MacOSX and files with decomposed utf-8 file names”
  1. Mark JunkerJan 16, 2008
  2. Johannes SchindelinJan 16, 2008
  3. Kevin BallardJan 16, 2008
  4. Johannes SchindelinJan 16, 2008
  5. Jakub NarebskiJan 16, 2008
  6. Kevin BallardJan 16, 2008
  7. Jakub NarebskiJan 16, 2008
  8. Kevin BallardJan 16, 2008
  9. Johannes SchindelinJan 16, 2008
  10. Kevin BallardJan 16, 2008
  11. Linus TorvaldsJan 16, 2008
  12. Linus TorvaldsJan 16, 2008
  13. Kevin BallardJan 16, 2008
  14. Linus TorvaldsJan 16, 2008
  15. Pedro MeloJan 16, 2008
  16. Linus TorvaldsJan 17, 2008
  17. Pedro MeloJan 17, 2008
  18. David KastrupJan 17, 2008
  19. Pedro MeloJan 17, 2008
  20. Wincent ColaiutaJan 17, 2008
  21. Johannes SchindelinJan 17, 2008
  22. Linus TorvaldsJan 17, 2008
  23. Kevin BallardJan 17, 2008
  24. Johannes SchindelinJan 17, 2008
  25. Pedro MeloJan 17, 2008
  26. Peter KarlssonJan 18, 2008
  27. Jakub NarebskiJan 18, 2008
  28. David KastrupJan 16, 2008
  29. Linus TorvaldsJan 17, 2008
  30. Kevin BallardJan 17, 2008
  31. Linus TorvaldsJan 17, 2008
  32. Johannes SchindelinJan 17, 2008
  33. Pedro MeloJan 17, 2008
  34. Johannes SchindelinJan 17, 2008
  35. Linus TorvaldsJan 17, 2008
  36. Linus TorvaldsJan 17, 2008
  37. Kevin BallardJan 17, 2008
  38. Linus TorvaldsJan 17, 2008
  39. Kevin BallardJan 17, 2008
  40. Martin LanghoffJan 17, 2008
  41. Kevin BallardJan 17, 2008
  42. Geert BoschJan 17, 2008
  43. Mitch TishmackJan 17, 2008
  44. Wincent ColaiutaJan 17, 2008
  45. Kevin BallardJan 17, 2008
  46. Johannes SchindelinJan 17, 2008
  47. Kevin BallardJan 17, 2008
  48. Robin RosenbergJan 18, 2008
  49. Andrew HeybeyJan 17, 2008
  50. Kevin BallardJan 17, 2008
  51. Kyle MoffettJan 19, 2008
  52. Kevin BallardJan 19, 2008
  53. Wincent ColaiutaJan 17, 2008
  54. Linus TorvaldsJan 17, 2008
  55. Mark JunkerJan 17, 2008
  56. Pedro MeloJan 17, 2008
  57. Johannes SchindelinJan 17, 2008
  58. Mark JunkerJan 17, 2008
  59. Pedro MeloJan 17, 2008
  60. Linus TorvaldsJan 17, 2008
  61. Pedro MeloJan 17, 2008
  62. Linus TorvaldsJan 17, 2008
  63. Mark JunkerJan 17, 2008
  64. Pedro MeloJan 17, 2008
  65. Theodore TsoJan 17, 2008
  66. Linus TorvaldsJan 17, 2008
  67. Kevin BallardJan 18, 2008
  68. Linus TorvaldsJan 18, 2008
  69. Robin RosenbergJan 18, 2008
  70. Linus TorvaldsJan 18, 2008
  71. Brian DessentJan 18, 2008
  72. Dmitry PotapovJan 18, 2008
  73. Robin RosenbergJan 18, 2008
  74. Dmitry PotapovJan 18, 2008
  75. Peter KarlssonJan 18, 2008
  76. Jakub NarebskiJan 18, 2008
  77. Peter KarlssonJan 18, 2008
  78. Dmitry PotapovJan 18, 2008
  79. Peter KarlssonJan 18, 2008
  80. Linus TorvaldsJan 18, 2008
  81. Kevin BallardJan 18, 2008
  82. Dmitry PotapovJan 19, 2008
  83. Kevin BallardJan 19, 2008
  84. Dmitry PotapovJan 19, 2008
  85. Linus TorvaldsJan 19, 2008
  86. Mark JunkerJan 19, 2008
  87. Johannes SchindelinJan 19, 2008
  88. Dmitry PotapovJan 20, 2008
  89. Linus TorvaldsJan 20, 2008
  90. Johannes SchindelinJan 20, 2008
  91. Wincent ColaiutaJan 20, 2008
  92. Linus TorvaldsJan 20, 2008
  93. Mike HommeyJan 20, 2008
  94. Linus TorvaldsJan 20, 2008
  95. Mike HommeyJan 20, 2008
  96. Linus TorvaldsJan 20, 2008
  97. Dmitry PotapovJan 20, 2008
  98. Dmitry PotapovJan 20, 2008
  99. Wincent ColaiutaJan 20, 2008
  100. Junio C HamanoJan 18, 2008
  101. Johannes SchindelinJan 18, 2008
  102. Eric W. BiedermanJan 23, 2008
  103. Junio C HamanoJan 23, 2008
  104. Nicolas PitreJan 23, 2008
  105. Junio C HamanoJan 23, 2008
  106. Peter KarlssonJan 21, 2008
  107. Kevin BallardJan 21, 2008
  108. David KastrupJan 21, 2008
  109. Kevin BallardJan 21, 2008
  110. Dmitry PotapovJan 21, 2008
  111. Kevin BallardJan 21, 2008
  112. David KastrupJan 21, 2008
  113. Dmitry PotapovJan 21, 2008
  114. Jeff KingJan 21, 2008
  115. Nicolas PitreJan 21, 2008
  116. Kevin BallardJan 21, 2008
  117. David KastrupJan 21, 2008
  118. David KastrupJan 21, 2008
  119. Linus TorvaldsJan 21, 2008
  120. Kevin BallardJan 21, 2008
  121. Linus TorvaldsJan 21, 2008
  122. Kevin BallardJan 21, 2008
  123. Linus TorvaldsJan 21, 2008
  124. Kevin BallardJan 21, 2008
  125. David KastrupJan 21, 2008
  126. Martin LanghoffJan 21, 2008
  127. Kevin BallardJan 21, 2008
  128. Martin LanghoffJan 21, 2008
  129. Linus TorvaldsJan 21, 2008
  130. Kevin BallardJan 21, 2008
  131. Linus TorvaldsJan 21, 2008
  132. Kevin BallardJan 21, 2008
  133. Martin LanghoffJan 21, 2008
  134. Theodore TsoJan 21, 2008
  135. Kevin BallardJan 21, 2008
  136. Linus TorvaldsJan 21, 2008
  137. Kevin BallardJan 22, 2008
  138. Linus TorvaldsJan 22, 2008
  139. Linus TorvaldsJan 22, 2008
  140. Kevin BallardJan 22, 2008
  141. Linus TorvaldsJan 22, 2008
  142. Kevin BallardJan 22, 2008
  143. Linus TorvaldsJan 22, 2008
  144. Martin LanghoffJan 22, 2008
  145. Kevin BallardJan 22, 2008
  146. Theodore TsoJan 21, 2008
  147. Kevin BallardJan 21, 2008
  148. Theodore TsoJan 21, 2008
  149. Kevin BallardJan 21, 2008
  150. Theodore TsoJan 21, 2008
  151. Kevin BallardJan 21, 2008
  152. Dmitry PotapovJan 21, 2008
  153. Kevin BallardJan 21, 2008
  154. Dmitry PotapovJan 21, 2008
  155. Kevin BallardJan 21, 2008
  156. Dmitry PotapovJan 21, 2008
  157. Mike HommeyJan 21, 2008
  158. Dmitry PotapovJan 21, 2008
  159. Martin LanghoffJan 21, 2008
  160. David KastrupJan 21, 2008
  161. Linus TorvaldsJan 21, 2008
  162. Martin LanghoffJan 21, 2008
  163. Dmitry PotapovJan 21, 2008
  164. Linus TorvaldsJan 21, 2008
  165. Dmitry PotapovJan 17, 2008
  166. JM IbanezJan 17, 2008
  167. Johannes SchindelinJan 17, 2008
  168. Robin RosenbergJan 18, 2008
  169. Linus TorvaldsJan 17, 2008
  170. Dmitry PotapovJan 17, 2008
  171. Dmitry PotapovJan 16, 2008
  172. Eyvind BernhardsenJan 16, 2008
  173. Wincent ColaiutaJan 16, 2008
  174. Miles BaderJan 17, 2008
  175. Jay SoffianJan 17, 2008
  176. Jay SoffianJan 17, 2008
  177. Junio C HamanoJan 17, 2008
  178. Wincent ColaiutaJan 17, 2008
  179. Johannes SchindelinJan 17, 2008
  180. Pedro MeloJan 17, 2008
  181. Wincent ColaiutaJan 17, 2008
  182. Johannes SchindelinJan 17, 2008
  183. Wincent ColaiutaJan 17, 2008
  184. Junio C HamanoJan 17, 2008
  185. Johan HerlandJan 17, 2008
  186. Johannes SchindelinJan 17, 2008
  187. Wincent ColaiutaJan 17, 2008
  188. Linus TorvaldsJan 17, 2008
  189. Theodore TsoJan 21, 2008
  190. Kevin BallardJan 21, 2008
  191. Martin LanghoffJan 21, 2008
  192. Kevin BallardJan 21, 2008
  193. Johannes SchindelinJan 22, 2008
  194. Kevin BallardJan 22, 2008
  195. David KastrupJan 22, 2008
  196. Martin LanghoffJan 22, 2008
  197. Johannes SchindelinJan 22, 2008
  198. Martin LanghoffJan 22, 2008
  199. Johannes SchindelinJan 22, 2008
  200. David KastrupJan 21, 2008
  201. Kevin BallardJan 22, 2008
  202. David KastrupJan 22, 2008
  203. Kevin BallardJan 21, 2008
  204. Martin LanghoffJan 21, 2008
  205. Kevin BallardJan 22, 2008
  206. Theodore TsoJan 23, 2008
  207. Kevin BallardJan 23, 2008
  208. Martin LanghoffJan 23, 2008
  209. Theodore TsoJan 23, 2008
  210. David KastrupJan 23, 2008
  211. Linus TorvaldsJan 23, 2008
  212. Martin LanghoffJan 23, 2008
  213. Kevin BallardJan 23, 2008
  214. Martin LanghoffJan 23, 2008
  215. Theodore TsoJan 23, 2008
  216. Linus TorvaldsJan 23, 2008
  217. Kevin BallardJan 23, 2008
  218. Mike HommeyJan 23, 2008
  219. Kevin BallardJan 23, 2008
  220. Dmitry PotapovJan 23, 2008
  221. Jonathan del StrotherJan 23, 2008
  222. Dmitry PotapovJan 23, 2008
  223. Mike HommeyJan 23, 2008
  224. Dmitry PotapovJan 23, 2008
  225. Mike HommeyJan 23, 2008
  226. Theodore TsoJan 23, 2008
  227. Linus TorvaldsJan 23, 2008
  228. Theodore TsoJan 23, 2008
  229. Kevin BallardJan 23, 2008
  230. Linus TorvaldsJan 23, 2008
  231. On pathnamesJunio C Hamano, Jan 24, 2008
  232. Nicolas PitreJan 24, 2008
  233. Martin LanghoffJan 25, 2008
  234. Junio C HamanoJan 25, 2008
  235. Junio C HamanoJan 25, 2008
  236. Pedro MeloJan 25, 2008
  237. Johannes SchindelinJan 25, 2008
  238. David KastrupJan 25, 2008
  239. Wincent ColaiutaJan 25, 2008
  240. SeanJan 24, 2008
  241. Johannes SchindelinJan 25, 2008
  242. Daniel BarkalowJan 25, 2008
  243. Junio C HamanoJan 25, 2008
  244. Johannes SchindelinJan 25, 2008
  245. Daniel BarkalowJan 25, 2008
  246. Johannes SchindelinJan 25, 2008
  247. Jeff KingJan 25, 2008
  248. Jay SoffianJan 23, 2008
  249. Martin LanghoffJan 23, 2008
  250. Kevin BallardJan 23, 2008
  251. Dmitry PotapovJan 23, 2008
  252. Kevin BallardJan 23, 2008
  253. Kevin BallardJan 24, 2008
  254. Junio C HamanoJan 24, 2008
  255. Martin LanghoffJan 24, 2008
  256. Kevin BallardJan 24, 2008
  257. Steffen ProhaskaJan 24, 2008
  258. Mitch TishmackJan 24, 2008
  259. Mitch TishmackJan 24, 2008
  260. Kevin BallardJan 24, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.