Line endings handling in SVN, Git and SubGit

People who use Git on Windows often complain about “LF will be replaced by CRLF” warning an other problems related to line endings. While googling error messages one can see a lot of posts written in 2008-2010 years that recommend to set “core.autocrlf” config option to true or false.

These recommendations are not optimal and obsolete, because

  1. “core.autocrlf” option is not under version control
  2. “core.autocrlf” option is set once for all files and doesn’t allow per file control
  3. “core.autocrlf” can cause wrongs line endings for some files (the set of problem files depends on the option value)
  4. since version 1.7.2 Git has better means to control line endings settings by means of git attributes

Git attributes are specified in .gitattributes files. Line endings are controlled by “text” and “eol” attributes.

  • “text” attribute tells Git whether the file is binary (i.e. no EOL conversion should be performed while checking out and in) or text (perform EOL conversion, always convert to LF while checking in). Possible values are set (EOLs conversion is turned on), unset(EOLs conversion is turned off, default value) and “auto”(if the file is detected as binary, no conversion, otherwise EOLs conversion is performed).
  • “eol” attribute: if set implicitly sets “text” attribute and defines EOL to which the file should be converted while checking out.

The most useful combinations of these attributes are:\ 1. Always convert to LF in all OSes while checking out to the working copy (useful for shell scripts because some unix shells like Dash fail when encountering into CRLFs)

/ text eol=lf

EOLs conversion for eol=lf

EOLs conversion for eol=lf attribute

2. Always convert to CRLF in all OSes(for bat-scripts, for example)

/file.bat text eol=crlf

EOLs conversion for eol=crlf

EOLs conversion for eol=crlf attribute

3. Always convert to OS-dependent EOL (LF for UNIX, CRLF for Windows), “!eol” means that Git will use “core.eol” setting of .git/config This setting is recommended for the most of source code files

/file.cs text !eol

EOLs conversion for !eol

EOLs conversion for !eol attribute

4. Binary file, no EOLs conversion.

/file.bin -text

No EOL conversion for -text

No EOL conversion for -text attribute

Because 3rd option is recommended for the most of the files it can be set as default with the rule

* text !eol

but thanks to possibility of text=auto value the rule can combine 3rd and 4th option:

* text=auto !eol

This means that for binary files no EOL conversion is performed, for text files all EOLs are replaced with OS-dependent EOL while checking out and replaced with LF while checking in.

In Subversion EOLs-related problem is solved long ago by svn:eol-style and svn:mime-type properties.\ 1. svn:eol-style=LF — convert to LF in all OSes while checking out to the working copy\ 2. svn:eol-style=CRLF — convert to CRLF in all OSes while checking out to the working copy\ 3. svn:eol-style=native — convert to OS-dependent EOL in all OSes while checking out to the working copy\ 4. svn:eol-style is not set (optionally svn:mime-type is set to any value that doesn’t start with “text/”) — binary file, no conversion

As one can see 1-4 options for Git correspond to 1-4 options for SVN, and SubGit actually performs this conversion on the fly, so Git users see the same behaviour after cloning an Git repository as SVN user after checking out the linked SVN repository.

How SubGit converts line

How SubGit converts line endings

Not that for the case svn:eol-style=CRLF Subversion keeps contents with CRLFs, but Git expects blobs to contain LFs, so the contents conversion is necessary in this case. Unlike SubGit git-svn doesn’t perform contents conversion at all, that can result into EOLs problems. Also from this picture one can see why it is better to use per-file rule rather than one global “core.autocrlf=true” (that is the same as “* eol=crlf” rule) or “core.autocrlf=false” (that corresponds to “* -text” rule) values.

Unfortunately not all Git EOLs settings can be mapped one-to-one to SVN settings. One of the reason: Git allows recursive rules like “* attribute=value” and Subversion doesn’t. In this case SubGit applies changes in recursive Git rules to every Subversion file. But usually this doesn’t lead to any problems.

Default “* text=auto !eol” rule of .gitattributes can be also considered as Git analog of autoproperties because it applies automatically to every newly added file.

Written on November 9, 2012