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

How to force converting worktree files after changing core.autocrlf?

我在 Windows 并且有全系统 core.autocrlf=true

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

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

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

TL;DR

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

  1. 使用:

    git add --renormalize .
    

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

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

  2. 对于 git status 抱怨的每个文件:rm <em>file</em>; git 签出 -- <em>文件</em>rm 删除了工作树副本,因此 git checkout 必须根据新的行结束规则实际重新提取文件。

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

  3. 像以前一样使用 dos2unix,然后对文件(或 .)使用 运行 git add。尽管有吸引力运行ces,这应该会使索引保持不变。

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

这不是 完全 Git: how to renormalize line endings in all files in all revisions? 的副本,因为您不想重新复制一堆现有的提交。但是,那里的 git add --renormalize 答案应该有效。

不行的话,你可以试试

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

If I convert the files manually with e.g. dos2unix, they show as altered.

您可以手动转换文件,然后 git add .,或者删除工作树副本并再次 git checkoutgit checkout --force HEAD 失败是因为 Git 太聪明了:它看到(错误地)工作树副本已经正确并避免对其进行操作。

这是怎么回事

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

   HEAD          index       work-tree
----------    ----------    ----------
README.txt    README.txt    README.txt
prog.cc       prog.cc       prog.cc

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

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

工作树中的副本是未压缩的,并且具有您告诉Git要在.gitattributes文件中使用的行尾(none) 和你的 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 被愚弄了:索引说——故意的!——工作树副本匹配(即使它不匹配),并且 运行ning git add 而工作-树副本有 CRLF 行结尾会更改索引副本。

如果您在文件上使用 dos2unix 来更改工作树副本,Git 现在会发现工作树副本的统计信息与保存的索引 "this file is clean" 不匹配统计数据。也就是说,git status 仍然被愚弄 但现在说工作树副本不同了!如果您 git add 文件 now,Git 将在更新索引副本时保留 LF-only 行尾。最终结果将是索引副本与 HEAD 副本匹配, Git 更新缓存的工作树统计 about 文件,以便它知道索引副本与工作树副本匹配。

本质上,在 .gitattributes and/or core.* 变量中更改行结束设置后,您必须 Git 修复索引的 "clean/dirty" 缓存数据.在 git add --renormalize 之前,唯一的方法是强制 Git 从索引复制到工作树:

rm worktreefile
git checkout -- worktreefile

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

git add worktreefile

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

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

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

  • 索引副本与往常一样匹配 HEAD 副本,因此它具有 CRLF 结尾;
  • 在工作树中以 CRLF 结尾,所有三个副本都匹配;
  • 但是如果您 select 在工作树中仅以 LF 结尾,并实现这一点,则工作树副本不同于 HEAD 和索引。

无论git status是否被愚弄,都是如此。

一旦将工作树的仅 LF 行结尾复制到索引中,使得索引也具有仅 LF 行结尾,现在 索引副本("staged for commit") 不同于 HEAD 副本。此时,如果您进行 new 提交,该提交将只有 LF 行尾,并且您将处于我们之前描述的状态。