Development

The Git Lowercase Nightmare: Why macOS Doesn't See It

You rename `UserController.js` to `userController.js`. Git doesn't see the change. You push. Production breaks. Your teammate on Linux can't pull. This isn't a bug, it's a 'feature' of how macOS handles files, and it's probably happened to you more than once. Here's why it happens, how to fix it when it does, and how to prevent it forever.

Git case sensitivity issue on macOS
Double2 Team
(updated November 14, 2025)
8 min read
Share:
X (Twitter)LinkedInFacebook
Development

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 find UserController.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 6

Symptom 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! 9

How to Fix It (Three Ways)

Method 1: The Git Move (Simplest)

1git mv UserController.js userController.js 2

If Git complains the file doesn't exist:

1git mv -f UserController.js userController.js 2

This 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 13

Method 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" 8

How to Prevent It Forever

Option 1: Configure Git (Per Repository)

1git config core.ignorecase false 2

Now 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} 7

Option 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} 8

The 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 11

The Real-World Checklist

Before renaming files with only case changes:

  • Use git mv -f not mv
  • 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 6

3. 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' 2

Now 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.

Tags

GitmacOSDevelopmentCase SensitivityDeveloper Tools