Seit dem legendären Ruby/JS WAT?-Talk von Gary Bernhardt sehe ich überall WAT-Momente. Der grösste, den ich bisher erlebt habe: Git hat eine Dateiumbenennung komplett ignoriert, und mein Deploy ist deswegen gescheitert.
Kein grosses Team, keine Merge-Konflikte, keine exotischen Workflows. Nur ich, ein Blog-Projekt und git add.
Wie eine simple Umbenennung zum Deploy-Fehler wurde
Ich habe meinen Blog von Nuxt (Vue.js) auf Next (React.js) migriert. Während der Entwicklung fiel mir auf, dass eine Komponentendatei Logo.js heissen sollte statt logo.js. Triviale Änderung, dachte ich.
mv Logo.js logo.js
git add .
git commit -m "fix: rename Logo.js to logo.js"
git push
Lokal lief alles. Das CI-System (damals Vercel/Zeit Now) meldete aber:
Module not found: Can't resolve './logo'
Der Import war korrekt. Die Datei existierte lokal mit dem richtigen Namen. Trotzdem schlug der Build fehl.
Was Git unter der Haube macht
Erst als ein Kollege das Repository frisch geklont hat, wurde das Problem sichtbar: Die Datei hiess bei ihm immer noch Logo.js. Mein Commit hatte die Umbenennung nie aufgezeichnet.
Der Grund liegt in der Git-Konfiguration core.ignorecase. Auf macOS (HFS+/APFS) und Windows (NTFS) ist das Dateisystem standardmässig case-insensitive. Git setzt deshalb bei git init oder git clone automatisch:
core.ignorecase = true
Das bedeutet: Logo.js und logo.js sind für Git dieselbe Datei. Ein mv Logo.js logo.js ändert den Namen auf dem Dateisystem, aber Git registriert keinen Unterschied im Index.
USEO’s Take
Wir sind bei USEO auf dieses Problem mehrfach in Rails-Projekten gestossen, besonders bei Autoloading. Rails erwartet, dass UserSerializer in user_serializer.rb liegt. Wenn jemand auf macOS die Datei als User_Serializer.rb anlegt und committet, funktioniert alles lokal. Auf dem Linux-CI-Server (case-sensitive ext4) schlägt der Autoloader dann fehl, und die Fehlermeldung zeigt auf eine scheinbar vorhandene Datei. Die Debugging-Zeit kann erheblich sein, weil man dem Dateisystem vertraut.
Schritt für Schritt reproduzieren
Testen Sie es selbst in einem frischen Repository (getestet mit Git 2.43 auf macOS Sonoma):
mkdir /tmp/git-case-test && cd /tmp/git-case-test
git init
# Datei anlegen und committen
echo "hello" > something.txt
git add something.txt
git commit -m "add something.txt"
# Umbenennen: nur Grossschreibung ändern
mv something.txt Something.txt
# Was sagt Git?
git status
Ergebnis: git status zeigt keine Änderungen. Die Datei heisst auf dem Dateisystem jetzt Something.txt, aber Git weiss davon nichts.
Drei Wege, das Problem zu lösen
1. git mv statt mv (empfohlen)
git mv something.txt Something.txt
git status
# renamed: something.txt -> Something.txt
git mv aktualisiert sowohl das Dateisystem als auch den Git-Index in einem Schritt. Das ist die sauberste Lösung.
2. Zweistufige Umbenennung
Falls git mv auf Ihrem System Probleme macht (selten, aber möglich bei älteren Git-Versionen):
git mv something.txt something-tmp.txt
git mv something-tmp.txt Something.txt
git commit -m "fix: correct filename capitalization"
3. core.ignorecase auf false setzen
git config core.ignorecase false
Vorsicht: Diese Einstellung kann auf case-insensitiven Dateisystemen zu Problemen führen. Git sieht dann something.txt und Something.txt als zwei verschiedene Dateien, obwohl das Dateisystem nur eine speichert. Das kann zu doppelten Einträgen im Index führen, die schwer aufzuräumen sind. Diese Option ist nur auf Linux (ext4, Btrfs) bedenkenlos einsetzbar.
USEO’s Take
In unseren Projekten haben wir eine einfache Regel: Dateiumbennungen immer mit git mv durchführen. Das gilt nicht nur für Case-Änderungen, sondern generell. git mv stellt sicher, dass Git die Umbenennung als solche erkennt und in der History korrekt abbildet. In Code-Reviews prüfen wir ausserdem, ob CI auf Linux läuft (was bei den meisten Cloud-Anbietern der Fall ist), damit solche Probleme sofort auffallen.
Warum das kein “Bug” ist, sondern ein Design-Entscheid
Streng genommen ist das Verhalten kein Bug. Git respektiert die Fähigkeiten des darunterliegenden Dateisystems. Auf einem case-insensitiven System wäre ein erzwungener Case-sensitiver Modus problematisch: zwei Dateien readme.md und Readme.md könnten nicht gleichzeitig existieren.
Trotzdem fühlt sich das Verhalten wie ein Bug an, wenn man nicht weiss, dass core.ignorecase existiert. Linus Torvalds hat Git ursprünglich für Linux entwickelt, wo Dateisysteme case-sensitive sind. Die macOS- und Windows-Unterstützung kam später, und core.ignorecase war der Kompromiss.
Checkliste für Teams
- CI immer auf Linux laufen lassen. Case-Probleme fallen dort sofort auf.
git mvfür jede Umbenennung verwenden. Auch wenn es nur ein Buchstabe ist.- Pre-commit Hooks einrichten, die auf doppelte Dateien mit unterschiedlicher Gross-/Kleinschreibung prüfen.
- Neue Teammitglieder auf macOS darauf hinweisen. Es ist einer der häufigsten “es funktioniert bei mir”-Fälle.
Fazit
Jede Software hat Ecken und Kanten, die aus historischen Entscheidungen stammen. Gits core.ignorecase ist so ein Fall. Die wichtigste Lektion: Vertrauen Sie nie blindlings darauf, dass git status die Wahrheit zeigt. Auf case-insensitiven Systemen kann der angezeigte Zustand vom tatsächlichen Repository-Zustand abweichen.
Und wenn es um Code-Reviews geht: Machen Sie immer, wirklich immer, eine eigene Überprüfung Ihrer Änderungen, bevor Sie pushen.