微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Git 在提交时不会将行尾转换为 LF core.autocrlf vs .gitattributes重新规范化选项

如何解决Git 在提交时不会将行尾转换为 LF core.autocrlf vs .gitattributes重新规范化选项

即使在设置 core.autocrlf=true 时,我们仍然看到行结尾提交为 CRLF 而不是 LF(我可以在运行 ^Mgit diff 时看到 git log -p 符号)>

这有时会导致合并冲突,因为不同的开发人员在他们的编辑器中使用不同的设置。

我们如何在非常活跃的存储库环境中以最小的未来冲突来解决这个问题?

解决方法

这看起来与 git 没有“规范化”故意更改的文件有关。通常,当引入一个新文件时,它会转换为 LF 并“规范化”,但是,如果有人在没有设置 autocrlf 的情况下编辑行尾,它们将被更改,然后 git 将不在乎。

我可以通过运行查看以这种方式设置的文件列表

git ls-files --eol

当您看到 i/crlfi/mixed 时,该文件未“标准化”并且 git 不会强制转换为 LF。

看起来可能有一些方法可以解决这个问题,但我会等待其他人加入。

,

我通常建议在此处使用 .gitattributes,而不是设置 core.autocrlf(并不是我实际处理这个问题,但 Git 项目人员就是这样做的,并且大概他们知道)。

这不会解决您的问题,但应该有助于避免将来出现问题。

要处理合并问题,请考虑运行:

git merge -X renormalize

和/或将 merge.renormalize 设置为 true

长:为什么

值得指出的是,Git 从不提交时进行任何转换。 实现 CRLF 到 LF-only 转换的机制不在 git commit 中,而是在 git add 中。要了解原因,我们必须从一些 Git 基础知识开始:

  • 每次提交都有每个文件的完整快照(Git 在您或任何人进行提交时就知道)。 (每个提交也有一些元数据,但这与这个特定问题无关。)
  • 没有提交,一旦提交,就不能更改。一旦某个文件提交,它就不受侵犯。
  • 存储在提交中的文件不是作为普通文件存储的。相反,它们以特殊的、只读的、仅限 Git 的、压缩的(有时是高度压缩的)和重复数据删除的形式存储。重复数据删除处理这样一个事实,即大多数提交大多使用与其他一些早期提交相同的文件副本。但这也意味着您实际上无法处理/使用这些文件副本。
  • 提交的快照不是来自您的文件的工作树副本,而是来自Git的文件“副本”,因为它们出现在GIt的中索引

索引也称为暂存区,就用户如何使用它而言,这是一个更好的名称,尽管 Git 使用它的方式不止于此(这就是为什么它具有名称 cache,给它三个名称:cacheindexstaging area)。无论如何,这些额外的文件“副本”存在于 Git 的索引中。我在这里将“副本”放在引号中,因为索引中的内容已经是 Git 使用的特殊形式。这些不是普通文件,也不能编辑(但它们可以替换)。

相反,每个文件都有一个第三副本。第三个副本是一个普通文件。这些是您可以查看和编辑的文件。问题在于这些文件不在 Git 中。它们是在 git checkoutgit switch 期间,或者在使用 git resetgit restore Git 中提取的。

在提取过程中,Git 有两个选项:可以不理会文件,也可以更改文件。更改可以包括用 CRLF 行结尾替换仅 LF 行结尾。您现在有一个可以查看的文件。如果您选择提取文件以使其具有 CRLF 行尾,那么此时您还配置了 Git,以便在您让 Git 替换索引副本时进行“撤消 CRLF 行尾”更改。

git add 所做的是告诉 Git:使索引副本与工作树副本匹配。 如果您更改了工作树副本 Git 现在将压缩和 Git-ify 工作树副本,并使用它来替换索引中的副本。不幸的是,对于 CRLF-line-end-fixing,如果 Git 认为没有需要替换索引副本,Git 什么都不做。1

