关于跨平台:Git最好的CRLF(回车,换行)处理策略是什么?

What's the best CRLF (carriage return, line feed) handling strategy with Git?

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

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

每个答案只能分享一个最佳实践。


在提出这个问题差不多四年后,我终于来了
找到了一个完全满足我的答案!

请参阅github中的详细信息:帮助指南
处理行结尾。

Git allows you to set the line ending properties for a
repo directly using the text attribute in the
.gitattributes file. This file is committed into
the repo and overrides the core.autocrlf setting,
allowing you to ensure consistent behaviour for all
users regardless of their git settings.

因此

The advantage of this is that your end of line
configuration now travels with your repository and you
don't need to worry about whether or not collaborators
have the proper global settings.

这是.gitattributes文件的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 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

对于最流行的编程语言,有一个方便的即用型.gitattributes文件集合。让你入门是有用的。

一旦创建或调整了.gitattributes,就应该执行一次又一次的所有行结束重新规范化。

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

最后,记住你的线路结束文章
提供了更多背景知识,并解释了Git如何发展
关于手头的事情。我认为这需要阅读。

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

This setting will not satisfy you completely if you have people working with Egit or JGit in your team, since those tools will just ignore .gitattributes and happily check in CRLF files https://bugs.eclipse.org/bugs/show_bug.cgi?id=342372

一个技巧可能是让他们在另一个客户端提交他们的更改,比如SourceTree。然后我们的团队更喜欢Eclipse的EGit工具,用于许多用例。

谁说软件很简单? : - /


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


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

下面的一些附加背景:

It should be either core.autocrlf=true if you like
DOS ending or core.autocrlf=input if you prefer
unix-newlines. In both cases, your Git repository will
have only LF, which is the Right Thing. The only
argument for core.autocrlf=false was that automatic
heuristic may incorrectly detect some binary as text
and then your tile will be corrupted. So,
core.safecrlf option was introduced to warn a user if
a irreversable change happens. In fact, there are two
possibilities of irreversable changes -- mixed
line-ending in text file, in this normalization is
desirable, so this warning can be ignored, or
(very unlikely) that Git incorrectly detected your
binary file as text. Then you need to use attributes to
tell Git that this file is binary.

上面的段落最初是从gmane.org上的一个帖子中提取出来的,但它已经失败了。


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

A.全局所有存储库设置

1)将所有转换为一种格式

1
2
find . -type f -not -path"./.git/*" -exec dos2unix {} \;
git commit -a -m 'dos2unix conversion'

2)在Linux / UNIX上将core.autocrlf设置为input或在MS Windows上设置true(存储库或全局)

1
git config --global core.autocrlf input

3)[可选]将core.safecrlf设置为true(停止)或warn(唱歌:)以添加额外的保护比较,如果反向换行转换将导致相同的文件

1
git config --global core.safecrlf true

B.或每个存储库设置

1)将所有转换为一种格式

1
2
find . -type f -not -path"./.git/*" -exec dos2unix {} \;
git commit -a -m 'dos2unix conversion'

2)将.gitattributes文件添加到您的存储库

1
2
3
echo"* text=auto"> .gitattributes
git add .gitattributes
git commit -m 'adding .gitattributes for unified line-ending'

不要担心你的二进制文件--Git应该足够聪明。

有关safecrlf / autocrlf变量的更多信息


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

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

If this is not the case for the current setting of core.autocrlf, git will reject the file.

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

最后,我觉得简单地"使用你给你的东西"并在Windows上使用LF终止线的建议将导致比它解决的更多问题。 Git有以上选项来尝试以合理的方式处理行结尾,因此使用它们是有意义的。


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

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

20多年后,我们仍在处理操作系统之间的线路结束差异......很难过。


这些是与Mac或Linux用户共享代码的Windows和Visual Studio用户的两个选项。有关扩展说明,请阅读gitattributes手册。

* text = auto

在repo的.gitattributes文件中添加:

1
*   text=auto

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

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

这是Microsoft .NET repos使用的配置。

例:

1
2
3
Hello

World

将在回购中标准化为:

1
2
Hello
World

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

1
2
3
Hello

World

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

1
2
Hello
World

