【发布时间】:2011-08-14 08:58:14
【问题描述】:
在执行git diff 时,它会显示“文件末尾没有换行符”。
这条信息的意义是什么,它试图告诉我们什么?
【问题讨论】:
-
也许,如果你有一个没有换行符结束的文件,并且你添加了另一行,git 必须显示前最后一行已更改,因为它包含换行符作为行吗?
在执行git diff 时,它会显示“文件末尾没有换行符”。
这条信息的意义是什么,它试图告诉我们什么?
【问题讨论】:
这表示文件末尾没有换行符(通常是'\n',又名 CR 或 CRLF)。
也就是说,简单地说,文件中的最后一个字节(或在 Windows 上的字节)不是换行符。
显示该消息是因为否则无法区分文件末尾有换行符的文件和没有换行符的文件。无论如何,Diff 都必须输出换行符,否则结果将难以自动读取或处理。
请注意,如果文件格式允许,始终将换行符放在最后一个字符是一种很好的风格。此外,例如,对于 C 和 C++ 头文件,它是语言标准所要求的。
【讨论】:
这不仅是糟糕的风格,它还可能导致在文件上使用其他工具时出现意外行为。
这里是test.txt:
first line
second line
最后一行没有换行符。让我们看看文件中有多少行:
$ wc -l test.txt
1 test.txt
也许这就是您想要的,但在大多数情况下,您可能希望文件中有 2 行。
另外,如果您想合并文件,它的行为可能不会像您预期的那样:
$ cat test.txt test.txt
first line
second linefirst line
second line
最后,如果您要添加一个新行,它会使您的差异稍微嘈杂一些。如果您添加了第三行,它将显示对第二行的编辑以及新添加的内容。
【讨论】:
'\n' 结尾。如果没有'\n',则不是一行。 -l --lines 参数是绝对正确的。
如果您在现有文件末尾添加 新的一行文本,而该文件末尾还没有 newline character,则差异将显示旧的最后一行已被修改,尽管从概念上来说它不是。
这至少是在末尾添加newline character 的一个很好的理由。
一个文件包含:
A() {
// do something
}
十六进制转储:
00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20 A() {. // do
00000010: 736f 6d65 7468 696e 670a 7d something.}
您现在将其编辑为
A() {
// do something
}
// Useful comment
十六进制转储:
00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20 A() {. // do
00000010: 736f 6d65 7468 696e 670a 7d0a 2f2f 2055 something.}.// U
00000020: 7365 6675 6c20 636f 6d6d 656e 742e 0a seful comment..
git diff 将显示:
-}
\ No newline at end of file
+}
+// Useful comment.
换句话说,它显示出比概念上更大的差异。它表明您删除了} 行并添加了}\n 行。事实上,这就是发生的事情,但不是概念上发生的事情,因此可能会令人困惑。
【讨论】:
唯一的原因是 Unix 历史上所有人类可读的文本文件都以换行符结尾的约定。当时,这避免了在显示或连接文本文件时进行额外处理,并避免将文本文件区别于包含其他类型数据的文件(例如人类不可读的原始二进制数据)。
由于这种约定,那个时代的许多工具都期望以换行符结尾,包括文本编辑器、差异工具和其他文本处理工具。 Mac OS X 建立在 BSD Unix 之上,而 Linux 被开发为与 Unix 兼容,因此两个操作系统都继承了相同的约定、行为和工具。
Windows 不是为与 Unix 兼容而开发的,因此它没有相同的约定,大多数 Windows 软件都可以很好地处理没有尾随的换行符。
但是,由于 Git 最初是为 Linux 开发的,而且很多开源软件都是建立在与 Unix 兼容的系统上,如 Linux、Mac OS X、FreeBSD 等,因此大多数开源社区及其工具(包括编程语言)继续遵循这些约定。
在 1971 年有一些技术原因是有意义的,但在这个时代,它主要是惯例和保持与现有工具的兼容性。
【讨论】:
它只是表示文件末尾没有换行符。这不是一场灾难,它只是一条信息,让您在查看命令行中的差异时更清楚地表明没有差异。
【讨论】:
这种约定付诸实践的原因是因为在类 UNIX 操作系统上,换行符被视为行终止符和/或消息边界(这包括进程之间的管道、行缓冲等)。
例如,考虑将仅包含换行符的文件视为单个空行。相反,长度为零字节的文件实际上是一个零行的空文件。这可以通过wc -l命令来确认。
总而言之,这种行为是合理的,因为如果 \n 字符只是一个行分隔符而不是行终止符,则没有其他方法可以区分空文本文件与具有单个空行的文本文件.因此,有效的文本文件应始终以换行符结尾。唯一的例外是文本文件是空的(没有行)。
【讨论】:
我在之前的回复中没有看到一件事。当文件的一部分被截断时,关于没有行尾的警告可能是警告。这可能是数据丢失的症状。
【讨论】:
核心问题是你定义的线是什么以及是否end-on-line 字符序列是否是行的一部分。基于 UNIX 的编辑器 (如 VIM)或工具(如 Git)使用 EOL 字符序列作为 行终止符,因此它是行的一部分。它类似于 在 C 和 Pascal 中使用分号 (;)。在 C 中以分号结束 语句,在 Pascal 中它将它们分开。
【讨论】:
这实际上确实会导致问题,因为行尾会自动修改脏文件而不对其进行任何更改。请参阅此帖子以获取解决方案。
【讨论】:
源文件通常由工具连接(C、C++:头文件,Javascript:捆绑器)。如果省略换行符,可能会引入讨厌的错误(其中一个源代码的最后一行与下一个源文件的第一行连接在一起)。希望所有源代码 concat 工具无论如何都会在连接的文件之间插入换行符,但似乎并非总是如此。
问题的症结在于 - 在大多数语言中,换行符具有语义含义,并且文件结尾不是换行符的语言定义替代方案。所以你应该用换行符结束每一个语句/表达式——包括最后一个。
【讨论】:
// 样式注释。
您的原始文件可能没有换行符。
但是,一些编辑器,如 linux 中的 gedit,会在文件末尾默默地添加换行符。使用此类编辑器时,您无法摆脱此消息。
我试图克服这个问题是用visual studio code editor打开文件
此编辑器清楚地显示最后一行,您可以随意删除该行。
【讨论】:
不管怎样,我在 Mac 上创建 IntelliJ 项目时遇到了这个问题,然后将项目移到我的 Windows 机器上。我不得不手动打开每个文件并更改 IntelliJ 窗口右下角的编码设置。大多数读过这个问题的人可能不会发生这种情况,但这可以为我节省几个小时的工作......
【讨论】:
ubuntu$> vi source.cpp
:set binary noeol
【讨论】: