Avoid "No newline at end of file" in git diff

Karl Richter picture Karl Richter · Aug 22, 2018 · Viewed 8.3k times · Source

I'm quite sure I understand what No newline at end of file means. I want to provide a pull request of a branch which I created long time ago and rebased (commits might be from the time before .gitattributes has been added). I see that some .java source code files have only the change

-}
\ No newline at end of file
+}

regardless of the configuration, I'd simply like these changes to be excluded from the PR commit. I'd like to avoid picking the changes with git difftool and broaden my understanding of git.

The thing is that I already don't understand how this change can exist since there's a .gitattributes with

# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
*.java text
*.css text
*.js text
*.xml text
*.dtd text
*.xsl text
*.properties text
*.txt text
*.svg text
*.yml text
*.md text

# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf

# Denote all files that are truly binary and should not be modified.
*.png binary
*.gif binary
*.jpg binary
*.jpeg binary
*.eot binary
*.ttf binary
*.woff binary
*.woff2 binary

in the repository and I ran git rm --cached -r . && git add . which should convert all line endings to LF because of *.java text (also tried *.java text), but there're no changes shown (against HEAD) and git diff still shows the line ending difference.

Also find ./ -type f -name '*.java' -exec dos2unix {} \; doesn't causes any changes to be recognized by git status and git diff master still displays the line ending change (while the working directory has no staged or unstaged changes).

I'm not interested in hiding the changes, like git diff --ignore-all-space would do.

I'm using git 2.17.1 on Ubuntu 18.04.

Answer

torek picture torek · Aug 22, 2018

The thing is that I already don't understand how this change can exist ...

The change shows that the a/ version of the whatever.java file is missing a newline, while the b/ version of the file has one. "Missing a newline" means that the file ends with no line terminator—it's not a question of CRLF vs LF, it's a question of "has terminator" vs "does not have terminator".

Some editors are capable of handling files that have a final line that lacks a terminator. Other editors are not and don't even notice that there is one missing (mostly just pretending there was one). Some editors will default to adding a terminator if it seems appropriate, perhaps with a warning. There is a lot of variation here.

You don't show what particular git diff command (with what options) generated the diff, so it's hard to say where the a/ and b/ versions came from, but I'll assume HEAD commit and either-index-or-work-tree below. (That is, we're looking at trimmed git diff --cached or git diff HEAD output. Either way we'll see the same thing since git diff has to run the work-tree file through any clean filter and any end-of-line filtering.)

... since there's a .gitattributes [that includes]

*.java text

... and I ran git rm --cached -r . && git add . which should convert all line endings to LF ...

This will convert any existing CRLF line endings in the work-tree to LF-only line endings in the index. I do not believe it will an add LF-ending to lines that have no ending at all (based on the code in crlf_to_git in convert.c).

So, that suggests that your work-tree copy of the file has a final CRLF as the last two bytes of the file, or a final LF-only newline byte as the last single byte of the file. Git would git add that as a final newline. Meanwhile, if the commit itself has a lone close-curly brace (with no line endings at all, neither CRLF nor newlne) as its final byte, that means that the committed (HEAD) copy of the file differs from the index version of the same file by having the committed copy lack any line terminator at all, and you'd see just what you do see.