The Git Lowercase Nightmare: Why macOS Doesn't See Your Rename
It's 4 PM. You're cleaning up code before a deploy. You rename UserController.js to userController.js for consistency. Git shows no changes. You push anyway. Production immediately crashes with "Cannot find module './userController.js'".
Your Linux server is case-sensitive. Your Mac isn't. And Git is stuck in the middle, confused.
This happens to every Mac developer eventually. Here's exactly what's going on and how to fix it.
Why This Happens: The Case Insensitivity "Feature"
macOS uses APFS (or HFS+ on older systems) with case-insensitive mode by default. To your Mac:
UserController.js===userController.js===USERCONTROLLER.JS- They're all the same file
- Only one can exist at a time
To Linux servers and Windows (usually):
- These are completely different files
- All three could exist simultaneously
require('./userController.js')won't findUserController.js
Git tries to track both worldviews and gets confused.
The Symptoms You've Definitely Seen
Symptom 1: The Invisible Rename
1mv UserController.js userController.js
2
3git status
4
5# nothing to commit, working tree clean
6Symptom 2: The Push That Breaks Production
- Works on your Mac ✓
- Works on your teammate's Mac ✓
- Explodes on Linux server ✗
- Windows teammate can't pull ✗
Symptom 3: The Mysterious Duplicate
1git pull
2
3# UserController.js
4# userController.js # Wait, what?
5
6ls
7
8# userController.js # Mac only shows one!
9How to Fix It (Three Ways)
Method 1: The Git Move (Simplest)
1git mv UserController.js userController.js
2If Git complains the file doesn't exist:
1git mv -f UserController.js userController.js
2This forces Git to track the rename properly.
Method 2: The Two-Step Dance
When git mv fails (it sometimes does):
1# Step 1: Rename to completely different name
2mv UserController.js TempName.js
3git add -A
4git commit -m "Temp rename"
5
6# Step 2: Rename to final name
7mv TempName.js userController.js
8git add -A
9git commit -m "Fixed case sensitivity"
10
11# Optional: Squash commits if you're picky
12git rebase -i HEAD~2
13Method 3: The Nuclear Option
When everything is broken and you need it fixed NOW:
1# Delete from Git but keep local file
2git rm --cached UserController.js
3git commit -m "Remove old casing"
4
5# Add with new name
6git add userController.js
7git commit -m "Add with correct casing"
8How to Prevent It Forever
Option 1: Configure Git (Per Repository)
1git config core.ignorecase false
2Now Git will treat UserController.js and userController.js as different files.
Warning: This can cause its own problems on Mac. You might end up with duplicate files that your OS can't distinguish.
Option 2: Team Rules
Add to your .gitattributes:
* text=auto eol=lf
And enforce naming conventions:
1// .eslintrc or similar
2{
3 "rules": {
4 "filenames/match-regex": ["error", "^[a-z][a-zA-Z0-9]+$", true]
5 }
6}
7Option 3: Pre-commit Hooks
Create .git/hooks/pre-commit:
1#!/bin/sh
2
3# Check for case-only renames
4git diff --cached --name-status | grep -E "^R.*\s+.*[a-z].*\s+.*[A-Z].*" && {
5 echo "❌ Case-only rename detected. Use 'git mv -f' instead."
6 exit 1
7}
8The Production Emergency Fix
It's broken in production RIGHT NOW? Here's the fastest fix:
1# On the server (Linux)
2cd /path/to/app
3
4# Find the actual filename
5ls -la | grep -i controller
6
7# Create a symlink as emergency fix
8ln -s UserController.js userController.js
9
10# Now fix it properly in Git using methods above
11The Real-World Checklist
Before renaming files with only case changes:
- Use
git mv -fnotmv - Commit immediately after renaming
- Test on Linux/Docker before pushing
- Warn your team in the PR description
- Check your imports match exactly
- Update any documentation mentioning filenames
Common Gotchas
1. The node_modules Trap
Some packages have case-sensitive imports internally. Renaming your file might break deeply nested dependencies.
2. The Import Cache
Node.js caches modules. After renaming, you might need:
1rm -rf node_modules/.cache
2
3# or
4
5npm start -- --reset-cache
63. The CI/CD Pipeline
Your CI might run on Linux. Always test case-sensitive changes in CI before merging.
The One-Liner Solution
Add this alias to your .zshrc or .bash_profile:
1alias gmv='git mv -f'
2Now just use gmv OldName.js newName.js every time.
This isn't a bug. It's three different philosophies about filesystems colliding. Your Mac thinks it's being helpful. Git tries to support everyone. Linux is technically correct.
Use git mv -f, commit immediately, move on.
Key takeaway: macOS and Linux disagree about filenames. Git gets stuck in the middle. Force Git to see your rename with git mv -f, or use the two-step dance through a temp name.
Next step: Add alias gmv='git mv -f' to your shell config right now. Save yourself the next production fire.


