ChatGPT解决这个技术问题 Extra ChatGPT

用 Git 处理 CRLF(回车、换行)的策略是什么?

我尝试使用以 CRLF 结尾的行提交文件,但失败了。

我花了一整天的时间在我的 Windows 计算机上尝试不同的策略,几乎被吸引停止尝试使用 Git 而是尝试 Mercurial

如何正确处理 CRLF 行尾?


r
randers

问了这个问题快四年了,我终于找到了一个让我完全满意的答案!

请参阅 github:helpDealing with line endings 指南中的详细信息。

Git 允许您直接使用 .gitattributes 文件中的 text 属性为 repo 设置行结束属性。该文件被提交到 repo 并覆盖 core.autocrlf 设置,允许您确保所有用户的行为一致,而不管他们的 git 设置如何。

因此

这样做的好处是您的终端配置现在随您的存储库一起移动,您无需担心协作者是否具有正确的全局设置。

这是 .gitattributes 文件的示例

# Auto detect text files and perform LF normalization
*        text=auto

*.cs     text diff=csharp
*.java   text diff=java
*.html   text diff=html
*.css    text
*.js     text
*.sql    text

*.csproj text merge=union
*.sln    text merge=union eol=crlf

*.docx   diff=astextplain
*.DOCX   diff=astextplain

# absolute paths are ok, as are globs
/**/postinst* text eol=lf

