5

This line endings problem is driving me nuts...

Background: Historically, I was using the core.autocrlf setting but I found I had problems with different repos behaving badly (I work on Windows and I have some repos that require LF endings and some that require CRLF endings). So I am trying to move away from that and use a .gitattributes in each repository (I want Git to just shut up and let me manage line endings!). So I now have core.autocrlf=false and a .gitattributes that looks like this for a Visual Studio project I am working on:

# Don't do any end of line normalization.
* -text

# Always treat these files as binary. Not strictly necessary, but can't hurt.
*.png binary
*.gif binary
*.jpg binary
*.jpeg binary
*.dll binary
*.doc binary
*.docx binary
*.xls binary
*.xlsx binary
*.pdf binary

I have forced all the files in the repo to CRLF endings with unix2dos and confirmed that they have the right line endings in the working directory, and checked them all in. And yes I followed the advice here Trying to fix line-endings with git filter-branch, but having no luck

This is almost perfect.

THE PROBLEM is that whenever I change a file git reports that it has line ending differences on the changed lines, for example, if the original line is

string s;

and the changed line is

string sucks;

git diff shows the change as

string sucks;^M

It looks like Git thinks that the files in the repo still have LF endings (because they were normalized in the past?). The ^M cause a large amount of visual noise and I am not sure whether this is a symptom of anything else. I don't understand why Git is reporting a difference because I have checked in all the files with CRLF endings in a previous commit, in fact the commit just before this one.

So why am I getting these "fake" differences and how can I get rid of them?

Community
  • 1
  • 1
Philip Daniels
  • 994
  • 1
  • 7
  • 25

1 Answers1

7

Woohoo! I think I found an answer. This website http://lostechies.com/keithdahlby/2011/04/06/windows-git-tip-hide-carriage-return-in-diff/ recommends doing

git config [--global] core.whitespace cr-at-eol

and this does indeed shut "git diff" up. Of course, the best solution is to do the equivalent in the .gitattributes file so that it sticks with the repo and is not dependent upon the user's settings.

Definitive settings for .gitattributes

Having spent some time experimenting with repos on Linux and Windows and with files having Unix and Windows line endings, I believe that the whole end-of-line issue can be made to go away if you just give your git repo a .gitattributes file with two lines:

* -text
* whitespace=cr-at-eol

The first line stops git from doing any line ending normalization and the second line stops git-diff highlighting CR characters at the end of lines. The only downside I could find is a slight oddity if you convert a file from DOS to Unix or vice-versa: git-diff will show the file as changed, but it doesn't highlight the reason why, instead it just shows every line as deleted then added. I can live with this, since it is such a rare operation (or should be).

The above places the burden of line-ending management on the committers. This is at it should be, I don't believe my VCS tool should be trying to do magic, the thousands of webpages and questions about git's handling of line endings is proof that they made a wrong decision here.

Variations

If you replace the first line with

* eol=lf

then files will always have LF endings in the working directory. This can be handy for repositories which must work across Unix and Windows, such as ".dotfiles". Likewise eol=crlf will force Windows style line endings. Warning This will also convert CRLFs in binary files! See my comment at how to config git repo so that all files are stored with line-ending LF ( not CFLF ) ? so you will also need to use file paths or tell git which files are binary. https://help.github.com/articles/dealing-with-line-endings/

Handy Aliases

The following two git aliases provide a quick way of converting all text files in a repo from DOS to Unix or vice-versa. They specifically exclude any processing of the .git folder. I could not find a way of defining them from the command line, so edit your .gitconfig and add the following two lines in the [alias] section:

godos = !find . -path ./.git -prune -o -type f -exec unix2dos {} "\\;"
gounix = !find . -path ./.git -prune -o -type f -exec dos2unix {} "\\;"  

You can then do git godos or git gounix in the root of the repo to set all text file endings to one or the other. n.b. The above only works on files in the current branch. I haven't found a way of converting every file in every branch yet.

References

gitattributes man page: https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html

gitconfig man page (discussion of cr-at-eol): https://www.kernel.org/pub/software/scm/git/docs/git-config.html

Git aliases for find: `find -exec` in git alias

Community
  • 1
  • 1
Philip Daniels
  • 994
  • 1
  • 7
  • 25