如何让git理解Mac(CR)行尾

时间:2018-09-05 07:43:08

标签: git macos cross-platform compatibility line-endings

由于某些原因,我的文件之一包含旧式Mac行结尾(在OSX上编辑后)。这些是“ CR”(回车符)字符,在git diff中显示为^ M。

Git无法理解它们是行尾代码(真的很难吗?),并将整个文件解释为一行。

我知道我可以将文件转换为LF或CRLF结尾,然后再提交回去,但是由于git自动将Windows(CRLF)行结尾转换为LF,因此我希望它能像处理CR行结尾一样好。否则,听起来功能不完整。

有没有办法让git将CR解释为行尾?

2 个答案:

答案 0 :(得分:1)

TL; DR

创建一个过滤器驱动程序以及.gitattributes:创建一个运行tr '\n' '\r'污迹过滤器和一个运行tr '\r' '\n' clean过滤器 ,并将有问题的文件标记为使用此过滤器。使用仅LF行尾将文件存储在Git中。 (过滤器驱动程序在.git/config$HOME/.gitconfig文件中定义,文件的名称或名称模式在.gitattributes中。)

如您所见,Git强烈喜欢换行符终止的行。 (它可以与换行符分隔的行一起使用,其中最后一行缺少终止符,但这意味着添加一行会导致对前一个最终行的更改,因为它现在有了换行符终止符,而新的最后一行丢失了这是换行符终止符。)这对于单个快照无关紧要,但对于产生有用的差异来说却很重要。

现代MacOS像其他所有人一样使用换行符。仅古老的向后兼容格式具有仅CR的行尾。参见例如this SuperUser Stack Exchange web site posting

Git没有内置的 过滤器,无法与此类行尾进行转换。但是,Git 确实具有用于在工作树文件中进行更改的通用机制

请记住,当Git在快照中存储任何文件时,该文件由Git称为 blob对象表示,该对象内部存储在特殊的,压缩的(有时是高度压缩的)Git-唯一的形式。这种形式对Git来说没有什么用,所以,当您以有用的形式(例如,通过git checkout)获取文件时,Git会将其扩展为计算机的常规形式。同时,只要您将这样的普通文件转换为仅Git格式,Git就会将文件压缩为仅Git格式。每当您使用git add将文件复制回Git的 index 时,就会发生这种情况。

当您放置了工作树时,每个文件的索引副本都会存在,就像提交的副本一样。索引副本采用相同的仅Git格式。此处的主要区别在于,不能更改已提交的副本 ,但是可以更改索引副本 。运行git commit会获取此时索引正确的中所有内容的快照,并为新提交创建新快照。因此,索引充当下一次提交的内容。使用git checkout,将一些现有的提交复制到索引中,然后让Git将其扩展到工作树中;然后使用git add,用已更改的工作树文件的压缩版本有选择地替换特定的索引副本。

在索引和工作树之间来回复制是进行Windows风格的LF到CRLF转换(反之亦然)的理想点,因此Git就是在这里进行的。如果要执行某些 other 转换,而不是直接将其内置到Git中,则可以在此处告诉Git执行此操作。

涂抹并清洁过滤器

污迹过滤器是Git在将文件从压缩索引副本转换为工作树副本时应用的过滤器。在这里,如果您选择将换行符替换为CRLF Windows样式的行尾或分隔符,则Git会使用一个内部转换器来执行此操作:eol=crlf clean过滤器是Git在将文件从未压缩的工作树副本转换为压缩的索引副本时应用的过滤器;在这里再次eol=crlf指示Git进行向后转换。

如果要用纯CR替换换行符,则必须发明自己的转换器。假设您将整个流程称为convert-cr

*.csv   filter=convert-cr

(而不是*.csv eol=crlf)。此行进入.gitattributes(这是一个可提交的文件,应该将其提交)。

现在,您必须定义convert-cr过滤器。这放在一个Git配置文件中,在这里我们发现一个较小的缺陷:该配置文件不可提交。这是一个安全问题:Git将在此处运行任意命令,如果我可以提交此文件并将其克隆,则将运行 I 指定的命令,而没有机会先审查它们。因此,您必须自己将其放入.git/config或全局配置中(例如git config --global --edit):

[filter "convert-cr"]
    clean = tr '\r' '\n'
    smudge = tr '\n' '\r'

现在,只要Git将仅Git格式转换,它将换行符转换为CR,并且每当Git将转换为仅Git格式时,它将转换CR。换行符。

这对现有的存储文件没有帮助

您今天拥有的其中有\r的任何现有快照将永远以这种方式存储。 Git永远不会更改任何现有的存储文件!存储的数据是宝贵的并且是非法的。您对此无能为力。嗯,几乎没有什么:您可以完全丢弃那些提交,而可以使用新的和改进的提交。但这很痛苦:每个提交都记住其 parent 提交,因此,如果替换存储库中的早期提交,则必须替换每个子级,孙级等等。他们都记得这个新的提交顺序。 (git filter-branch完成了这项工作。)

但是,您也可以使用.gitattributes diff驱动程序来指导Git如何在现有提交中 diff 特定文件。有多种方法可以执行此操作,但是最简单的方法是定义一个 textconv 属性,该属性将“二进制”文件(例如,其存储版本可能仅包含CR字符的文件)转换为文本(面向行,即基于换行符)文件。这里使用的textconv过滤器与污迹过滤器完全相同。

有关更多详细信息,请参见the gitattributes documentation

答案 1 :(得分:1)

自从人们接受了答案以来,就引入了一种新的方法。

在创建差异之前,您可以教git diffgit log通过特殊命令来运行文件。这是一个单向过程,仅用于生成差异,而不会影响文件在磁盘或存储库中的存储方式。

创建一个名为“ cr”的新差异驱动程序,该驱动程序在计算差异之前通过tr运行文件。在您的.git/config中:

[diff "cr"]
    textconv = tr '\\r' '\\n' <

或者:

git config diff.cr.textconv "tr '\r' '\n' <"

然后告诉git使用.gitattributes使用它(例如,用于所有.csv文件):

*.csv diff=cr

请注意,此 only 仅影响差异。它不会帮助您合并!