Git 是否在内部存储行尾?

Ara*_*kis 0 git newline

Git 是否在内部存储特定的行尾?

我的猜测是,Git 仅存储行数据本身(行结束中性),并且根据操作系统,它将使用平台特定的行结束。

或者换句话说:每个行尾都可以“混合”存储在同一个文件中吗?

我不是在谈论.gitattributes文件中的设置。

iBu*_*Bug 5

Git 将整个文件存储为“blob”,并且仅在读取(签出)或写入(索引)时进行转换。混帐并没有存储“中性线”。

所以是的,Git 可以保存带有混合行尾的文件,但这通常是一个不好避免的做法。


tor*_*rek 5

正如iBug所说,Git只是存储一个原始数据块。Linus 最初在 Git 中没有任何形式的 CRLF 行结尾黑客技术:2005 年的最初版本根本没有这样做。第一个行结束转换代码于 2007 年 2 月在commit6c510bee2013022fbce52f4b0ec0cc593fc0cc48中引入 Git 。该.gitattributes文件本身稍后在 2007 年 4 月的commitd0bfd026a8241d544c339944976927b388d61a5e中引入。

\n

然而,理解这些的真正关键是注意每个文件的索引副本和每个文件的工作树副本之间的区别。请记住,索引实际上保存了建议的下一次提交,或者至少是其快照。(下一次提交的元数据是在您运行或执行任何其他命令时生成的。)任何现有git commit提交的内容都是神圣不可侵犯的:没有任何东西,甚至 Git 本身,都可以更改它们。

\n

萃取侧

\n

当您第一次检查一些提交\xe2\x80\x94时,例如,或,或与原始哈希ID相同(尽管需要这种情况的标志)\xe2\x80\x94Git将填写Git\的索引提交并填写您的工作树。(Git 索引和工作树中的任何先前提交都会首先从这两个位置删除,当当前分支上存在未提交的更改时,在签出另一个分支中概述了一些奇特的警告。)git checkout branchgit switch branchgit switch--detach

\n

索引准确获取提交中的内容。这意味着,如果提交的文件具有仅 LF 行和 CRLF 行的奇怪组合,或者是像 JPG 这样的二进制文件,其中随机散布二进制数据,天真的程序会认为行结尾,那么,那就是什么也进入索引。更准确地说,索引中文件的“副本”实际上只是Git 数据库中某些blob的原始哈希 ID 。保存某些现有的已提交文件的 blob 对象是只读的,因此很容易共享。因此,为了进行初始签出,Git 只是让索引共享该副本。Blob 哈希 ID 进入索引,存储在提交中列出的文件名下的槽零条目中。1 此 blob 存储在 Git 的对象数据库中,并且以松散对象形式压缩 xe2x80x94,或者以打包对象形式高度压缩。Git 可以读取其中任何一个;尽管 Git 可以很容易地创建新的(不同的)松散对象,但没有人也没有任何东西可以重写它。

\n

然而,工作树副本却是另一回事。Git必须解压缩 blob 对象。这意味着读取压缩的 blob 字节并对其运行 zlib 解压缩代码,以获得表示您希望看到的文件内容的不同字节。因为 Git 已经在做这项工作,所以这是 Git 做更多工作的理想场所: Git 可以用 CRLF 行结尾替换仅 LF 行结尾。2

\n

因此,当 Git将索引副本提取工作树中时,Git 可以将仅 LF 行转换为 CRLF 行。如果某个文件被标记(通过或任何其他方式)需要这种转换,Git 就会执行此转换;如果文件的 LF 前面没有 CR,Git 将确保写入工作树文件的内容首先具有 CR,然后是 LF。.gitattributes

\n

这是git checkout事情的一面。3 让我们暂停一下,看一下脚注,然后看看git add事情的侧面。

\n
\n

1从技术上讲,提交列出了代表快照的对象的树哈希 ID 。该树对象包含名称组成部分和哈希 ID。哈希 ID 可以是包含更多名称组件片段的子树的哈希 ID,也可以是表示应检出的文件的 blob 的哈希 ID。好吧,哈希 ID 可能是符号链接或 gitmodule 的哈希 ID,但这些相对较少:常见的树条目用于子树和文件 blob。

\n

2这种从 \xe2\x80\x94LF-only 到 CRLF\xe2\x80\x94 的转换是 Git 文件提取代码中内置的唯一行结束转换。您可以使用 Git 的污迹过滤器添加您自己的任意附加转换,但这些转换取决于您的编写。(请注意,Git-LFS 使用预先编写的污迹过滤器,从网络上其他位置的单独存储区域中提取“大型”文件系统。这是 Git 的附加组件,而不是 Git 本身的一部分。)

\n

