Line endings: LF, CRLF and .gitattributes
Updated July 31, 2026
Linux and macOS end a line of text with one character (LF), Windows with two (CR and LF). Git reads that difference as changed text, so without any setup a file where one line was edited shows up as rewritten from top to bottom.
$ git diff
diff --git a/notes.txt b/notes.txt
index 0c2aa38..8c902f2 100644
--- a/notes.txt
+++ b/notes.txt
@@ -1,3 +1,3 @@
-line one
-line two
-line three
+line one
+line two CHANGED
+line threeline one and line three look identical on both sides, which is what makes this confusing. The difference is an invisible CR stuck to the end of every line on the right. A git diff like that is useless: the real edit is impossible to spot, and review turns into guesswork. On a team where some people work in Windows and some in Linux, files end up looking this way all the time.
Setting it on your own machine
The core.autocrlf key in git config says what to do when a file goes into the repository and when it comes back out to disk:
$ git config --global core.autocrlf true # Windows
$ git config --global core.autocrlf input # Linux, macOStrue on Windows means: LF goes into the repository, and files in the working directory get the CRLF that local editors expect. input on Linux and macOS means: CRLF becomes LF on commit, and nothing is put back on the way out. Either way the history holds one variant, LF, and files stop looking rewritten from top to bottom.
.gitattributes: the setting that travels with the project
core.autocrlf lives on one machine, and every person on the team has to set it. One of them forgets, and whole-file changes are back in the repository. The sturdier way is a .gitattributes file in the root of the repository, committed along with the code:
$ cat .gitattributes
* text=auto
*.png binary* text=auto means: store anything git considers text with LF in the history, and hand it to the working directory in whatever form the system uses. The rule arrives with the clone and behaves the same for everyone, whatever their personal config says.
The second line matters as much as the first. binary switches off every conversion for images, archives and fonts: a 0x0d byte inside a PNG is data, and replacing it with something else corrupts the file. Git usually detects binary files on its own, but for your project's own formats it is better to say it out loud.
git ls-files --eol shows what you ended up with:
$ git ls-files --eol
i/lf w/crlf attr/text=auto notes.txti/ is how the file sits in the index and the history, w/ is how it looks in the working directory.
Common mistakes
A Linux script saved with CRLF will not run:
$ ./deploy.sh
bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directoryThe ^M in the message is that same CR, glued to the path /bin/bash. No such interpreter exists on the system, hence the error. Rewriting the file with LF fixes it, and a line *.sh text eol=lf in .gitattributes keeps it from coming back.
The second mistake is turning the setting on in a repository whose line endings are already mixed. The rules apply to what gets committed from now on; they do not rewrite old snapshots. The files have to be rewritten once, by you:
$ git add --renormalize .
$ git commit -m "normalize line endings"The result is one large commit touching nearly every file. Keep it apart from real changes and warn the team: open branches will merge with conflicts after it.