Peter Alfvin
Peter Alfvin

Reputation: 29439

How do I deal with git error resulting from pulling commit on MacOs involving files whose filename has case differences

I get the following error from git when trying to pull (or checkout or cherry-pick) a commit involving files whose filename has changed only in terms of upper vs. lower case:

The following untracked working tree files would be overwritten by merge

after which it lists all the files with only case differences. A git status shows nothing in my working directory.

I was able to perform the pull successfully after first deleting the local copy of the files in question, but I was wondering if there was a more elegant way to work around, or better yet, avoid the problem in the first place. Other users running on MacOS with the same repository on their local machines had no such symptoms/problem.

Upvotes: 6

Views: 855

Answers (4)

James Risner
James Risner

Reputation: 6102

If case-sensitivity is required

The git utility assumes your filesystem is case-sensitive because most, if not all, Linux and FreeBSD filesystems are so. But on macOS, by default the file system is case-insensitive. This mismatch can result in the problems you are seeing.

If case-sensitivity is not required by the project/code, you can use this answer.

If case-sensitivity is required, and you are operating on macOS with a case-insensitive file system, you can make a new virtual case-sensitive volume sharing the same APFS partition as your other volumes.

You would use the new case-sensitive volume exclusively for your git repo work. If you compile an application on this volume, you can test it with configuration and data files on your other case-insensitive volumes if required.

Open Disk Utility and click the + button for a new Volume:

disk utility

Name the new Volume:

new volume

Store your files in this directory:

% ls -ld /Volumes/GitRepo
drwxr-xr-x  5 risner  staff  160 Dec 15 21:25 /Volumes/GitRepo

Upvotes: 3

micromoses
micromoses

Reputation: 7507

TL;DR

Having trouble with case-sensitive file/directory renames in a git repo on a case-insensitive FS/OS; Follow these 3 steps:

  1. Make sure git config --global core.ignorecase returns nothing, or at the very least true. If false - reset to null, so git can autodetect this configuration according to the filesystem a repo is cloned onto.
  2. In your repo, if it resides on a case-insensitive filesystem (or OS), make sure git config core.ignorecase is set to true. If none or false - set to true.
  3. Update git to the latest version.

The problem

Git was built on, and therefore most compatible with, case-sensitive filesystems. As git became more popular its application on case-insensitive filesystems increased, and so did the amount of issues found and bugs opened for case handling incompatibilities. Don't get me wrong - these issues were (and if still open - are being) addressed, but still a rocky road.
I cannot recommend formatting an entire filesystem to be case-sensitive as a resolution, as it may break other applications which expect filenames to be case-agnostic. True story.

The progress

This is just my personal opinion, but I feel like things have drastically shifted for git enthusiasts in the past 15 years, from requiring a case-sensitive formatted FS, to better (and almost seamless) handling of case-insensitivities by git itself.

The "why did I tell this so long story?" part

An ad-hoc method can be employed to resolve a specific file-rename causing problems, but that is a workaround - not a solution. Git should be able to handle insensitive filesystems "out-of-the-box":

core.ignoreCase
Internal variable which enables various workarounds to enable Git to work better on filesystems that are not case sensitive, like APFS, HFS+, FAT, NTFS, etc. For example, if a directory listing finds "makefile" when Git expects "Makefile", Git will assume it is really the same file, and continue to remember it as "Makefile".

The default is false, except git-clone(1) or git-init(1) will probe and set core.ignoreCase true if appropriate when the repository is created.

Git relies on the proper configuration of this variable for your operating and file system. Modifying this value may result in unexpected behavior.

Git should be able to handle file/directory letter-case-renames automatically when cloning or initializing a repository on an insensitive environment. There will be problems, of course, if the repository contains two paths that should coexist, and differ only in letter-case (e.g. mypath/myfile.txt and mypath/MyFile.txt) - this is not possible on an insensitive filesystem. Another reason for trouble is if the repository was first cloned/initialized on a case-sensitive FS, but later physically moved (mv/cp) to an insensitive one; in such case setting the repository's core.ignorecase config to true should make things better.

