devAlice
← Multi-OS

Git-Zeilenenden — die CRLF-Hölle zwischen Mac/Linux und Windows beenden

Eine einzige .gitattributes-Datei garantiert überall dieselben Zeilenenden. Die core.autocrlf-Falle und der richtige Weg.

Wird dasselbe Repository zwischen Mac/Linux und Windows geteilt, tritt es fast zwangsläufig auf — CRLF vs. LF. PR-Diffs explodieren in „1000 geänderte Zeilen, obwohl nichts geändert wurde", Shell-Skripte brechen mit \r: command not found, Python wirft SyntaxError: invalid character.

Ich denke, was das CRLF-Problem so hartnäckig macht, ist nicht die technische Komplexität, sondern dass core.autocrlf eine per-Entwickler-Einstellung ist — weil jedes neue Teammitglied ohne Anleitung dieselbe Einstellung anders konfiguriert, statt dass das Repository selbst das Verhalten vorschreibt. Früher habe ich auf core.autocrlf=input vertraut; heute ist .gitattributes mein Standard, da es für alle Mitwirkenden gleichermaßen gilt, unabhängig von ihrer lokalen git-Konfiguration.

Diese Anleitung zeigt den richtigen Weg, um auf jedem OS dieselben Zeilenenden zu garantieren — per .gitattributes. Einmal einrichten; nie wieder daran denken.

TL;DR

  1. .gitattributes mit * text=auto eol=lf im Repo-Stamm ablegen
  2. core.autocrlf deaktivieren.gitattributes überschreibt es ohnehin
  3. LF für Windows-Shell-Skripte erzwingen (*.sh text eol=lf)
  4. CRLF für Windows-Batch-Dateien erzwingen (*.bat text eol=crlf)
  5. Nach der Vereinheitlichung git add --renormalize . ausführen, um vorhandene Dateien anzugleichen

Voraussetzungen

  • Git 2.10+ (unterstützt eol= in .gitattributes)
  • Repo wird gleichzeitig von Mac/Win-Nutzern verwendet

1. Problem — Warum CRLF nicht verschwindet

1.1 Standard-Zeilenenden je OS

OSZeilenendeGeschichte
Unix · macOS · LinuxLF (\n, 0x0A)Unix 1970+
WindowsCRLF (\r\n, 0x0D 0x0A)DOS 1980+
Classic Mac OS 9CR (\r)1984–2001

1.2 Gits automatische Konvertierung — core.autocrlf

WertBeim AuscheckenBeim Commit
true (Windows-Standard)LF → CRLFCRLF → LF
input (Unix empfohlen)keineCRLF → LF
falsekeinekeine

Problem: Jeder Nutzer hat unterschiedliche Einstellungen, neue Rechner variieren, und stille Konvertierungen führen zu „Ich habe es nicht geändert, aber es hat sich geändert"-Vorfällen.

1.3 Der richtige Weg: .gitattributes

Im Repo platziert, erzwingt es für alle dieselben Regeln — und überschreibt core.autocrlf.


2. .gitattributes-Grundlage — 5 min

.gitattributes im Repo-Stamm:

# Standard — alle Textdateien als LF speichern; Text automatisch erkennen
* text=auto eol=lf
 
# Explizit Text — LF erzwingen
*.c          text eol=lf
*.cc         text eol=lf
*.cpp        text eol=lf
*.h          text eol=lf
*.hpp        text eol=lf
*.cs         text eol=lf
*.go         text eol=lf
*.java       text eol=lf
*.kt         text eol=lf
*.py         text eol=lf
*.rb         text eol=lf
*.rs         text eol=lf
*.swift      text eol=lf
*.ts         text eol=lf
*.tsx        text eol=lf
*.js         text eol=lf
*.jsx        text eol=lf
*.mjs        text eol=lf
*.cjs        text eol=lf
*.json       text eol=lf
*.jsonc      text eol=lf
*.yml        text eol=lf
*.yaml       text eol=lf
*.toml       text eol=lf
*.md         text eol=lf
*.mdx        text eol=lf
*.txt        text eol=lf
*.html       text eol=lf
*.css        text eol=lf
*.scss       text eol=lf
*.svg        text eol=lf
*.xml        text eol=lf
*.sql        text eol=lf
 
# Shell-Skripte — LF (CRLF bricht sie)
*.sh         text eol=lf
*.bash       text eol=lf
*.zsh        text eol=lf
*.fish       text eol=lf
Dockerfile*  text eol=lf
Makefile*    text eol=lf
 
# Nur Windows — CRLF
*.bat        text eol=crlf
*.cmd        text eol=crlf
*.ps1        text eol=crlf
*.psm1       text eol=crlf
 
# Binär — keine Konvertierung
*.png        binary
*.jpg        binary
*.jpeg       binary
*.gif        binary
*.ico        binary
*.webp       binary
*.avif       binary
*.pdf        binary
*.zip        binary
*.gz         binary
*.tar        binary
*.7z         binary
*.exe        binary
*.dll        binary
*.so         binary
*.dylib      binary
*.woff       binary
*.woff2      binary
*.ttf        binary
*.otf        binary
*.mp3        binary
*.mp4        binary
*.mov        binary
 
# Stille Diffs — Text, aber Diff sinnlos
*.lock       text eol=lf -diff
pnpm-lock.yaml text eol=lf -diff
package-lock.json text eol=lf -diff
yarn.lock    text eol=lf -diff
Cargo.lock   text eol=lf -diff

⚠️ PowerShell-Dateien (*.ps1) benötigen CRLF. Windows PowerShell lehnt LF-only ps1 gelegentlich ab.


3. Auf vorhandenes Repo anwenden — 5 min

