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

时间:2018-12-02 14:59:01

标签: git line-endings

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

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

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

  • 如果我使用例如dos2unix,它们显示为已更改。
  • 也尝试过git checkout --force HEAD,但没有效果。

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

1 个答案:

答案 0 :(得分:1)

TL; DR

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

  1. 使用:

    git add --renormalize .
    

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

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

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

    您可以使用git rm -r .; git checkout HEAD -- .(仅两个命令)来简化此操作,但这会产生副作用,即触摸工作树中的所有文件,甚至包括不需要更改的任何文件(不带回车符的文件,返回它们。)

  3. 像往常一样使用dos2unix,然后在文件(或git add)上运行.。尽管出现了,但这应该使索引保持不变。

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

这不是相当Git: how to renormalize line endings in all files in all revisions?的重复,因为您不想重新复制一堆现有的提交。但是,git add --renormalize的答案应该可以解决。

如果失败,您可以尝试PetSerAl's comment answer

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

  

如果我使用例如dos2unix,它们显示为已更改。

您可以手动转换文件,然后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的行尾。它也被压缩了(起初它实际上只是对已提交副本的引用)。

工作树中的副本未压缩,并且具有您告诉Git通过.gitattributes文件(无)以及core.autocrlf和{{ 1}},依此类推。您已经将它们设置为将LF更改为CRLF,因此您的工作树中的副本目前具有CRLF结尾。

现在(在结帐后 之后),您更改您的设置,这样,被结帐的文件将具有仅LF的行尾,或保留其中的行尾。指数。不幸的是,文件的每个索引副本中的一项是关于工作树副本的信息。这使得Git 假定工作树副本与索引副本相同。

很显然,由于工作树副本具有CRLF结尾,而索引副本具有仅LF结尾,因此两者是不同的。但是,如果您没有更改行尾设置,则要求core.eol 否则,因此必须进行此假设。

如果您没有更改EOL设置,git status不会说什么,这不会打扰任何人,因为如果您在git status上运行git add,则会复制工作树复制回索引。这样一来,这会将CRLF行尾转换为仅LF行尾,并重新压缩文件。生成的文件将与README.txt副本匹配,而HEAD则无需说什么。

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

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

基本上,在HEAD和/或.gitattributes变量中更改行尾设置后,必须让Git修复索引的“干净/脏”缓存数据。在core.*之前,唯一的方法就是强制Git从索引复制到工作树:

git add --renormalize

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

rm worktreefile
git checkout -- worktreefile

这两种方法都可以修复索引的缓存数据,但是显然在此过程中还会造成一些额外的暴力行为。

请注意,如果已提交的git add worktreefile 副本的结尾为CRLF,则情况会发生变化

假定HEAD的提交副本具有CRLF结尾。然后,最初:

  • 索引副本照常与README.txt副本匹配,因此具有CRLF结尾;
  • 工作树中的CRLF结尾,三个副本都匹配;
  • 但是,如果您在工作树中选择仅LF结尾,并使其实现,则工作树副本与HEAD和索引都不同。

这是真的,无论是否HEAD被愚弄。

一旦您将工作树的仅LF行尾复制到索引中,以便索引也具有仅LF行尾,则 now 索引副本(“暂存为提交”)与git status副本。此时,如果您进行 new 提交,则该提交将仅具有LF行尾,并且您将处于我们先前所述的状态。