Git: How to change the case of filenames
Different filesystems have different case sensitivity, for example:
- The Linux filesystem ext4 is case sensitive, by default. It allows both
- macOS’ default APFS is case insensitive, by default. It interprets the filenames
eXaMpLe.TxTas all referring to the same file.
When working on a case-insensitive filesystem, Git detects the case-insensitivity and stores this in the
core.ignoreCase option for the repo. With this option set to
true, Git doesn’t consider a file as renamed if it has changed case. This can make it a challenge to add and commit such a change when it’s actually necessary.
Let’s look at how tp re-case an individual file, a loop to re-case multiple files, and how to
git add case changes made with any tool.
To change one filename’s case, use
$ git mv Mathematics.py mathematics.py
This command both changes the file in the filesystem and stages the change in Git:
$ git status On branch main Changes to be committed: renamed: Mathematics.py -> mathematics.py
Great, job done.
You might have tried to use a standard
mv to re-case the file, but due to the
core.ignoreCase option, Git would not detect this change. You can follow up
git mv to stage the change in Git:
$ mv Mathematics.py mathematics.py overwrite mathematics.py? (y/n [n]) y $ git mv Mathematics.py mathematics.py $ git status On branch main Changes to be committed: renamed: Mathematics.py -> mathematics.py
To re-case multiple files, you can use a shell loop running
git mv on each file. You can use
git ls-files to list files matching a pattern, and
tr to translate a string’s case. For example, to lower-case all
.py files on bash/zsh:
$ for f in $(git ls-files -- '*.py'); do git mv -k $f $(tr '[:upper:]' '[:lower:]' <<< "$f"); done $ git status On branch main Changes to be committed: renamed: Mathematics.py -> mathematics.py renamed: MONETARY.py -> monetary.py
Using such a shell loop might not fit your use case, so you might use another tool to batch re-case files. If you do, you can add the changes by telling Git to stop tracking the files with
git rm --cached, then adding them back:
$ git rm -r --cached . $ git add . $ git status On branch main Changes to be committed: renamed: Mathematics.py -> mathematics.py renamed: MONETARY.py -> monetary.py
--cached option to
git rm tells Git to stage the files for removal the files, but leave them on disk. After re-adding them, Git detects the files as renames, because they have the same content.
Beware though that
git add . will also pick up any new files you may not mean to commit.
Whilst researching for this post I saw several sources that recommended disabling
core.ignoreCase for a repo, or even globally. This will make Git to detect case changes on case-insensitive filesystems, but it’s unsafe, as the docs warn:
Git relies on the proper configuration of this variable for your operating and file system. Modifying this value may result in unexpected behavior.
So, yeah, best to leave the option alone.
If your Django project’s long test runs bore you, I wrote a book that can help.
One summary email a week, no spam, I pinky promise.
- Git: How to set up a global ignore file
- Git: How to alias “master” as “main”
- How to Run a Command on Many Files in Your Git Repository