3在新git restore命令进入 Git 2.23 之前,只有索引到工作树的转换才进行这种转换。现在git restore可以直接从工作树的提交中提取文件,还有一个地方可以做到这一点。请注意,or首先写入 Git 的索引,然后才写入工作树,因此此特定代码路径会经过索引到工作树函数。这就是为什么这个新功能值得一个脚注:在 Git 2.23 之前,你必须先让 Git 在 Git 的索引上乱写乱画;现在你可以避免这种情况了。git checkout -- path/to/filegit checkout commit -- path to filegit restore

\n
\n

旁边git add

\n

当您运行git add\xe2\x80\x94(包括来自 a 的隐含添加)时git commit -a,例如 \xe2\x80\x94Git此时会执行实际的艰苦工作,而不是等待稍后的git commit. 如果您使用的是git commit -a,那么提交可能会在几毫秒内发生,但逻辑仍然是相同的:首先,Git 会执行一个git add.

\n

重点git add是更新 Git 的索引。我们必须首先更新索引\xe2\x80\x94建议的下一次提交(或其快照)\xe2\x80\x94。只有当提议的提交与我们想要提交的内容匹配时,我们才能提交。

\n

由于索引保存路径名和 blob 哈希 ID(以及文件模式),因此此时 Git 必须将工作树文件转换为 blob。为此,Git 必须首先将该 blob 生成为新的松散对象\xe2\x80\x94,或者至少计算出它的哈希 ID(如果这样做的话)。事实证明,找出哈希 ID 的最快方法继续写入对象,计算哈希时使用 zlib\ 的压缩器\xe2\x80\x94 压缩\xe2\x80\x94。由于我们还不知道对象的名称(哈希 ID),因此我们只使用一个临时名称:例如,.git/objects/tmpXXXXXXXXes 中填充一些唯一的名称。(准确的临时名称在这里并不重要。)

\n

然而,为了将数据提供压缩和哈希函数,Git 必须读取文件的工作树副本。如果工作树副本被标记(通过.gitattributes或任何其他机制)需要转换,那么,这是检查 CR 后跟 LF 的最佳时机,并删除CR 部分,以便我们得到 LF-ony 行结尾。这样,散列函数zlib 压缩器都将获得仅 LF 的行。4

\n

一旦整个文件通过哈希函数和压缩器,Git 现在就拥有了正确的 blob 哈希 ID。Git 检查该对象是否已作为散列对象存在。如果是这样,Git 只是重新使用该现有对象,删除临时文件。5 否则,Git 会重命名临时文件,使其成为.git/objects/. 现在该文件有了一个对象,这就是 Git 索引中的内容。请注意,此时整个文件已转换为仅 LF 行结尾,无论之前的提交是什么。

\n
\n

4和以前一样,“删除紧随其后的 CR”过滤器是这些代码路径中 Git 中唯一内置的过滤器。您可以自己进行任意过滤,使用干净的过滤器自己进行任意过滤,但您必须自己编写。(不出所料,\xe2\x80\x94,如果您已阅读并理解脚注 2\xe2\x80\x94,这也是由 Git-LFS 作为附加组件自动提供的:如果文件“大”并且要存储在其他地方,Git-LFS\ 的“清理”过滤器将文件存储在其他位置,并生成 LFS 数据,以便稍后检出可以检索该文件,作为“清理”数据。)

\n

5 Git 可能应该检查\xe2\x80\x94,也许可选,因为这可能会很慢\xe2\x80\x94,这不是哈希冲突的结果。我认为目前还没有这样做。意外哈希冲突的可能性很小,可以忽略不计,但考虑到破坏 SHA-1的存在证据的存在证据,在我看来,可选检查将是一个好主意。

\n
\n

这一切仅在启用时才会发生

\n

要让 Git 对文件进行更改,必须将该文件标记为“应该修改”。设置core.autocrlftrue可以标记某些文件:Git 现在将尝试猜测某些文件是文本还是二进制。列出文件.gitattributes可以标记一些文件标记为特定的文本或特定的二进制文件,还可以将它们标记为特定的转换。

\n

不过,同样,唯一内置的转换是:

\n
    \n
  • 提取:将仅 LF 转换为 CRLF
  • \n
  • 添加:将 CRLF 改为仅 LF
  • \n
\n

有些设置启用这两种转换,有些设置仅启用添加端转换(crlf=input旧版本的 Git 中的 old ,以及eol=lf现代 Git 中的旧 )。

\n

请注意,由于 blob-to-working-tree 提取永远不会删除CR-before-LF,因此在其内部 blob 形式(一致或混合)中具有 CRLF 结尾的现有已提交文件始终会被检出为具有 CRLF 结尾的文件工作树。如果您不触摸处于此状态的签出文件,Git 会注意到您没有触摸它,并且不会添加该文件,因此它在下一次提交中继续具有混合或一致的 CRLF 结尾。

\n

git add --renormalize标志旨在强制 Git 重新添加文件,即使它们看起来未受影响。这样他们就可以完成 CRLF-to-LF-only 转换(如果已设置)。

\n