我在Windows上,有全系统的core.autocrlf=true
。
对于特定的存储库,我已经将其本地重写为false
。
但这并没有转换签出文件中的行尾。我该怎么做?
- 如果我使用例如
dos2unix
手动转换文件,则它们显示为已更改 - 也尝试了
git checkout --force HEAD
,但没有效果
我找到的唯一工作方法是删除所有文件,然后删除git reset --hard
,这相当尴尬(=没有简单可靠的命令可以做到这一点,而且它做了很多不必要的工作——所有东西都是从头开始重新创建的,而不仅仅是覆盖需要转换的文件)。
TL;DR
这是三种可能的解决方案(不一定只有三种)。
-
使用:
git add --renormalize .
(在存储库的顶层完成一次)。这需要更新的Git,但这是最简单的方法。
注意:我根本不清楚这是否会影响工作树版本;您可能仍然需要git checkout -- .
来从索引重新复制到工作树。
- 对于
git status
抱怨的每个文件:rmfile; git checkout --file
。rm
删除工作树副本,因此git checkout
必须根据新行结束规则重新提取文件
您可以使用git rm -r .; git checkout HEAD -- .
(仅两个命令)在一定程度上简化这一过程,但这会产生副作用,即接触工作树中的所有文件,甚至是不需要更改的任何文件(其中没有回车的文件)。
- 按原样使用
dos2unix
,然后在文件上(或在.
上)运行git add
。尽管表面上如此,这应该会使指数保持不变
在所有情况下,之后,git status
应为nothing to commit, working tree clean
。
Long
这不是完全Git的副本:如何在所有修订版中重新规范所有文件中的行尾?,因为您不想重新复制一堆现有的提交。然而,git add --renormalize
的答案应该有效。
或者,如果失败,或者你的Git太旧,无法使用--renormalize
选项:
如果我使用例如
dos2unix
手动转换文件,则它们显示为已更改。
您可以手动转换文件,然后git add .
,或者删除工作树副本,然后再次git checkout
。git 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风格的行尾。)它也是压缩的。
索引中的副本是可写的,但最初与提交中的副本匹配。因此,它也将具有仅LF的行结尾。它也被压缩了(一开始它实际上只是对提交副本的引用)。
工作树中的副本是未压缩的,并且具有您通过.gitattributes
文件(无)、core.autocrlf
和core.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 HEAD
和git checkout-index
似乎都没有认识到EOL转换的差异