Git-windows case sensitive file names not handled properly
Asked Answered
G

7

41

We have the git bare repository in unix that has files with same name that differs only in cases.

Example:

GRANT.sql
grant.sql

When we clone the bare repository from unix in to a windows box, git status detects the file as modified. The working tree is loaded only with grant.sql, but git status compares grant.sql and GRANT.sql and shows the file as modified in the working tree.

I tried using the core.ignorecase false but the result is the same.

Is there any way to fix this issue?

Gastrulation answered 27/3, 2010 at 8:1 Comment(2)
Awesome question and Greg's answer just rocks!Passionate
This can also happen, when you create the file with different casing in different branches on Windows.Clearheaded
C
43

Windows is case-insensitive (more precisely, case-preserving). There is simply no possible way for two files to exist whose names only differ in case: two filenames which differ only in case are the same filename. Period.

So, Git is walking the repository, checking out one file after the other, until it hits the first one of the two problem files. Git checks it out, then goes further about its business until it hits the second file. Again, Git checks it out. Since from Windows' point of view the filename is the same as the first one, the first file simply gets overwritten with the second one. Which now makes Git think that the first file was changed to have the same content as the second one.

Note that this has nothing to do with Git: exactly the same would happen if you had a tarball, a zipfile or a Subversion repository.

If you want to do development on multiple different platforms, you have to respect the restrictions of those platforms and you have to confine yourself to the lowest common denominator of all the platforms you support. Windows supports ADS, Linux doesn't. OSX supports resource forks, Windows doesn't. BSD supports case-sensitivity, Windows doesn't. So, you can't use any of those. That's just the way it is.

core.ignorecase isn't going to help you here, because that handles exactly the opposite problem.

Chantay answered 27/3, 2010 at 15:45 Comment(6)
Fixing this doesn't mean making it just work. Fixing this means that you convert crazy behavior into a clear error.Colombi
There most definitely is a way for two files to exist whose names only differ in case, but it needs a registry change. Cygwin does it, but I don't know if msys can (there's a special flag that needs to be passed into the Win32 API): cygwin.com/cygwin-ug-net/using-specialnames.htmlSachiko
Note that NTFS is actually case-Sensitive.Biomedicine
@DiegoBarros: Yes, I was being brief in my answer. On Windows, it is actually a combination of the filesystem, the API and the user interface. For example, there are restrictions in the Explorer that different from restrictions in the command line, and there legacy APIs and more modern ones and so on. From what I understand, NTFS actually has multiple namespaces, a DOS one (8+3 ASCII 7-bit characters, case-insensitive), a "long" one similar to the VFAT LFN (255 UTF-16 characters, case-insenstive but case-preserving), a special one for long filenames that are short enough to fit in the …Cymose
… DOS namespace (8+3 7-bit ASCII characters encoded as UTF-16, case-insensitive but case-preserving), and a POSIX one (255 opaque bytes, remember that the first versions of NT shipped with a fully functional POSIX personality, that later became Services for Unix). Then there are legacy APIs and Unicode APIs, there's the explorer, there's special reserved filenames for the OS, and so on and so forth.Cymose
"There is simply no possible way for two files to exist whose names only differ in case" This is not true anymore, you can create case-different files using WSL and a registry hack: devblogs.microsoft.com/commandline/…Tennilletennis
H
35

I just encountered a similar problem. In my case, the two files with similar names differing only in case were in a subdirectory that wasn't relevant on the Windows clone. Git 1.7 has a sparse checkout feature that lets you exclude certain files from a working copy. To exclude this directory:

git config core.sparsecheckout true
echo '*' >.git/info/sparse-checkout
echo '!unwanted_dir/' >>.git/info/sparse-checkout
git read-tree --reset -u HEAD

After this, the unwanted_dir/ subdirectory was completely gone from my working copy and Git continues to work with the rest of the files as normal.

If your GRANT.sql and grant.sql are not relevant on the Windows clone, then you can add their names to .git/info/sparse-checkout to exclude those files specifically.

Heretical answered 23/7, 2010 at 2:56 Comment(2)
This answer was just awesome and works for filename too long issues as well. Thanks for relieving the headaches I've had!!! Gave a +1 a couple months ago.Passionate
Great solution! I needed the setting to be core.sparseCheckout to get it to workVesicate
S
4

I'm not sure this is even possible. Git's ignorecase handles discrepancies in the case of the one file. It won't work around Window's inability to have two filenames in the one directory that differ only by case.

FWIW, having two identical filenames but for their case is a really bad idea, even on Unix.

Samuele answered 27/3, 2010 at 8:9 Comment(1)
If only the Linux kernel developers all agreed with you…Oakum
M
3

If you want to keep your repository friendly to non-case sensitive file systems, you can add a commit hook that prevents you to check in clashing files.

#!/bin/bash

# Save current state
git stash -u -q --keep-index || exit 1

# Get the list of clashing files in the whole repository
CLASHING=`find "$(git rev-parse --show-toplevel)" | sort | uniq -d -i`

# Restore previous state
git stash pop -q

if [[ $CLASHING ]]; then
  echo "Found clashing files on case-insensitive file systems"
  echo "$CLASHING"
  exit 1
fi

exit 0

This script requires git version >= 1.7.7, because it uses stash -u, to avoid failing on untracked files.

Marshland answered 2/1, 2013 at 1:46 Comment(3)
It might fail on untracked files in .gitignore.Marshland
Does anyone know how to get the current tree including staged changes? Beware that simply appending git diff --staged to HEAD is not good enough; for instance it wouldn't work when you change the case on a filename.Marshland
Great solution. Saying "Don't do that" is very unhelpful. I don't imagine that a reasonable shop would allow two files or folders that differ only in case even if they used Linux.Foothill
P
1

Cygwin handles case sensitivity and funny characters in filenames much better than MSys.

Change this registry key to enable case sensitivity in Windows:

HKLM\System\CurrentControlSet\Control\Session Manager\Kernel\ObCaseInsensitive=0

See here for some caveats in how case sensitivity is supported in Cygwin.

Piecrust answered 16/10, 2011 at 5:5 Comment(0)
B
1

The simplest way to actually fix the issue is to rename one of the files so that they won't conflict on a case-insensitive file system like Windows or OS X.

Following a commit from the Linux/Unix system where you can most easily address the problem everything will be fine on Windows after a pull. To prevent this problem from occurring you would need to add a commit hook similar to what djjeck suggested.

The symptoms on Windows for this are very confusing and include:

  • Files that always show as changed even if you revert them which makes changing branches or rebasing very difficult.
  • Two copies of the file with the names differing only in case both showing changes in git gui

Since both files can not coexist on a case insensitive platform you have to change one of the file names to avoid the trouble.

Bots answered 4/8, 2015 at 13:44 Comment(0)
B
0

Another simple approach which can avoid git read-tree --reset -u HEAD

1. Clone without checking out
  `git clone --no-checkout`

2. Configure sparse checkout

3. Check out required branch
`git checkout -b branch_name remotes/origin/branch_name`
Bagnio answered 9/2, 2022 at 5:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.