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