Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does git keep messing with my line endings?

Tags:

git

windows

I'm on Windows and I have core.autocrlf disabled:

$ git config core.autocrlf; git config --global core.autocrlf
false
false

Now, I would assume, that git does not mess with any line endings, but some files in my repo keep causing issues.

I just cloned a fresh copy of the repo, and git is already telling me that there are unstaged changes. When I git diff -R, I can see that CRLF line endings have been added to a file:

diff --git b/nginx-1.11.1/contrib/geo2nginx.pl a/nginx-1.11.1/contrib/geo2nginx.pl
index bc8af46..29243ec 100644
--- b/nginx-1.11.1/contrib/geo2nginx.pl
+++ a/nginx-1.11.1/contrib/geo2nginx.pl
@@ -1,58 +1,58 @@
-#!/usr/bin/perl -w
-
-# (c) Andrei Nigmatulin, 2005
+#!/usr/bin/perl -w^M
+^M
+# (c) Andrei Nigmatulin, 2005^M

I don't understand where these line endings come from, but I'm also unable to "revert" this change. When I checkout the file again, it will still be modified afterwards:

$ git checkout -f nginx-1.11.1/contrib/geo2nginx.pl
$ git status
On branch dev
Your branch is up-to-date with 'origin/dev'.
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:   nginx-1.11.1/contrib/geo2nginx.pl

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

This makes no sense to me, so I just run dos2unix on the file:

$ dos2unix nginx-1.11.1/contrib/geo2nginx.pl
dos2unix: converting file nginx-1.11.1/contrib/geo2nginx.pl to Unix format...

Now there surely shouldn't be any changes, right? But the file is still being shown as modified in git status and git diff will still report CRLF line endings in the working copy.

When I now stage and commit the file, the resulting file will have LF line endings, even though the diff showed CRLF line endings.

I don't have a global .gitattributes (git config --global core.attributesfile does not output anything). And the .gitattributes in the project has * text eol=lf set (full .gitattributes).

What is going on here and how can I resolve this?

Reproduce the issue

I can repro this issue with an open source project I'm maintaining:

$ git clone [email protected]:fairmanager/fm-log.git
Cloning into 'fm-log'...
remote: Counting objects: 790, done.
remote: Total 790 (delta 0), reused 0 (delta 0), pack-reused 790
Receiving objects: 100% (790/790), 201.71 KiB | 138.00 KiB/s, done.
Resolving deltas: 100% (418/418), done.
Checking connectivity... done.

$ cd fm-log/
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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:   .idea/dictionaries/OliverSalzburg.xml
        modified:   .idea/inspectionProfiles/Project_Default.xml
        modified:   .idea/inspectionProfiles/profiles_settings.xml

no changes added to commit (use "git add" and/or "git commit -a")
like image 221
Oliver Salzburg Avatar asked Aug 22 '16 10:08

Oliver Salzburg


2 Answers

Interesting: with the newly added-to-question repo I tried cloning it and, on BSD, see the same thing:

$ git clone [email protected]:fairmanager/fm-log.git
Cloning into 'fm-log'...
remote: Counting objects: 790, done.
remote: Total 790 (delta 0), reused 0 (delta 0), pack-reused 790
Receiving objects: 100% (790/790), 201.71 KiB | 0 bytes/s, done.
Resolving deltas: 100% (418/418), done.
Checking connectivity... done.
$ cd fm-log/
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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:   .idea/dictionaries/OliverSalzburg.xml
    modified:   .idea/inspectionProfiles/Project_Default.xml
    modified:   .idea/inspectionProfiles/profiles_settings.xml

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

Trying to see what's going on:

$ git diff
warning: CRLF will be replaced by LF in .idea/dictionaries/OliverSalzburg.xml.
[snip]

It seems that HEAD:.idea/ files actually have carriage-returns in them (and the last line has no newline, hence the prompt right after the close angle bracket):

$ git show HEAD:.idea/dictionaries/OliverSalzburg.xml | vis
<component name="ProjectDictionaryState">\^M
  <dictionary name="OliverSalzburg">\^M
    <words>\^M
      <w>colorizer</w>\^M
      <w>multiline</w>\^M
    </words>\^M
  </dictionary>\^M
</component>$ 

The work-tree version likewise has carriage returns (no cut and paste but vis shows the same \^M line endings).

So what has happened in this case, at least, is that due to the .gitattributes setting of:

$ head -2 .gitattributes 
# In general, use LF for text
* text eol=lf

Git will convert these files to LF-only during commit, vs the HEAD version that contains CR-LF endings. This is what git status is saying here.

Commenting out the * text eol=lf in .gitattributes makes the status go away (since the files won't be converted), though of course .gitattributes is now marked as modified. Interestingly, putting the attribute line back again, the status goes completely silent: it's necessary to force git checkout to replace the work-tree version to get the status back (e.g., manually rm -rf .idea and check out again, or git reset --hard).

(Presumably—I'm guessing a bit here—the index entry gets marked specially during git checkout when Git notices that the work-tree and HEAD version differ. This makes Git inspect the file closely. Modifying .gitattributes and running an internal diff via git status probably un-marks the index entries. This part is pure speculation meant to explain the weird behavior of git status...)

like image 147
torek Avatar answered Oct 08 '22 23:10

torek


If the local setting git config core.autocrlf is indeed to false, then those eol changes must come from a .gitattributes file (see man page).

Look for one in your local repo, which would include eol rules (like * text=auto eol=crlf I mentioned here).

Or check if you have a global gitattributes file.

git config --global core.attributesfile

Regarding fairmanager/fm-log, you can see one of the "modified" files .idea/dictionaries/OliverSalzburg.xml added in commit e6f823b with crlf at the end.

Since the .gitattributes has * text eol=lf rule, the working directory checkout blobs with lf eol, hence the git diff.

like image 37
VonC Avatar answered Oct 09 '22 01:10

VonC