# paths that don't start with / are treated relative to the .gitattributes folder
relative/path/*.txt text eol=lf

对于最流行的编程语言,有一个方便的 collection of ready to use .gitattributes files。帮助您入门很有用。

创建或调整 .gitattributes 后,您应该执行一次性的 line endings re-normalization

请注意,在您在应用中打开项目的 Git 存储库后,GitHub Desktop 应用可以建议并创建一个 .gitattributes 文件。要尝试这样做,请单击齿轮图标(在右上角)>存储库设置...>行尾和属性。系统会要求您添加推荐的 .gitattributes,如果您同意,该应用程序还将对您的存储库中的所有文件执行规范化。

最后,Mind the End of Your Line 文章提供了更多背景知识,并解释了 Git 如何在手头的问题上发展。我认为这是必读

您的团队中可能有用户使用 EGit 或 JGit(Eclipse 和 TeamCity 等工具使用它们)来提交他们的更改。然后你不走运,正如@gatinueta 在这个答案的评论中解释的那样:

如果您的团队中有使用 Egit 或 JGit 的人,此设置将无法完全满足您,因为这些工具只会忽略 .gitattributes 并愉快地签入 CRLF 文件 https://bugs.eclipse.org/bugs/show_bug.cgi?编号=342372

一个技巧可能是让他们在另一个客户端中提交更改,例如 SourceTree。那时,我们的团队在许多用例中更喜欢使用该工具而不是 Eclipse 的 EGit。

谁说软件很简单? :-/


愿意共享 Windows .gitattributes
如果您的团队中有使用 Egit 的人员,则此设置不会完全满足您的要求,因为 egit 只会忽略 .gitattributes 并愉快地签入 CRLF 文件bugs.eclipse.org/bugs/show_bug.cgi?id=342372
对于 Windows,我通常倾向于设置全局 core.autocrlf = false - 我更喜欢任何地方的 LF,但一些 Windows 工具(如 Visual Studio)坚持在某些文件中使用 CRLF 结尾(甚至将它们混合在一些文件中......);不修改行尾是最安全的选择。如果您知道自己在做什么,我可能会使用 core.autocrlf = input 并为您知道对行尾敏感的 Windows 项目设置例外。正如其他人指出的那样,现在每个体面的文本编辑器都支持 LF 结尾。我实际上认为 core.autocrlf = true 可能会造成比它所阻止的更多的麻烦。
@gatinueta 更具体地说,这是一个 JGit 问题。这意味着同样使用 JGit 的 TeamCity 直接忽略了 .gitattributes。
我还建议使用 *.sh text eol=lf
J
John Millikin

不要转换行尾。解释数据不是 VCS 的工作——只是存储和版本化它。无论如何,每个现代文本编辑器都可以读取这两种行尾。


借调。如果您遇到行尾不一致的问题,最好的解决方案是对使用错误编辑器设置的人大喊大叫,直到他们修复它。
不同意。所有平台上的本机换行都很方便。
当涉及到 CRLF 以外的任何内容时,Visual Studio 就是一个 PITA。
Git 有一个不转换行尾的选项,它是 autocrlf=false 并且除非你正在做跨平台开发,比如 Mono,在 Windows 下运行时最好将其设置为 false,如果你将开发开源代码,则设置为 true对于单声道。
行尾的问题是计算正确的差异。所以答案是错误的和误导性的。
C
Cory

除非您真的知道自己在做什么,否则您几乎总是想要 autocrlf=input

下面的一些附加上下文:

如果你喜欢 DOS 结尾,它应该是 core.autocrlf=true,如果你喜欢 unix-newlines,它应该是 core.autocrlf=input。在这两种情况下,您的 Git 存储库都将只有 LF,这是正确的。 core.autocrlf=false 的唯一参数是自动启发式可能会错误地将某些二进制文件检测为文本,然后您的图块将被损坏。因此,引入了 core.safecrlf 选项以在发生不可逆转的更改时警告用户。事实上,不可逆转的更改有两种可能性——文本文件中的混合行尾,在这种规范化中是可取的,所以可以忽略这个警告,或者(非常不可能)Git 错误地将你的二进制文件检测为文本。然后你需要使用属性告诉Git这个文件是二进制的。

上述段落最初是从 gmane.org 上的一个线程中提取的,但此后它已被删除。


为什么它是“正确的事情”?
core.autocrlf=true 是一个糟糕的主意。我对这个选项没有任何问题,而且你必须记住在克隆存储库时设置它。
除非您知道自己在做什么,否则不要使用 autocrlf=true。如果您在 DOS/Win 中开发,那么 autocrlf=false 将使远程和本地 repo 的结尾保持相同,并且几乎在所有情况下都是最佳选择。
@Chris - 如果您的开发人员有一些多平台开发人员在 OSX 或 Linux 上工作的 Windows 和多平台项目怎么办?那么最好的选择不应该是 autocrlf=true 吗?
赞成,有保留。介绍性段落没有帮助。 core.autocrlf=input 是规范答案。对于大多数用例,core.autocrlf=truecore.autocrlf=false 过于热情(……当然,以相反但同样可怕的方式),因此具有内在的破坏性。 “Git for Windows”应该真的附带“按原样结帐,提交 Unix 样式的行尾”(即 core.autocrlf=input)作为其默认换行策略。它没有。所以我们在这里——in frickin' 2015——仍在无休止地争论这个问题。
M
Michael

在混合环境(Microsoft + Linux + Mac)中获得一致的行尾的两种替代策略:

A. 全局所有存储库设置

将所有转换为一种格式 find . -type f -not -path "./.git/*" -exec dos2unix {} \; git commit -a -m 'dos2unix conversion' 将 core.autocrlf 设置为 Linux/UNIX 上的输入或 MS Windows(存储库或全局)上的 true git config --global core.autocrlf input 可选地,将 core.safecrlf 设置为 true(停止) 或警告 (唱:) 添加额外的保护比较反向换行符转换是否会导致相同的文件 git config --global core.safecrlf true

B. 或按存储库设置

将所有转换为一种格式 find . -type f -not -path "./.git/*" -exec dos2unix {} \; git commit -a -m 'dos2unix conversion' 将 .gitattributes 文件添加到您的存储库 echo "* text=auto" > .gitattributes git add .gitattributes git commit -m 'adding .gitattributes for统一行尾'

不要担心你的二进制文件——Git 应该对它们足够聪明。

More about safecrlf/autocrlf variables


全局方法 == 为所有存储库设置并忘记每个存储库 == 不需要其他人更改其全局配置。
dos2unix 是一个命令行工具,取决于您可能需要额外安装的系统
它们不是排他的,您可以同时使用这两种方法。此外,使用 dos2unix 时要非常小心 - 存在 corrupting .git/index 的风险,我们不需要将其应用于每个文件。最好使用 find ./ -name "*.html" 之类的内容并指定要将其应用于哪些文件。
警告:在运行 find 行之前,请注意:Windows 版 Git 附带的 dos2unix 有一个特殊的(IMO 愚蠢和危险的)行为,没有参数:它不是更改为 UNIX,而是 切换 换行格式 (DOS <-> UNIX)
还有另一个警告:不要 DOS2UNIX 你的 .git 文件夹。只是说。
M
Marinos An

--- 更新 3 ---(与更新 2 不冲突)

考虑到 Windows 用户更喜欢在 CRLF 上工作而 linux/mac 用户更喜欢在 LF 上处理文本文件的情况。从存储库维护者的角度提供答案:

对我而言,最佳策略(要解决的问题更少)是:将 所有文本文件LF 保存在 git repo 中,即使您正在工作在仅限 Windows 的项目上。然后让客户自由处理他们喜欢的行尾样式,前提是他们选择的core.autocrlf属性值将尊重您的策略(LF on repo) 暂存文件以进行提交。

分期是许多人在尝试了解换行策略如何工作时所混淆的。在为 core.autocrlf 属性选择正确值之前,必须了解以下几点:

添加文本文件以进行提交(暂存)就像将文件复制到 .git/ 子目录中的另一个位置,并转换了行尾(取决于客户端配置上的 core.autocrlf 值)。所有这些都是在本地完成的。

设置 core.autocrlf 就像提供问题的答案(在所有操作系统上完全相同的问题):“应该 git-client:a. 在从远程签出(拉出)repo 更改时将 LF 转换为 CRLF?b . 为提交添加文件时将 CRLF 转换为 LF?”

一个。从远程签出(拉出)回购更改时将LF转换为CRLF?

湾。添加文件以提交时将 CRLF 转换为 LF?”

可能的答案(值)是:假:“以上都不做”,输入:“只做b”真:“做a和b”注意没有“只做a”

false: "以上都不做",

输入:“只做b”

真:“做 a 和 b”

请注意,没有“只做一个”

幸运的是

git 客户端默认值 (windows: core.autocrlf: true, linux/mac: core.autocrlf: false) 将与 LF-only-repo 策略兼容。含义:默认情况下,Windows 客户端在签出存储库时将转换为 CRLF,并在添加提交时转换为 LF。默认情况下,Linux 客户端不会进行任何转换。从理论上讲,这使您的 repo 仅保留。

很遗憾:

可能有不尊重 git core.autocrlf 值的 GUI 客户端

可能有些人不使用价值来尊重您的 lf-repo 策略。例如,他们使用 core.autocrlf=false 并添加一个带有 CRLF 的文件以进行提交。

要尽快检测上述客户端提交的非 lf 文本文件,您可以按照 --- update 2 ---: (git grep -I --files-with-matches --perl-regexp '\r' HEAD, on a client compiled using: --with-libpcre旗帜)

这是关键:。我作为 repo 维护者保留一个 git.autocrlf=input 以便我可以通过再次添加它们来修复任何错误提交的文件以进行提交。我提供了一个提交文本:“修复错误提交的文件”。

.gitattributes 而言。我不指望它,因为有更多的ui客户端不理解它。我只用它来为文本和二进制文件提供提示,并可能标记一些应该在任何地方都保持相同行尾的特殊文件:

*.java          text !eol # Don't do auto-detection. Treat as text (don't set any eol rule. use client's)
*.jpg           -text     # Don't do auto-detection. Treat as binary
*.sh            text eol=lf # Don't do auto-detection. Treat as text. Checkout and add with eol=lf
*.bat           text eol=crlf # Treat as text. Checkout and add with eol=crlf

问题:但是为什么我们对换行处理策略感兴趣呢?

回答:为避免单个字母更改提交,显示为 5000 行更改,因为执行更改的客户端在添加提交之前将完整文件从 crlf 自动转换为 lf(或相反)。当涉及解决冲突时,这可能会相当痛苦。或者在某些情况下它可能是不合理冲突的原因。

--- 更新 2 ---

git 客户端的默认设置在大多数情况下都会起作用。即使您只有 Windows 客户端、Linux 客户端或两者都有。这些是:

windows: core.autocrlf=true 表示结帐时将行转换为 CRLF,添加文件时将行转换为 LF。

linux: core.autocrlf=input 表示不要在结帐时转换行(不需要,因为文件预计会使用 LF 提交)并在添加文件时将行转换为 LF(如果需要)。 (-- update3 -- : 似乎默认情况下这是错误的,但又没问题)

该属性可以在不同的范围内设置。我建议在 --global 范围内明确设置,以避免最后描述的一些 IDE 问题。

git config core.autocrlf
git config --global core.autocrlf
git config --system core.autocrlf
git config --local core.autocrlf
git config --show-origin core.autocrlf

此外,我强烈不鼓励使用 在 Windows 上 git config --global core.autocrlf false(如果您只有 Windows 客户端)与建议的内容相反 {1 }。设置为 false 将在 repo 中提交带有 CRLF 的文件。但是真的没有理由。您永远不知道是否需要与 linux 用户共享项目。另外,对于每个加入项目而不是使用默认值的客户来说,这是一个额外的步骤。

现在对于某些特殊情况的文件(例如 *.bat *.sh),您希望它们使用 LF 或 CRLF 签出,您可以使用 .gitattributes

总结一下我的最佳实践是:

确保在 git repo 上使用 LF 提交每个非二进制文件(默认行为)。

使用此命令确保没有使用 CRLF 提交任何文件: git grep -I --files-with-matches --perl-regexp '\r' HEAD (注意:在 Windows 客户端上只能通过 git-bash 和在 linux 上工作客户端仅在使用 ./configure 中的 --with-libpcre 编译时)。

如果您通过执行上述命令找到任何此类文件,请更正它们。这涉及(至少在 linux 上): set core.autocrlf=input (--- update 3 --) 更改文件 恢复更改(文件仍显示为已更改)提交它

设置 core.autocrlf=input (--- 更新 3 --)

更改文件

还原更改(文件仍显示为已更改)

提交它

仅使用最低限度的 .gitattributes

指示用户将上述 core.autocrlf 设置为其默认值。

不要指望 .gitattributes 的存在 100%。 IDE 的 git-clients 可能会忽略它们或区别对待它们。

如前所述,可以在 git 属性中添加一些内容:

# Always checkout with LF
*.sh            text eol=lf
# Always checkout with CRLF
*.bat           text eol=crlf

我认为 .gitattributes 有一些其他安全选项,而不是对二进制文件使用自动检测:

-text(例如,对于 *.zip 或 *.jpg 文件:不会被视为文本。因此不会尝试行尾转换。通过转换程序可能会出现差异)

text !eol(例如,对于 *.java、*.html:视为文本,但未设置 eol 样式首选项。因此使用客户端设置。)

-text -diff -merge(例如,对于 *.hugefile:不被视为文本。没有 diff/merge 可能)

--- 以前的更新 ---

错误提交文件的客户端的一个痛苦示例:

netbeans 8.2(在 Windows 上)会错误地提交所有带有 CRLF 的文本文件,除非明确core.autocrlf 设置为全局。这与标准的 git 客户端行为相矛盾,并在以后更新/合并时导致很多问题。这就是使某些文件看起来不同(尽管它们不是)即使您还原的原因。
即使您添加了正确的 {2 } 到你的项目。

在提交后使用以下命令,至少可以帮助您及早检测您的 git repo 是否存在行尾问题:git grep -I --files-with-matches --perl-regexp '\r' HEAD

我花了好几个小时想出最好的 .gitattributes 用法,最终意识到,我不能指望它。
不幸的是,只要存在基于 JGit 的编辑器(无法正确处理 .gitattributes),安全的解决方案就是在任何地方强制使用 LF,即使在编辑器级别也是如此。

使用以下抗 CRLF 消毒剂。

windows/linux 客户端:core.autocrlf=input

提交的 .gitattributes: * text=auto eol=lf

提交的 .editorconfig (http://editorconfig.org/) 这是一种标准化格式,结合编辑器插件:https://github.com/editorconfig/ https://github.com/welovecoding/editorconfig-netbeans/

https://github.com/editorconfig/

https://github.com/welovecoding/editorconfig-netbeans/


我同意你的观点,这是最好的方法,没有人应该使用没有 LF 支持的编辑器。但是要小心你的 .gitattributes 行,它在 Git < 中有意想不到的后果。 2.10,见stackoverflow.com/a/29508751/2261442
该死...我有很多我提倡 git config --global core.autocrlf false 的答案,并建议仅在 .gitattributes 指令中处理 eol。
P
Peter Mortensen

一旦我在我的 Visual Studio 2010 项目中签出所有文件,使用 core.autocrlf=false 就不会将它们标记为已更新。开发团队的另外两名成员也使用 Windows 系统,因此混合环境没有发挥作用,但存储库附带的默认设置始终将所有文件标记为克隆后立即更新。

我想底线是找到适合您环境的 CRLF 设置。特别是因为在我们的 Linux 机器上的许多其他存储库中,设置 autocrlf = true 会产生更好的结果。

20 多年过去了,我们仍在处理操作系统之间的行尾差异……可悲。


@orange80,这种差异是不幸的,但没有理由称其为 Windows 的错。也许从极简主义的角度来看,LF-only 是有意义的;但是根据 CR 和 LF 的含义,CRLF 更有意义。 “回车”是指回到行首; “换行”意味着直接向下移动到下一行,而不是到下一行的开头。从语义的角度来看,Windows 更正确的是两者兼有:回到开头 (CR),然后向下移动一行 (LF)。
@Kyralessa“更正确”仍然假装计算机是打字机,顺便说一句,事实并非如此。考虑到这不是最终用户将要处理的事情,并且两个字符而不是一个字符是毫无意义的,因此保持打字机的类比没有任何意义。
晚了几年,但你忽略了 CR 和 LF 是光标定位工具的事实。在历史的这一点上,“CR”也可能是“光标返回”。如果我希望光标返回到行首,我会告诉应用程序这样做。否则,它需要留在我放置的地方。
此外,如果 CRLF 是“更正确的”,因为文本文件换行符实际上既是“下移一行”又是“移到行首”,那么只有 CR 会导致文本编辑器用下一行。我知道没有编辑器真正支持这一点,这意味着不需要将 CRLF 和 CR 表达为不同的东西,实际上并不存在。
@avl_sweden 在 DOS 之前这是非常常见的行为,并且由于微软认为兼容性很重要,所以从那以后它就一直保持这种状态。这也是美国的标准方式(as pere ASA)——ISO 允许 CR+LF 和 LF(同样,DOS 符合标准);在这两种情况下,自六十年代以来。 Multics(Unix前身)支持CR粗体/罢工。现在的许多应用程序(包括 .NET 的“按行分割”功能)寻找三个(单独的 CR、单独的 LF、CRLF)中的任何一个,并将它们中的每一个都视为结束行。但是,许多应用程序仍然对文件中的混合行尾感到困惑。
G
Greg Hewgill

尝试将 core.autocrlf 配置选项设置为 true。另请查看 core.safecrlf 选项。

实际上听起来 core.safecrlf 可能已经在您的存储库中设置,因为(强调我的):

如果 core.autocrlf 的当前设置不是这种情况,git 将拒绝该文件。

如果是这种情况,那么您可能需要检查您的文本编辑器是否配置为一致地使用行尾。如果文本文件包含 LF 和 CRLF 行尾的混合,您可能会遇到问题。

最后,我觉得在 Windows 上简单地“使用你给定的”和使用 LF 终止行的建议会导致比它解决的问题更多的问题。 Git 有上述选项可以尝试以合理的方式处理行尾,因此使用它们是有意义的。


通过 .gitattributes 文件使用存储库范围的设置不是更好吗?只是想知道:强迫每个用户在他的机器上照顾他的线路结束设置是不方便的......或者还有其他缺点吗?
k
kiewic

这是 WindowsVisual Studio 用户与 MacLinux 用户共享代码的两个选项。有关详细说明,请阅读 gitattributes manual

* 文字=自动

在您的存储库的 .gitattributes 文件中添加:

*   text=auto

这将规范化 repo 中所有以 LF 行结尾的文件。

并且根据您的操作系统(core.eol 设置),工作树中的文件将被标准化为 LF(对于基于 Unix 的系统)或 CRLF(对于 Windows 系统)。

这是 Microsoft .NET 存储库使用的配置。

例子:

Hello\r\nWorld

将在 repo 中标准化为:

Hello\nWorld

结帐时,Windows 中的工作树将转换为:

Hello\r\nWorld

在结帐时,Mac 中的工作树将保留为:

Hello\nWorld

注意:如果您的 repo 已经包含未规范化的文件,git status 将在您下次对它们进行任何更改时将这些文件显示为完全修改,并且其他用户稍后合并他们的更改可能会很痛苦。有关更多信息,请参阅更改行尾后刷新存储库。

core.autocrlf = true

如果 .gitattributes 文件中未指定 text,Git 将使用 core.autocrlf 配置变量来确定是否应转换文件。

对于 Windows 用户,git config --global core.autocrlf true 是一个不错的选择,因为:

只有在添加到 repo 时,文件才会被规范化为 LF 行结尾。如果 repo 中有未规范化的文件,此设置将不会触及它们。

所有文本文件都转换为工作目录中的 CRLF 行尾。

这种方法的问题在于:

如果您是使用 autocrlf = 输入的 Windows 用户,您将看到一堆以 LF 行结尾的文件。对团队的其他成员来说不是危险,因为您的提交仍将使用 LF 行结尾进行规范化。

如果您是 core.autocrlf = false 的 Windows 用户,您将看到一堆以 LF 行结尾的文件,您可以将带有 CRLF 行结尾的文件引入 repo。

大多数 Mac 用户使用 autocrlf = 输入,并且可能会从带有 core.autocrlf = false 的 Windows 用户那里获得带有 CRLF 文件结尾的文件。


您对 Windows 用户的命令是 git config --global core.autocrl true。你的意思是git config --global core.autocrlf true
M
Michael

这只是一个解决方法:

在正常情况下,使用 git 附带的解决方案。这些在大多数情况下都很好用。如果您通过设置 .gitattributes 在基于 Windows 和 Unix 的系统上共享开发,则强制使用 LF。

在我的例子中,有超过 10 个程序员在 Windows 中开发一个项目。这个项目是用 CRLF 签入的,没有强制到 LF 的选项。

一些设置是在我的机器内部编写的,对 LF 格式没有任何影响;因此,在每次小文件更改时,一些文件都会全局更改为 LF。

我的解决方案:

Windows-Machines:让一切保持原样。什么都不在乎,因为您是默认的 Windows '孤狼'开发人员,您必须像这样处理:“世界上没有其他系统,是吗?”

Unix机器

将以下行添加到配置的 [alias] 部分。此命令列出所有更改(即修改/新)的文件: lc = "!f() { git status --porcelain \ | egrep -r \"^(\?| ).\*\\(.[a-zA -Z])*\" \ | cut -c 4- ; }; f " 将所有更改的文件转换为 dos 格式: unix2dos $(git lc) 可选...为此操作创建一个 git 挂钩以自动化此过程使用参数并包含它并修改 grep 函数以仅匹配特定的文件名,例如:... | egrep -r "^(\?| ).*\.(txt|conf)" | ...随意使用附加快捷方式使其更加方便: c2dos = "!f() { unix2dos $(git lc) ; }; f " ... 并通过键入 git c2dos 触发转换后的内容