{"thread":{"id":"27354","subject":"git commit -a reports untracked files after a clone","startedAt":"2011-05-15T00:46:37Z","lastAt":"2011-10-06T16:06:09Z","messageCount":13,"participants":["Philipp Metzler","Junio C Hamano","Jeff King","Joerg Rosenkranz"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"167871","messageId":"7B399C74-8048-42BA-8672-9D7964F24888@goli.at","threadId":"27354","inReplyTo":null,"subject":"git commit -a reports untracked files after a clone","fromName":"Philipp Metzler","fromEmail":"phil@goli.at","sentAt":"2011-05-15T00:46:37Z","receivedAt":"2011-05-15T00:46:37Z","isPatch":false,"sender":{"key":"phil@goli.at","avatar":"https://gravatar.com/avatar/f4c5448347447641988def5de6705b3493b0c8a65eb1da353171ce17b3b7504f?d=mp&s=160"},"body":"Hi,\n\nI have a problem with git on OS X 10.6.7\n\n5102c6173c5a1c683dfdd8ccd07528adddd51745 is the first bad commit\n\nSHA:\t5102c6173c5a1c683dfdd8ccd07528adddd51745\nAuthor:\tJoshua Jensen <jjensen@workspacewhiz.com>\nDate:\tSun Oct 03 2010 11:56:43 GMT+0200 (CEST)\n\nThis is how you can reproduce the problem:\n1. clone a repo\n2. run the command \"git commit -a\"\n\nI would expect: nothing to commit (working directory clean)\nInstead it reports: nothing added to commit but untracked files present (use \"git add\" to track)\n\nStarting with commit \n\nSHA:\t8c8674fc954d8c4bc46f303a141f510ecf264fcd\nAuthor:\tJeff King <peff@peff.net>\nDate:\tFri Mar 25 2011 19:13:31 GMT+0100 (CET)\n\nthe following behaviour can be observed when these three commands are run in this order:\n\n1. git commit -a \n   nothing added to commit but untracked files present (use \"git add\" to track)\n2. git status\n   nothing to commit (working directory clean)\n3. git commit -a\n   nothing to commit (working directory clean)\n\nSo \"git status\" makes the untracked files \"go away\".\n\nCheers,\n\nPhilipp\n\n_______________________________________________________________\n\nDI Philipp Metzler\nGoli.at GesbR.\nDorf Rieden 7/11\nA-6900 Bregenz\nEU - Austria\n\nE-Mail: phil@goli.at\nSkype: googol\nTel: +43 / 676 / 72 94 176\nICQ: 13950954\n\no www.philippmetzler.com - Softwareentwicklung und Websites mit Django und Typo3.\no www.goli.at - Ihr Speicherplatz im Netz. Messen Sie uns an unseren Daten.\no www.clickshopping.at - Wir bringen Ihre Produkte auf den Punkt.\no www.greencar.at - Elektroautos und mehr ...\n_______________________________________________________________\n"},{"id":"167876","messageId":"7v39kgr5ln.fsf@alter.siamese.dyndns.org","threadId":"27354","inReplyTo":"7B399C74-8048-42BA-8672-9D7964F24888@goli.at","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-05-15T03:43:32Z","receivedAt":"2011-05-15T03:43:32Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Philipp Metzler <phil@goli.at> writes:\n\n> This is how you can reproduce the problem:\n> 1. clone a repo\n> 2. run the command \"git commit -a\"\n\nDoes it reproduce with _any_ repository, or just a particular one?  If it\nis the latter, then the above description is useless for anybody to start\nformulating any theory on what goes wrong. Sorry.\n\nYou being on OS X, I would guess that you may have a pathname in the\nproject that HFS+ does not like.\n\nOn HFS+, when a program creates a file with \"open(filename, O_CREAT)\",\nreading the directory the created path is in with readdir() does not\nreturn the string given when it was created but something else for certain\npathnames; you may be seeing that git is confused by that behaviour.\n\nBut that is just a wild guess.\n"},{"id":"167882","messageId":"4501A58F-46F8-410C-BCEF-DD2FC10BC3A5@goli.at","threadId":"27354","inReplyTo":"7v39kgr5ln.fsf@alter.siamese.dyndns.org","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Philipp Metzler","fromEmail":"phil@goli.at","sentAt":"2011-05-15T08:26:01Z","receivedAt":"2011-05-15T08:26:01Z","isPatch":false,"sender":{"key":"phil@goli.at","avatar":"https://gravatar.com/avatar/f4c5448347447641988def5de6705b3493b0c8a65eb1da353171ce17b3b7504f?d=mp&s=160"},"body":"Hi,\n\non OS X 10.6.7 it seemes to happen with any git repository - also with the git git repo itself:\n\n[phil@Silberpfeil tmp]$ git --version\ngit version 1.7.5.1\n[phil@Silberpfeil tmp]$ git clone git://git.kernel.org/pub/scm/git/git.git\nCloning into git...\nremote: Counting objects: 140383, done.\nremote: Compressing objects: 100% (33498/33498), done.\nremote: Total 140383 (delta 105777), reused 139383 (delta 104980)\nReceiving objects: 100% (140383/140383), 27.61 MiB | 642 KiB/s, done.\nResolving deltas: 100% (105777/105777), done.\n[phil@Silberpfeil tmp]$ cd git\n[phil@Silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@Silberpfeil git]$ git status\n# On branch master\nnothing to commit (working directory clean)\n[phil@Silberpfeil git]$ git commit -a\n# On branch master\nnothing to commit (working directory clean)\n\nCheers,\n\nPhilipp\n\n_______________________________________________________________\n\nDI Philipp Metzler\nGoli.at GesbR.\nDorf Rieden 7/11\nA-6900 Bregenz\nEU - Austria\n\nE-Mail: phil@goli.at\nSkype: googol\nTel: +43 / 676 / 72 94 176\nICQ: 13950954\n\no www.philippmetzler.com - Softwareentwicklung und Websites mit Django und Typo3.\no www.goli.at - Ihr Speicherplatz im Netz. Messen Sie uns an unseren Daten.\no www.clickshopping.at - Wir bringen Ihre Produkte auf den Punkt.\no www.greencar.at - Elektroautos und mehr ...\n_______________________________________________________________\n\nAm 15.05.2011 um 05:43 schrieb Junio C Hamano:\n\n> Philipp Metzler <phil@goli.at> writes:\n> \n>> This is how you can reproduce the problem:\n>> 1. clone a repo\n>> 2. run the command \"git commit -a\"\n> \n> Does it reproduce with _any_ repository, or just a particular one?  If it\n> is the latter, then the above description is useless for anybody to start\n> formulating any theory on what goes wrong. Sorry.\n> \n> You being on OS X, I would guess that you may have a pathname in the\n> project that HFS+ does not like.\n> \n> On HFS+, when a program creates a file with \"open(filename, O_CREAT)\",\n> reading the directory the created path is in with readdir() does not\n> return the string given when it was created but something else for certain\n> pathnames; you may be seeing that git is confused by that behaviour.\n> \n> But that is just a wild guess.\n"},{"id":"167981","messageId":"20110516103829.GA23889@sigill.intra.peff.net","threadId":"27354","inReplyTo":"4501A58F-46F8-410C-BCEF-DD2FC10BC3A5@goli.at","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-05-16T10:38:29Z","receivedAt":"2011-05-16T10:38:29Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, May 15, 2011 at 10:26:01AM +0200, Philipp Metzler wrote:\n\n> [phil@Silberpfeil tmp]$ git --version\n> git version 1.7.5.1\n> [phil@Silberpfeil tmp]$ git clone git://git.kernel.org/pub/scm/git/git.git\n> Cloning into git...\n> remote: Counting objects: 140383, done.\n> remote: Compressing objects: 100% (33498/33498), done.\n> remote: Total 140383 (delta 105777), reused 139383 (delta 104980)\n> Receiving objects: 100% (140383/140383), 27.61 MiB | 642 KiB/s, done.\n> Resolving deltas: 100% (105777/105777), done.\n> [phil@Silberpfeil tmp]$ cd git\n> [phil@Silberpfeil git]$ git commit -a\n> # On branch master\n> # Untracked files:\n> #   (use \"git add <file>...\" to include in what will be committed)\n> #\n> #\tvcs-svn/\n> #\txdiff/\n\nCan you try this again with \"git commit -uall\" so we can see which\nspecific files in those directories are causing the issue?\n\n-Peff\n"},{"id":"167982","messageId":"7C2AE1EE-4CAE-4E86-A53C-C97BE1F2573B@goli.at","threadId":"27354","inReplyTo":"20110516103829.GA23889@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Philipp Metzler","fromEmail":"phil@goli.at","sentAt":"2011-05-16T10:49:07Z","receivedAt":"2011-05-16T10:49:07Z","isPatch":false,"sender":{"key":"phil@goli.at","avatar":"https://gravatar.com/avatar/f4c5448347447641988def5de6705b3493b0c8a65eb1da353171ce17b3b7504f?d=mp&s=160"},"body":"Hi,\n\nHope this helps. Just let me know if you need additional infos.\n\n[phil@Silberpfeil tmp]$ git clone git://git.kernel.org/pub/scm/git/git.git\nCloning into git...\nremote: Counting objects: 140472, done.\nremote: Compressing objects: 100% (33586/33586), done.\nremote: Total 140472 (delta 105841), reused 139396 (delta 104981)\nReceiving objects: 100% (140472/140472), 27.67 MiB | 638 KiB/s, done.\nResolving deltas: 100% (105841/105841), done.\n[phil@Silberpfeil tmp]$ cd git\n[phil@Silberpfeil git]$ git commit -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@Silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9154/\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@Silberpfeil git]$ git commit -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@Silberpfeil git]$ git commit -u\n# On branch master\nnothing to commit (working directory clean)\n[phil@Silberpfeil git]$ git commit --all\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9154/\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n\nCheers,\n\nPhilipp\n\n_______________________________________________________________\n\nDI Philipp Metzler\nGoli.at GesbR.\nDorf Rieden 7/11\nA-6900 Bregenz\nEU - Austria\n\nE-Mail: phil@goli.at\nSkype: googol\nTel: +43 / 676 / 72 94 176\nICQ: 13950954\n\no www.philippmetzler.com - Softwareentwicklung und Websites mit Django und Typo3.\no www.goli.at - Ihr Speicherplatz im Netz. Messen Sie uns an unseren Daten.\no www.clickshopping.at - Wir bringen Ihre Produkte auf den Punkt.\no www.greencar.at - Elektroautos und mehr ...\n_______________________________________________________________\n\nAm 16.05.2011 um 12:38 schrieb Jeff King:\n\n> On Sun, May 15, 2011 at 10:26:01AM +0200, Philipp Metzler wrote:\n> \n>> [phil@Silberpfeil tmp]$ git --version\n>> git version 1.7.5.1\n>> [phil@Silberpfeil tmp]$ git clone git://git.kernel.org/pub/scm/git/git.git\n>> Cloning into git...\n>> remote: Counting objects: 140383, done.\n>> remote: Compressing objects: 100% (33498/33498), done.\n>> remote: Total 140383 (delta 105777), reused 139383 (delta 104980)\n>> Receiving objects: 100% (140383/140383), 27.61 MiB | 642 KiB/s, done.\n>> Resolving deltas: 100% (105777/105777), done.\n>> [phil@Silberpfeil tmp]$ cd git\n>> [phil@Silberpfeil git]$ git commit -a\n>> # On branch master\n>> # Untracked files:\n>> #   (use \"git add <file>...\" to include in what will be committed)\n>> #\n>> #\tvcs-svn/\n>> #\txdiff/\n> \n> Can you try this again with \"git commit -uall\" so we can see which\n> specific files in those directories are causing the issue?\n> \n> -Peff\n"},{"id":"167986","messageId":"20110516120825.GA24418@sigill.intra.peff.net","threadId":"27354","inReplyTo":"7C2AE1EE-4CAE-4E86-A53C-C97BE1F2573B@goli.at","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-05-16T12:08:25Z","receivedAt":"2011-05-16T12:08:25Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, May 16, 2011 at 12:49:07PM +0200, Philipp Metzler wrote:\n\n> [phil@Silberpfeil git]$ git commit -uall\n> # On branch master\n> nothing to commit (working directory clean)\n\nHmm, nothing. That's odd.\n\n> [phil@Silberpfeil git]$ git commit -a\n> # On branch master\n> # Untracked files:\n> #   (use \"git add <file>...\" to include in what will be committed)\n> #\n> #\tt/t9154/\n> #\tt/t9601/\n> #\tt/t9602/\n> #\tt/t9603/\n> #\tt/t9700/\n> #\tt/valgrind/\n> #\ttemplates/\n> #\tvcs-svn/\n> #\txdiff/\n> nothing added to commit but untracked files present (use \"git add\" to track)\n\nAnd now totally different output from before, and from the previous run.\n\nSo this is really strange. The fact that the list of directories is\n_different_ from your previous posting implies to me that it is not\nsomething about those particular files, but rather some weird race\ncondition in the creation of those directories or the index.\n\nBut then the fact that we see them with no \"-u\", but don't see them with\n\"-uall\" implies some weird heisenbug in git's directory traversal. What\nhappens if you do \"git commit --all -uall\"? I'd like to see if the thing\nthat switches the behavior is the presence of \"--all\" or the absence of\na \"-u\" option.\n\n-Peff\n"},{"id":"167988","messageId":"56A79A18-E53A-4E84-8054-5D2ED6F01835@goli.at","threadId":"27354","inReplyTo":"20110516120825.GA24418@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Philipp Metzler","fromEmail":"phil@goli.at","sentAt":"2011-05-16T12:25:09Z","receivedAt":"2011-05-16T12:25:09Z","isPatch":false,"sender":{"key":"phil@goli.at","avatar":"https://gravatar.com/avatar/f4c5448347447641988def5de6705b3493b0c8a65eb1da353171ce17b3b7504f?d=mp&s=160"},"body":"Hi,\n\ncreated two directories tmp and tmp2 and executed the commands in a different order with different results:\n\n[phil@silberpfeil tmp]$ git clone git://git.kernel.org/pub/scm/git/git.git\nCloning into git...\nremote: Counting objects: 140472, done.\nremote: Compressing objects: 100% (33586/33586), done.\nremote: Total 140472 (delta 105843), reused 139396 (delta 104981)\nReceiving objects: 100% (140472/140472), 27.66 MiB | 641 KiB/s, done.\nResolving deltas: 100% (105843/105843), done.\n[phil@silberpfeil tmp]$ cd git\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9150/\n#\tt/t9151/\n#\tt/t9153/\n#\tt/t9154/\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9150/\n#\tt/t9151/\n#\tt/t9153/\n#\tt/t9154/\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n\n\n******************************************************************************\n\n\n[phil@silberpfeil tmp2]$ git clone git://git.kernel.org/pub/scm/git/git.git\nCloning into git...\nremote: Counting objects: 140472, done.\nremote: Compressing objects: 100% (33586/33586), done.\nremote: Total 140472 (delta 105841), reused 139396 (delta 104981)\nReceiving objects: 100% (140472/140472), 27.67 MiB | 602 KiB/s, done.\nResolving deltas: 100% (105841/105841), done.\n[phil@silberpfeil tmp2]$ cd git\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git status\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\nnothing to commit (working directory clean)\n\nCheers,\n\nPhilipp\n\n_______________________________________________________________\n\nDI Philipp Metzler\nGoli.at GesbR.\nDorf Rieden 7/11\nA-6900 Bregenz\nEU - Austria\n\nE-Mail: phil@goli.at\nSkype: googol\nTel: +43 / 676 / 72 94 176\nICQ: 13950954\n\no www.philippmetzler.com - Softwareentwicklung und Websites mit Django und Typo3.\no www.goli.at - Ihr Speicherplatz im Netz. Messen Sie uns an unseren Daten.\no www.clickshopping.at - Wir bringen Ihre Produkte auf den Punkt.\no www.greencar.at - Elektroautos und mehr ...\n_______________________________________________________________\n\nAm 16.05.2011 um 14:08 schrieb Jeff King:\n\n> On Mon, May 16, 2011 at 12:49:07PM +0200, Philipp Metzler wrote:\n> \n>> [phil@Silberpfeil git]$ git commit -uall\n>> # On branch master\n>> nothing to commit (working directory clean)\n> \n> Hmm, nothing. That's odd.\n> \n>> [phil@Silberpfeil git]$ git commit -a\n>> # On branch master\n>> # Untracked files:\n>> #   (use \"git add <file>...\" to include in what will be committed)\n>> #\n>> #\tt/t9154/\n>> #\tt/t9601/\n>> #\tt/t9602/\n>> #\tt/t9603/\n>> #\tt/t9700/\n>> #\tt/valgrind/\n>> #\ttemplates/\n>> #\tvcs-svn/\n>> #\txdiff/\n>> nothing added to commit but untracked files present (use \"git add\" to track)\n> \n> And now totally different output from before, and from the previous run.\n> \n> So this is really strange. The fact that the list of directories is\n> _different_ from your previous posting implies to me that it is not\n> something about those particular files, but rather some weird race\n> condition in the creation of those directories or the index.\n> \n> But then the fact that we see them with no \"-u\", but don't see them with\n> \"-uall\" implies some weird heisenbug in git's directory traversal. What\n> happens if you do \"git commit --all -uall\"? I'd like to see if the thing\n> that switches the behavior is the presence of \"--all\" or the absence of\n> a \"-u\" option.\n> \n> -Peff\n"},{"id":"167989","messageId":"1D7CF554-A4AC-49EF-A095-9B05167BC458@goli.at","threadId":"27354","inReplyTo":"20110516120825.GA24418@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Philipp Metzler","fromEmail":"phil@goli.at","sentAt":"2011-05-16T12:38:33Z","receivedAt":"2011-05-16T12:38:33Z","isPatch":false,"sender":{"key":"phil@goli.at","avatar":"https://gravatar.com/avatar/f4c5448347447641988def5de6705b3493b0c8a65eb1da353171ce17b3b7504f?d=mp&s=160"},"body":"Hi,\n\nCould be a race condition / heisenbug yep. The result of \"git commit -a\" differs - the directories vcs-svn and xdiff are there all the time but not the others. The only constant thing is that the command \"git status\" always \"cleans up\" everything. Another run: \n\n[phil@silberpfeil git]$ git --version\ngit version 1.7.5.1\n[phil@silberpfeil tmp3]$ git clone git://git.kernel.org/pub/scm/git/git.git\nCloning into git...\nremote: Counting objects: 140472, done.\nremote: Compressing objects: 100% (33586/33586), done.\nremote: Total 140472 (delta 105843), reused 139396 (delta 104981)\nReceiving objects: 100% (140472/140472), 27.66 MiB | 642 KiB/s, done.\nResolving deltas: 100% (105843/105843), done.\n[phil@silberpfeil tmp3]$ git commit --all -uall\nfatal: Not a git repository (or any parent up to mount parent /Users)\nStopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).\n[phil@silberpfeil tmp3]$ cd git\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\n# Untracked files:\n#   (use \"git add <file>...\" to include in what will be committed)\n#\n#\tt/t9601/\n#\tt/t9602/\n#\tt/t9603/\n#\tt/t9700/\n#\tt/valgrind/\n#\ttemplates/\n#\tvcs-svn/\n#\txdiff/\nnothing added to commit but untracked files present (use \"git add\" to track)\n[phil@silberpfeil git]$ git status\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit -a\n# On branch master\nnothing to commit (working directory clean)\n[phil@silberpfeil git]$ git commit --all -uall\n# On branch master\nnothing to commit (working directory clean)\n\nCheers,\n\nPhilipp\n\n_______________________________________________________________\n\nDI Philipp Metzler\nGoli.at GesbR.\nDorf Rieden 7/11\nA-6900 Bregenz\nEU - Austria\n\nE-Mail: phil@goli.at\nSkype: googol\nTel: +43 / 676 / 72 94 176\nICQ: 13950954\n\no www.philippmetzler.com - Softwareentwicklung und Websites mit Django und Typo3.\no www.goli.at - Ihr Speicherplatz im Netz. Messen Sie uns an unseren Daten.\no www.clickshopping.at - Wir bringen Ihre Produkte auf den Punkt.\no www.greencar.at - Elektroautos und mehr ...\n_______________________________________________________________\n\nAm 16.05.2011 um 14:08 schrieb Jeff King:\n\n> On Mon, May 16, 2011 at 12:49:07PM +0200, Philipp Metzler wrote:\n> \n>> [phil@Silberpfeil git]$ git commit -uall\n>> # On branch master\n>> nothing to commit (working directory clean)\n> \n> Hmm, nothing. That's odd.\n> \n>> [phil@Silberpfeil git]$ git commit -a\n>> # On branch master\n>> # Untracked files:\n>> #   (use \"git add <file>...\" to include in what will be committed)\n>> #\n>> #\tt/t9154/\n>> #\tt/t9601/\n>> #\tt/t9602/\n>> #\tt/t9603/\n>> #\tt/t9700/\n>> #\tt/valgrind/\n>> #\ttemplates/\n>> #\tvcs-svn/\n>> #\txdiff/\n>> nothing added to commit but untracked files present (use \"git add\" to track)\n> \n> And now totally different output from before, and from the previous run.\n> \n> So this is really strange. The fact that the list of directories is\n> _different_ from your previous posting implies to me that it is not\n> something about those particular files, but rather some weird race\n> condition in the creation of those directories or the index.\n> \n> But then the fact that we see them with no \"-u\", but don't see them with\n> \"-uall\" implies some weird heisenbug in git's directory traversal. What\n> happens if you do \"git commit --all -uall\"? I'd like to see if the thing\n> that switches the behavior is the presence of \"--all\" or the absence of\n> a \"-u\" option.\n> \n> -Peff\n"},{"id":"167996","messageId":"20110516145535.GA25930@sigill.intra.peff.net","threadId":"27354","inReplyTo":"1D7CF554-A4AC-49EF-A095-9B05167BC458@goli.at","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-05-16T14:55:35Z","receivedAt":"2011-05-16T14:55:35Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, May 16, 2011 at 02:38:33PM +0200, Philipp Metzler wrote:\n\n> Could be a race condition / heisenbug yep. The result of \"git commit\n> -a\" differs - the directories vcs-svn and xdiff are there all the time\n> but not the others. The only constant thing is that the command \"git\n> status\" always \"cleans up\" everything. Another run:\n\nOK, I'm making some progress. I can replicate on Linux with:\n\n  $ git config core.ignorecase true\n  $ git clone git foo\n  $ cd foo && git commit -a\n\nwhich gives a bunch of directories which contain tracked contents. Doing\n\"git status\" makes the problem go away, but then doing this makes it\ncome back:\n\n  $ rm .git/index\n  $ git read-tree --reset HEAD\n\nAnd like you, it doesn't trigger with \"-uall\".\n\nSo it is something about:\n\n  1. A fresh index that is perhaps missing stat information for some\n     entries.\n\n  2. Stopping the traversal at the directory boundary rather than\n     looking at details of each directory (\"-uall\").\n\n  3. core.ignorecase\n\nBut it is definitely repeatable. Which is good, because that will make\nit easier to track down. :)\n\nI'll see what I can do, but it may not be today.\n\n-Peff\n"},{"id":"168886","messageId":"20110527180045.GA29119@sigill.intra.peff.net","threadId":"27354","inReplyTo":"20110516145535.GA25930@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-05-27T18:00:45Z","receivedAt":"2011-05-27T18:00:45Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"[+cc Joshua, as the problem is in his 5102c61 (Add case insensitivity\n support for directories when using git status, 2010-10-03)]\n\nOn Mon, May 16, 2011 at 10:55:35AM -0400, Jeff King wrote:\n\n> OK, I'm making some progress. I can replicate on Linux with:\n> \n>   $ git config --global core.ignorecase true\n>   $ git clone git foo\n>   $ cd foo && git commit -a\n>   # On branch private\n>   # Untracked files:\n>   #       Documentation/\n>   #       block-sha1/\n>   #       builtin/\n>   #       compat/\n>   #       contrib/\n>   #       git-gui/\n>   #       git_remote_helpers/\n>   #       gitk-git/\n>   #       gitweb/\n>   #       perl/\n>   #       po/\n>   #       ppc/\n>   #       t/\n>   #       templates/\n>   #       vcs-svn/\n>   #       xdiff/\n>   nothing added to commit but untracked files present\n>\n> [i.e., all of those directories are listed as containing untracked\n>  contents even though they clearly have tracked files in them]\n\nOK, I figured it out. Unfortunately, the fix is non-trivial. Basically\nwe are getting bogus data out of the index's name_hash for directories.\n\nWhat is happening is this:\n\n  1. We load the index, and for each entry, insert it into the index's\n     name_hash. In addition, if ignorecase is turned on, we make an\n     entry in the name_hash for the directory (e.g., \"contrib/\"), which\n     uses the following code from 5102c61's hash_index_entry_directories:\n\n        hash = hash_name(ce->name, ptr - ce->name);\n        if (!lookup_hash(hash, &istate->name_hash)) {\n                pos = insert_hash(hash, &istate->name_hash);\n                ce->next = *pos;\n                *pos = ce;\n        }\n\n     Note that we only add the directory entry if there is not already an\n     entry.\n\n  2. We run add_files_to_cache, which gets updated information for each\n     cache entry. It helpfully inserts this information into the cache,\n     which calls replace_index_entry. This in turn calls\n     remove_name_hash() on the old entry, and add_name_hash() on the new\n     one. But remove_name_hash doesn't actually remove from the hash, it\n     only marks it as \"no longer interesting\" (from cache.h):\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_name_hash(struct cache_entry *ce)\n      {\n              ce->ce_flags |= CE_UNHASHED;\n      }\n\n     This is OK in the specific-file case, since the entries in the hash\n     form a linked list, and we can just skip the \"not here anymore\"\n     entries during lookup.\n\n     But for the directory hash entry, we will _not_ write a new entry,\n     because there is already one there: the old one that is actually no\n     longer interesting!\n\n  3. While traversing the directories, we end up in the\n     directory_exists_in_index_icase function to see if a directory is\n     interesting. This in turn checks index_name_exists, which will\n     look up the directory in the index's name_hash. We see the old,\n     deleted record, and assume there is nothing interesting. The\n     directory gets marked as untracked, even though there are index\n     entries in it.\n\nAnd that explains the three prerequisites I needed for replicating the\nbug:\n\n  1. You must be using ignore-case. The bug is in that code path.\n\n  2. You must have an index with stale stat information; this is what\n     provokes add_files_to_cache to update the index entries.\n\n  3. You must stop traversal at the directory boundary (i.e., not using\n     \"-uall\"). Otherwise we end up looking for the exact filenames,\n     which do get found.\n\nThe problem is in the code I showed above:\n\n        hash = hash_name(ce->name, ptr - ce->name);\n        if (!lookup_hash(hash, &istate->name_hash)) {\n                pos = insert_hash(hash, &istate->name_hash);\n                ce->next = *pos;\n                *pos = ce;\n        }\n\nHaving a single cache entry that represents the directory is not enough;\nthat entry may go away if the index is changed. It may be tempting to\nsay that the problem is in our removal method; if we removed the entry\nentirely instead of simply marking it as \"not here anymore\", then we\nwould know we need to insert a new entry. But that only covers this\nparticular case of remove-replace. In the more general case, consider\nsomething like this:\n\n  1. We add \"foo/bar\" and \"foo/baz\" to the index. Each gets their own\n     entry in name_hash, plus we make a \"foo/\" entry that points to\n     \"foo/bar\".\n\n  2. We remove the \"foo/bar\" entry from the index, and from the\n     name_hash.\n\n  3. We ask if \"foo/\" exists, and see no entry, even though \"foo/baz\"\n     exists.\n\nSo we need that directory entry to have the list of _all_ cache entries\nthat indicate that the directory is tracked. So that implies making a\nlinked list as we do for other entries, like:\n\n  hash = hash_name(ce->name, ptr - ce->name);\n  pos = insert_hash(hash, &istate->name_hash);\n  ce->next = *pos;\n  *pos = ce;\n\nBut that's not right either. In fact, it shows a second bug in the\ncurrent code, which is that the \"ce->next\" pointer is supposed to be\nlinking entries for a specific filename entry, but here we are\noverwriting it for the directory entry. I _think_ this can't be\ntriggered as a bug, because:\n\n  1. This is the first entry in the directory (otherwise lookup_hash\n     would not have returned NULL), and is therefore the first entry\n     for this specific file. So ce->next must already be NULL.\n\n  2. lookup_hash returned NULL, which means \"*pos\" is going to be NULL.\n\nSo even though it looks like we might be truncating an existing list,\nit's not possible to do so in practice. But if we start actually keeping\na directory list, we will run into problems, because we'll be splicing\nunrelated lists together.\n\nSo we need to have a separate next pointer for the list in the directory\nbucket, and we need to traverse that list in index_name_exists when we\nare looking up a directory.\n\nThe patch below seems to fix it for me. I'm not 100% happy with adding\nextra icase-only cruft to \"struct cache_entry\", but I don't really see a\nway around it, short of separating out the \"next\" pointers from\ncache_entry entirely (i.e., having a separate \"cache_entry_list\"\nstruct that gets stored in the name_hash). In practice, it probably\ndoesn't matter; we have thousands of cache entries, compared to the\nmillions of objects (where adding 4 bytes to the struct _does_ impact\nperformance).\n\n---\ndiff --git a/cache.h b/cache.h\nindex 96cfc9a..2868dac 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -153,6 +153,7 @@ struct cache_entry {\n \tunsigned int ce_flags;\n \tunsigned char sha1[20];\n \tstruct cache_entry *next;\n+\tstruct cache_entry *dir_next;\n \tchar name[FLEX_ARRAY]; /* more */\n };\n \ndiff --git a/name-hash.c b/name-hash.c\nindex 1a8c619..30cb2e3 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -57,11 +57,9 @@ static void hash_index_entry_directories(struct index_state *istate, struct cach\n \t\tif (*ptr == '/') {\n \t\t\t++ptr;\n \t\t\thash = hash_name(ce->name, ptr - ce->name);\n-\t\t\tif (!lookup_hash(hash, &istate->name_hash)) {\n-\t\t\t\tpos = insert_hash(hash, &istate->name_hash);\n-\t\t\t\tce->next = *pos;\n-\t\t\t\t*pos = ce;\n-\t\t\t}\n+\t\t\tpos = insert_hash(hash, &istate->name_hash);\n+\t\t\tce->dir_next = *pos;\n+\t\t\t*pos = ce;\n \t\t}\n \t}\n }\n@@ -162,7 +160,10 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na\n \t\t\tif (same_name(ce, name, namelen, icase))\n \t\t\t\treturn ce;\n \t\t}\n-\t\tce = ce->next;\n+\t\tif (icase && name[namelen - 1] == '/')\n+\t\t\tce = ce->dir_next;\n+\t\telse\n+\t\t\tce = ce->next;\n \t}\n \n \t/*\n"},{"id":"168888","messageId":"20110527181321.GB29119@sigill.intra.peff.net","threadId":"27354","inReplyTo":"20110527180045.GA29119@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-05-27T18:13:21Z","receivedAt":"2011-05-27T18:13:21Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, May 27, 2011 at 02:00:45PM -0400, Jeff King wrote:\n\n>   1. We load the index, and for each entry, insert it into the index's\n>      name_hash. In addition, if ignorecase is turned on, we make an\n>      entry in the name_hash for the directory (e.g., \"contrib/\"), which\n>      uses the following code from 5102c61's hash_index_entry_directories:\n> \n>         hash = hash_name(ce->name, ptr - ce->name);\n>         if (!lookup_hash(hash, &istate->name_hash)) {\n>                 pos = insert_hash(hash, &istate->name_hash);\n>                 ce->next = *pos;\n>                 *pos = ce;\n>         }\n\nUrgh, sorry, I was looking at this on one of my topic branches which\ntweaked the calling convention of the hash code. All of my analysis is still\nvalid, but the code in question actually looks like this:\n\n          hash = hash_name(ce->name, ptr - ce->name);\n          if (!lookup_hash(hash, &istate->name_hash)) {\n                  pos = insert_hash(hash, ce, &istate->name_hash);\n                  if (pos) {\n                          ce->next = *pos;\n                          *pos = ce;\n                  }\n          }\n\nAnd therefore my patch needs tweaked slightly, too. Updated version is below.\n\ndiff --git a/cache.h b/cache.h\nindex 009b365..54f8c05 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -153,6 +153,7 @@ struct cache_entry {\n \tunsigned int ce_flags;\n \tunsigned char sha1[20];\n \tstruct cache_entry *next;\n+\tstruct cache_entry *dir_next;\n \tchar name[FLEX_ARRAY]; /* more */\n };\n \ndiff --git a/name-hash.c b/name-hash.c\nindex c6b6a3f..225dd76 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -57,12 +57,10 @@ static void hash_index_entry_directories(struct index_state *istate, struct cach\n \t\tif (*ptr == '/') {\n \t\t\t++ptr;\n \t\t\thash = hash_name(ce->name, ptr - ce->name);\n-\t\t\tif (!lookup_hash(hash, &istate->name_hash)) {\n-\t\t\t\tpos = insert_hash(hash, ce, &istate->name_hash);\n-\t\t\t\tif (pos) {\n-\t\t\t\t\tce->next = *pos;\n-\t\t\t\t\t*pos = ce;\n-\t\t\t\t}\n+\t\t\tpos = insert_hash(hash, ce, &istate->name_hash);\n+\t\t\tif (pos) {\n+\t\t\t\tce->dir_next = *pos;\n+\t\t\t\t*pos = ce;\n \t\t\t}\n \t\t}\n \t}\n@@ -166,7 +164,10 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na\n \t\t\tif (same_name(ce, name, namelen, icase))\n \t\t\t\treturn ce;\n \t\t}\n-\t\tce = ce->next;\n+\t\tif (icase && name[namelen - 1] == '/')\n+\t\t\tce = ce->dir_next;\n+\t\telse\n+\t\t\tce = ce->next;\n \t}\n \n \t/*\n-- \n1.7.5.3.12.g99e25.dirty\n"},{"id":"176926","messageId":"loom.20111005T161522-357@post.gmane.org","threadId":"27354","inReplyTo":"20110527181321.GB29119@sigill.intra.peff.net","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Joerg Rosenkranz","fromEmail":"joerg.rosenkranz@gmail.com","sentAt":"2011-10-05T14:26:30Z","receivedAt":"2011-10-05T14:26:30Z","isPatch":false,"sender":{"key":"joerg.rosenkranz@gmail.com","avatar":null},"body":"> On Fri, May 27, 2011 at 02:00:45PM -0400, Jeff King wrote:\n>   1. We load the index, and for each entry, insert it into the index's\n>      name_hash. In addition, if ignorecase is turned on, we make an\n>      entry in the name_hash for the directory (e.g., \"contrib/\"), which\n>      uses the following code from 5102c61's hash_index_entry_directories:\n\nSorry for reactivating this old thread.\nWe are running in this problem too. The behavior is the same in msysgit and on \nLinux. Your patch resolves that problem for us.\n\nIs there any chance to drive this patch forward? \n\nThanks,\nJoerg\n"},{"id":"177041","messageId":"20111006160608.GA22920@sigill.intra.peff.net","threadId":"27354","inReplyTo":"loom.20111005T161522-357@post.gmane.org","subject":"Re: git commit -a reports untracked files after a clone","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-10-06T16:06:09Z","receivedAt":"2011-10-06T16:06:09Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 05, 2011 at 02:26:30PM +0000, Joerg Rosenkranz wrote:\n\n> > On Fri, May 27, 2011 at 02:00:45PM -0400, Jeff King wrote:\n> >   1. We load the index, and for each entry, insert it into the index's\n> >      name_hash. In addition, if ignorecase is turned on, we make an\n> >      entry in the name_hash for the directory (e.g., \"contrib/\"), which\n> >      uses the following code from 5102c61's hash_index_entry_directories:\n> \n> Sorry for reactivating this old thread.\n> We are running in this problem too. The behavior is the same in msysgit and on \n> Linux. Your patch resolves that problem for us.\n> \n> Is there any chance to drive this patch forward?\n\nThanks for prodding. I wrote a big analysis at the end of that thread,\nbut didn't get much response. I've been meaning to pick it up again.\n\nSo I spent a few minutes revisiting the topic today. I think it's\ndefinitely a bug, and the fix is definitely correct. The patch is below,\nwith what I hope is a coherent analysis adapted from the previous\nthread.\n\nThe only question is whether or not it's OK to add a few bytes to\n\"struct cache_entry\" to cater to an uncommon case (see the comments at\nthe end of the commit message). Junio might have thoughts on that.\n\n-- >8 --\nSubject: [PATCH] fix phantom untracked files when core.ignorecase is set\n\nWhen core.ignorecase is turned on and there are stale index\nentries, \"git commit\" can sometimes report directories as\nuntracked, even though they contain tracked files.\n\nYou can see an example of this with:\n\n    # make a case-insensitive repo\n    git init repo && cd repo &&\n    git config core.ignorecase true &&\n\n    # with some tracked files in a subdir\n    mkdir subdir &&\n    > subdir/one &&\n    > subdir/two &&\n    git add . &&\n    git commit -m base &&\n\n    # now make the index entries stale\n    touch subdir/* &&\n\n    # and then ask commit to update those entries and show\n    # us the status template\n    git commit -a\n\nwhich will report \"subdir/\"  as untracked, even though it\nclearly contains two tracked files. What is happening in the\ncommit program is this:\n\n  1. We load the index, and for each entry, insert it into the index's\n     name_hash. In addition, if ignorecase is turned on, we make an\n     entry in the name_hash for the directory (e.g., \"contrib/\"), which\n     uses the following code from 5102c61's hash_index_entry_directories:\n\n        hash = hash_name(ce->name, ptr - ce->name);\n        if (!lookup_hash(hash, &istate->name_hash)) {\n                pos = insert_hash(hash, &istate->name_hash);\n\t\tif (pos) {\n\t\t\tce->next = *pos;\n\t\t\t*pos = ce;\n\t\t}\n        }\n\n     Note that we only add the directory entry if there is not already an\n     entry.\n\n  2. We run add_files_to_cache, which gets updated information for each\n     cache entry. It helpfully inserts this information into the cache,\n     which calls replace_index_entry. This in turn calls\n     remove_name_hash() on the old entry, and add_name_hash() on the new\n     one. But remove_name_hash doesn't actually remove from the hash, it\n     only marks it as \"no longer interesting\" (from cache.h):\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_name_hash(struct cache_entry *ce)\n      {\n              ce->ce_flags |= CE_UNHASHED;\n      }\n\n     This is OK in the specific-file case, since the entries in the hash\n     form a linked list, and we can just skip the \"not here anymore\"\n     entries during lookup.\n\n     But for the directory hash entry, we will _not_ write a new entry,\n     because there is already one there: the old one that is actually no\n     longer interesting!\n\n  3. While traversing the directories, we end up in the\n     directory_exists_in_index_icase function to see if a directory is\n     interesting. This in turn checks index_name_exists, which will\n     look up the directory in the index's name_hash. We see the old,\n     deleted record, and assume there is nothing interesting. The\n     directory gets marked as untracked, even though there are index\n     entries in it.\n\nThe problem is in the code I showed above:\n\n        hash = hash_name(ce->name, ptr - ce->name);\n        if (!lookup_hash(hash, &istate->name_hash)) {\n                pos = insert_hash(hash, &istate->name_hash);\n\t\tif (pos) {\n\t\t\tce->next = *pos;\n\t\t\t*pos = ce;\n\t\t}\n        }\n\nHaving a single cache entry that represents the directory is\nnot enough; that entry may go away if the index is changed.\nIt may be tempting to say that the problem is in our removal\nmethod; if we removed the entry entirely instead of simply\nmarking it as \"not here anymore\", then we would know we need\nto insert a new entry. But that only covers this particular\ncase of remove-replace. In the more general case, consider\nsomething like this:\n\n  1. We add \"foo/bar\" and \"foo/baz\" to the index. Each gets\n     their own entry in name_hash, plus we make a \"foo/\"\n     entry that points to \"foo/bar\".\n\n  2. We remove the \"foo/bar\" entry from the index, and from\n     the name_hash.\n\n  3. We ask if \"foo/\" exists, and see no entry, even though\n     \"foo/baz\" exists.\n\nSo we need that directory entry to have the list of _all_\ncache entries that indicate that the directory is tracked.\nSo that implies making a linked list as we do for other\nentries, like:\n\n  hash = hash_name(ce->name, ptr - ce->name);\n  pos = insert_hash(hash, &istate->name_hash);\n  if (pos) {\n\t  ce->next = *pos;\n\t  *pos = ce;\n  }\n\nBut that's not right either. In fact, it shows a second bug\nin the current code, which is that the \"ce->next\" pointer is\nsupposed to be linking entries for a specific filename\nentry, but here we are overwriting it for the directory\nentry. So the same cache entry ends up in two linked lists,\nbut they share the same \"next\" pointer.\n\nAs it turns out, this second bug can't be triggered in the\ncurrent code. The \"if (pos)\" conditional is totally dead\ncode; pos will only be non-NULL if there was an existing\nhash entry, and we already checked that there wasn't one\nthrough our call to lookup_hash.\n\nBut fixing the first bug means taking out that call to\nlookup_hash, which is going to activate the buggy dead code,\nand we'll end up splicing the two linked lists together.\n\nSo we need to have a separate next pointer for the list in\nthe directory bucket, and we need to traverse that list in\nindex_name_exists when we are looking up a directory.\n\nThis bloats \"struct cache_entry\" by a few bytes. Which is\nannoying, because it's only necessary when core.ignorecase\nis enabled. There's not an easy way around it, short of\nseparating out the \"next\" pointers from cache_entry entirely\n(i.e., having a separate \"cache_entry_list\" struct that gets\nstored in the name_hash). In practice, it probably doesn't\nmatter; we have thousands of cache entries, compared to the\nmillions of objects (where adding 4 bytes to the struct\nactually does impact performance).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h     |    1 +\n name-hash.c |   15 ++++++++-------\n 2 files changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 607c2ea..1334fbf 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -168,6 +168,7 @@ struct cache_entry {\n \tunsigned int ce_flags;\n \tunsigned char sha1[20];\n \tstruct cache_entry *next;\n+\tstruct cache_entry *dir_next;\n \tchar name[FLEX_ARRAY]; /* more */\n };\n \ndiff --git a/name-hash.c b/name-hash.c\nindex c6b6a3f..225dd76 100644\n--- a/name-hash.c\n+++ b/name-hash.c\n@@ -57,12 +57,10 @@ static void hash_index_entry_directories(struct index_state *istate, struct cach\n \t\tif (*ptr == '/') {\n \t\t\t++ptr;\n \t\t\thash = hash_name(ce->name, ptr - ce->name);\n-\t\t\tif (!lookup_hash(hash, &istate->name_hash)) {\n-\t\t\t\tpos = insert_hash(hash, ce, &istate->name_hash);\n-\t\t\t\tif (pos) {\n-\t\t\t\t\tce->next = *pos;\n-\t\t\t\t\t*pos = ce;\n-\t\t\t\t}\n+\t\t\tpos = insert_hash(hash, ce, &istate->name_hash);\n+\t\t\tif (pos) {\n+\t\t\t\tce->dir_next = *pos;\n+\t\t\t\t*pos = ce;\n \t\t\t}\n \t\t}\n \t}\n@@ -166,7 +164,10 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na\n \t\t\tif (same_name(ce, name, namelen, icase))\n \t\t\t\treturn ce;\n \t\t}\n-\t\tce = ce->next;\n+\t\tif (icase && name[namelen - 1] == '/')\n+\t\t\tce = ce->dir_next;\n+\t\telse\n+\t\t\tce = ce->next;\n \t}\n \n \t/*\n-- \n1.7.7.37.g0e376\n"}]}