git core.autocrlf在不同操作系统之间如何处理换行符转换问题

287

我在 Stack Overflow 上读到了很多关于 git 文档中 core.autocrlf 设置如何工作的不同问题和答案。

这是我从阅读中得到的理解:

Unix 和 Mac OSX(pre-OSX 使用 CR)客户端使用 LF 行尾。
Windows 客户端使用 CRLF 行尾。

当客户端将 core.autocrlf 设置为 true 时,git 存储文件始终采用 LF 行尾格式,并且对于使用非 LF 行尾(如 Windows)的客户端,在检出 / 提交时,客户端中的文件行尾会来回转换,无论客户端中的行尾格式是什么(这与 Tim Clem 的定义不符 - 请参见下面的更新)。

以下是一个矩阵,尝试记录 core.autocrlf 的“input”和“false”设置的相同内容,并在不确定行尾转换行为时带有问号。

我的问题是:

  1. 问号应该填什么?
  2. 这个矩阵对于“非问号”是否正确?

随着共识形成,我将从答案中更新问号。

                       core.autocrlf value
            true            input              false
----------------------------------------------------------
commit   |  转换为 LF          ?                 ?
new      |  (转换为 LF?)    (不转换)     (不转换)
commit | 转换为 LF ? 不转换 existing | (转换为 LF?) 转换
checkout | 转换为 CRLF ? 不转换 existing | (不转换) 转换

我并不是真的在寻求关于各种设置的利弊的意见。我只是在寻找能够清晰说明git在每个设定下如何运作的数据。

--

更新于2012年04月17日:在评论中由JJD提供的Tim Clem的文章之后,我修改了上表中"unknown"值,并将"checkout existing | true"更改为转换为CRLF而不是转换为客户端。以下是他给出的定义,比我在其他地方看到的任何内容都更清晰:

core.autocrlf = false

这是默认设置,但大多数人被鼓励立即更改此设置。使用false的结果是Git永远不会处理文件的行尾。您可以检入带有LF或CRLF或CR或这三者的某些随机组合的文件,Git不会在意。这可能使差异更难以阅读,合并更加困难。大多数在Unix / Linux世界中工作的人使用此值,因为他们没有CRLF问题,也不需要Git在将文件写入对象数据库或写入工作目录时进行额外的工作。

core.autocrlf = true

这意味着Git将处理所有文本文件,并确保在将该文件写入对象数据库时将CRLF替换为LF,并在写入工作目录时将所有LF转换回CRLF。这是Windows上推荐的设置,因为它确保您的存储库可以在其他平台上使用,同时保留工作目录中的CRLF。

core.autocrlf = input

这意味着Git将处理所有文本文件,并确保在将该文件写入对象数据库时CRLF被替换为LF。但是,它不会执行相反的操作。当您从对象数据库中读取文件并将其写入工作目录时,它们仍将具有LF以表示行尾。通常在Unix/Linux/OS X上使用此设置,以防止将CRLFs写入存储库。其想法是,如果您从Web浏览器复制代码并意外地将CRLFs插入到其中一个文件中,Git将确保在写入对象数据库时将其替换为LF。
蒂姆的文章很好,我能想到唯一缺少的是他假设存储库采用LF格式,这不一定正确,特别是对于仅限Windows的项目。将蒂姆的文章与jmlane迄今为止最高投票的answer进行比较,可以在真实和输入设置方面达成完全一致,但在错误设置方面存在分歧。

10
保持autocrlf为false似乎更加简单 ;) https://dev59.com/unE95IYBdhLWcg3wY85l#2354278 - VonC
7
如果Windows也能将换行符规范化为LF,那不是很好吗?Mac在版本10之前使用CR作为换行符,但现在已经规范化为LF。 - Brett Ryan
5
我需要添加一个链接到Timothy Clem的优秀文章 - 请阅读Mind the End of Your Line的全部内容。 - JJD
2
场景:我是一名分裂的Linux/Windows开发人员。我只使用可以识别两种类型行尾(即vim,eclipse)的文本编辑器。我只需要(想要)使用以LF结尾的文件。我目前在我的全局git配置中设置了core.autocrlf=input。我可以开始工作了吗?我会遇到冲突吗? - Chris
1
它似乎也在这里:https://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/ - ojchase
显示剩余3条评论
8个回答

