如何让 git 理解 Mac (CR) 行结尾
How to make git understand Mac (CR) line endings
由于某些原因,我的一个文件包含旧式 Mac 行尾(在 OSX 上编辑后)。这些是 "CR"(回车 return)字符,在 git diff
.
中显示为 ^M
Git 不理解它们是行结束代码(真的有多难?)并将整个文件解释为一行。
我知道我可以将文件转换为 LF 或 CRLF 结尾,然后将它们提交回去,但是由于 git 自动将我的 Windows (CRLF) 行结尾转换为 LF,我希望它也会处理 CR 行结尾。
有没有办法让 git 将 CR 解释为行结尾?
TL;DR
创建滤镜 driver 加 .gitattributes
:创建 涂抹滤镜 运行s tr '\n' '\r'
和 清理 运行s tr '\r' '\n'
的过滤器,并将有问题的文件标记为使用此过滤器。使用 LF-only 行结尾将文件存储在 Git 中。 (过滤器 driver 在 .git/config
或 $HOME/.gitconfig
文件中定义,文件的名称或 name-patterns 进入 .gitattributes
。)
长
如您所见,Git 非常喜欢 newline-terminated 行。 (它可以与 newline-separated 行一起使用,其中最后一行缺少终止符,但这意味着添加一行会导致对先前最后一行的更改,因为它现在有一个换行符终止符,而新的最后一行缺少换行符。)这对单个快照无关紧要,但对产生有用的差异很重要。
现代 MacOS 和其他人一样使用换行符。只有古老的 backwards-compatible 格式有 CR-only 行结尾。参见,例如,this SuperUser Stack Exchange web site posting.
Git 没有 内置 过滤器来转换到或从这样的行结尾。 Git 确实,但是,有一个 通用机制 用于在 work-tree 文件中进行更改。
请记住,当 Git 将任何文件存储在快照中时,该文件由 Git 所称的 blob object 表示,它以一种特殊的、压缩的(有时是高度压缩的)Git-only 形式在内部存储。这种形式对任何东西都没有用 但 Git,所以当你以有用的形式获得文件时——例如通过 git checkout
——Git将它们扩展为您计算机的常用形式。同时,任何时候你将这样的普通文件转换为 Git-only 格式,Git 会将文件压缩为 Git-only 格式。每当您使用 git add
.
将文件复制回 Git 的 index 时,就会发生这种情况
当您有 work-tree 时,每个文件的索引副本都存在,就像提交的副本一样。索引副本采用相同的 Git-only 格式。这里的关键区别是提交的副本不能被改变,但是索引副本可以被改变。 运行 git commit
为索引 中的任何内容拍摄快照 ,并将其作为新提交的新快照。因此,索引充当 将进入下一次提交的内容 。使用 git checkout
,您将一些现有的提交 复制到 索引中,并让 Git 将其扩展到 work-tree;然后使用 git add
,您有选择地将特定索引副本替换为您已更改的 work-tree 文件的压缩版本。
与索引和 work-tree 之间的这种复制是进行 Windows-style LF-to-CRLF 转换的理想点,反之亦然,所以这就是 Git 做到了。如果你有一些 other 转换要执行,而不是直接内置到 Git,这就是你告诉 Git 执行它的地方。
涂抹并清洁过滤器
涂抹过滤器 是 Git 在将文件从压缩索引副本转换为 work-tree 副本时应用的过滤器。在这里,如果您选择用 CRLF Windows-style 行 enders-or-separators 替换换行符,Git 有一个内部转换器可以做到这一点:eol=crlf
。 clean 过滤器 是 Git 在将文件从未压缩的 work-tree 副本转换为压缩索引副本时应用的过滤器;同样,eol=crlf
指示 Git 进行向后转换。
如果你想用 CR-only 替换换行符,你必须发明自己的转换器。假设您将整个过程称为 convert-cr
:
*.csv filter=convert-cr
(而不是 *.csv eol=crlf
)。此行进入 .gitattributes
(这是一个 commit-able 文件,您应该提交它)。
现在您必须定义 convert-cr
过滤器。这在 Git 配置文件中,在这里我们发现一个小缺陷:配置文件不是 commit-able。这是一个安全问题:Git 将在此处 运行 任意命令,如果我可以提交此文件并且您克隆它,您将 运行 命令 I 指定,没有机会先审查它们。所以你必须自己把它放到你的 .git/config
中,或者放到你的全局配置中(例如 git config --global --edit
):
[filter "convert-cr"]
clean = tr '\r' '\n'
smudge = tr '\n' '\r'
现在,每当 Git 将 从 Git-only 格式转换为 Git-only 格式时,它会将换行符转换为 CR,并且每当 Git 将 到 Git-only格式,它会将CR转换为换行符。
这对现有的存储文件没有帮助
您今天拥有的任何现有快照,其中包含 \r
,将以这种方式永久存储。 Git 永远不会更改任何现有的存储文件!存储的数据是珍贵且不可侵犯的。你对此无能为力。好吧,几乎 什么都没有:您可以完全丢弃这些提交,而改用新的和改进的提交。但这非常痛苦:每个提交都会记住它的 parent 提交,所以如果你如果你在你的仓库中替换了一个早期的提交,你必须替换 every child, grandchild, 等等,这样他们都会记住这个新的提交顺序. (git filter-branch
完成这项工作。)
但是,您可以指示 Git 如何 diff 现有提交中的特定文件,也可以使用 .gitattributes
和 diff drivers。有多种方法可以做到这一点,但最简单的是定义一个 textconv 属性,它可以转换一个 "binary" 文件——比如一个文件,其存储版本可能有 CR-only 个字符——转换为文本(line-oriented,即 newline-based)文件。这里使用的 textconv 过滤器与 smudge 过滤器完全相同。
有关详细信息,请参阅 the gitattributes documentation。
自接受答案以来,引入了一种新方法。
您可以在创建 diff 之前通过特殊命令将 git diff
和 git 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
请注意,这 仅 影响差异。它不会帮助你合并!
由于某些原因,我的一个文件包含旧式 Mac 行尾(在 OSX 上编辑后)。这些是 "CR"(回车 return)字符,在 git diff
.
Git 不理解它们是行结束代码(真的有多难?)并将整个文件解释为一行。
我知道我可以将文件转换为 LF 或 CRLF 结尾,然后将它们提交回去,但是由于 git 自动将我的 Windows (CRLF) 行结尾转换为 LF,我希望它也会处理 CR 行结尾。
有没有办法让 git 将 CR 解释为行结尾?
TL;DR
创建滤镜 driver 加 .gitattributes
:创建 涂抹滤镜 运行s tr '\n' '\r'
和 清理 运行s tr '\r' '\n'
的过滤器,并将有问题的文件标记为使用此过滤器。使用 LF-only 行结尾将文件存储在 Git 中。 (过滤器 driver 在 .git/config
或 $HOME/.gitconfig
文件中定义,文件的名称或 name-patterns 进入 .gitattributes
。)
长
如您所见,Git 非常喜欢 newline-terminated 行。 (它可以与 newline-separated 行一起使用,其中最后一行缺少终止符,但这意味着添加一行会导致对先前最后一行的更改,因为它现在有一个换行符终止符,而新的最后一行缺少换行符。)这对单个快照无关紧要,但对产生有用的差异很重要。
现代 MacOS 和其他人一样使用换行符。只有古老的 backwards-compatible 格式有 CR-only 行结尾。参见,例如,this SuperUser Stack Exchange web site posting.
Git 没有 内置 过滤器来转换到或从这样的行结尾。 Git 确实,但是,有一个 通用机制 用于在 work-tree 文件中进行更改。
请记住,当 Git 将任何文件存储在快照中时,该文件由 Git 所称的 blob object 表示,它以一种特殊的、压缩的(有时是高度压缩的)Git-only 形式在内部存储。这种形式对任何东西都没有用 但 Git,所以当你以有用的形式获得文件时——例如通过 git checkout
——Git将它们扩展为您计算机的常用形式。同时,任何时候你将这样的普通文件转换为 Git-only 格式,Git 会将文件压缩为 Git-only 格式。每当您使用 git add
.
当您有 work-tree 时,每个文件的索引副本都存在,就像提交的副本一样。索引副本采用相同的 Git-only 格式。这里的关键区别是提交的副本不能被改变,但是索引副本可以被改变。 运行 git commit
为索引 中的任何内容拍摄快照 ,并将其作为新提交的新快照。因此,索引充当 将进入下一次提交的内容 。使用 git checkout
,您将一些现有的提交 复制到 索引中,并让 Git 将其扩展到 work-tree;然后使用 git add
,您有选择地将特定索引副本替换为您已更改的 work-tree 文件的压缩版本。
与索引和 work-tree 之间的这种复制是进行 Windows-style LF-to-CRLF 转换的理想点,反之亦然,所以这就是 Git 做到了。如果你有一些 other 转换要执行,而不是直接内置到 Git,这就是你告诉 Git 执行它的地方。
涂抹并清洁过滤器
涂抹过滤器 是 Git 在将文件从压缩索引副本转换为 work-tree 副本时应用的过滤器。在这里,如果您选择用 CRLF Windows-style 行 enders-or-separators 替换换行符,Git 有一个内部转换器可以做到这一点:eol=crlf
。 clean 过滤器 是 Git 在将文件从未压缩的 work-tree 副本转换为压缩索引副本时应用的过滤器;同样,eol=crlf
指示 Git 进行向后转换。
如果你想用 CR-only 替换换行符,你必须发明自己的转换器。假设您将整个过程称为 convert-cr
:
*.csv filter=convert-cr
(而不是 *.csv eol=crlf
)。此行进入 .gitattributes
(这是一个 commit-able 文件,您应该提交它)。
现在您必须定义 convert-cr
过滤器。这在 Git 配置文件中,在这里我们发现一个小缺陷:配置文件不是 commit-able。这是一个安全问题:Git 将在此处 运行 任意命令,如果我可以提交此文件并且您克隆它,您将 运行 命令 I 指定,没有机会先审查它们。所以你必须自己把它放到你的 .git/config
中,或者放到你的全局配置中(例如 git config --global --edit
):
[filter "convert-cr"]
clean = tr '\r' '\n'
smudge = tr '\n' '\r'
现在,每当 Git 将 从 Git-only 格式转换为 Git-only 格式时,它会将换行符转换为 CR,并且每当 Git 将 到 Git-only格式,它会将CR转换为换行符。
这对现有的存储文件没有帮助
您今天拥有的任何现有快照,其中包含 \r
,将以这种方式永久存储。 Git 永远不会更改任何现有的存储文件!存储的数据是珍贵且不可侵犯的。你对此无能为力。好吧,几乎 什么都没有:您可以完全丢弃这些提交,而改用新的和改进的提交。但这非常痛苦:每个提交都会记住它的 parent 提交,所以如果你如果你在你的仓库中替换了一个早期的提交,你必须替换 every child, grandchild, 等等,这样他们都会记住这个新的提交顺序. (git filter-branch
完成这项工作。)
但是,您可以指示 Git 如何 diff 现有提交中的特定文件,也可以使用 .gitattributes
和 diff drivers。有多种方法可以做到这一点,但最简单的是定义一个 textconv 属性,它可以转换一个 "binary" 文件——比如一个文件,其存储版本可能有 CR-only 个字符——转换为文本(line-oriented,即 newline-based)文件。这里使用的 textconv 过滤器与 smudge 过滤器完全相同。
有关详细信息,请参阅 the gitattributes documentation。
自接受答案以来,引入了一种新方法。
您可以在创建 diff 之前通过特殊命令将 git diff
和 git 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
请注意,这 仅 影响差异。它不会帮助你合并!