Still, while git "should" be able to handle such cases, it is important to keep git up-to-date. As issues are discovered, newer versions have them resolved, making for a much smoother experience. I'm posting some examples from the release notes; These are NOT to be regarded as a "shame list" in any way, but rather to show the engagement of the maintainers and the goals they achieved towards seamless support for case-insensitive FS/OS. And to convince the ones who managed to read so far of the importance of software updates:

  • Fixed in Git v2.31.1: Fix a corner case bug in "git mv" on case insensitive systems, which was introduced in 2.29 timeframe.
  • Fixed in Git v2.21.0: On a case-insensitive filesystem, we failed to compare the part of the path that is above the worktree directory in an absolute pathname, which has been corrected.
  • Fixed in Git v2.20.0: Running "git clone" against a project that contain two files with pathnames that differ only in cases on a case insensitive filesystem would result in one of the files lost because the underlying filesystem is incapable of holding both at the same time. An attempt is made to detect such a case and warn.
  • Fixed in Git v2.17.6: CVE-2021-21300:
    On case-insensitive file systems with support for symbolic links, if Git is configured globally to apply delay-capable clean/smudge filters (such as Git LFS), Git could be fooled into running remote code during a clone.
  • Fixed in Git v2.16: The code internal to the recursive merge strategy was not fully prepared to see a path that is renamed to try overwriting another path that is only different in case on case insensitive systems. This does not matter in the current code, but will start to matter once the rename detection logic starts taking hints from nearby paths moving to some directory and moves a new path along with them.
  • Fixed in Git v2.1: On a case insensitive filesystem, merge-recursive incorrectly deleted the file that is to be renamed to a name that is the same except for case differences.
  • Fixed in Git v1.8.3: The code to keep track of what directory names are known to Git on platforms with case insensitive filesystems could get confused upon a hash collision between these pathnames and would loop forever.

Upvotes: 2

LeGEC
LeGEC

Reputation: 52226

"Other uses running on MacOS with the same repository on their local machines had no such symptoms/problem."

On case insensitive file systems: the names of the files on a user's disk keep a trace of the sequence of actions that happened on that user machine. Once a file is created on disk (either because a git swicth or git pull created on disk a new file with the casing listed in git, or because the user named the file himself) it will retain that casing.

To illustrate this : on Windows and MacOS, mv Readme.md README.md is a no-op -- even if the file is actually named rEaDmE.md, it will keep its initial name.

In a similar way: if you switch between branches where a specific file is named either Readme.md or README.md, the name of the file on your disk will actually not change -- it will keep the name it initially had.

So you may end up with a repo on your disk where files have not the same names as on your colleague's disk, just because you happened to git pull the feature-xyz branch before switching to master.


To spot discrepancies between your files on disk and the names in git, you can compare git ls-files with git ls-files | xargs ls :

diff <(git ls-files) <(git ls-files | xargs ls)

(note: I don't have a MacOS filesystem at hand, I hope ls readme.md will output README.md if the file on disk is actually in uppercase, and that git ls-files | xargs ls will show the actual case of your names on your filesystem rather than just repeating the output of git ls-files)

A common workaround to rename files on Windows/MacOS is to use a temporary name :

mv Readme.md tmp-name && mv tmp-name README.md

Same thing goes for git mv : git mv Readme.md README.md will succesfully change the name stored in git but not the name on disk.

You can :

git mv Readme.md tmp-name && git mv tmp-name README.md

If your intention is to have your files on disk match the names in git, you may use the output of the first command to issue renaming commands.

Upvotes: 1

VonC
VonC

Reputation: 1329752

Deleting the local file (as in here) is one solution.

Check first if a git config --global core.ignoreCase true could help on your next git pull.
Check if other users have already set that setting.

Upvotes: 1

Related Questions