不幸的是,Git 在决定是否需要替换某个文件的索引副本时,既不检查 core.autocrlf 也不检查 .gitattributes 中的任何设置。因此,更改其中任何一个都不算作对任何文件的“更改”。2在 Git 2.16 及更高版本中,git add --renormalize 有助于告诉 Git:嘿,你这个笨蛋,我改变了 在 2.16 之前的 Git 版本中,您必须让 Git 相信您更改了文件 '正在尝试做的是修复行尾。 (有很多方法可以做到这一点,但让我们假设您有 git add --renormalize。?)

将 Git 的索引视为保存 提议的 下一次提交。当您运行 git commit 时,Git 只是对索引进行快照。因为它已经保存了 Git 化的文件,所以这个过程非常快。

无论如何,这里的最终结果是这样的:

  • 当 Git 将文件从 Git 的索引(或使用 git restore,从提交)复制到您的工作树时,Git 会应用“使文件对您个人有用”结尾- 行更改。

  • 当 Git 将文件从您的工作树复制到 Git 的索引时,Git 会应用“为存储库规范化文件”更改。

当您运行 git commit 时,Git 使用 Git 索引中的副本进行提交。因此,无论 index 副本中出现什么行尾,这些都是新提交中进入永久副本的行尾。 不能看到那些副本,但git ls-files可以。


1其实把一个文件重新压缩成Git格式是个很大的工作,所以Git尽可能巧妙地避免了。这就是 Git 索引存在的原因之一。其他版本控制系统无一幸免;其他版本控制系统较慢。这里的问题只是 Git 认为这种工作避免可能太频繁

2当然,如果你修改了.gitattributes,Git会意识到.gitattributes被修改了。只是它永远不会扩展到思考:哦,嘿!也许这意味着其他文件已更改 EOL 设置。


core.autocrlf vs .gitattributes

使用 core.autocrlf 和使用 .gitattributes 指定存储库内行结束格式之间存在许多差异。最大和最明显的当然是 core.autocrlf 是每个人都必须在他们的个人 .git/config$HOME/.gitconfig 或他们喜欢放置的任何地方进行的设置,但是 .gitattributes提交的文件

成为一个提交的文件有很多后果:特别是,当你检查一些提交时,你会得到现有的文件。在每个提交中都有一个该文件的副本——好吧,每次提交都是在 .gitattributes 在 Git 的索引中进行提交时进行的,并且当你检查时提交,Git 遵守该提交的 .gitattributes 设置。当您 git add 文件时,Git 遵守您工作树的 .gitattributes 设置,因此您可以更改设置和 git add 文件(包括 .gitattributes),并且您更新的设置将应用 将进入下一次提交。

重要的是,当在 .gitattributes 中列出文件时,您可以控制。你可以告诉 Git 文件 xyz 是一个二进制文件,如果它是二进制的。你可以告诉 Git 文件 abc 是一个文本文件,如果它是文本。您可以说 *.js*.py 是文本,而 *.jpg 是二进制的。当您使用 core.autocrlf 时,您只是在让 Git 猜测。 Git 可能会猜错,并对您的二进制文件进行 CRLF 更改,或者不对您的文本文件进行更改。

有关在 .gitattributes 文件中放入什么内容的详细信息,请参阅 the gitattributes documentation

重新规范化选项

当您使用 git merge 进行三向合并时,3 有三个输入提交:当前提交、您通过命令行选择的提交以及合并基础.您可以在当前提交中规范行尾,只需使用固定行尾进行新提交即可。如果你真的必须这样做,你可以检查你将在命令行上选择的提交,标准化它的行尾,并提交它,并在命令行上使用那个。但是您实际上无法修复基于合并的提交:Git 根据提交图(存储在提交元数据中的提交之间的链接)自行找到这个提交。

行尾对 git diff 很重要,合并取决于两个差异(从合并基础到您的提交,以及从合并基础到您选择的提交)。所以有时需要修复合并基础,以及当前提交和其他提交。 renormalize 选项正是这样做的。这样,就没有必要做不可能的事情——修复历史提交的行尾。需要重新规范化的合并会慢一点,但总比不进行要好。


3请记住,git merge 可能会执行快进合并,这根本不是合并。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。