与其直接回答问题本身——请参阅链接问题的VonC's answer——让我们专注于这个:
我目前有一个包含多个损坏文件的存储库,我想将其转换为 crlf 表示形式。
首先,让我们注意这些选项都不能更改任何现有的提交。这是 Git 的一个基本属性:一旦提交,现有的提交就无法更改。您可以做的是 new 提交。这通常没什么大不了的,因为通常我们只希望 new 的东西是正确的(但请参阅git filter-branch,它在将过滤器应用于其内容后复制提交,并可用于重新复制整个存储库:新存储库不再与旧存储库兼容,但您可以通过这种方式“修复历史记录”)。
接下来,我认为这是理解所有这些行尾/CRLF 属性选项的关键:文件在移入或移出索引时应用到文件。
请记住,Git 的索引是您构建 next 提交的位置。索引的内容最初与当前提交的内容相同:例如,您运行 git checkout master,Git 将名称 master 解析为提交 ID,并将该特定提交复制到您的工作树中——但是复制通过索引。
换句话说,Git 首先发现文件foo.txt 在提交中(并且需要提取)。所以 Git 将那个版本的 foo.txt 移动到索引中。索引的版本完全匹配HEAD commmit 的版本。 Git 不会对索引版本应用任何过滤器,也不会更改任何行尾。
一旦更新了索引版本,Git 就会将该版本的文件从从索引复制到工作树。1 一些转换在此提取过程中现在发生。如果有污迹过滤器,Git 现在会应用它。如果要进行行尾转换,Git 现在会应用这些转换。
在此过程中,工作树文件可能会与索引版本不同。现在 Git 有一个问题,因为现在文件是“脏的”(在工作树中修改)。这是事情变得特别令人困惑的地方,尽管大多数时候,这里的细节是不可见的。
最终,在使用您的工作树之后,您可以在某个文件路径名上运行git add(或使用git add -a 或其他任何方式来添加许多文件)。这会将工作树中的文件复制到索引中。2 现在,在此复制期间会发生更多转换:如果有一个干净的过滤器,Git 现在会应用它。如果要进行行尾转换,Git 现在会应用它们。
换句话说,在git add-ing 这些文件之后,index 版本可能与工作树版本不匹配。但是,无论如何,Git 都会将索引版本标记为“匹配”。 git status 将直接跳过工作树版本,因为 Git 现在声称索引版本与工作树版本匹配。 有点,因为如果您再次运行git add,索引版本与将添加的内容相匹配。
实际实现使用时间戳,通常分辨率为一秒。 Git 将继续相信索引版本与工作树版本匹配,除非并且直到操作系统触及文件工作树版本上的时间戳。 即使您更改要应用的过滤器和/或行尾转换集也是如此。 Git 没有意识到您已经更改了行尾应该工作的方式,或者更改了“干净” 过滤器做一些不同的事情:它只是看到索引的“缓存”方面说“我匹配工作树版本时间戳T”。只要工作树版本的时间戳仍然是T,文件就必须是“干净的”。
因此,要在更改任何文本转换设置后更新这些内容,您需要让 Git 意识到文件不干净。您可以touch <path> 设置一个新的时间戳“现在”,它不会匹配索引中的旧时间戳。现在git add -a(或其他)会像往常一样扫描,但由于时间戳不匹配,这次它会找到文件,并重新过滤以将其添加到索引中。
同样,这些转换发生在您git add 文件时。
通常,在类似 Windows 的系统上,您的目标是获取仅 LF 存储库格式的文件并将它们转换为 CR-LF 文件供 Windows 处理。这种转换发生在索引 out 的路上,到 工作树:即在git checkout 期间。然后,您可能希望在 git add 过程中将这些 CR-LF 工作树文件转换为仅 LF 格式,以便存储库中的形式是 Linux(以及 Linus Torvalds 和 Git :-) 更喜欢它们的方式.但是,如果您真的想惹恼所有 Unix/Linux 人员,您可以将它们以 CR-LF 格式存储在存储库中。这完全取决于您在哪个步骤应用哪些转换(如果有):git checkout 时间和git add 时间。
.gitattributes 文件指定将哪些转换应用于哪些文件。 core.autocrlf 和 core.eol 设置不要:Git 必须对哪些文件在哪个步骤进行哪些转换做出最佳猜测。
1从技术上讲,索引中的所有内容都是文件的哈希 ID。文件本身作为 Git blob 对象存储在存储库数据库中。就像提交对象一样,这些 blob 对象是不可变的。这就是 为什么它不能在索引中更改:它实际上只是一个哈希 ID。
2git add 进程简单地写入一个 new blob,新的 blob 在任何过滤后写入。如果新 blob 与某个现有 blob 逐位完全匹配,则新 blob 会重新使用现有 blob 的数据库条目和哈希 ID,并且不会实际保存 - 现有 blob 就足够了。如果不是,则 blob 的数据将存储为具有新 ID 的新文件。这是进入索引的新哈希 ID。