.gitattributes hinzufügen und committen; anschließend bereits falsche Zeilenenden angleichen.

3.1 renormalize

# Im Repo-Stamm
git add .gitattributes
git commit -m "feat: add .gitattributes baseline"
 
# Alle Dateien gemäß .gitattributes angleichen
git add --renormalize .
git status   # was sich geändert hat anzeigen
git commit -m "chore: renormalize line endings"

--renormalize lässt den Arbeitsbaum unberührt; nur der Index wird neu geschrieben. Andere Entwickler gleichen beim nächsten Pull automatisch an.

3.2 Große Repos / Aktive Zusammenarbeit

--renormalize erzeugt einen riesigen PR. Koordinieren:

  • Zeitfenster festlegen („Pushes nach X halten")
  • Den Renormalize-Commit mergen und alle einmalig git pull ausführen lassen
  • Oder git rebase origin/main auf Feature-Branches, um ihn aufzunehmen

3.3 Kleine Repos / Einzelnutzer

Einfach als einen PR mergen.


4. Globale Nutzereinstellung — core.autocrlf zähmen — 1 min

.gitattributes macht core.autocrlf in diesem Repo wirkungslos. Für Legacy-/externe Repos ohne .gitattributes bleibt es jedoch relevant. Empfehlung:

# macOS / Linux
git config --global core.autocrlf input
# Kein Konvertierung beim Auschecken; CRLF → LF beim Commit
 
# Windows
git config --global core.autocrlf input
# Auch auf Windows input bevorzugen — Dateien werden als LF committed. CRLFs anderer werden trotzdem normalisiert.
 
# Oder nur .gitattributes vertrauen
git config --global core.autocrlf false

Warum input auch unter Windows: Die Ausgabe wird nicht von OS-Standards beeinflusst — immer LF.

core.eol

git config --global core.eol lf

Standard-Zeilenende beim Auschecken. eol= in .gitattributes hat Vorrang.


5. Editor-Einstellungen — IDE anpassen

VS Code (.vscode/settings.json oder Nutzereinstellungen):

{
  "files.eol": "\n",
  "files.insertFinalNewline": true,
  "files.trimTrailingWhitespace": true
}

JetBrains: Einstellungen → Editor → Code-Stil → Zeilentrenner → Unix und macOS (\n).

Vim:

" ~/.vimrc
set fileformats=unix,dos
set fileformat=unix

6. Überprüfung — Sind es wirklich LF?

6.1 Einzelne Datei

# macOS/Linux
file scripts/build.sh
# ASCII text  ← OK
file scripts/build.sh | grep -q CRLF && echo has CRLF
 
# Oder
od -c scripts/build.sh | head -3
# Zeilen enden nur mit \n. Wenn \r \n zu sehen, ist CRLF vorhanden.

6.2 Gesamtes Repo

# Dateien mit CRLF finden
git grep --cached -lI $'\r' -- ':!*.bat' ':!*.cmd' ':!*.ps1'

-I schließt Binärdateien aus. Beabsichtigte CRLF-Dateien ausschließen (ps1, bat).

6.3 CI-Gate hinzufügen

GitHub Actions:

- name: Check no CRLF in LF files
  run: |
    crlf=$(git grep --cached -lI $'\r' -- ':!*.bat' ':!*.cmd' ':!*.ps1' || true)
    if [ -n "$crlf" ]; then
      echo "CRLF found in LF-expected files:"
      echo "$crlf"
      exit 1
    fi

7. Häufige Fallstricke und Lösungen

7.1 .sh schlägt unter Windows fehl mit ^M: command not found

Ein CRLF-Shell-Skript wurde committet. Schnellkorrektur:

dos2unix scripts/build.sh
# Oder
sed -i 's/\r$//' scripts/build.sh

Grundlegende Behebung: *.sh text eol=lf in .gitattributes + git add --renormalize.

7.2 Python SyntaxError: invalid character

.py-Datei mit CRLF + UTF-8-BOM. LF in .gitattributes erzwingen und BOM im Editor entfernen.

sed -i '1s/^\xEF\xBB\xBF//' file.py

7.3 .gitattributes wird nicht tatsächlich angewendet

  • .gitattributes nicht im Stamm sondern in einem Unterverzeichnis — gilt nur dort
  • Dateien wurden nicht renormalisiert — vorhandene Zeilenenden bleiben (Renormalize einmalig ausführen)

7.4 IDE speichert als CRLF

.editorconfig hinzufügen:

# .editorconfig (im Repo-Stamm)
root = true
 
[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
 
[*.{bat,cmd,ps1}]
end_of_line = crlf

VS Code, JetBrains, Vim, Sublime Text usw. lesen .editorconfig automatisch.

7.5 Windows-Editor fügt automatisch BOM hinzu

PowerShell Out-File verwendet standardmäßig UTF-16-BOM. UTF-8 erzwingen:

$content | Out-File "file.txt" -Encoding utf8 -NoNewline
# Oder PowerShell 7+
$content | Out-File "file.txt" -Encoding utf8NoBOM

8. PR-Checkliste für ein vorhandenes Repo

  • .gitattributes-Grundlage hinzufügen (§2)
  • .editorconfig hinzufügen (§7.4)
  • git add --renormalize . + commit
  • CI CRLF-Check hinzufügen (§6.3)
  • Team benachrichtigen — „Nach dem Pull git config --global core.autocrlf input setzen"
  • Eine README-Zeile ergänzen — „Dieses Repo verwendet LF. Siehe .gitattributes."

Nächste Schritte

Referenzen

Changelog

  • 2026-05-12 — Erster Entwurf (devAlice M2-Seed-Erweiterung)