【问题标题】:How are version control histories stored and calculated?版本控制历史是如何存储和计算的?
【发布时间】:2012-01-11 18:26:03
【问题描述】:

考虑一下这个简单的 python 代码,它演示了一个非常简单的字典版本控制设计:

def build_current(history):
    current = {}
    for action, key, value in history:
        assert action in ('set', 'del')
        if action == 'set':
            current[key] = value
        elif action == 'del':
            del current[key]
    return current

history = []
history.append(('set', '1', 'one'))
history.append(('set', '2', 'two'))
history.append(('set', '3', 'three'))
print build_current(history)
history.append(('del', '2', None))
history.append(('set', '1', 'uno'))
history.append(('set', '4', 'four'))
print build_current(history)
for action, key, value in history:
    if key == '2':
        print '(%s, %s, %s)' % (action, key, value)

请注意,通过使用历史列表,您可以将当前字典重建为它曾经存在的任何状态。我认为这是一个“前向构建”(因为没有更好的术语),因为要构建当前字典,必须从头开始并处理整个历史列表。我认为这是最明显和最直接的方法。

据我所知,早期的版本控制系统使用这种“前向构建”过程,但它们并不是最优的,因为大多数用户更关心构建的最新版本。此外,当用户只关心查看最新版本时,他们不想下载整个历史记录。

那么我的问题是,还有哪些其他方法可以在版本控制系统中存储历史记录?也许可以使用“向后构建”?这可能允许用户只下载最近的修订而不需要整个历史。我还有seen 几种不同的历史存储格式,即:变更集、快照和补丁。变更集、快照和补丁之间有什么区别?

在可用的现代流行版本控件中,它们如何存储历史记录以及各种设计的优势是什么?

【问题讨论】:

  • 这可能属于programmers.SE。
  • 我正在寻找有关特定算法和应用程序的具体细节;这是否属于程序员 SE 的所有职业建议问题?
  • 实际上,职业建议在那里是题外话,这类问题很快就解决了。算法非常切题。见the FAQ
  • 听起来不错。问题是如何转移的?
  • @Buttons840:将其标记为离题。

标签: python git svn version-control mercurial


【解决方案1】:

您提到了这3种存储(文件)历史的方法:

  1. patch :补丁是(通常是文本的,但也可能是二进制补丁)表示两个文件之间的差异。它是 unix 命令 diff 的输出,可以通过 unix 命令 patch 应用。许多版本控制系统正在使用补丁来存储文件的历史记录(例如 SVN、CVS、GIT..)。有时这些补丁在技术上被称为“delta”,因为希腊字母 "Δ" 描述了两件事的区别。
  2. 变更集:变更集是一个术语,用于将“属于一起”的变更组合到单个实体中的不同文件。并非所有版本控制系统都支持变更集(最著名的 CVS 和 SourceSafe)。开发人员正在使用变更集来避免损坏的构建(例如:在一个文件中更改方法的签名,在第二个文件中更改调用。您需要同时进行这两项更改才能运行程序,否则会出现错误)。 See also here for the difference between changeset and patch
  3. 快照:是该文件/文件系统到该时间点的状态的完整副本。它们通常很大,它们的使用取决于性能特征。快照对于补丁列表总是多余的,但是为了更快地检索信息,版本控制系统有时会混合或组合补丁和快照

Subversion 在 FSFS 存储库中使用前向增量(又名补丁),在 BDB 存储库中使用后向增量。 请注意,这些实现具有不同的性能特征:

  • 前向增量提交速度很快,但签出速度很慢(因为 “当前”版本必须重建)

  • 向后的 delta 签出速度很快,但在提交时很慢 deltas必须被构造来构造新的current,并将之前的“current”重写为一堆deltas

另请注意,FSFS 使用"skipping delta" 算法,该算法可最大限度地减少跳转以重建特定版本。然而,这个跳过的 delta 并没有像 mercurials 快照那样进行大小优化;它只是最大限度地减少了构建完整版本所需的“修订”数量,而不管整体大小。

这是一个包含 9 个修订版的文件的小型 ascii 艺术(从规范中复制):

0 <- 1    2 <- 3    4 <- 5    6 <- 7
0 <------ 2         4 <------ 6
0 <---------------- 4
0 <------------------------------------ 8 <- 9

其中“0

N个版本的跳转次数最多为log(N)。

对 FSFS 的另一个非常好的影响是旧版本将仅被写入 一次,之后它们将仅通过进一步的操作读取。 这就是 subversion 存储库非常稳定的原因:只要您的硬盘上没有硬件故障,即使上次提交中发生了一些损坏,您也应该能够获得一个工作存储库:您仍然拥有所有旧版本。

