I’m reasonably proficient with git, but I came across the core.safecrlf
configuration for the first time only today. This is the opportunity to consolidate my thoughts on the subject of line endings in git.
What happens inside git repo
Due to its origins in unix/linux world, git thinks the “normal” way to end lines is with a line feed (lf
, 0x0a). So this is always the way it will store files internally when it decides to normalize them. Now when does this normalization happen ? When git thinks it is a text file, which happens in two cases :
- The preferred way : when there is an appropriate entry in a
.gitattributes
file, typically a* text=auto
line, where git will use its heuristics to decide if a file is text or not. The advantage is that the.gitattributes
file(s) come with the repo, so there is no risk of local settings to come in the way. - When
core.autocrlf
is set totrue
orinput
, and the same heuristics as above identify a text file.
I used to think that on a pure Windows project, there was no reason not to keep the native line endings (carriage return followed by line feed, crlf
, 0x0d 0x0a) all the way. However, the “new Microsoft” general openness means it is less and less frequent to purely stay in Windows world. Moreover, having git know additional info about files through .gitattributes
has other advantages. So now I would advise to always include a .gitattributes
file and let git do its magic with text files.
How about the working directory ?
This is where it gets a bit more complicated. One of the reasons is that git configuration comes from multiple places, so for instance the local .git/config
settings override the global ~/.gitconfig
ones. A nice trick to better understand your configuration is the command git config --list --show-origin
. Another invaluable command is git ls-files --eol
, which will show you for each file its line ending style in the repo (index) and in the working directory, along with its attributes.
- Of course any manipulation done by any tool or editor may affect the current state of your files, here we’re interested in what’s there after a clone or checkout.
- First, if there is an attribute
eol
for a file (identified as text), the corresponding value (lf
orcrlf
) is always used. A classical use is*.sh text eol=lf
, where even on Windows, you want your bash to understand the script - Else, if
core.autocrlf
is set toinput
, nothing is done, which usually meanslf
are used - Else, if
core.autocrlf
is set totrue
,crlf
are used - Else (i.e.
core.autocrlf
is set tofalse
), the value ofcore.eol
is used : eitherlf
,crlf
, ornative
, the default, where the platform usual ending is used.
warn: CRLF would be replaced by LF in file
Before staging a file, git checks if a round-trip commit
then checkout
will leave it unchanged. A rather frequent case when this is not the case, is when using cross-platform tools under Windows, with core.autocrlf
set to true
. If the tool generates of modifies a file so that it has lf
endings, then it will of course also have lf
within the repo, but theses will be changed back to crlf
on checkout ! Similarly, if core.autocrlf
is set to false
or input
, with a handling of line endings through .gitattributes
, then a text file with crlf
will be normalized to lf
in the repo, and checked out the same, losing the cr
.
This is where core.safecrlf
comes into the picture : when a staging is deemed not reversible, according to its value, git will either prevent the operation (if set to true
), issue a warning (set to warn
, the default), or do nothing (set to false
).
Conclusion
With most tools on Windows now properly handling lf
, I’m not sure that core.autocrlf
set to true
is that useful. So I think that I will go with input
for the global setting, with possible overrides on per-repo basis.
Since most of my repos have (or will have) an appropriate .gitattributes
file, I’m confident enough that text files are handled properly, and since my tooling is not always consistent eol-wise, I’ll set core.safecrlf
to false
globally, trying not to forget overriding it in the few repos without .gitattributes
.
For any new project, I’ll have a .gitattributes
file along the lines of :
* text=auto
*.sh text eol=lf
*.sln text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlf