Seeing "warning: LF will be replaced by CRLF the next time Git touches it" spam in your terminal means Git is about to convert files on your next checkout, and if a teammate on a different setting already committed those same files as CRLF, that conversion is exactly what makes a file show up 100% changed in your pull request. Relying on individual Git configurations to handle line endings across different operating systems practically guarantees polluted commits and broken deployment pipelines. Fixing an already-corrupted repository requires a one-time global normalization, completely bypassing whatever local settings your team is running.

  • The root cause: Windows uses CRLF (carriage return + line feed), while Unix, macOS, and Linux use LF.
  • The real solution: a committed .gitattributes file at the root of your project.
  • The command: git add --renormalize . fixes existing broken files without losing history.
  • Local setup: set git config --global core.autocrlf false and let .gitattributes do the heavy lifting.

Why core.autocrlf Is Failing Your Team

Git offers a built-in mechanism called core.autocrlf to handle the Windows (CRLF) and Unix (LF) divide. It operates in three states: true (converts LF to CRLF on checkout, back to LF on commit), input (leaves checkouts alone, converts to LF on commit), and false (no conversion at all).

The Problem with Local Git Configurations

The fatal flaw of core.autocrlf is that it exists strictly on the developer's local machine. A mixed-OS team ends up with deeply inconsistent line endings even when everyone insists they configured their setups correctly. All it takes is one new hire or one fresh OS install with the wrong default Git settings to push CRLF line endings directly into your codebase.

Once those incorrect line endings hit the remote repository, every other developer starts seeing massive, unexplainable file diffs.

The Real Fix: Enforce Line Endings with .gitattributes

The only bulletproof way to manage line endings is at the repository level. A .gitattributes file overrides any local developer settings and forces Git to apply consistent rules whenever a file is read or written. Committing this file ensures that anyone cloning the repository automatically follows the correct line ending behavior.

The Exact .gitattributes Configuration You Need

Create a new file named .gitattributes in the root of your project. Drop these rules into it to standardize text handling across all environments:

# Set default behavior, automatically normalizing line endings to LF
* text=auto eol=lf

# Explicitly declare text files you want to always be normalized
*.js text eol=lf
*.ts text eol=lf
*.css text eol=lf
*.html text eol=lf
*.sh text eol=lf

# Declare files that must always be treated as binary
*.png binary
*.jpg binary
*.woff binary

The * text=auto directive tells Git to guess whether a file is text or binary and normalize the text files automatically. Relying purely on auto-detection is risky, especially with obscure file formats or templating engines. Defining explicit extensions (*.sh text eol=lf) removes the guesswork entirely. It guarantees Git treats your critical source code as text and enforces LF line endings, regardless of the developer's operating system.

Advertisement

How to Renormalize an Already Broken Git Repository

Adding a .gitattributes file only dictates how Git handles future changes. It does nothing to fix the files currently sitting in your repository with mixed or corrupted line endings. You need to execute a one-time renormalization to clean up the existing history without ruining your git blame records.

Step 1: Commit your current work. Before rewriting the index, make sure your working directory is completely clean. Commit any pending changes or stash them to prevent accidental data loss.

git add .
git commit -m "Save work before renormalization"

Step 2: Rewrite the git index. With the .gitattributes file committed, force Git to re-evaluate every file in the repository against your new rules. The --renormalize flag safely updates the index to match the .gitattributes policy.

git add --renormalize .

Step 3: Commit the normalized files. Check git status to see the full list of files Git has converted. You will likely see a long list of modified files representing the newly fixed line endings. Commit this as a standalone, isolated commit so it never gets mixed into an unrelated code review.

git commit -m "chore: renormalize line endings across repository"

Push this commit and have the rest of your team pull it. Anyone with local changes should stash them first with git stash, pull, then bring their work back with git stash pop, pulling with uncommitted changes sitting on top of a mass line-ending rewrite is exactly when merge conflicts show up. If files still show as modified once they're on the new commit, that's just Git re-checking out the normalized version. Run git diff first to confirm the changes are only whitespace, then let it happen. Never run git checkout -- . (or its modern equivalent, git restore .) to "fix" this; that command discards any real uncommitted work right along with the line endings, with no way to get it back.

Reading git status After Renormalizing

A wall of modified files right after git add --renormalize . looks alarming, but check what actually changed before you panic. Run git diff --stat first: if the changed-lines count roughly matches the file's total line count and git diff shows the content itself is identical (just the invisible \r moving), you're looking at a clean line-ending fix. If you see actual code differences mixed in, stop and separate that file out into its own commit before continuing.

To check what a file is using right now, without guessing: git config --get core.autocrlf shows your local setting, and file <filename> on macOS/Linux reports "with CRLF line terminators" for anything still affected. If your project also ships an .editorconfig, keep its end_of_line values in sync with .gitattributes, they don't override each other, but a mismatch means your editor and Git disagree about what "correct" looks like, and you're back to inconsistent diffs.

Advertisement

CI/CD Nightmares: When CRLF Ruins Bash Scripts

Line ending conflicts cause more than just annoying terminal warnings. They actively break build pipelines and deployment servers. Developers working on Windows often inadvertently save bash scripts (.sh files) or Dockerfiles with CRLF line endings.

Fixing the "Bad Interpreter" Error on Linux Runners

When a Windows-authored script executes on a Linux CI runner (like GitHub Actions or GitLab CI), the Linux shell misinterprets the hidden \r character. It usually throws a cryptic /bin/bash^M: bad interpreter: No such file or directory error and immediately fails the build.

Your CI/CD pipeline does not run your personal git config, so it is completely defenseless against raw repository files. Enforcing *.sh text eol=lf in your .gitattributes file solves this permanently. Git strips the offending carriage returns before the CI runner even attempts to execute the script, so the build passes without anyone touching the pipeline config.

I chased that exact bad interpreter error for an hour once, on a script that ran fine locally on Windows and failed on every single CI run. It wasn't the script, it was the checkout. Once .gitattributes forced *.sh to LF, the error never came back, on that repo or any other one I applied the same file to.

If your team is also cleaning up inconsistent branch names, it's worth doing both in the same pass, you're already asking everyone to pull a disruptive commit once.