Note: If your repo already contains files not normalized, git status will show these files as completely modified the next time you make any change on them, and it could be a pain for other users to merge their changes later. See refreshing a repository after changing line endings for more information.

core.autocrlf = true

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

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

  • 只有在添加到仓库时,文件才会标准化为LF行结尾。如果存储库中没有标准化的文件,则此设置不会触及它们。
  • 所有文本文件都将转换为工作目录中的CRLF行结尾。

这种方法的问题是:

  • 如果您是autocrlf = input的Windows用户,您将看到一堆LF行结尾的文件。对团队的其他成员而言并不构成危险,因为您的提交仍将使用LF行结尾进行规范化。
  • 如果您是core.autocrlf = false的Windows用户,您将看到一堆LF行结尾的文件,您可以将带有CRLF行结尾的文件引入repo。
  • 大多数Mac用户使用autocrlf = input并且可能会获得带有CRLF文件结尾的文件,可能来自使用core.autocrlf = false的Windows用户。


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

使用以下anti-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/

---更新2 ---

git客户端的dafaults在大多数情况下都可以使用。即使你只有windows客户端,linux只有客户端或两者兼而有之。这些是:

  • windows:core.autocrlf=true表示在结帐时将行转换为CRLF,并在添加文件时将行转换为LF。
  • linux:core.autocrlf=input表示不在checkout上转换行(不需要因为文件应该用LF提交),并在添加文件时将行转换为LF(如果需要)。

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

1
2
3
4
5
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

此外,我强烈反对使用git config --global core.autocrlf false(如果你只有Windows客户端)与git文档的建议形成对比。设置为false将在repo中提交具有CRLF的文件。但实际上没有理由。你永远不知道是否需要与linux用户共享项目。此外,对于加入项目而不是使用默认值的每个客户端,这是一个额外的步骤。

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

总结一下,最佳做法是:

  • 确保在git repo上使用LF提交每个非二进制文件(默认行为)。
  • 使用此命令确保没有使用CRLF提交文件:git grep -I --files-with-matches --perl-regexp '
    ' HEAD
    (注意:在Windows客户端上仅通过git-bash工作,并且只有在./configure中使用--with-libpcre进行编译时才能在Linux客户端上工作)。
  • 如果通过执行上述命令找到任何此类文件,请更正它们。
  • 仅使用最低.gitattributes
  • 指示用户将上述core.autocrlf设置为其默认值。
  • 存在.gitattributes时不要100%计算。 IDE的git-clients可能会忽略它们或对它们进行不同的处理。

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

1
2
3
4
# 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:未视为文本。无差异/合并可能)

---上一次更新---

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

netbeans 8.2(在Windows上)将错误地使用CRLF提交所有文本文件,除非您已将core.autocrlf明确设置为全局。这与标准的git客户端行为相矛盾,并且在更新/合并时会导致很多问题。这使得某些文件看起来不同(尽管它们不是),即使你还原也是如此。
即使您已将正确的.gitattributes添加到项目中,也会发生netbeans中的相同行为。

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


这只是一种解决方案:

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

在我的案例中,有超过10名程序员在Windows中开发项目。该项目已通过CRLF签入,并且没有强制选择LF的选项。

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

我的解决方案

Windows的机器:
让一切都保持原样。什么都不关心,因为你是一个默认的Windows'孤狼'开发者,你必须像这样处理:"广阔的世界里没有其他系统,是吗?"

Unix的机

  • 将以下行添加到配置的[alias]部分。此命令列出所有已更改(即已修改/新)的文件:

    1
    2
    3
    lc ="!f() { git status --porcelain \
                 | egrep -r "^(\?| ).\*\\(.[a-zA-Z])*" \
                 | cut -c 4- ; }; f"
  • 将所有这些已更改的文件转换为dos格式:

    1
    unix2dos $(git lc)
  • 可选......

  • 为此操作创建一个git hook以自动执行此过程

  • 使用params并包含它并修改grep函数以仅匹配特定的文件名,例如:

    1
    ... | egrep -r"^(\?| ).*\.(txt|conf)" | ...
  • 使用其他快捷方式随意使其更方便:

    1
    c2dos ="!f() { unix2dos $(git lc) ; }; f"

    ...并通过键入来解雇转换后的内容

    1
    git c2dos