".gitattributes"中的`* text=auto`和`* text eol=lf`有什么区别?

9
我一遍又一遍地查看了.gitattributes文档,但我无法找到清晰的答案,说明这两者之间的区别是什么:

* text=auto

* text eol=lf

此外,text=auto是否仅适用于使用*或也可以与特定扩展名一起使用?在这种情况下,区别是什么?

*.txt text=auto

*.txt text eol=lf

1个回答

20

TL;DR

在Git 2.36.0之前,eol=lf设置会覆盖任何text设置,如果你将其应用于每个路径,则只有eol=lf设置会起作用。自Git 2.36.0以后,eol=lf仅适用于text被设置、未指定或设置为auto且Git确定它是文本文件的情况下。

Full explanation

让我们从这个问题入手并逐步解释:

text = auto是否仅适用于使用*或也可以与特定扩展名一起使用?

模式可以包括扩展名。 text = auto部分是一个属性设置,而模式选择哪些属性应用于哪些文件。

Git如何读取.gitattributes文件

gitattributes中的每行都匹配或不匹配某些路径名称,例如dir1/dir2/file.extREADME.md或其他路径名。正如gitattributes文档所述:

gitattributes文件中的每一行都具有以下格式:

pattern attr1 attr2 ...

也就是说,一个模式后面跟着一个属性列表,用空格隔开。前导和尾随空格将被忽略。以#开头的行将被忽略。以双引号开头的模式将以C风格引用。当模式与所讨论的路径匹配时,列在该行上的属性将赋给该路径。

