Line Endings (CRLF vs LF)
Text files end each line with a special character. Unfortunately, different operating systems use different characters. If you ever clone a repository and Git warns about “LF will be replaced by CRLF”, this is the topic that explains why — and how to make the warnings go away forever.
The basic fact
System | Line ending | Bytes |
|---|---|---|
Unix, Linux, macOS | LF | \n (0x0A) |
Windows, DOS | CRLF | \r\n (0x0D 0x0A) |
Classic Mac (pre-OSX) | CR | \r (0x0D) — extinct |
A text file written on Windows ends each line with two bytes (\\r\\n). The same file written on Linux ends each line with one byte (\\n). The visible content is identical, but the bytes are not, and Git tracks bytes.
Why Git cares
If a Mac user and a Windows user collaborate on the same file without managing line endings, every commit shows the entire file as changed, because every line’s ending byte flipped. Diffs become useless. Merges become painful. Histories balloon.
Git solves this with two settings: core.autocrlf (a per-user default) and .gitattributes (a per-repo override).
core.autocrlf
core.autocrlf controls automatic conversion between Windows and Unix line endings. There are three values:
Value | On checkout | On commit | Use on |
|---|---|---|---|
true | LF → CRLF | CRLF → LF | Windows |
input | no conversion | CRLF → LF | macOS / Linux |
false | no conversion | no conversion | when using .gitattributes |
Set it once per machine
# Windows git config --global core.autocrlf true # macOS / Linux git config --global core.autocrlf input # If you control line endings with .gitattributes instead: git config --global core.autocrlf false
.gitattributes — the modern, portable way
core.autocrlf is a personal setting — every contributor must configure it themselves, and they might not. The robust solution is to put a .gitattributes file in your repo so the rules ship with the project.
.gitattributes
# Default: normalize to LF in the repo, native on checkout * text=auto # Force LF for these (good for scripts and config that must be LF) *.sh text eol=lf *.bash text eol=lf Makefile text eol=lf # Force CRLF for Windows-only files *.bat text eol=crlf *.cmd text eol=crlf # Treat these as binary (never convert) *.png binary *.jpg binary *.pdf binary *.zip binary *.woff2 binary
Commit .gitattributes to the repo. Now every contributor — regardless of their core.autocrlf setting — gets the same consistent behaviour.
Fixing an already-messed-up repo
If your repository already has inconsistent line endings, do a one-time normalisation:
One-time renormalize
# 1. Add or update .gitattributes (see above) # 2. Tell Git to renormalize git add --renormalize . # 3. Commit the result git commit -m "Normalize all line endings"
Inspecting line endings
Useful inspections
# Show how Git interprets each path git ls-files --eol # Look at the raw bytes of a file cat -A myfile.txt # macOS/Linux — CR appears as ^M od -c myfile.txt | head -1 # Find files with CRLF on Linux grep -rl $'\r' . --exclude-dir=node_modules
The “warning: LF will be replaced by CRLF” message
This warning fires on Windows when core.autocrlf=true and you commit a file that contains LF. Git is telling you: I am about to convert this LF to CRLF when I check it out next time. The repository stores LF either way; only the working-tree copy changes.
It is harmless. You can suppress it if you want:
git config --global core.safecrlf false
Shell scripts on Windows
Recommended setup
Add a
.gitattributesto every repo with at least* text=auto.On macOS/Linux:
core.autocrlf=inputglobally.On Windows:
core.autocrlf=trueglobally — but.gitattributestakes precedence anyway.For mixed-OS teams, use
.gitattributesand treatcore.autocrlfas a fallback for contributors who did not configure their machines.Force LF for any file that runs on Linux (
*.sh,Makefile, Dockerfiles).Force CRLF for Windows-only files (
*.bat,*.cmd,*.ps1if you want them readable in Windows tools).Mark known binary types (
*.png,*.pdf,*.zip) asbinary.