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



我在Windows上,有全系统的core.autocrlf=true

对于特定的存储库,我已经将其本地重写为false

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

  • 如果我使用例如dos2unix手动转换文件,则它们显示为已更改
  • 也尝试了git checkout --force HEAD,但没有效果

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

TL;DR

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

  1. 使用:

    git add --renormalize .
    

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

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

  1. 对于git status抱怨的每个文件:rmfile; git checkout --filerm删除工作树副本,因此git checkout必须根据新行结束规则重新提取文件

您可以使用git rm -r .; git checkout HEAD -- .(仅两个命令)在一定程度上简化这一过程,但这会产生副作用,即接触工作树中的所有文件,甚至是不需要更改的任何文件(其中没有回车的文件)。

  1. 按原样使用dos2unix,然后在文件上(或在.上)运行git add。尽管表面上如此,这应该会使指数保持不变

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

Long

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

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

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

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

这是怎么回事

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

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

承诺中的副本是神圣不可侵犯的,不可侵犯,以任何形式永远冻结(或只要承诺存在)。(我现在假设每个文件都有LF风格的行尾。)它也是压缩的。

索引中的副本是可写的,但最初与提交中的副本匹配。因此,它也将具有仅LF的行结尾。它也被压缩了(一开始它实际上只是对提交副本的引用)。

工作树中的副本是未压缩的,并且具有您通过.gitattributes文件(无)、core.autocrlfcore.eol等告诉Git使用的行结尾。已将它们设置为将LF更改为CRLF,因此工作树中的副本目前具有CRLF结尾。

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

显然,由于工作树副本有CRLF结尾,而索引副本只有LF结尾,所以两者是不同的。但是,如果您没有更改行尾设置,则git status所必需的,因此必须做出此假设。

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

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

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

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

rm worktreefile
git checkout -- worktreefile

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

git add worktreefile

这两种方法都修复了索引的缓存数据,但显然在这个过程中会带来一些额外的暴力。

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

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

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

无论git status是否被愚弄,这都是真的。

一旦将工作树的仅LF行结尾复制到索引中,使得索引也具有仅LF行末尾,现在索引副本("提交暂存")与HEAD副本不同。在这一点上,如果您进行新的提交,该提交将只有LF行结尾,并且您将处于我们前面描述的状态。

git read-tree --empty
git reset --hard

注意:此覆盖所有文件。从好的方面来说,这比在git reset之前移除除.git之外的所有东西并处理回收站更容易。

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

我找不到从树中更新索引的方法(Git中的"树"表示保存在repo中的修订)和/或从索引中选择性地更新工作树。git read-tree HEADgit checkout-index似乎都没有认识到EOL转换的差异

最新更新