3

Based on this answer I used git mv to change the case of the extension of a filename.

Now however, whenever I try to change branch, I get the following error:

git checkout MyBranch
error: The following untracked working tree files would be overwritten by checkout:
    MyFile/With/The/OldExtension.Ext
Please move or remove them before you switch branches.
Aborting

I can still change branches with --force, but don't really want to rely on this for obvious reasons.

It seems to me that git's index is out of sync with reality, but I'm not sure how to fix it.

What's my next move?

Tom Wright
  • 11,278
  • 15
  • 74
  • 148
  • Did you change extension ? or Filename ? – Venkataraman R Oct 30 '18 at 04:27
  • @VenkataramanR I changed the extension from Titlecase to lowecase. Does git treat extension changes as a special case compared to changing the rest of the filename? – Tom Wright Oct 30 '18 at 04:29
  • 1
    I think for some reason, the old extension file is still present in your system. you can clean it using `git clean -d -x -f` – Venkataraman R Oct 30 '18 at 04:47
  • I'm a bit wary of `git clean`, so I ran it with interactive first. The file in question does not appear in the list of files that would be removed. – Tom Wright Oct 30 '18 at 04:56
  • take a copy of the file and just try git clean -d -x -f --dry-run and see. As we are having -x (all files, irrespective of ignored files will be removed) – Venkataraman R Oct 30 '18 at 04:59
  • Same result. The file it is complaining about does not appear in the list of files that `clean` returns (whether I run it with `-i` or `--dry-run`) – Tom Wright Oct 30 '18 at 05:00
  • Aside: why are you using `-c diff.mnemonicprefix=false -c core.quotepath=false --no-optional-locks`? `diff.mnemonicprefix` affects only `git diff` and `--no-optional-locks` affects only `git status`. `core.quotepath` does affect the error output slightly, though. – torek Oct 30 '18 at 06:22
  • @torek That's just the command my client (Sourcetree) uses by default. I get the same error when I run `git checkout` myself in bash, so will update the question for clarity. – Tom Wright Oct 30 '18 at 06:24

2 Answers2

2

TL;DR: you can probably simply remove the file before checking out the desired commit.

Assuming you're on a system that does case-folding with file names, the issue here is that if Git attempts to create and write on a file named readme.txt when a file named README.TXT exists, this will overwrite the existing README.TXT without creating a readme.txt at all.

As VonC says, if the index is out of step with reality, you can override it. Similarly, if there is nothing of value in the index, you can remove and rebuild it:

rm .git/index
git reset --mixed HEAD

This makes the index match the current commit (not the current work-tree!).

The main issue here is that Git's internals, and Git's index (because it's just a data file in .git/index), all work with raw byte-strings that can hold any file name, including for instance a simultaneous readme.txt and README.TXT. Any Linux system can store both files in the work-tree,1 but a typical MacOS or Windows file system can't. In such a situation—where the index holds both files under those two names that can exist simultaneously on Linux, but not on your own system—the index is guaranteed to be out of step with reality no matter what.

Otherwise—if there's only one case-variant in the index—it may just be that your work-tree file name case differs from that stored in the index, which initially matches that stored in the commit you check out. If/when the underlying OS subverts the file name that Git attempts to create when switching commits, Git is sure that it's created (say) readme.txt and stores that name in the index even though the OS overwrote the existing README.TXT and left that name in the work-tree.

The core.ignorecase setting, which Git sets itself when you first git init the repository (or when git clone initializes it), records how the OS treats file names, so that Git will know to check whether a README.TXT exists before Git attempts to create a readme.txt. In this, er, case, you'll get the error message you're seeing, because Git isn't sure if the README.TXT that's in the work-tree is really the readme.txt that it extracted earlier (maybe you removed that one and put a different README.TXT in that you want to keep).


1This is actually file-system-type-dependent behavior, but the default Linux file systems are case-sensitive. On HFS/HFS+ on MacOS you can choose, at file system build time, whether the file system is to be case-sensitive. I believe the same is true of NTFS, not that I have ever built an NTFS file system.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Tried all the different combinations of `rm MyFile`, `rm .git/index`, `git rm --cached`, and `git reset --mixed HEAD`, but in the end the only thing that would fix it is setting `core.ignorecase` to `true`. Feels a bit unsatisfying (why can't I expunge that filename!?!), but pragmatically I can do what I need to do. Thanks! – Tom Wright Oct 30 '18 at 22:04
  • You're never supposed to (have to) change `core.ignorecase` yourself, so if that's the only way to do it, Git is at least a little broken. (But none of this applies if you move a Git repository across file systems, in which case the `core.ignorecase` setting gets copied when it should be re-initialized based on the new file system.) – torek Oct 30 '18 at 23:19
1

It seems to me that git's index is out of sync with reality

Then, as shown here, try the same git mv in a new clone of the repo, to see if the index there is "less" polluted by that invisible untracked file.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250