Set your repository’s line-ending rules in .gitattributes first, then let Git, IntelliJ IDEA, and VS Code follow that rule instead of guessing. That single file prevents noisy diffs, broken shell scripts, and those maddening “whole file changed” commits caused by CRLF and LF mismatches.
TLDR: CRLF means a line ends with carriage return plus line feed, written as \r\n, while LF uses only line feed, written as \n. Windows traditionally uses CRLF, while macOS and Linux use LF. For example, a team with 12 Windows developers and 8 macOS developers can easily produce hundreds of false changed lines unless Git normalizes text files. The safest common setup is * text=auto in .gitattributes, plus explicit rules for scripts and binary files.
Table of Contents
What CRLF and LF actually mean
Every text file needs a way to mark where one line ends and the next begins. That invisible marker is the line separator.
- CRLF: carriage return plus line feed, shown as
\r\n - LF: line feed only, shown as
\n - CR: carriage return only, shown as
\r, now rare in modern projects
The confusing names come from old printers and terminals. A carriage return moved the print head back to the start of the line. A line feed moved the paper down. Windows kept both characters. Unix systems used only LF. macOS also uses LF today.
Most of the time, you do not see these characters. Your editor hides them. Then Git notices them, and suddenly a one-line change looks like a 900-line rewrite. That is when everyone gets annoyed.
Why line endings cause real project pain
Line endings are easy to ignore until they break something. The most common symptom is an ugly diff. A developer changes one function, but the pull request shows the whole file as modified. Reviewers waste time checking code that did not really change.
There are worse failures too. A Bash script saved with CRLF can fail on Linux with strange errors such as /bin/bash^M: bad interpreter. Docker entrypoint scripts can refuse to run. Configuration files may behave oddly in strict parsers. Honestly, it feels like losing five minutes to a problem that should have been solved once in 1998.
Line endings also affect blame history. If a file is accidentally converted from LF to CRLF, Git may mark every line as changed. Later, git blame points to the wrong person for nearly every line. That makes bug research slower and messier.
How Git handles CRLF and LF
Git has two places where line-ending behavior matters: the working tree and the repository index.
The working tree is what you edit on your machine. The index is what Git stores and commits. A common best practice is to store text files in the repository with LF, while allowing Windows users to check them out as CRLF when needed.
Git can do this through settings such as:
core.autocrlf=true: common on Windows. Git checks out text files as CRLF and commits them as LF.core.autocrlf=input: common on macOS and Linux. Git commits CRLF as LF but does not convert files on checkout.core.autocrlf=false: no automatic conversion.core.eol: controls the line ending used in the working tree when conversion is active.core.safecrlf: warns or blocks conversions that may lose data.
Personal Git settings help, but they are not enough for teams. Each developer may have different global settings. That is where .gitattributes wins.
The best fix: use .gitattributes
A .gitattributes file lives in the repository. It travels with the project. It tells Git how to treat files, no matter who edits them.
A practical starter file looks like this:
* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.zsh text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.png binary
*.jpg binary
*.gif binary
*.zip binary
*.pdf binary
The first line asks Git to detect text files and normalize them. Scripts for Unix-like systems are forced to LF. Windows batch files are forced to CRLF. Binary files are marked as binary so Git does not try to “fix” them.
After adding or changing .gitattributes, normalize the repository with care:
git add --renormalize .
git status
git commit -m "Normalize line endings"
Review the status before committing. Expect a large commit if the project had mixed endings for years. It is usually worth doing once, then keeping future diffs clean.
How IntelliJ IDEA handles line endings
IntelliJ IDEA shows and controls line separators per file. Look at the bottom-right area of the editor. You may see LF, CRLF, or another marker. Click it to convert the current file.
You can set defaults in Settings or Preferences, then Editor, Code Style, and Line separator. Many teams set this to Unix and macOS (\n), especially for Java, Kotlin, JavaScript, Python, Docker, and CI-heavy projects.
IntelliJ also respects .editorconfig when EditorConfig support is enabled. A simple file can say:
root = true
[*]
end_of_line = lf
insert_final_newline = true
This helps the IDE format files consistently before Git even gets involved. The downside is that IntelliJ may still show warnings or convert files only when they are opened or reformatted. It drives me crazy that you can fix one file in two seconds, then still discover another CRLF file during CI. Use Git rules as the final guard.
How VS Code handles line endings
VS Code displays the current line ending in the status bar, usually at the bottom right. It says LF or CRLF. Click that label and choose the target option to convert the open file.
You can also set a default in settings.json:
{
"files.eol": "\n"
}
For Windows-style endings, use:
{
"files.eol": "\r\n"
}
VS Code also supports .editorconfig, either natively for many settings or through extensions depending on the setup. If your team uses VS Code and IntelliJ IDEA together, .editorconfig is a handy shared hint. Still, it should support .gitattributes, not replace it.
One small annoyance: VS Code changes the current file quickly, but it will not magically repair every file in the project unless you run formatters, scripts, or Git normalization. Expect to waste time on random files if the repository has no clear rule.
Image not found in postmetaCRLF vs LF: which should your project use?
For most modern software projects, LF is the clean default. It works well with Linux servers, containers, CI pipelines, package managers, and shell scripts. Since many deployment targets run Linux, LF reduces surprises.
CRLF still makes sense for some Windows-specific files. Batch scripts, legacy Windows tooling, and certain generated files may expect it. The goal is not to ban CRLF everywhere. The goal is to be explicit.
A sensible policy is:
- Use LF for source code, scripts, configs, Docker files, and documentation.
- Use CRLF for Windows command files such as
.batand.cmd. - Mark images, archives, fonts, and PDFs as binary.
- Commit
.gitattributesand.editorconfigto the repository. - Ask developers to avoid committing mass line-ending changes with feature work.
A clean team setup
Use this layered setup for fewer surprises:
- Repository rule: add
.gitattributeswith explicit endings. - Editor hint: add
.editorconfigwithend_of_line = lf. - Git sanity: normalize existing files once with
git add --renormalize .. - Review habit: reject pull requests that mix real changes with line-ending rewrites.
Line separators are tiny characters with oversized consequences. Git can normalize them, IntelliJ IDEA can display and convert them, and VS Code makes them easy to switch per file. The stable answer is simple: define the rule in the repo, make editors follow it, and stop letting invisible characters hijack your diffs.


