eol=lf 指令将阻止 Git 添加回车,但不会阻止 Git保留现有的回车。
真正理解这里发生的事情需要一点关于 Git 如何在提交中存储文件的知识。关键是:
-
每次提交都以只读、压缩、仅 Git 和去重的格式存储每个文件的完整快照。这意味着您实际看到和处理/使用的文件不是 in 存储库:您使用的文件在您的 working tree 或 work-tree。
-
任何提交的所有部分,包括它的所有文件,实际上都是不可更改的。如果您从存储库中取出一个 Git 内部对象(包括提交),以某种方式对其进行修改,然后将其放回去,您并没有更改 original; 相反,您只是添加了 另一个,这个新的得到一个不同的哈希ID。
-
要从提交中获取文件到您的工作树中,Git 必须将它们复制出来。这很明显;不明显的是每个文件都有第三个“副本”。第三个(实际上是中间)副本存在于 Git 所谓的 index 或 暂存区 中,或者(现在很少见)缓存。这三个名称都指同一个实体。
也就是说,假设HEAD 附加到分支名称master 和master 当前表示其哈希ID 为a123456... 的提交。换句话说,这个带有丑陋哈希 ID 的提交是你的当前提交。在这个提交中,我们有名为 README.md 和 main.py 的文件,在你的情况下是 web/migrate.sh。该文件有三个“副本”。这里的“副本”用引号引起来,因为其中两个是自动去重的格式,所以实际上只有一个底层副本。
我们可以在一张表中说明这三个副本,使用特殊名称HEAD 来指代提交a123456...(当前提交):
HEAD index work-tree
-------------- -------------- --------------
README.md README.md README.md
main.py main.py main.py
web/migrate.sh web/migrate.sh web/migrate.sh
这些文件是从哪里来的?好吧,当您第一次克隆存储库时,您的 Git 会从其他 Git 获取所有提交。这些提交在每个 Git 中完全相同,并且在每个 Git 中具有相同的哈希 ID。然后,您的 Git 将其中一个提交(您正在签出的提交)复制到您的 Git 索引,并将文件从其索引复制到您的工作树。这样就可以得到每个文件的三个副本。
工作树文件是普通的日常文件,您可以使用计算机上的任何程序对其进行读写。其他文件不是。当(或之后)您对其中一个文件的工作树副本完成一些工作时,您可以在其上运行git add。原因是git add 告诉 Git:使索引副本与工作树副本匹配。因此,如果您更改了main.py,例如,索引中main.py 的版本 现在与存储库中main.py 的版本 不同:
HEAD index work-tree
-------------- -------------- --------------
README.md(1) README.md(1) README.md
main.py(1) main.py(2) main.py
web/migrate.sh(1) web/migrate.sh(1) web/migrate.sh
提交中的副本实际上是不可更改的,因此HEAD(目前是commit a123456... 的缩写)始终包含这三个版本的文件。但是该索引虽然使用内部格式,但它不是提交1 并且不是 只读的。所以git add 可以替换索引副本。
(运行 git commit 获取索引中的任何内容并使用它来进行新的提交。然后新的提交成为当前的提交,因此 name HEAD 和当前的分支名称,现在参考 new 提交,而不是提交 a123456...。但我们还不需要走那么远。)
1它是什么,有点复杂,但大致而言,您可以将索引视为保存您的建议的下一次提交时间>。每次你签出某个提交时,Git 必须设置索引以准备下一个提交:通常,通过从你刚刚签出的提交中填写它。
从索引复制或复制到索引是 Git 调整行尾的时候
Git 索引中的文件副本采用压缩的、仅限 Git 的、去重复的格式。工作树中文件的副本是普通的日常计算机格式。因此,任何时候 Git 将 从 Git 的索引 复制到您的工作树,它都必须扩展文件;并且任何时候 Git 将您的工作树 从复制到其索引到,它必须压缩和去重文件。
此复制过程是对文件进行任何更改的理想时机。所以这就是.gitattributes 和行尾的东西发挥作用的地方。假设文件 in 通过存储库中的索引到达那里,具有换行符终止的行,只有\n。假设您希望文件的 work-tree 副本具有 \r\n 或 CRLF 行结尾。
如果 Git 在 索引的途中将 \n 变成 \r\n,并在 到索引,这可以实现您的目标。这就是* text eol=crlf 会做的事情。
但如果你不想这样呢?如果您希望\n 结尾保持\n 结尾怎么办?这就是* text eol=lf 会做的事情。 \n 结尾如何保持\n 结尾? 不做任何改变。
所以* text eol=lf 表示不要进行更改。但是,如果存储库中的文件(因此被复制到索引中)有 \r\n (CRLF) 行结尾怎么办?那么,你的工作树文件也是如此。
要使 存储库 中的某些文件具有 \n-only 行尾,您需要:
- 从工作树副本中删除
\r;
-
git add 生成的文件;和
-
git commit 进行新的提交。
然后可以将此新提交分发到此存储库的所有其他副本,并用于代替这些文件具有 \r\n (CRLF) 结尾的现有(错误)提交。
请注意,错误提交将继续存在:这就是修订控制的全部意义所在。我们不会消除坏的,因为其他人也有它们,我们要记住他们使用的是坏的。
现在,如果没有其他人拥有此存储库的副本,或者提交错误,那么我们处于特殊情况。在 这种 情况下,我们可以放弃错误的提交,转而使用新的和改进的提交。 (具体如何在 Git 中做到这一点是另一个答案的主题。)但一般来说,我们只是添加一个修复,并保留原来的。