The Line Ending Problem
If you've ever pulled code from a teammate and suddenly Git shows every line as changed—even though nobody touched the logic—you've met the line ending problem. Windows uses CRLF (carriage return + line feed), while Unix and Mac use LF (line feed alone), and without a plan, your repository becomes a minefield of invisible whitespace noise that masks real changes and frustrates everyone involved.
Handling line endings: a practical guide for developers means setting up your team's Git configuration once, normalizing your existing codebase, and then letting the system handle conversion automatically. We'll walk through the concrete steps that eliminate this friction for good.
Understanding CRLF vs LF
Before jumping to solutions, it helps to understand what we're dealing with. LF is a single byte (0x0A) that moves the cursor to the next line. CRLF is two bytes (0x0D 0x0A) that first return the carriage to the start and then feed a new line—a holdover from typewriter days. Windows tools historically default to CRLF; Unix, Linux, and modern macOS default to LF.
When a Windows developer commits a file with CRLF and a Unix developer clones it, their editor may convert those line endings to LF. Git then sees the entire file as modified, even though the logic is identical. Over months, this creates commit noise that makes git blame, git log, and code review confusing.
The goal is simple: store everything in your repository with LF, but let each developer's local Git instance convert to their OS preference transparently.
Setting Up .gitattributes with text=auto
The most reliable solution is a .gitattributes file at your repository root. This file tells Git exactly how to handle line endings for each file type, and it travels with the code, so every developer and every CI/CD pipeline applies the same rules.
# .gitattributes
* text=auto
*.js text eol=lf
*.ts text eol=lf
*.jsx text eol=jsx
*.tsx text eol=tsx
*.json text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.py text eol=lf
*.java text eol=lf
*.go text eol=lf
# Binary files
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.zip binary
*.jar binary
*.exe binary
*.dll binaryThe * text=auto line tells Git to treat all files as text by default and auto-detect whether they're truly text or binary. For each source code file type, we explicitly set text eol=lf to ensure LF is stored in the repository. Binary files get binary to prevent Git from trying to convert them.
Key point: text=auto handles most files sensibly without extra configuration, but being explicit about your source code extensions ensures no surprises. A developer checking out on Windows will have CRLF on their disk, but the repository stores LF. On Linux or Mac, both are LF.
Commit this file early in your repository's life, or as soon as you notice line ending chaos.
Configuring core.autocrlf for Windows Developers
Even with .gitattributes in place, each Windows developer should configure their local Git to handle conversions automatically. The setting is core.autocrlf.
# Windows users should run this once
git config --global core.autocrlf true
# Or for a single repository
git config --local core.autocrlf trueWith core.autocrlf true, Git automatically converts LF to CRLF when you check out code on Windows, and converts CRLF back to LF when you commit. Unix and Mac developers should leave this setting alone (or set it to input if they want extra safety, though .gitattributes usually makes it unnecessary).
You might document this in your CONTRIBUTING.md or onboarding guide:
Windows developers: after cloning, run
git config --local core.autocrlf trueto enable automatic line ending conversion. Mac and Linux developers don't need this step.
The combination of .gitattributes and core.autocrlf true (on Windows only) covers 95% of real-world teams. The file-level rules in .gitattributes are the source of truth, and the local setting ensures smooth conversion on checkout and commit.
Normalizing Your Existing Codebase
If your repository already has a mix of CRLF and LF files—or worse, some files with mixed line endings—you need a one-time normalization. This is a separate commit that fixes all line endings at once, making it clear to future developers what you did and why.
Here's the process:
- Add your
.gitattributesfile as described above. - Tell Git to respect it:
git config --global core.safecrlf false(temporarily, on each developer's machine). - Remove the index and re-add all files so Git reprocesses them with the new rules:
git rm --cached -r .
git reset --hard HEADThis command removes all files from Git's staging area without deleting them on disk, then re-adds them. Git now applies your .gitattributes rules and sees the files as they should be normalized.
Check what changed:
git statusIf .gitattributes is doing its job, you'll see modifications to the files that needed normalization. Create a single commit with a clear message:
git add -A
git commit -m "Normalize line endings to LF (CRLF -> LF)"Include a note in your commit message explaining why:
Normalize line endings to LF across the repository. This eliminates spurious diffs caused by CRLF/LF mismatches. .gitattributes ensures future commits maintain consistency automatically.
After this commit, all developers pull the normalized state, and .gitattributes plus core.autocrlf true (Windows) keep things consistent going forward. No more false positives in code review.
Marking Binary Files Explicitly
Git is smart about text detection, but binary files sometimes get misclassified. If a .png or .jar file has a line-feed-like byte sequence, Git might try to treat it as text, which corrupts it or creates spurious diffs.
The .gitattributes example above already marks common binary types, but be thorough:
# Images
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.svg binary
# Archives and compiled code
*.zip binary
*.tar binary
*.gz binary
*.jar binary
*.war binary
*.exe binary
*.dll binary
*.so binary
*.dylib binary
# Fonts
*.ttf binary
*.otf binary
*.woff binary
*.woff2 binary
# Video and audio
*.mp4 binary
*.mov binary
*.avi binary
*.mp3 binary
*.wav binary
# PDFs and documents
*.pdf binary
*.docx binary
*.xlsx binaryFor custom binary formats—think Figma files, Photoshop PSDs, or proprietary data formats—add them explicitly. Use git ls-files --stage to inspect what Git thinks about your files. If you see binary files creeping into text handling or vice versa, add them to .gitattributes.
Common Mistakes
Forgetting to commit .gitattributes to version control. The file must be in your repository so every clone and every CI/CD pipeline sees it. Storing it locally in .git/config only affects that one machine.
Using .gitattributes without core.autocrlf on Windows. File-level rules handle storage and checkout; core.autocrlf true handles the actual conversion on Windows machines. Both are needed for seamless experience.
Normalizing without communicating to the team. A surprise line-ending normalization commit can break active branches or confuse developers reviewing history. Announce it in your team channel, coordinate the timing, and merge it to main before developers resume new work.
Setting eol=crlf for source code. Unless you have a specific requirement (rare), always use eol=lf for source files. It's the Unix standard and works across all platforms.
Treating .gitattributes as optional. It's not. Add it to your starter template or new-repo checklist. It costs nothing and prevents hours of frustration later.
Key Takeaways
- Create a
.gitattributesfile at your repository root with* text=autoand explicit rules for your source code file types usingeol=lf. Commit it to version control. - Windows developers should set
git config --local core.autocrlf trueafter cloning. This ensures transparent conversion between the repository's LF and their OS's CRLF. - If your existing codebase has mixed line endings, perform a one-time normalization with
git rm --cached -r .followed bygit reset --hard HEAD, then commit the result with a clear message. - Explicitly mark binary files (images, archives, compiled code) as
binaryin.gitattributesto prevent Git from attempting text conversion. - Document the line ending policy in your
CONTRIBUTING.md` or onboarding guide so new team members know what to expect and what to configure.






Comments
Loading comments…