How line ending conversions work with git core.autocrlf between different operating systems
The best explanation of how core.autocrlf
works is found on the gitattributes man page, in the text
attribute section.
This is how core.autocrlf
appears to work currently (or at least since v1.7.2 from what I am aware):
core.autocrlf = true
- Text files checked-out from the repository that have only
LF
characters are normalized toCRLF
in your working tree; files that containCRLF
in the repository will not be touched - Text files that have only
LF
characters in the repository, are normalized fromCRLF
toLF
when committed back to the repository. Files that containCRLF
in the repository will be committed untouched.
core.autocrlf = input
- Text files checked-out from the repository will keep original EOL characters in your working tree.
- Text files in your working tree with
CRLF
characters are normalized toLF
when committed back to the repository.
core.autocrlf = false
core.eol
dictates EOL characters in the text files of your working tree.core.eol = native
by default, which means Windows EOLs areCRLF
and *nix EOLs areLF
in working trees.- Repository
gitattributes
settings determines EOL character normalization for commits to the repository (default is normalization toLF
characters).
I've only just recently researched this issue and I also find the situation to be very convoluted. The core.eol
setting definitely helped clarify how EOL characters are handled by git.
The issue of EOLs in mixed-platform projects has been making my life miserable for a long time. The problems usually arise when there are already files with different and mixed EOLs already in the repo. This means that:
- The repo may have different files with different EOLs
- Some files in the repo may have mixed EOL, e.g. a combination of
CRLF
andLF
in the same file.
How this happens is not the issue here, but it does happen.
I ran some conversion tests on Windows for the various modes and their combinations.
Here is what I got, in a slightly modified table:
| Resulting conversion when | Resulting conversion when | committing files with various | checking out FROM repo - | EOLs INTO repo and | with mixed files in it and | core.autocrlf value: | core.autocrlf value: --------------------------------------------------------------------------------File | true | input | false | true | input | false--------------------------------------------------------------------------------Windows-CRLF | CRLF -> LF | CRLF -> LF | as-is | as-is | as-is | as-isUnix -LF | as-is | as-is | as-is | LF -> CRLF | as-is | as-isMac -CR | as-is | as-is | as-is | as-is | as-is | as-isMixed-CRLF+LF | as-is | as-is | as-is | as-is | as-is | as-isMixed-CRLF+LF+CR | as-is | as-is | as-is | as-is | as-is | as-is
As you can see, there are 2 cases when conversion happens on commit (3 left columns). In the rest of the cases the files are committed as-is.
Upon checkout (3 right columns), there is only 1 case where conversion happens when:
core.autocrlf
istrue
and- the file in the repo has the
LF
EOL.
Most surprising for me, and I suspect, the cause of many EOL problems is that there is no configuration in which mixed EOL like CRLF
+LF
get normalized.
Note also that "old" Mac EOLs of CR
only also never get converted.
This means that if a badly written EOL conversion script tries to convert a mixed ending file with CRLF
s+LF
s, by just converting LF
s to CRLF
s, then it will leave the file in a mixed mode with "lonely" CR
s wherever a CRLF
was converted to CRCRLF
.
Git will then not convert anything, even in true
mode, and EOL havoc continues. This actually happened to me and messed up my files really badly, since some editors and compilers (e.g. VS2010) don't like Mac EOLs.
I guess the only way to really handle these problems is to occasionally normalize the whole repo by checking out all the files in input
or false
mode, running a proper normalization and re-committing the changed files (if any). On Windows, presumably resume working with core.autocrlf true
.
core.autocrlf
value does not depend on OS type but on Windows default value is true
and for Linux - input
. I explored 3 possible values for commit and checkout cases and this is the resulting table:
╔═══════════════╦══════════════╦══════════════╦══════════════╗║ core.autocrlf ║ false ║ input ║ true ║╠═══════════════╬══════════════╬══════════════╬══════════════╣║ ║ LF => LF ║ LF => LF ║ LF => LF ║║ git commit ║ CR => CR ║ CR => CR ║ CR => CR ║║ ║ CRLF => CRLF ║ CRLF => LF ║ CRLF => LF ║╠═══════════════╬══════════════╬══════════════╬══════════════╣║ ║ LF => LF ║ LF => LF ║ LF => CRLF ║║ git checkout ║ CR => CR ║ CR => CR ║ CR => CR ║║ ║ CRLF => CRLF ║ CRLF => CRLF ║ CRLF => CRLF ║╚═══════════════╩══════════════╩══════════════╩══════════════╝