关于行结尾:避免在git diff中“文件末尾没有换行符”

Avoid “No newline at end of file” in git diff

我很确定我明白文件末尾没有换行意味着什么。我想提供一个我很久以前创建并重新启动的分支的pull请求(提交可能是从.gitattributes添加之前的时间开始)。我看到一些.java源代码文件只有变化

1
2
3
-}
\ No newline at end of file
+}

无论配置如何,我都希望将这些更改从PR提交中排除。我想避免用git difftool选择更改并扩展我对git的理解。

问题是我已经不明白这种变化是如何存在的,因为它有一个.gitattributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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

在存储库中我运行git rm --cached -r . && git add .,因为*.java text(也尝试了*.java text),它应该将所有行结尾转换为LF,但是没有显示任何更改(针对HEAD),git diff仍然显示行结束差异。

此外find ./ -type f -name '*.java' -exec dos2unix {} \;不会导致git status识别任何更改,并且git diff master仍然显示行结束更改(而工作目录没有暂存或未暂存的更改)。

我不想隐藏这些变化,就像git diff --ignore-all-space那样。

我在Ubuntu 18.04上使用git 2.17.1。


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

此更改显示whatever.java文件的a/版本缺少换行符,而文件的b/版本有一个。"缺少换行符"意味着文件以无终止符结束 - 这不是CRLF与LF的问题,而是"有终结符"与"没有终结符"的问题。

某些编辑器能够处理具有缺少终结符的最终行的文件。其他编辑不是甚至没有注意到有一个缺失(大多只是假装有一个)。如果看起来合适,某些编辑器将默认添加终结符,可能带有警告。这里有很多变化。

你没有显示什么特定的git diff命令(有什么选项)生成差异,所以很难说a/b/版本来自哪里,但我会假设HEAD提交并且 - 或者 - index - 或 - 下面的工作树。 (也就是说,我们正在查看修剪后的git diff --cachedgit diff HEAD输出。无论哪种方式,我们都会看到同样的事情,因为git diff必须通过任何clean过滤器运行工作树文件,并且任何结束 - 线过滤。)

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

1
*.java text

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

这会将工作树中任何现有的CRLF行结尾转换为索引中仅LF的行结尾。我不相信它会向没有结尾的行添加LF结尾(基于convert.c中的crlf_to_git中的代码)。

因此,这表明文件的工作树副本具有最终CRLF作为文件的最后两个字节,或者最终的仅LF换行字节作为文件的最后一个单字节。 Git将git add作为最终换行符。同时,如果提交本身有一个单独的大括号(根本没有行结尾,CRLF和newlne都没有)作为其最后一个字节,这意味着文件的已提交(HEAD)副本与索引版本不同通过让提交的副本完全没有任何行终止符来保存同一个文件,你会看到你所看到的内容。