Two characters from the typewriter
Both are control characters, invisible in an editor. RFC 5322 gives their ASCII values, 13 and 10, and RFC 5234, which defines the grammar notation used by many internet standards, writes them in hex:
| Name | Meaning | Decimal | Hex | Escape |
|---|---|---|---|---|
| CR | Carriage return | 13 | 0x0D | \r |
| LF | Line feed | 10 | 0x0A | \n |
The same RFC defines CRLF, CR followed by LF, and calls it the Internet standard newline. So one line ending is one byte or two, depending on the convention.
Who uses which
Python's documentation sums up the three conventions in its entry on universal newlines: \n for Unix, \r\n for Windows and \r for the old Macintosh. The Pro Git book adds that macOS and Linux use only the line feed, while Windows uses both characters. Internet protocols use CRLF:
| Where | Line ending | Per |
|---|---|---|
| Unix | LF | Python docs: the Unix convention |
| Linux and macOS | LF | Pro Git book |
| Windows | CRLF | Python docs: the Windows convention |
| Classic Mac OS | CR | Python docs: the old Macintosh convention |
| Email messages | CRLF | RFC 5322 |
| HTTP/1.1 headers | CRLF (LF accepted) | RFC 9112 |
| CSV files | CRLF | RFC 4180 |
| Inside a git repository | LF, when normalization is on | gitattributes docs |
RFC 5322, the email format, says a line is delimited by CR followed immediately by LF, and that CR and LF must not appear on their own in the body. It also caps a line at 998 characters, and recommends 78, not counting the CRLF. RFC 9112 makes CRLF the line ending of HTTP/1.1 headers, but allows a recipient to accept a single LF. It forbids a bare CR, a CR not followed by LF, outside the content. RFC 4180 describes CSV records as delimited by CRLF too.
What goes wrong
- Every line changed. A file saved with a different ending differs on every line. In a text compare or a pull request, the real change disappears in the noise.
- Hidden characters in values. A program that splits on LF keeps the CR at the end of each line. The last field of every row in a CSV then ends with an invisible character, and a comparison with
"yes"fails. - Mixed files. A file with some LF and some CRLF lines is the worst case. The git config docs warn that Git cannot recreate such a file exactly after converting it.
Size changes too, by exactly one byte per line:
| Lines | Extra bytes after LF to CRLF |
|---|---|
| 100 | 100 bytes |
| 10,000 | 10,000 bytes |
| 1,000,000 | 1,000,000 bytes |
For text copied out of a PDF or an email, where line breaks land in the middle of sentences, the remove line breaks tool joins the lines back into paragraphs, whatever ending they used.
Why protocols care: the empty line
In email and HTTP, line endings are not cosmetic. They carry structure. RFC 9112 describes an HTTP/1.1 message as a start line, then zero or more header lines, then an empty line that ends the header section, then the optional body. Every one of those lines ends in CRLF, so the end of the headers is the sequence CR LF CR LF:
GET /index.html HTTP/1.1 CR LF
Host: example.com CR LF
Accept: text/html CR LF
CR LF Email works the same way. RFC 5322 separates the header section from the body with an empty line, a line with nothing before its CRLF. It also uses CRLF for folding: a long header can be split across lines by inserting a CRLF before a space, and a reader unfolds it again by removing every CRLF that is directly followed by white space. That is why software that builds emails or HTTP requests by hand has to write \r\n, not just \n, even on Linux. The HTTP header checker shows the header lines a server sends back, already split at those line endings.
How git handles it
Git can store text files with LF in the repository and convert them on the way in and out. The gitattributes documentation describes it: when a file is marked as text, its line endings are normalized to LF in the index every time it is checked in, and on checkout they may be converted to CRLF depending on the eol attribute, the config and the platform.
There are two ways to turn this on. The per-machine setting is core.autocrlf:
| core.autocrlf | On commit | On checkout | Typical use |
|---|---|---|---|
| true | CRLF to LF | LF to CRLF | Windows, per the Pro Git book |
| input | CRLF to LF | No conversion | Linux and macOS, per the Pro Git book |
| false | No conversion | No conversion | Repositories controlled by .gitattributes |
The git config docs say true is the same as setting the text attribute to auto on all files with core.eol set to crlf, and that input performs no output conversion. core.eol itself defaults to native, the platform's own ending, and is ignored when core.autocrlf is true or input.
The better way is a .gitattributes file committed to the repository, because it applies to everyone who clones it. The documentation's own example:
* text=auto
*.txt text
*.vcproj text eol=crlf
*.sh text eol=lf
*.jpg -text Line by line: let git detect text files and normalize them; always treat .txt as text; check out .vcproj files with CRLF; check out shell scripts with LF; never touch .jpg files. The -text line matters. The git config docs warn that a binary file wrongly classified as text can be corrupted by the conversion.
Fixing a repository that is already mixed
The gitattributes docs give the steps, from a clean working directory:
echo "* text=auto" >.gitattributes
git add --renormalize .
git status
git commit -m "Introduce end-of-line normalization" Check git status before committing. Any file that should not be normalized, such as a PDF, gets -text in .gitattributes first. Do this in its own commit, so the one big line-ending change does not hide real edits, and tell collaborators to pull it before continuing their work.
Rules of thumb
- Store LF in repositories, with a
.gitattributesfile that says so. - Pin
eol=lffor shell scripts andeol=crlfonly for files that Windows tools insist on. - Mark binary files
-text. - Set your editor to show and keep the file's existing line endings.
- When parsing text from unknown sources, accept LF, CRLF and CR, the way Python's universal newlines do. Text encoding is a separate question, covered in the UTF-8 and Unicode guide.
Frequently asked questions
What is the difference between CRLF and LF?
Both mark the end of a line of text. LF is one byte, the line feed character with value 10. CRLF is two bytes, carriage return (13) followed by line feed (10). Unix, Linux and macOS end lines with LF, Windows with CRLF, and internet protocols such as email and HTTP/1.1 use CRLF on the wire.
Why does git warn about LF and CRLF when I add a file?
Because line ending conversion is active for that file, through core.autocrlf or a text attribute in .gitattributes, and Git checks whether the conversion can be undone. The git config docs describe this check as core.safecrlf: set to true, Git rejects an irreversible conversion; set to warn, it only prints a warning and carries on. The warning is a notice that the file will look different after the next checkout.
Should I use core.autocrlf or .gitattributes?
A .gitattributes file in the repository is the more reliable choice, because it travels with the repository and applies to everyone who clones it. core.autocrlf is a setting on one person's machine. The gitattributes docs show * text=auto to normalize all text files, plus eol=lf or eol=crlf for file types that need a fixed ending.
Why does every line show as changed in my diff?
Usually because the line endings changed, not the text. A file saved with CRLF compared to the same file with LF differs on every line, even though the visible content is identical. Normalize the endings first, or tell your diff tool to ignore whitespace at the end of lines.
Do CRLF line endings break anything on Linux?
They can. A program that splits lines on LF leaves the CR at the end of each line, which then becomes part of the last value on the line. That turns a value like yes into yes plus an invisible character, and comparisons fail. Scripts meant for Unix systems should be stored with LF, which is why the gitattributes example pins *.sh files to eol=lf.
Sources
- RFC 5234, ABNF core rules (CR = %x0D carriage return, LF = %x0A linefeed, CRLF = CR LF, the Internet standard newline)
- RFC 5322, Internet Message Format, sections 2.1, 2.2.3 and 2.3 (folding a long header field by inserting CRLF before white space, and unfolding; lines are delimited by CR, ASCII 13, followed by LF, ASCII 10; CR and LF only together as CRLF in the body; 998-character line limit, 78 recommended)
- RFC 9112, HTTP/1.1, sections 2.1 and 2.2 (a message is a start line, field lines and an empty line, each ended by CRLF; CRLF ends the start line and fields; a recipient may accept a single LF; a sender must not generate a bare CR)
- RFC 4180, CSV format (each record on a separate line, delimited by a line break, CRLF)
- Python documentation, Glossary: universal newlines (the Unix convention \n, the Windows convention \r\n and the old Macintosh convention \r)
- Pro Git book, Customizing Git: Git Configuration (Windows uses carriage return and line feed, macOS and Linux only the line feed; core.autocrlf true on Windows, input on Linux and macOS)
- Git documentation: gitattributes (the text and eol attributes; normalization to LF in the index; text=auto; the example with *.vcproj eol=crlf and *.sh eol=lf; git add --renormalize)
- Git documentation: git-config (core.autocrlf true equals text=auto with core.eol crlf, input does no output conversion; core.eol lf, crlf or native, default native; core.safecrlf and the warning that conversion can corrupt mixed files)