在 BDB 后端中,您不断地在签入/提交时重写当前版本,这使得该过程容易出现数据损坏。此外,由于您仅在当前版本中存储全文,因此在提交时损坏数据可能会破坏您的大部分历史记录。

【讨论】:

  • 我认为这并不完全准确。要使用反向增量签入,它只需要计算一个增量 - 只是最近的一个。
  • @Ariel,签入你是对的,但是如果你签出一个有 1000 个修订版本的文件,你不想添加 1000 个增量,因此 svn 使用跳过增量。另请参阅原始开发人员说明的附加说明
  • 而且您不需要添加 1000 个增量,文件中就有最后一个修订版。对于结帐,它是即时的,对于签入,您需要一个增量。跳过 delta 只有助于一件事:获取旧版本。但是检查速度较慢,并且在文件中占用更多空间。而且由于获取旧版本非常罕见,但签入非常频繁,因此跳过增量并不常见。
  • @Ariel,对不起,您没有得到正确的通知:SVN 使用前向增量(在 FSFS 中),因此文件的当前版本必须只存储增量(到以前的版本)。因此,要检查除第一个版本之外的任何版本,SVN 将从一系列 deltas 中组装文件。再次,请参阅附件说明了解详细信息。
  • 是的,我知道。你说:“向后的增量......作为新的增量提交速度很慢......重写以前的......”。这是不正确的。在后向增量上,只需要制作一个单一的增量。向后的增量在两者上都很快。
【解决方案2】:

我认为颠覆在向后构建方面做了一些尝试。但我可以解释一下我更了解的东西:Mercurial 快照。

Mercurial 使用前向构建方案。但是为了使每个修订版易于重建,有重新同步点:每次重建修订版所需的所有增量大小大于全文的两倍时,都会存储一个全文版本(压缩快照)并所有后续的增量都是相对于这个新快照计算的。

这意味着您无需阅读超过 3 倍大小的文本即可检索任何修订。

您可以找到更多详细信息in the Hg Book

【讨论】:

    【解决方案3】:

    作为更通用的答案,您需要将 CVCS(集中式 VCS,如 CVS、SVN、Perforce、ClearCase 等)与 DVCS (Distributed VCS, like Git or Mercurial) 区分开来。
    他们涉及different workflows and usage

    特别是,CVCS客户端与其服务器之间的数据交换将比使用 DVCS 更重要(在推送或拉取所有 repo 时确实需要 delta)

    这就是为什么增量对于 CVCS 中的大多数操作都非常重要,而对于某些操作和 DVCS 中的不同原因仅重要。

    Eric Sink 的两本书中描述了 Delta:

    存储库 = 文件系统 * 时间

    树是文件夹和文件的层次结构。增量是两棵树之间的差异。理论上,这两棵树不需要相关。然而,在实践中,我们计算它们之间差异的唯一原因是因为其中一个是从另一个派生而来的。一些开发人员从树 N 开始并进行了一项或多项更改,从而产生了树 N+1。

    我们可以将增量视为一组更改。事实上,许多 SCM 工具正是为此目的使用术语“变更集”。变更集只是表示两棵树之间差异的变更列表。

    delta 意义很重要(请参阅this thread):正向 delta 或反向 delta。

    一些 SCM 工具使用某种折衷设计。在一种方法中,我们不是只存储一棵完整的树并将所有其他树表示为一个增量,而是沿途再撒一些完整的树。

    您可以在Eric Raymond's Understanding Version-Control Systems 中看到“旧”VCS 的演变。

    许多现代版本控制工具使用二进制文件增量作为存储库存储。
    一种流行的文件增量算法称为vcdiff
    它输出已更改的字节范围列表。这意味着它可以处理任何类型的文件、二进制文件或文本。作为附带的好处,vcdiff 算法同时压缩数据。

    不要忘记增量管理也会影响为代表历史而创建的Directed Acyclic Graphs (DAGs)(请参阅“Arrows direction in ProGit book”和inconvenient behind DAG)。

    您可以找到关于 delta 管理的细节

    Veracity 支持两种 DAG:

    • 树 DAG 从文件系统中保存目录结构的版本历史。 DAG 的每个节点代表整棵树的一个版本。

    • 数据库(或“db”)DAG 保存数据库的版本历史或记录列表。 DAG 的每个节点代表整个数据库的一种状态。

    最后一点说明第三代(第四代?)VCS 不仅必须处理文件(树)的分布,还必须处理数据库(用于各种目的)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-22
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      • 2016-12-07
      相关资源
      最近更新 更多