Line Endings (LF, CRLF) and their interplay with git and eslint
Line endings are tricky. There are a lot of articles/questions/answers that exist about it but to get my answer I had to read (and re-read) many of them and spent a lot of time. Here is my attempt to give a detailed one stop explanation.
Motivation
I would often see eslint errors on each line of my code, which made no sense to me. They were like:
Background
Different types of line endings exist. Unix/Linux uses LF, while Windows uses CRLF, both of them are invisible and you cannot visually differentiate between them.
git has a way it handles line endings. In the default settings, it will use LF for its own purposes but let you have/see CRLF in Windows and LF in Unix/Linux.
eslint does not like CRLF file endings.
If you are in Windows, git will give you CRLF and eslint will complain about it.
The proposed solution I recommend (let me know your thoughts/issues) is to understand how these things work and use LF file endings for everything.
git
When using a versioning system like git, with multiple contributors who might have different Operating Systems (and thus different line endings) it is very important to think about this.
If git ignored the different line endings (you can make it)
This would be terrible.
For example:
- Developer A (using LF) pushes a file
- Developer B (using CRLF) modifies one line in it, but unintentionally updates the line endings and pushes a commit
- The commit diff will show the file having changes in all the lines rather than just one actual change which was made. This will also be confusing as both the before/after will look exactly the same.
What does git actually do
git gives you options to configure the behaviour as per your preferences.
Let’s take a bit of a detour to understand the above statement in detail. When you are working in a directory which is a git repository, it has two parts “the working directory” and the .git
folder. The .git
folder contains all the commits and versioning related information.
There are two different workflows which can be handled differently:
- working directory -> git (example: git add, git commit)
- git -> working directory (example git clone, git checkout)
Checking-in (working directory -> git)
git by default only has LF file endings in their system.
Whenever you make a commit, the changes you have made will be added somewhere in the .git
directory.
While doing that if your changes have CRLF endings it will convert them to LF (default setting).
This is unaffected by the file endings you use in your working directory.
Checking-out (git -> working directory)
This is where things get interesting, if you are in Windows. By default it will conver the LF file endings to CRLF for you. This can be problematic (Ref Motivation, Background.
git line ending configuration options
Refer to git configuration options to understand how configuration works in general.
Old System
autocrlf
core.autocrlf=true: core.autocrlf=input: core.autocrlf=false:
repository repository repository
^ V ^ V ^ V
/ \ / \ / \
crlf->lf lf->crlf crlf->lf \ / \
/ \ / \ / \
It is the old system, so you can skip this. But if you must, I suggest using core.autocrlf=input
safecrlf
Skipped for brevity. Refer to this blog for more details
New System
text
- For the Checking-in working directory - git workflow.
- Controls what get stored in git.
- Recommended:
text=auto
, i.e. for all the files git considers having text it will convert file endings to LF.
eol
- Fore the Checking-out git - working directory
- Checkint-out git - working directory) workflow.
- Controls what you see in working directory.
- Recommended:
eol=lf
, i.e. it will keep LF file endings in your working directly (NOT convert them to CRLF if you are windows).
Conclusion
Repo level
.gitattributes
Set .gitattributes
to:
* text=auto eol=lf
*
matches to all the files in the repositorytext=auto
text
asks git to check in (working directory -> git) with LF line endings=auto
for the files it detects as having text. That is, not apply this setting to files like binary/image/media.
eol=lf
will keep files in working directory with LF line endings.
Fix your remote (Reset check-in)
The following command can be used to change CRLF -> LF on your remote (in case some CRLF files made their way to remote already).
git add --renormalize .
Check git diff and Commands for debugging
Followed by something like:
git status
git add --all
git commit -m "git add --renormalize ."
git push
Fix your working directory (Reset check-out)
git rm --cached -r . # Remove every file from git's index.
git reset --hard # Rewrite git's index to pick up all the new line endings.
to reindex stuff with line endings issues
Global level
Keep LF in working directory
git config --global core.eol lf # (new setting system)
git config --global core.autocrlf input # (old setting system)
Commands for debugging
List files along with their line endings
git ls-files --eol | code -
Somehow the output format was not given on man page, but this blog has it.
Ref: https://stackoverflow.com/a/45546610/2806163
For checking files with a particular extension:
git ls-files --eol '*.ts*' | code -
Check git configurations
git config --list --show-origin | cat | code -
can be very helpful to see the config files and the source that it is coming from. https://stackoverflow.com/questions/38928903/git-config-list-shows-duplicate-names/38928961#38928961
Find all extensions tracked with git
$ git ls-tree -r HEAD --name-only | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u
Output:
bml
css
db
dot
eslintignore
feature
gif
gitattributes
gitignore
js
json
lintrc
md
MD
png
py
rsd
sh
strings
svg
ts
tsx
txt
wav
xsd
yaml
Ref: https://stackoverflow.com/a/34088712/2806163
References
- https://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/
- Very nice and detailed explanation by Tim Clem (one of the first 20 engineers of GitHub!)
- Official Documentation (highly recommended read)
- https://stackoverflow.com/a/20653073/2806163
- A detailed answer with 1373 upvotes. Has explanation about the autocrlf system
- https://stackoverflow.com/a/42135910
- Very nice solution to the problem, but lacks explanation