CRLF vs LF: The Invisible Line Endings That Break Your Text Files
What's Actually Going On
A text file that looks single-spaced in one app and double-spaced — or full of stray ^M marks — in another usually isn't corrupted. It's almost always a mismatch between CRLF and LF, the two invisible characters different operating systems use to mark the end of a line. Windows writes CRLF. Unix, Linux, and modern macOS write LF. Neither is wrong on its own, but mix them in the same file or repo and things break. This is a different problem from why your Markdown text runs together — that's about Markdown's own line-break syntax, not the raw bytes a file is saved with.
What CR and LF Actually Are
LF — line feed, one byte, 0x0A — is what Unix, Linux, and macOS write. CRLF — carriage return plus line feed, two bytes, 0x0D 0x0A — is what Windows writes. There's a third, mostly-extinct convention too: pre-OS X classic Mac used a bare CR (0x0D) on its own. All three do the same job, marking where one line ends and the next begins, but a program built for one will misread a file written in another.
Where the split came from
The two-character version isn't an accident of software design. It comes from physical teletype hardware. Early teleprinters needed two separate mechanical actions to start a new line: a carriage return to slide the print head back to column one, then a line feed to advance the paper. Skip the pause between them and the next character would smudge onto the page mid-carriage-return. MS-DOS inherited CR+LF from CP/M's terminal conventions back in 1981, and Windows kept it ever since. Unix took a different path. Following Multics, it collapsed the two mechanical actions into a single LF byte and let a device driver sort out whatever the actual printer needed — smaller files, simpler code, one less thing to think about.
Why It Breaks Things
The mismatch shows up in a few predictable ways:
- A file looks fine in Notepad but every line looks doubled in an old terminal or a Unix
cat, because a lone CR isn't recognized as a line break there. git diffreports every single line in a file as changed, even though you only touched one — because the line-ending byte itself differs from what's already in the repo.- A shell script edited on Windows fails outright once it's copied to a Linux server. Why? The shebang line (
#!/bin/bash\r) now carries a trailing CR the kernel wasn't expecting.
Cross-platform teams sharing a Git repo hit this constantly. So do strict file parsers and config formats that only tolerate one convention.
Spotting the Line Ending in a File
Confirm what you're dealing with before you try to fix it. On macOS or Linux, file yourfile.txt reports the convention directly — something like ASCII text, with CRLF line terminators if it's Windows-style. Most code editors show the current format right in the status bar too, usually labeled LF, CRLF, or CR. In a pinch, cat -A yourfile.txt prints a visible ^M at the end of every CRLF-terminated line.
Fixing It With Git
If line-ending headaches keep showing up in git diff or during a merge, Git gives you two ways to handle it.
`core.autocrlf` is the global, per-machine setting. true on Windows converts LF to CRLF on checkout and back to LF on commit, so the repo itself stays consistent. input on Mac and Linux leaves checkout alone and just strips any stray CR on commit. It's a reasonable default — but it's local to each machine, so every teammate has to set it themselves.
A committed `.gitattributes` file is the more reliable fix for a shared repo, since it overrides everyone's local config automatically:
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
*.png binarytext=auto lets Git decide per file. text eol=lf or text eol=crlf pin a specific ending no matter what platform someone's on. binary tells Git never to touch — or diff — the file's bytes at all. This pairs naturally with keeping your Markdown itself diffing cleanly in Git, since .gitattributes solves line endings, not line-wrapping.
One catch: changing core.autocrlf or adding .gitattributes doesn't retroactively touch files already sitting in the repo. To normalize an existing project, commit any pending work first, then run git add --renormalize . and check the result with git status before committing it as its own change.
Fixing It Without Git
Outside of Git, dedicated tools handle bulk conversion. dos2unix converts CRLF to LF; unix2dos does the reverse. For a quick one-off, tr -d '\r' < input.txt > output.txt strips every carriage return by hand. Most text editors also tuck a "convert line endings" command into their save or format menu — no terminal required.
Frequently Asked Questions
What's the difference between CRLF and LF?
LF (line feed, one byte, 0x0A) is the line-ending convention used by Unix, Linux, and modern macOS. CRLF (carriage return + line feed, two bytes, 0x0D 0x0A) is what Windows uses. Both mark the end of a line, but a file written with one convention can look broken — extra blank lines, a trailing ^M, or a script that won't run — when opened or executed on a system expecting the other.
Why does Windows use CRLF and Unix use LF?
It comes down to teletype hardware. Early teleprinters needed two separate mechanical actions to start a new line: a carriage return to move the print head back to column one, and a line feed to advance the paper. MS-DOS inherited CR+LF from CP/M's terminal conventions, and Windows kept it. Unix, following Multics, collapsed this into a single LF byte and let a device driver handle whatever the printer actually needed.
How do I stop Git from changing my line endings?
Set core.autocrlf to true on Windows or input on Mac/Linux — or, more reliably for a shared repo, commit a .gitattributes file with explicit text eol=lf or text eol=crlf rules per file type. The .gitattributes file overrides each contributor's local config, so everyone on the team gets the same behavior regardless of OS.
How can I tell which line ending a file already has?
On macOS or Linux, run file yourfile.txt. It'll report something like "ASCII text, with CRLF line terminators" if the file uses Windows-style endings. Most code editors also show the current line-ending format in the status bar, usually labeled LF, CRLF, or CR.
How Carets Fits In
Carets is a native notes and code editor for iPhone, iPad, and Mac, and every file it saves is plain text you own — no proprietary wrapper to fight with when a file moves between platforms. Because it's built for writers and developers alike, it applies syntax highlighting to config files like .gitattributes the same way it does to your code, so a stray CR or a misconfigured eol rule is easy to catch at a glance instead of hiding in an invisible byte. Your notes, scripts, and config files all stay organized in files and projects you control, in the same plain-text format your terminal and Git already expect.
Get Carets on iPhone, iPad, and Mac from the App Store.