Mind the Line Break: Cross-Platform CRLF/LF Setup
Line endings are one of those small but painful details in
cross-platform development. Windows tools (like Visual Studio) prefer
CRLF (\r\n) line endings, while Linux and macOS use LF (\n).
Git adds its own layer of normalization to keep repositories consistent across different platforms. If you donβt set things up correctly, you can run into endless diffs, merge conflicts, or the dreaded Visual Studio βLine endings inconsistent, do you want to normalize?β dialog.
This article explains a clean setup: CRLF in your working copy on Windows, LF stored in Git, and how Linux/macOS users can stay on LF without conflict.
The Problem
- Windows editors default to CRLF endings.
- Linux/macOS build tools and Docker images expect LF endings.
- Git doesnβt enforce a line ending style unless you tell it to.
- Mixing the two leads to noisy diffs and warnings when files contain both CRLF and LF.
The Solution: Normalize with Git
Git has a built-in setting to handle line ending conversion automatically:
git config --global core.autocrlf true
With this enabled:
- On checkout: Git converts LF β CRLF so your local files look natural in Windows editors.
- On commit: Git converts CRLF β LF so the repository always stores files with LF endings.
This way, Windows developers get the endings they expect, and the repo stays consistent for cross-platform builds.
Adding a .gitattributes File
To lock the behavior down, commit a .gitattributes file at the root of
your repo. For example:
# Normalize all text files
* text=auto
# Explicitly treat JSON and XML as text with proper diffs
*.json text diff
*.xml text diff
# Protect binary files
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
This ensures:
- All text files are normalized to LF in the repo.
- JSON/XML are always diffed as text.
- Images are never corrupted by line ending conversion.
Editor Configuration for Windows Developers
Developers on Windows often want Visual Studio to always save files with
CRLF. You can enforce this with .editorconfig:
[*]
end_of_line = crlf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
Now Visual Studio wonβt nag about mismatched line endings and will always use CRLF locally.
Linux and macOS Developers: Staying on LF
Linux/macOS developers usually want LF locally, not CRLF. But since the
repoβs .editorconfig may specify end_of_line = crlf for Windows
users, how do they avoid being forced into CRLF?
Option 1: Global EditorConfig override
Each user can create a personal ~/.editorconfig file in their home
directory:
# ~/.editorconfig
[*]
end_of_line = lf
This overrides the repoβs setting, so files are always saved with LF locally. Editors like VS Code, Rider, and Visual Studio for Mac respect this.
Option 2: Per-repo local override (not committed)
If a developer only wants LF overrides for a single repo, agree on a
local file pattern like .editorconfig.local and add it to
.gitignore:
# Ignore per-user overrides
.editorconfig.local
Developers can then create .editorconfig.local in the repo root with:
[*]
end_of_line = lf
Git will ignore this file, but editors will respect it.
Putting It All Together
- Repo: Always stores LF endings (thanks to
core.autocrlfand.gitattributes). - Windows users: Work with CRLF locally (via
.editorconfig+core.autocrlf=true). - Linux/macOS users: Work with LF locally (via
~/.editorconfigor.editorconfig.local). - CI/CD: Gets normalized LF files straight from Git, ensuring consistent builds.
Why This Matters
- Avoids messy diffs where every line shows up as changed.
- Keeps your repository platform-neutral.
- Lets developers on Windows work naturally with CRLF, while Linux/macOS developers stay on LF.
- Prevents Visual Studioβs CRLF/LF warning dialog from interrupting your flow.
TL;DR
- Use
git config core.autocrlf trueon Windows. - Commit a
.gitattributesto normalize text and protect binaries. - Windows devs:
.editorconfigβend_of_line = crlf. - Linux/Mac devs: personal
~/.editorconfigor.editorconfig.local(ignored by Git) βend_of_line = lf. - Repository ends up with clean LF, Windows sees CRLF, Linux/Mac see LF, and everyone is happy.
Happy Coding