因此,*模式。这些“模式”与.gitignore文件中的那些相同,只是不允许使用否定模式。因此,您可以使用类似*.txt*.jpg的模式来匹配文件名扩展名,或者使用dir1/*这样的模式来匹配特定目录中的文件。 .gitignore.gitattributes文件也可以是特定目录本地的,这种情况下它们适用于该目录及其子目录中的文件,但不适用于树中更高的路径。

现在,对于texttext=auto,以及eol=lf或不是,我们得到以下结果:

对于给定路径,每个属性可以处于以下状态之一:

已设置
路径具有具有特殊值“true”的属性;这是通过仅在属性列表中列出属性名称来指定的。

未设置 [详情略过,但请参见下文]

设置为值
路径具有具有指定字符串值的属性;这是通过在属性列表中列出属性名称,后跟等号=及其值来指定的。

未指定
没有任何模式与路径匹配,并且没有任何内容指明路径是否具有属性,该路径的属性被称为未指定。

(最后一个的措辞特别糟糕,我认为它实际上意味着“对于与路径匹配的所有模式,没有一个说了什么关于该属性的事情。”)

因此,对于text,该属性被设置,对于text=auto,该属性被设置为值。在这种情况下,部分是auto。由于模式是*,它适用于所有文件。

同样的逻辑也适用于eol=lf项。如果,首先,这

* text=auto
* text eol=lf

第二行text覆盖了第一行,因此text设置(但值未被赋予),eol设置为一个值,值为lf。两行都匹配,并且第二行覆盖了第一行。

如果你交换这两行:

* text eol=lf
* text=auto

再次提醒,两行都匹配,但现在第二行仅覆盖了text设置,所以现在text被设置为autoeol被设置为lf

text属性如何应用于文件

接下来一节gitattributes文档说:

此属性[text]启用并控制行末规范化...[如果它是]

已设置
...启用行末规范化,并将路径标记为文本文件...

未设置
...告诉Git不要尝试在签入或签出时进行任何行末转换...

设置为字符串值"auto"
...若Git决定内容是文本...

未指定
...Git使用core.autocrlf配置变量...

(这意味着您必须去查找git config文档,以找出如果您未指定textcore.autocrlf变量做什么)。

您选择要么为每个文件设置它,要么为每个文件设置为auto。前者表示“为每个文件进行转换”,后者(auto设置)表示:嘿,Git,请为我决定文件是否为文本。如果您决定它是文本,请进行转换。

eol=lf如何应用于文件

text设置的描述下面,是对eol设置的描述。在Git 2.36.0之前,它读作:

此属性设置要在工作目录中使用的特定行结束样式。它启用行末转换而不进行任何内容检查,有效地设置了text属性。

设置为字符串值"crlf"
...[因为您设置了lf而被忽略]

设置为字符串值"lf"
此设置强制Git在签入时将换行符规范化为LF,并防止检出文件时将其转换为CRLF。

所以,如果你为一个路径设置了eol=lf(并且使用*作为模式,它将应用于每个路径),Git 将把每个文件都视为文本,并在“检入”时从 CRLF 行尾转换为 LF 行尾(此描述措辞不当,实际上转换发生在 git add 步骤中)。Git 在检出期间什么都不会做(这也没有表述得完美:转换或者在这种情况下非转换发生在从索引到工作树的提取期间)。

在 Git 2.36.0 之后,描述如下:

此属性设置特定的行尾样式以在工作目录中使用。只有在文本属性设置或未设置或设置为 auto 的情况下,或者如果检测到文件为文本并存储在索引中具有 LF 结尾,则此属性才有效。
[其余说明已省略]。

这意味着现在对eol考虑了text。在您的情况下,您可以设置text或将其设置为auto。在第一种情况下,eol属性始终适用于匹配模式。在第二种情况下,它仅在 git 确定文件为文本文件时适用。

如果使用不同的模式,则会得到不同的结果

请注意,如果选择像*.txt这样的模式,则这些属性仅针对与模式匹配的路径设置。对于其他路径,这些属性保持未设置状态。因此,您应该回顾一下文档,看看当这些属性未设置时会发生什么。

当然,你可以这样做:

* -text
*.txt eol=lf

第一行会显式地在所有文件中 取消设置 text,使所有文件上的 eol 未指定。第二行然后为 *.txt 文件 设置 eol=lf 的值,覆盖未指定的值。现在 Git 将应用 eol=lf 规则到其文件名与 *.txt 匹配的所有文件上,并对其余文件使用未指定的 (eol取消设置 的) 文本规则。

这个特殊的 -text 语法是我上面删除的内容。使用 text=false 不会 取消设置 text,而是将 text 设置为字符串值 false。这与将 text 未指定(没有特别取消设置)具有相同的效果。使用 -text 给它一个特殊的 取消设置 的设置。

取消设置text未指定text 之间的区别在于当 text 未指定时,Git 可能会尝试根据 core.* 设置(如 core.autocrlf)猜测是否进行转换。但是,当 text 明确地被 取消设置 时,Git 将不会对该文件进行任何猜测或转换。


感谢您的详细解释。我可以从上面得出结论,在这个页面上:https://help.github.com/articles/dealing-with-line-endings/ "text eol=lf: Git将始终在检出时将行尾转换为LF"是错误的吗?它应该是“git在检出时不会执行任何转换”,还是它们实际上具有完全相同的含义? - Marinos An
是的:使用'eol=lf'设置时,'text'设置是无关紧要的,Git将在“输入”转换(大致上,在git add时间)上应用CRLF-to-LF规则,并且不会在“输出”转换(大致上,在git checkout时间)上进行任何换行符更改。请注意,一旦发生了任何输入侧转换(添加和提交),神圣的提交版本就只有LF结尾,因此“无转换”和“转换为LF结尾”将具有相同的效果。这使得很难检测是否没有发生转换 - 但源代码在这里表示“没有转换”。 - torek
这个解释非常完整和有帮助。我认为你的意思是“两行序列:\n * text=auto\n * text eol=lf”,而不是最后一个text eol设置为elf(除非我在这里漏掉了一些汇编程序)。 - bballdave025
@bballdave025:哎呀,是的,那应该是eol=lf,打错了!我会修复的。 - torek
我认为这个答案是错误的。 "eol" 用于检出/输出(工作目录),而不是检入/输入。请参阅最近的文档:https://github.com/bk2204/git/commit/a63d17f20642a7a27beaf9c044eb6fd8ac7a3000#diff-8c2ad6307733b92b3f5a6a70a3f3bbe1076c0fdf4da66c3633f90d15c6cf118a。 - Bruno Gomes
1
@BrunoGomes:这很混乱,而且版本依赖性很强,因为一个错误在Git 2.10(2016年9月)中得到了修复。我在撰写答案时使用的文档仍然涉及到2.10之前的行为。我会尽快更新这个答案,但是描述起来真的很困难,因为每五个Git发布版本就必须修复另一个错误。 :-) - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接