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