Javascript is required
PitfallsPublished 2026-07-28Updated 2026-08-087 min read

CRLF vs LF: Fix Line Ending Diffs with Git

Running git diff after pulling code can reveal a frustrating surprise: every single line in a file appears modified despite zero visible text changes! This issue stems from CRLF (\r\n) vs LF (\n) line ending mismatches. This article covers root causes and Git fixes.

By: Tiny
CRLFLFLine EndingsGitCode Diff

1. Problem: whole-file false diffs in cross-platform collaboration

Windows uses Carriage Return Line Feed (CRLF, \r\n, ASCII 0x0D 0x0A), while Linux and macOS use Line Feed (LF, \n, ASCII 0x0A). In cross-platform teams, if an IDE converts LF files to CRLF, line-by-line diff engines evaluate trailing \r characters as additions on every line, flagging the entire file as modified.

2. Minimal reproduction: CRLF vs. LF byte-level false diff comparison

This snippet demonstrates how identical visible text produces false diffs due to line ending mismatches:

/* 1. Source File A (Linux/macOS format, LF \n): */

const fileLF = "function hello() {\n  console.log('world');\n}";

/* 2. Target File B (Windows format, CRLF \r\n): */
const fileCRLF = "function hello()\r\n  console.log('world');\r\n}";

const linesA = fileLF.split("\n");
const linesB = fileCRLF.split("\n");

console.log("Line Count A:", linesA.length, "Line Count B:", linesB.length);
console.log("Line 0 Byte Length A:", linesA[0].length); // 18
console.log("Line 0 Byte Length B:", linesB[0].length); // 19 (extra \r!)
console.log("Byte Comparison Match:", linesA[0] === linesB[0]); // false -> Triggers whole-file false diff!

3. Root cause: carriage return \r byte entity and autocrlf conflicts

1. Carriage Return `\r` Byte Entity: line endings are physical byte codes. \r\n adds 1 byte per line compared to \n. Line diff algorithms evaluate line strings as tokens; trailing \r destroys string equality. 2. `core.autocrlf` Misconfigurations: setting core.autocrlf = true on Windows converts LF to CRLF on checkout and back to LF on commit. If editors overwrite line endings without triggering conversion, CRLF slips into repositories. 3. Missing `.gitattributes`: lacking repository-level line ending rules leaves line ending handling to local OS defaults.

4. Recommendation: full-stack line ending normalization and .gitattributes

1. Repository .gitattributes: add a .gitattributes file in project roots containing * text=auto eol=lf to enforce LF normalization on checkout and commit. 2. Normalize Repository History: run git add --renormalize . to re-normalize staged files. 3. Diff Option Whitespace Ignore: enable 'Ignore Whitespace' options in diff tools, stripping \r prior to comparisons.

5. Complete code: line ending normalization and \r diagnostic helper

This TypeScript snippet demonstrates diagnosing CRLF line endings in raw strings and performing non-destructive LF normalization.

interface LineEndingAnalysis {

  hasCRLF: boolean;
  hasLF: boolean;
  crlfCount: number;
  lfCount: number;
  normalizedText: string;
}

function analyzeAndNormalizeLineEndings(rawText: string): LineEndingAnalysis {
  const crlfMatches = rawText.match(/\r\n/g) || [];
  const lfMatches = rawText.match(/(?<!\r)\n/g) || [];

  const normalizedText = rawText.replace(/\r\n/g, "\n");

  return {
    hasCRLF: crlfMatches.length > 0,
    hasLF: lfMatches.length > 0,
    crlfCount: crlfMatches.length,
    lfCount: lfMatches.length,
    normalizedText
  };
}

const mixedText = "Header\r\nLine 1\r\nLine 2\nFooter";
const result = analyzeAndNormalizeLineEndings(mixedText);
console.log("CRLF Count:", result.crlfCount, "Normalized Length:", result.normalizedText.length);

6. Incorrect approaches

Globally setting git config --global core.autocrlf false on Windows (causing CRLF commits that break Linux CI/CD builds). Manually stripping \r line by line. Misconfiguring binary files in .gitattributes, corrupting binary byte streams.

7. Boundaries: protecting binary files from line ending conversion

Always exclude binary files when configuring .gitattributes. Explicitly declare binary files (*.png binary, *.jpg binary, *.woff2 binary) to prevent Git from converting 0x0D 0x0A byte sequences in binary streams.

8. Verification

Declare the repository EOL policy in .gitattributes and configure a matching linter rule. Keep fixtures for LF, CRLF, and mixed endings, and assert normalization changes only permitted text file types.

9. FAQ

Why does git diff mark every line modified when code looks identical? File line endings changed from \n to \r\n, adding invisible 0x0D carriage return bytes to every line.

What does * text=auto eol=lf in .gitattributes mean? text=auto enables automatic text file detection, while eol=lf forces LF line endings on checkout and commit.

How do I fix CRLF line endings across an entire repository at once? Add .gitattributes, run git add --renormalize ., commit, and push.

10. Summary

Whole-file false diffs stem from cross-platform line ending mismatches. Standardizing repository line endings via .gitattributes and ignoring whitespace in diff tools eliminates false diff noise.

Sources and further reading

Specifications and references for checking the article’s technical details.

Related articles

Keep exploring

Use the related browser tool to test the workflow with your own sample data.

Open related tool