Git tip: Add a .gitattributes file to deal with line endings

Dec 21, 2018 ยท 4 minute read

Lately, I needed to install Docker in order to setup a server locally on my machine for testing purposes. Despite all my efforts of setting up the server, I failed over and over again and of course I immediately blamed myself. As I am quite a forgetful person, I thought that most probably I forgot to do something from the instructions. After quite a number of tries I noticed that during the installation process some scripts would complain and display an error that \r\n is not recognized as a command. This seemed too suspicious so eventually I decided to investigate further. The machine that I was working was Windows while the Docker image was a Linux distribution so I thought that maybe after all it was a line endings problem. I manually changed the script that was CRLF to LF and behold - the installation stopped complaining about line endings. But alas, it started complaining for another script! So, what should I do in this case? Is it viable to just change the line endings for all the files in my machine manually? And would that solve the problem for the rest of team?

Enter .gitattributes ๐Ÿ”—

After consulting with a colleague, he suggested that I take a look at .gitattributes. But what the hell is a .gitattributes file? Let’s tackle the root of the problem. Since I never changed the default configuration of git, it would try to automatically convert any files to CRLF line ending. So I ended up having scripts that ran on the Docker Linux image with Windows' line ending(meaning that at the end of every line the \r\n special characters are appended), and since the line endings on Linux are LF(appending only \n at the end of every line) the scripts would fail. This meant that these specific files needed to have LF line ending no matter what operating system the git repository was checked out, as it would eventually be executed on the Docker Linux image.

Specifying a .gitattributes file imposes explicit settings for a specific set of files per repository, overriding the local settings of the environment in which a repository is cloned. This means that if you need some files to have a specific line ending regardless of the operating system in which they are checked out, then you need to specify their line ending in .gitattributes. In our case, we would like some shell scripts and configuration files to always have LF line ending, as they will eventually be executed on the Docker Linux image. What’s more, we would like some .bat files to have CRLF line ending since they will always be executed in Windows environment and some files like images to be treated like binary so as to prevent git from performing any line endings conversion and computing a diff. A sample .gitattributes would be something like this:

# Auto detect text files and perform LF normalization
* text=auto

# These files should have specific line endings
*.sh	text eol=lf
*.bat	text eol=crlf

# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.jpeg	binary	

Refreshing the repository after committing .gitattributes ๐Ÿ”—

Just committing a .gitattributes file is not enough for the changes on the line endings of the files to take place. It’s essential to refresh the files in the repository in order for git to apply the correct line endings. Let’s say that we have a repository which contains two files that just echo a message, one echoes.bat and one If we happen to develop on a Windows machine both files will have CRLF ending and if we develop on Linux or mac they will have LF when they will be committed. Then we add a .gitattributes file like the one above. Unfortunately, the files would continue to have the original line ending that they had when they were committed. The fastest way to refresh them would be to delete and reset them. That can happen with the commands below.

rm -rf *
git reset --hard

Another way to do this without losing files that are not tracked in git is inspired from stackoverflow .

git ls-files -z | xargs -0 rm
git reset --hard

The first command will delete all the files that are tracked in git and then will reset them. Now all the files are updated and we can continue development without any pesky line endings errors.