您将一些单独的概念混合在一起(正如您使用 annotate 子命令发现的那样)。注释(git annotate
、svn annotate
等)或有时称为“责备”要求 VCS 向您显示有关源本身和具有该特定源片段的最早修订版的信息——通常是一行,因为我们主要处理“代码行” “——与它在一个特定版本中的形式相同。
但这不是您仍在寻找的内容,所以让我们深入研究。所有版本控制系统都有一个问题:如果您要存储每个文件的每个版本,则可能需要大量存储空间。
因此,大多数 VCS 使用某种压缩方式,并且最直接跳到使用delta 编码,也称为delta 压缩:完整地存储一个版本,而不是完整地存储第二个版本,只存储一组可用于修改存储的完整版本以获得第二个版本。
对许多版本重复此操作,您将获得链式 deltas,您从一些初始版本开始并反复修改它以获得最终版本。最初,SCCS 将其用于“正向”方向:存储初始版本,然后将对其的第一个更改存储为第二个版本,然后将第二个版本的更改存储为第三个版本,依此类推。显然,这有点慢。因此,RCS 使用反向增量:完整地存储最新版本,同时存储增量以计算其后继版本的早期版本。这使得检索最新版本很快,而最旧版本很慢——通常是人们想要的——但它有一个不同的缺点:它不能与分支一起正常工作。因此,RCS 实际上在其主干上使用反向增量并在其分支上转发 deltas 。
由于 CVS 是(或曾经)基于 RCS 文件格式构建的,因此它也使用这种反向但也正向的增量存储格式。
为了性能,现代 SCCS 使用称为交错增量的概念,其中存储库存储相当于所有版本的一种联合的内容。通过存储文件的线性传递足以提取任何特定版本。(链接的维基百科页面说 Bitkeeper 也使用交错的增量。)
我个人不知道 Subversion 在内部使用什么,但是subversion 究竟是如何将文件存储在存储库中的?建议它使用反向增量和快照。烘焙快照(另一个“完全完整”的版本)时不时地提供了一种设置要应用的增量数量限制的方法。
Git 做了一些完全不同的事情。Git 不是单独存储每个文件并针对该文件的先前版本进行增量压缩,而是存储它所谓的objects。每个对象至少在逻辑上是独立的(快照)——因此任何文件的任何版本都不会被增量压缩,尽管每个快照都是 zlib 压缩的。为了节省额外的空间,Git 偶尔会将多个对象压缩到一个“包文件”中,在这里,包文件内部对象确实得到了增量压缩……但针对任何其他对象,而不仅仅是代表相同源文件的对象。(特别是这允许 Git 使用其他树对象压缩树对象。)
最后,这对 Git 意味着它在可能的任何方向上使用增量压缩——这可能是正向和反向混合的——使用快照来保持链长度有限(--depth
或pack.depth
),但对象与对象的关系,不一定只是一个文件针对同一文件的另一个版本。出于实际目的,Git 不会完全随意选择这些对象(很难知道哪些对象会与其他对象一起压缩),而是根据对象类型、对象大小、在树对象中找到的文件名以及年龄(我不确定它从哪里得到这些年龄),所以 Git 经常以相当于反向增量的结果结束,但不能保证。(Git 还会重用之前包中预先计算的 delta 链,除非你告诉它不要这样做;参见--no-reuse-delta
aka -f
。)
作为一般规则,没有办法告诉修订系统完全修改其内部存储格式。一些 VCS 可能有例外。例如,Git 有点不寻常,因为git repack
它确实做到了,但对您的特定用例没有用处!