更改core.autocrlf后如何强制转换工作树文件?

iva*_*eev 5 git line-endings

我在 Windows 上并且在系统范围内拥有core.autocrlf=true.

对于特定的存储库,我已在本地将其覆盖为false.

但这并没有转换签出文件中的行尾。我怎么做?

  • 如果我使用 eg 手动转换文件dos2unix,它们会显示为已更改。
  • 也试过git checkout --force HEAD,没有效果。

我发现的唯一有效方法是删除所有文件,然后git reset --hard这很尴尬(=没有简单可靠的命令可以做到这一点,而且它做了很多不必要的工作——一切都是从头开始重新创建的,而不仅仅是覆盖需要转换的文件)。

tor*_*rek 7

TL; 博士

这是三种可能的解决方案(不一定是唯一的三种)。

  1. 用:

    git add --renormalize .
    
    Run Code Online (Sandbox Code Playgroud)

    (在存储库的顶层完成,一次)。这需要更新的 Git,但这是最简单的方法。

    注意:我完全不清楚这是否会影响工作树版本;您可能仍需git checkout -- .要从索引重新复制到工作树。

  2. 对于每个git status抱怨的文件:. 在删除工作树副本,以便必须真正按照新的行结束规则重新提取该文件。rm file; git checkout -- filermgit checkout

    您可以使用git rm -r .; git checkout HEAD -- .(仅两个命令)稍微简化一下,但这会产生影响工作树中所有文件的副作用,即使是不需要更改的任何文件(其中没有回车的文件)。

  3. dos2unix原样使用,然后git add在文件上运行(或在 上运行.)。尽管出现,这应该使指数保持不变。

在所有情况下,之后,git status应该说nothing to commit, working tree clean

这不是Git 的完全重复:如何在所有修订版中重新规范所有文件中的行尾?,因为您不想重新复制一堆现有的提交。但是,git add --renormalize那里的答案应该有效。

如果失败,您可以尝试PetSerAl 的评论答案

或者,如果失败或您的 Git 太旧而无法--renormalize选择:

如果我使用 eg 手动转换文件dos2unix,它们会显示为已更改。

您可以手动转换文件,然后git add .,或删除工作树副本,git checkout然后再次删除它们。将git checkout --force HEAD失败,因为Git的是为自己的好太聪明:它看到(错误地),工作树副本已经是正确的,避免了它做的工作。

这里发生了什么

在任何时候,每个文件都有三个活动副本。假设您有 aREADME.txt和 a prog.cc,它们在您的工作树中都有 CRLF 结尾,但在存储库中只有 LF 行结尾。

   HEAD          index       work-tree
----------    ----------    ----------
README.txt    README.txt    README.txt
prog.cc       prog.cc       prog.cc
Run Code Online (Sandbox Code Playgroud)

提交中的副本无论以何种形式存在,都是神圣不可侵犯的,永远冻结(或只要该提交存在)。(我现在假设这些文件中的每一个都有 LF 样式的行尾。)它也被压缩了。

索引中的副本是可写的,但最初与提交中的副本匹配。所以它也将有 LF-only 行尾。它也被压缩了(起初它实际上只是对提交副本的引用)。

在复制工作树是未压缩的,有你告诉GIT中使用通过你的行尾.gitattributes文件(无)和你的core.autocrlfcore.eol等。您已将它们设置为将 LF 更改为 CRLF,因此您的工作树中的副本此时具有 CRLF 结尾。

现在——签出之后——你改变你的设置,这样被签出的文件将只有 LF 行尾,或者将保留索引中的内容。不幸的是,文件的每个索引副本中的条目之一是有关工作树副本的信息。这使得 Git假设工作树副本与索引副本相同。

显然,由于工作树副本具有 CRLF 结尾而索引副本具有仅 LF 结尾,因此两者是不同的。但是如果你没有改变你的行尾设置,git status需要另外说,所以它必须做出这个假设。

如果您没有更改 EOL 设置,则git status什么也不说,这不会打扰任何人,因为如果您继续运行git add,例如README.txt,这会将工作树副本复制回索引。在此过程中,这会将 CRLF 行尾转换为仅 LF 行尾,并重新压缩文件。生成的文件将与HEAD副本匹配,并且git status不必说什么。

但是您确实更改了 EOL 设置,因此如果您git add现在运行,Git 应该将 CRLF 结尾复制到索引中。本质上,git status已经被愚弄了:索引说 - 故意! - 工作树副本匹配(即使它不匹配),并且git add在工作树副本具有 CRLF 行结尾时运行会更改索引副本。

如果您使用dos2unix文件更改工作树副本,Git 现在会看到工作树副本的统计信息与索引保存的“此文件是干净的”统计信息不匹配。也就是说,git status 仍然被愚弄,但现在说工作树副本是不同的!如果你现在git add文件,Git 将在更新索引副本时保留 LF-only 行尾。最终结果将是索引副本毕竟副本匹配,并且Git 更新有关文件的缓存工作树统计信息,以便它知道索引副本与工作树副本匹配。HEAD

本质上,在更改行结束设置(在.gitattributes和/或core.*变量中)之后,您必须让 Git 修复索引的“干净/脏”缓存数据。直到git add --renormalize唯一的方法是强制 Git 从索引复制到工作树:

rm worktreefile
git checkout -- worktreefile
Run Code Online (Sandbox Code Playgroud)

或强制 Git 从工作树复制到索引:

git add worktreefile
Run Code Online (Sandbox Code Playgroud)

两者都修复了索引的缓存数据,但显然在这个过程中做了一些额外的暴力。

请注意,如果提交的HEAD副本具有 CRLF 结尾,则情况会发生变化

假设提交的副本README.txt具有 CRLF 结尾。然后,最初:

  • 索引副本HEAD照常与副本匹配,因此它具有 CRLF 结尾;
  • 在工作树中以 CRLF 结尾,所有三个副本都匹配;
  • 但是如果您在工作树中选择 LF-only 结尾并实现这一点,则工作树副本与 index.htmlHEAD和 .

不管是否git status上当,这都是事实。

一旦您将工作树的仅 LF 行结尾复制到索引中,以便索引也具有仅 LF 行结尾,现在索引副本(“暂存以提交”)与HEAD副本不同。此时,如果您进行新的提交,该提交将只有 LF 行结尾,并且您将处于我们之前描述的状态。


iva*_*eev 3

git read-tree --empty
git reset --hard
Run Code Online (Sandbox Code Playgroud)

注意:这覆盖所有文件。从好的方面来说,这比删除除.gita 之前的所有内容git reset并处理回收站更容易。

注意:如果您使用的是 Windows 或正在使用 Windows 中生成的文件,并且该项目不是 Windows 独有的,请设置core.autocrlfinput而不是false. 这将防止您意外地将 CRLF 提交到存储库中。

我找不到从树(Git 中的“树”意味着存储库中保存的修订版)和/或有选择地从索引更新工作树的索引的方法。git read-tree HEAD两者似乎都没有git checkout-index意识到 EOL 转换之间的差异。