9

When I revert changes to a file with Windows line endings and .gitattributes defines the EOL as CRLF, git thinks the line endings have changed to LR, even though a hex editor shows CRLF.

This only happens when .gitattributes defines the EOL character.

Without .gitattributes:

This works correctly.

This is the original version of my file Web.config. The last two characters are 0d 0a (CR LF):

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e0d 0a              "utf-8"?>..     

I add a space character to the end of the first line, 20 0d 0a:

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e20 0d0a            "utf-8"?> ..    

Git diff shows the space character:

diff --git a/Web.config b/Web.config
index bc3c3c3..6215f5e 100644
--- a/Web.config
+++ b/Web.config
@@ -1,4 +1,4 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>{+ +}

Revert file and all changes are gone:

$ git checkout Web.config

$ git status Web.config
On branch develop
Your branch is up-to-date with 'origin/develop'.

nothing to commit, working directory clean

With .gitattributes

This does not work correctly.

Define CRLF to .gitattributes:

*.config eol=crlf

Add the space character to the end of the first line:

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e20 0d0a            "utf-8"?> ..    

Git diff shows the space, but the CR is missing (^M):

diff --git a/Web.config b/Web.config
index bc3c3c3..9d3bc53 100644
--- a/Web.config
+++ b/Web.config
@@ -1,248 +1,248 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>[-^M-]{+ +}

Revert file:

$ git checkout Web.config

$ git status Web.config
On branch develop
Your branch is up-to-date with 'origin/develop'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   Web.config

no changes added to commit (use "git add" and/or "git commit -a")

Git thinks the CR has been removed from ALL lines:

$ git diff --word-diff-regex=. Web.config
diff --git a/Web.config b/Web.config
index bc3c3c3..094d1d5 100644
--- a/Web.config
+++ b/Web.config
@@ -1,248 +1,248 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>[-^M-]
<!--[-^M-]
  For more information on how to configure your ASP.NET application, please visit[-^M-]
  http://go.microsoft.com/fwlink/?LinkId=152368[-^M-]
  -->[-^M-]
<configuration>[-^M-]

But this is not the case in the hex editor:

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e0d 0a3c 212d 2d0d  "utf-8"?>..<!--.
00000030: 0a20 2046 6f72 206d 6f72 6520 696e 666f  .  For more info
00000040: 726d 6174 696f 6e20 6f6e 2068 6f77 2074  rmation on how t
00000050: 6f20 636f 6e66 6967 7572 6520 796f 7572  o configure your
00000060: 2041 5350 2e4e 4554 2061 7070 6c69 6361   ASP.NET applica
00000070: 7469 6f6e 2c20 706c 6561 7365 2076 6973  tion, please vis
00000080: 6974 0d0a 2020 6874 7470 3a2f 2f67 6f2e  it..  http://go.
00000090: 6d69 6372 6f73 6f66 742e 636f 6d2f 6677  microsoft.com/fw
000000a0: 6c69 6e6b 2f3f 4c69 6e6b 4964 3d31 3532  link/?LinkId=152
000000b0: 3336 380d 0a20 202d 2d3e 0d0a 3c63 6f6e  368..  -->..<con
000000c0: 6669 6775 7261 7469 6f6e 3e0d 0a20 203c  figuration>..  <

What is going on here and how do I get it to work correctly?

Ryan Jenkin
  • 864
  • 8
  • 11

4 Answers4

5

The key here is that whether you use text=auto, text eol=crlf or text eol=lf, git will:

  1. Convert line endings to LF in the repository (i.e. upon git commit)
  2. Convert line endings to your preferred format when copying from the repository to your working tree (i.e. upon git checkout or git merge)

This is perhaps counter-intuitive, but remember git's origins from the Linux world and is not a bug. From the git documentation: "when a text file is normalized, its line endings are converted to LF in the repository".

As a corollary to this, I find when I join an existing project and .gitattributes needs to be introduced to normalise line endings, I find it's best to write a Powershell script (or whatever method you prefer) to normalise line endings in all files across the repository at one time. This is to avoid ongoing confusing diffs where the only change are line endings introduced by .gitattributes.

Finally in case it assists, I have previously published a sandbox to play with line ending settings on my GitHub: https://github.com/teamtam/git-line-endings

TeamTam
  • 1,598
  • 11
  • 15
  • I'm not sure line endings are converted to LR on commit. There's one file in my repo that has mixed line endings, the majority are LR but a few lines are CRLF. If I don't have .gitattributes set and I delete and recheckout the files I can see the mixed line endings in hex editor; git diff shows no change. If I set line endings to CRLF in .gitattributes, delete the file and checkout again, all lines are converted to CRLF but git diff show the lines that were originally CRLF as changed (the CR removed). It doesn't show any change on lines that were LF and were changed to CRLF. – Ryan Jenkin Nov 17 '16 at 01:19
  • When I refer to "the repository", I mean the internal git workings which you wouldn't normally touch - i.e. the (hidden) .git folder. So when you do a commit with .gitattributes text set, my understanding is that it strips out CR in the repository, but your working tree copy (i.e. the files you actually work with) remains untouched. This means you will notice when you checkout, where whatever is in the repository at that time will get converted to your specified format, be it auto, crlf or lf. – TeamTam Nov 17 '16 at 04:43
  • 1
    This is actually the most exact answer. In short, if you make some CRLF file `text` in `.gitattributes`, you should convert it to LF in blobs. – George Sovetov Dec 04 '18 at 13:13
3

This is a bug on git I faced some times. You may report it to the git team:

Questions or comments for the Git community can be sent to the mailing list by using the email address git@vger.kernel.org. Bug reports for git should be sent to this mailing list.

https://git-scm.com/community

If you are on Windows you could also try to open a bug report on:

  1. https://github.com/git-for-windows/git/issues

May be this bug is fixed on the latest version of git 2.10.2, you could to update yours to the latest released, if is not already:

  1. https://en.wikipedia.org/wiki/Git
tony19
  • 125,647
  • 18
  • 229
  • 307
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
2

Check first your git config core.autocrlf value.

To be sure your .gitattributes directives are the only one to be applied, make sure to type:

git config --global core.autocrlf false

Then clone again your Git repo and see if the issue persists.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • core.autocrlf was not set in system, global, or local configs. I set it to false in the global, then deleted the repository completely and cloned again. I added space characters to the end of the first line and saved but git diff shows that every line has had it's CR removed even though I can see it in the hex editor so there has been no change from the original problem. – Ryan Jenkin Nov 11 '16 at 04:04
  • 1
    .gitattributes takes precedence over local or global config – TeamTam Nov 17 '16 at 04:51
2

To have unix line endings on Windows I'm using:

git config --global core.autocrlf input
git config --global core.eol lf

so in your case you should probably set:

git config --global core.autocrlf input
git config --global core.eol crlf

You can do it obviously for specific folders and not globally as I showed above

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
  • Yes, the issue is that when I set the line endings to CRLF, git diff thinks that any files that originally had CRLF lines endings have been modified and the CR removed, even though I can see it in the hex editor and I'm telling it that it should be CRLF. – Ryan Jenkin Nov 17 '16 at 01:22
  • .gitattributes takes precedence over local or global config – TeamTam Nov 17 '16 at 04:52