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: VS Code CRLF Error

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 repository
  • text=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 -

Ref: https://stackoverflow.com/questions/33221873/how-do-i-list-all-files-with-a-specific-extension-in-my-git-repository

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

Written on September 28, 2022