Line Ending Troubles with Git, a Virtual Machine, and Windows

This post originally from my blog.


This is some technical stuff I learned today using Git, VVV with a Linux virtual machine, and Windows.

I recently started getting these annoying messages again whenever I’d commit anything using Git:

warning: LF will be replaced by CRLF in payment_methods/Paypal_Standard/help_tabs/payment_methods_overview_paypalstandard.help_tab.php.

The file will have its original line endings in your working directory.

I had recently played around with my code editor’s settings (PHPStorm), so that’s probably why.

I mostly ignored these for a while, but I found I couldn’t apply patches from WordPress core without having to run dos2unix on each file. Eventually I finally broke down and tried to figure out what happened, and thanks to my two co-workers, Brent Christensen and Darren Ethier, I think I finally have it figured out what was my problem, and a pretty good solution.

My Setup

I actually previously blogged about my development setup, but here’s the gist:

  • my computer’s operating system is Windows 7 (ie my “host machine” is Windows).
  • I use VVV, which uses a virtual machine (when I type in the URL of a website I’m developing locally, its this virtual machine that handles the request; ie my “guest machine” is Linux).
  • Also importantly, VVV magically copies certain folders from the guest Linux machine to my host Windows machine. This is convenient so that the guest Linux machine can use those files in order to serve web pages, but I can read and edit those files from my host Windows machine.
  • I use Git from my host machine, and sometimes run commands from within the guest machine.

Like was suggested on GitHub’s help page about line endings, I had the git configuration setting core.autocrlf true.An Important Note on Line Endings

On Windows, each new line in a file is actually represented by the hidden characters \r\n a.k.a. “carriage return, line feed” characters.

Whereas on Mac and Linux machines, new lines in files are represented just by \n, ie, *just* a new line character.

So, by default, my host Windows machine adds \r\n, but my guest Linux machine adds \n. So if I edited a file using Windows it would add one type of line ending, but then when I edited the same file from my guest Linux machine, it replaced that line ending with a different one. So there was a bit of a tug-of-war going on.

Git’s core.autocrlf Setting

They say

The git config core.autocrlf command is used to change how Git handles line endings. It takes a single argument.

On Windows, you simply pass true to the configuration.

Sounds simple enough, set that setting to true then all your problems are solved.No need to worry about line endings. Well, those warnings I was getting showed otherwise.

What core.autocrlf Actually Does

When you tell Git to use core.autocrlf true, you’re telling it that when Git checks out a file, regardless of whatever line endings are in the file in the original repository, it should replace those with \r\n. So when viewing the file, all the line endings are \r\n. But when you commit the file and then push it back to the original repository, Git takes care of changing those \r\ns back into \ns, which the original repository wants.

Using a Linux Virtual Machine Means You’re More Linux-y than Windows-y

Theoretically, this is what every Windows user wants, because Windows will see its favourite line endings, \r\n.

But the problem was: I’m not just a Windows user. I’m also using Linux, in the guest virtual machine. And like I described earlier, the host Windows machine was always adding \r\n, but the guest Linux machine would replace that with \n.

What’s more, the only program on my host Windows machine that ever reads or edits the files is PHPStorm, which handles \ns just fine. So it really isn’t of any advantage to me to tell Git to use \r\n when it checks files out from the repository. It just causes a tug-of-war.

My Solution

My solution was to tell Git to use core.autocrlf input. This tells Git to leave the line endings alone when checking out (so usually the original repository used \n, which I want), and only when committing to make sure the files being sent use \n.

This way, even when I checked out files from my host Windows machine, it would leave the line endings as \n, which my guest Linux machine was also happy with. No more warnings. No more Git Diffs showing only a line ending change. WordPress core patches apply cleanly. Much better.

One Gotcha in Application

So what if you’ve already checked out a Git repository and it’s filled with /r/ns? The setting I described only works when you checkout a file, which Git avoids doing unnecessarily. The workaround I found was to create a new branch, delete everything in it (except the hidden .git folder), make a commit (maybe that’s unnecessary, but it’s what I did) and then re-checkout the original branch. That will force Git to checkout all the files, which will now all have the desired /n line endings.

Conclusion

GitHub’s recommended setting for core.autocrlf truefor Windows isn’t the best option if the repository in use is actually in a folder that’s copied to guest Linux virtual machine.

Using core.autocrlf input allows the host Windows machine and guest Linux machine to play much nicer together.

If you see a better way to avoid having my host Windows machine play tug-of-war with my guest Linux machine over line endings, please let me know!