157
core.autocrlf 的最佳解释在 gitattributes man 页面的 text 属性部分中。以下是目前(或至少从我所知道的 v1.7.2 开始)core.autocrlf 的工作方式:
  • core.autocrlf = true
  1. 仓库中只包含 LF 字符的文本文件,在您的工作树中将规范化为 CRLF;包含 CRLF 的文件不受影响。
  2. 仓库中只包含 LF 字符的文本文件,在提交到仓库时,将从 CRLF 规范化为 LF。包含 CRLF 的文件将保持不变。
  • core.autocrlf = input
  1. 从仓库中检出的文本文件将保留原始的EOL字符。
  2. 在提交到仓库时,您的工作树中包含 CRLF 字符的文本文件将被规范化为 LF
  • core.autocrlf = false
  1. core.eol 决定您的工作树中文本文件的 EOL 字符。
  2. 默认情况下,core.eol = native,这意味着工作树 EOL 取决于 git 运行的位置:在 Windows 机器上为 CRLF,在 *nix 上为 LF
  • 源代码库中的gitattributes设置决定了提交到代码库中的行尾字符规范化方式(默认为将行尾字符规范化为LF字符)。
  • 我最近才研究了这个问题,我也觉得情况非常复杂。设置core.eol可以帮助澄清git如何处理行尾字符。


    4
    对于 autocrlf=true,以下内容是否正确?仓库中只包含 CRLF 行尾符的文本文件在提交到仓库时会被转换为 LF 行尾符。而仓库中已经包含 LF 行尾符的文件则不会改变。 - Piotr Lewandowski
    4
    对我来说,即使 autocrlf=false,git 仍然会将行尾转换为 CRLF。阅读了这个答案后,我意识到我的 .gitattribute 文件中设置了 text=auto,导致出现问题。 - irsis
    1
    如果core.autocrlf=false,而我没有gitattributes文件,这是否意味着不会进行标准化?还是说它将使用默认的标准化方式? - Chin
    2
    .gitattributes”文件不应该比“core.autocrlf”设置更具优先权吗? - Qwerty
    需要注意的是,在更改此设置后,建议执行 git rm --cached -r . 然后执行 git reset --hard 以重写工作树中的所有文件。这样做会丢失未提交的更改! - joki
    1
    @irsis说得对;正如@jmlane所述,当autocrlf=false时,“core.eol规定了工作树中text文件的EOL字符…”。实际上,即使是最不干涉的git配置,即autocrlf=input,仍然需要(再次感谢core.eol)“[将CRLF → LF]提交时…”,这意味着您不能简单地将新的CRLF文件添加到存储库中as-is。真正的罪魁祸首是首先为此滥用指定各种文件的东西…,即.gitattributes条目* text[=auto]。如果您希望git让您的文件保持原样,请从禁用它开始:* -text - Glenn Slayden

    80
    在混合平台项目中的EOL问题让我苦恼已久。问题通常出现在存储库中已经存在不同和混合EOL的文件时。这意味着:
    1. 存储库可能具有不同EOL的不同文件 2. 存储库中的某些文件可能具有混合的EOL,例如同一文件中的CRLF和LF组合。
    这里不是如何发生的问题,但它确实会发生。
    我在Windows上对各种模式及其组合进行了一些转换测试。以下是我得到的、稍加修改的表格: | 文件 | 提交文件时的转换结果 | 从存储库检出时的转换结果 | | -------------- | --------------------------------- | --------------------------- | | Windows-CRLF | CRLF -> LF | 不变 | | Unix-LF | 不变 | LF -> CRLF | | Mac-CR | 不变 | 不变 | | Mixed-CRLF+LF | 不变 | 不变 | | Mixed-CRLF+LF+CR | 不变 | 不变 |
    可以看出,在提交时有2种情况会发生转换(左3列)。在其他情况下,文件将按原样提交。
    在检出时(右3列),只有1种情况会发生转换:
    1. core.autocrlf值为true并且
  • 仓库中的文件采用 LF 行尾格式。
  • 对我来说最令人惊讶的是,也是导致许多行尾格式问题的原因,就是没有任何配置可以使混合使用 CRLF+LF 的行尾格式得到规范化。

    请注意,“旧”的 Mac 行尾格式只有 CR,也永远不会被转换。
    这意味着,如果一个糟糕的行尾格式转换脚本尝试将具有混合结尾的文件(包括 CRLFLF)进行转换,只将 LF 转换为 CRLF,那么它会在任何一个 CRLF 被转换为 CRCRLF 的地方留下“孤立”的 CR
    Git 将不会转换任何内容,即使在 true 模式下,行尾格式的混乱仍然会持续下去。这实际上发生在我身上,并且严重破坏了我的文件,因为一些编辑器和编译器(如 VS2010)不喜欢 Mac 行尾格式。

    我想,真正处理这些问题的唯一方法是,偶尔通过以 inputfalse 模式检出所有文件,运行适当的规范化,并重新提交已更改的文件(如果有)。在 Windows 上,可能需要使用 core.autocrlf true 继续工作。


    6
    回答很好,但我不同意其中一句话——“在Windows上,可能要恢复使用‘core.autocrlf true’”。我个人认为应该始终使用input - G. Demecki

    79

    core.autocrlf的值与操作系统无关,但在Windows上默认为true,在Linux上为input。我探索了三种可能的值用于checkout和commit情况。

    以下是结果表格:

    ╔═══════════════╦══════════════╦══════════════╦══════════════╗
    ║ core.autocrlf ║     false    ║     input    ║     true     ║
    ╠═══════════════╬══════════════╬══════════════╬══════════════╣
    ║               ║ LF   => LF   ║ LF   => LF   ║ LF   => CRLF ║
    ║ git checkout  ║ CR   => CR   ║ CR   => CR   ║ CR   => CR   ║
    ║               ║ CRLF => CRLF ║ CRLF => CRLF ║ CRLF => CRLF ║
    ╠═══════════════╬══════════════╬══════════════╬══════════════╣
    ║               ║ LF   => LF   ║ LF   => LF   ║ LF   => LF   ║
    ║ git commit    ║ CR   => CR   ║ CR   => CR   ║ CR   => CR   ║
    ║               ║ CRLF => CRLF ║ CRLF => LF   ║ CRLF => LF   ║
    ╚═══════════════╩══════════════╩══════════════╩══════════════╝
    

    15
    简短概述:只有包含CR的文件不会被更改。false选项不会更改行尾标识。true选项总是作为LF提交并检出为CRLF。而input选项则总是作为LF提交并以原样检出。 - Furkan Kambay
    @pratt,这确实取决于操作系统->当autocrlf=falseeol=native时。 - go2null

    41

    "eol conversion" 领域即将发生变化,这将在即将发布的 Git 1.7.2 版本中得以体现:

    一个新的配置设置core.eol正在被添加/演进

    This is a replacement for the 'Add "core.eol" config variable' commit that's currently in pu (the last one in my series).
    Instead of implying that "core.autocrlf=true" is a replacement for "* text=auto", it makes explicit the fact that autocrlf is only for users who want to work with CRLFs in their working directory on a repository that doesn't have text file normalization.
    When it is enabled, "core.eol" is ignored.

    Introduce a new configuration variable, "core.eol", that allows the user to set which line endings to use for end-of-line-normalized files in the working directory.
    It defaults to "native", which means CRLF on Windows and LF everywhere else. Note that "core.autocrlf" overrides core.eol.
    This means that:

    [core]
      autocrlf = true
    

    puts CRLFs in the working directory even if core.eol is set to "lf".

    core.eol:
    

    Sets the line ending type to use in the working directory for files that have the text property set.
    Alternatives are 'lf', 'crlf' and 'native', which uses the platform's native line ending.
    The default value is native.


    还有其他的进化正在考虑:

    对于1.8版本,我会考虑让core.autocrlf只开启规范化,并将工作目录的行结尾决策留给core.eol,但这将会破坏人们的设置。


    git 2.8(2016年3月)改进了core.autocrlf对行尾符的影响方式:

    请参阅 commit 817a0c7(2016年2月23日),commit 6e336a5commit df747b8commit df747b8(2016年2月10日),commit df747b8commit df747b8(2016年2月10日),commit 4b4024fcommit bb211b4commit 92cce13commit 320d39ccommit 4b4024fcommit bb211b4commit 92cce13commit 320d39c(2016年2月5日),由Torsten Bögershausen (tboegi)提交。
    (由Junio C Hamano -- gitster --合并于commit c6b94eb,2016年2月26日)

    convert.c:重构crlf_action

    重构确定和使用crlf_action的过程。
    今天,当文件上没有设置"crlf"属性时,crlf_action被设置为CRLF_GUESS。改用CRLF_UNDEFINED,并像以前一样搜索"text"或"eol"。

    替换旧的CRLF_GUESS用法:

    CRLF_GUESS && core.autocrlf=true -> CRLF_AUTO_CRLF
    CRLF_GUESS && core.autocrlf=false -> CRLF_BINARY
    CRLF_GUESS && core.autocrlf=input -> CRLF_AUTO_INPUT
    

    通过定义来使事物更加清晰明确:
    - CRLF_UNDEFINED : No attributes set. Temparally used, until core.autocrlf
                       and core.eol is evaluated and one of CRLF_BINARY,
                       CRLF_AUTO_INPUT or CRLF_AUTO_CRLF is selected
    - CRLF_BINARY    : No processing of line endings.
    - CRLF_TEXT      : attribute "text" is set, line endings are processed.
    - CRLF_TEXT_INPUT: attribute "input" or "eol=lf" is set. This implies text.
    - CRLF_TEXT_CRLF : attribute "eol=crlf" is set. This implies text.
    - CRLF_AUTO      : attribute "auto" is set.
    - CRLF_AUTO_INPUT: core.autocrlf=input (no attributes)
    - CRLF_AUTO_CRLF : core.autocrlf=true  (no attributes)
    

    torek添加在评论中时:

    所有这些翻译(从eol=autocrlf设置进行的任何EOL转换和“clean”过滤器)都是在文件从工作树移动到索引时运行的,即在git add而不是git commit时间。
    (请注意,git commit -a--only--include会在此时将文件添加到索引中。)

    有关更多信息,请参见“autocrlf和eol之间的区别”。


    24
    遗憾的是,这对我来说并没有增加清晰度。似乎他们在说当前实现存在问题(不清楚是什么问题),并且为了解决这些未指明的问题而增加了复杂性。在我看来,core.autocrlf设置已经过于复杂且文档不足,而这种情况似乎正在变得更糟。再次感谢您提供信息。 - Michael Maddox
    2
    这似乎不是一个令人满意的解决方案,并且似乎有与core.autocrlf相同的问题。我的偏好是,如果git从不自动修改任何内容,但会警告想要添加或提交错误行尾的用户。因此,您需要一个命令行选项来允许“git add”添加“错误”的行尾。(可能检查这一点的最佳位置是git add,而不是git commit) - donquixote
    啊,好的。所以如果我理解正确的话,如果.gitattributes是空的,core.autocrlf会假定一些默认文件类型始终进行转换,而core.eol则不会做任何假设。但是一旦你创建了.gitattributes,那么core.eol和core.autocrlf就会表现得相同? - donquixote
    是的,你同意了,但我仍然想在这里说一下,因为这似乎比在其他问题中说更相关。 - donquixote
    2
    @donquixote:我知道这已经很老了,但我现在才看到你的评论。实际上,所有这些翻译(任何EOL转换从eol=或autocrlf设置,“clean”过滤器)都是在文件从工作树移动到索引时运行的,即在git add而不是在git commit时间。(请注意,git commit -a--only--include会在那时将文件添加到索引中。)值得一提的是,你、我和Linus Torvalds都讨厌VCS *修改正在提交的内容的想法。但是有所有那些Windows用户... :-) - torek
    显示剩余10条评论

    10

    这是我对它的理解,希望能帮到某些人。

    core.autocrlf=truecore.safecrlf=true

    你有一个存储库,其中所有行结尾都相同,但你在不同的平台上工作。Git 会确保将你的行结尾转换为你平台的默认值。这很重要吗?假设你创建了一个新文件。你的平台文本编辑器将使用其默认的行结尾。当你提交时,如果没有设置核心自动crlf 为真,则为默认采用其他行结尾的平台引入行结尾的不一致性。 我总是设置safecrlf,因为我想知道crlf操作是可逆的。有了这两个设置,git 在修改你的文件,但它验证修改是可逆的。

    core.autocrlf=false

    你有一个已经混合了行结尾的存储库,修复不正确的行结尾可能会破坏其他东西。在这种情况下最好不要告诉 Git 转换行结尾,因为这会使它设计解决的问题更加严重 - 使差异更容易阅读,并减少合并的痛苦。“这个设置下,git 不会修改你的文件

    core.autocrlf=input

    我不使用这个设置,因为它的用途是覆盖一个使用情况:在默认为 LF 行结尾的平台上创建了一个具有 CRLF 行结尾的文件。相反,我更喜欢让我的文本编辑器始终以该平台的行结尾默认值保存新文件。


    6
    不,@jmlane的答案是错误的。
    对于Checkin(git add, git commit):
    1. 如果text属性被设置为“Set, Set value to 'auto'”,则即使文件已经使用“CRLF”提交,转换也会发生。 2. 如果text属性被设置为“Unset”,则不会发生任何事情,包括Checkout。 3. 如果text属性被设置为“Unspecified”,则转换取决于core.autocrlf的值。
    - 如果autocrlf = input或autocrlf = true,则只有当存储库中的文件是'LF'时才会进行转换,如果它是'CRLF',则不会发生任何事情。 - 如果autocrlf = false,则不会发生任何事情。
    对于Checkout:
    1. 如果text属性被设置为“Unset”,则不会发生任何事情。 2. 如果text属性被设置为“Set, Set value to 'auto'”,则取决于core.autocrlf和core.eol的值。
    - 如果core.autocrlf = input,则不会发生任何事情。 - 如果core.autocrlf = true,则只有当存储库中的文件是'LF'时才会进行转换,从'LF'转到'CRLF'。 - 如果core.autocrlf = false,则只有当存储库中的文件是'LF'时才会进行转换,从'LF'转到core.eol。
    3. 如果text属性被设置为“Unspecified”,则取决于core.autocrlf的值。
    - 与2.1相同。 - 与2.2相同。 - 当text属性为“Unspecified”时,core.eol无效,不会发生任何转换。
    默认行为:
    所以默认行为是text属性为“Unspecified”,core.autocrlf为“false”:
    1. 对于checkin,不会发生任何事情。 2. 对于checkout,不会发生任何事情。
    结论:
    1. 如果text属性被设置,checkin行为取决于它本身,而不是autocrlf。 2. autocrlf或core.eol是checkout行为的因素,其中autocrlf>core.eol。

    3

    我在Linux和Windows上进行了一些测试。我使用了一个包含以LF和CRLF结尾的行的测试文件。
    文件已提交,删除后再次检出。 在提交之前和检出之前设置了core.autocrlf的值。 结果如下。

    commit core.autocrlf false, remove, checkout core.autocrlf false: LF=>LF   CRLF=>CRLF  
    commit core.autocrlf false, remove, checkout core.autocrlf input: LF=>LF   CRLF=>CRLF  
    commit core.autocrlf false, remove, checkout core.autocrlf true : LF=>LF   CRLF=>CRLF  
    commit core.autocrlf input, remove, checkout core.autocrlf false: LF=>LF   CRLF=>LF  
    commit core.autocrlf input, remove, checkout core.autocrlf input: LF=>LF   CRLF=>LF  
    commit core.autocrlf input, remove, checkout core.autocrlf true : LF=>CRLF CRLF=>CRLF  
    commit core.autocrlf true, remove, checkout core.autocrlf false: LF=>LF   CRLF=>LF  
    commit core.autocrlf true, remove, checkout core.autocrlf input: LF=>LF   CRLF=>LF  
    commit core.autocrlf true,  remove, checkout core.autocrlf true : LF=>CRLF CRLF=>CRLF  
    

    1
    声明core.autocrlf=true会导致提交时的 CRLF -> LF 是错误的!实际情况并不是那么简单,正如您将看到的...文档docs说该设置对应于“在.gitattributes中设置text=auto和在git config中设置core.eol=crlf”,这意味着什么?这意味着,如果一个文件没有设置.gitattributes text属性,并且core.autocrlftrue,那么现在取决于您提交的文件是新文件(在这种情况下,是的,它将在git存储库数据库中被规范化为LF)还是您编辑并现在提交的现有文件(在这种情况下,什么也不会发生...除非您运行git add --renormalize .在这种情况下,它将在git存储库数据库中规范化)。
    你会看到...整个机制仅适用于没有放置文本属性变量的.gitattributes文件: text, -text, text=auto
    因此,你真正需要关注的是在所有文件上使用具有默认设置的.gitattributes,可以是以下任一设置:
    * -text
    # followed by specialization
    

    这将将所有内容(除了特定的专业术语)默认为“as-is”,并完全覆盖“core.autocrlf”,或使用以下默认设置:

    *  text=auto
    # followed by specialization
    

    这意味着所有被git自动检测为非二进制(文本)的文件(除了专业化文件),且在git数据库中有[参见注1]LF的文件,将会在以下情况下获得CRLF
        • core.autocrlftrue,或
        • core.eolcrlf,或
        • core.eolnative(默认)且你正在使用Windows平台。
    在其他情况下,你将获得LF
    我所说的专业化文件是什么?例如,通过以下方式使.bat文件成为CRLF,而.sh文件成为LF
    *.sh           text eol=lf
    
    # *.bat
    *.[bB][aA][tT] text eol=crlf
    

    或者

    # *.sh are committed correctly as-is (LF)
    *.sh           -text
    
    # *.bat are committed correctly as-is (CRLF)
    *.[bB][aA][tT] -text
    

    所以,是的...这一切都不是那么简单。


    [note 1]:
    这将适用于所有匹配text=auto属性的文件(即没有其他专业化),因为我假设您的存储库在创建.gitattribute时已经被正确规范化。

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