本文档旨在描述 Subversion 1.2。如果您运行的是更新版本的 Subversion,我们强烈建议您访问 https://svnbooks.subversion.org.cn/ 并查阅适合您 Subversion 版本的书籍。
目录
Subversion 版本库是任何数量项目的版本化数据的中央存储库。因此,它成为管理员可以提供的所有关怀和关注的明显目标。虽然版本库通常是低维护的项目,但了解如何正确配置和维护它以避免潜在问题并安全地解决实际问题非常重要。
在本章中,我们将讨论如何创建和配置 Subversion 版本库。我们还将讨论版本库维护,包括使用 svnlook 和 svnadmin 工具(随 Subversion 提供)。我们将解决一些常见问题和错误,并提供一些关于如何安排版本库中数据的建议。
如果您计划以版本控制下数据用户的身份(即通过 Subversion 客户端)访问 Subversion 版本库,则可以完全跳过本章。但是,如果您是或希望成为 Subversion 版本库管理员,[13] 您一定要关注本章。
在深入探讨版本库管理的更广泛主题之前,让我们进一步定义版本库是什么。它看起来像什么?感觉如何?它喝茶是热的还是冰的,加糖,加柠檬吗?作为管理员,您应该从逻辑角度(处理数据如何在版本库中表示)和物理层面(版本库相对于非 Subversion 工具的外观和行为)来理解版本库的组成。以下部分将非常简要地介绍一些这些基本概念。
从概念上讲,Subversion 版本库是一系列目录树。每个树都是对您版本库中版本化的文件和目录在某个时间点的快照。这些快照是由于客户端操作而创建的,称为版本。
每个版本都作为事务树开始其生命周期。当进行提交时,客户端会构建一个 Subversion 事务,它反映了他们的本地更改(加上自客户端提交过程开始以来可能对版本库进行的任何其他更改),然后指示版本库将该树存储为序列中的下一个快照。如果提交成功,事务将有效地提升为新的版本树,并被分配一个新的版本号。如果提交由于某种原因失败,事务将被销毁,并通知客户端失败。
更新以类似的方式工作。客户端会构建一个临时事务树,它反映了工作副本的状态。然后版本库将该事务树与请求版本的版本树(通常是最新的或“最年轻”的树)进行比较,并发送回信息,告知客户端需要进行哪些更改才能将他们的工作副本转换为该版本树的副本。更新完成后,将删除临时事务。
使用事务树是永久更改版本库版本化文件系统的唯一方法。但是,重要的是要了解事务的寿命是完全灵活的。在更新的情况下,事务是立即销毁的临时树。在提交的情况下,事务被转换为永久版本(如果提交失败则删除)。如果发生错误或 bug,可能会意外地在版本库中留下一个事务(实际上不影响任何内容,但仍然占用空间)。
理论上,有一天,整个工作流程应用程序可能会围绕对事务生命周期的更细粒度控制展开。可以想象一个系统,其中每个计划成为版本的交易都将在客户端完成对版本库的更改描述之后很长时间内处于停滞状态。这将使每个新的提交都可以由其他人(可能是经理或工程 QA 团队)进行审查,他们可以选择将事务提升为版本,或中止它。
Subversion 版本库中的事务和版本可以附加属性。这些属性是通用的键值映射,通常用于存储与其附加的树有关的信息。这些属性的名称和值与您树数据的其余部分一起存储在版本库的文件系统中。
版本和事务属性对于将与树相关联的信息关联起来很有用,这些信息与树中文件和目录没有严格关系,即由客户端工作副本不管理的信息。例如,当在版本库中创建新的提交事务时,Subversion 会向该事务添加一个名为 svn:date
的属性,它是一个日期戳,表示事务创建的时间。在提交过程完成并且事务被提升为永久版本时,该树也被赋予了一个属性来存储版本的作者用户名 (svn:author
) 和一个属性来存储附加到该版本的日志消息 (svn:log
)。
版本和事务属性是 非版本化属性,因为它们被修改时,其先前的值将被永久丢弃。此外,虽然版本树本身是不可变的,但附加到这些树的属性却不是。您可以在将来任何时间添加、删除和修改版本属性。如果您提交了一个新的版本,后来意识到您的日志消息中有一些错误信息或拼写错误,您只需将 svn:log
属性的值替换为新的更正后的日志消息即可。
从 Subversion 1.1 开始,有两种在 Subversion 版本库中存储数据的选择。一种类型的版本库将所有内容存储在 Berkeley DB 数据库中;另一种类型的版本库使用自定义格式将数据存储在普通的平面文件中。由于 Subversion 开发人员通常将版本库称为“(版本化的)文件系统”,因此他们习惯于将后一种类型的版本库称为 FSFS [14],它是一种使用本地操作系统文件系统来存储数据的版本化文件系统实现。
创建版本库时,管理员必须决定是使用 Berkeley DB 还是 FSFS。两者各有优缺点,我们稍后会介绍。两种后端都没有比另一种更“官方”,访问版本库的程序与这种实现细节无关。程序不知道版本库是如何存储数据的;它们只通过版本库 API 查看版本和事务树。
表 5.1,“版本库数据存储比较” 概述了 Berkeley DB 和 FSFS 版本库的比较。接下来的部分将详细介绍。
表 5.1。版本库数据存储比较
特性 | Berkeley DB | FSFS |
---|---|---|
对中断的敏感度 | 非常敏感;崩溃和权限问题会导致数据库“卡住”,需要进行日志恢复过程。 | 相当不敏感。 |
可从只读挂载使用 | 否 | 是 |
平台无关存储 | 否 | 是 |
可通过网络文件系统使用 | 否 | 是 |
版本库大小 | 略大 | 略小 |
可扩展性:版本树数量 | 数据库;没有问题 | 一些旧的本地文件系统无法很好地扩展到单个目录中的数千个条目。 |
可扩展性:包含许多文件的目录 | 速度较慢 | 速度较快 |
速度:检出最新代码 | 速度较快 | 速度较慢 |
速度:大型提交 | 速度较慢,但工作分散在整个提交过程中 | 速度较快,但最终化延迟可能导致客户端超时 |
组权限处理 | 对用户 umask 问题敏感;最好只由一个用户访问。 | 绕过 umask 问题 |
代码成熟度 | 自 2001 年起使用 | 自 2004 年起使用 |
在 Subversion 的初始设计阶段,开发人员决定使用 Berkeley DB,原因有很多,包括它的开源许可证、事务支持、可靠性、性能、API 简洁性、线程安全性、对游标的支持等等。
Berkeley DB 提供真正的交易支持,这可能是它最强大的功能。访问您的 Subversion 版本库的多个进程不必担心意外地破坏彼此的数据。事务系统提供的隔离使 Subversion 版本库代码能够看到数据库的静态视图(而不是由其他进程不断更改的数据库),并可以根据该视图做出决策。如果做出的决策恰好与其他进程正在做的事情发生冲突,整个操作将被回滚,就像它从未发生过一样,Subversion 会优雅地针对新的更新的(但仍然是静态的)数据库视图重试操作。
Berkeley DB 的另一个很棒的功能是 热备份,即能够在不将数据库“脱机”的情况下备份数据库环境。我们将在 名为“版本库备份”的部分 中讨论如何备份您的版本库,但能够在没有任何停机时间的情况下创建您的版本库的功能副本的好处应该是显而易见的。
Berkeley DB 也是一个非常可靠的数据库系统。Subversion 使用 Berkeley DB 的日志记录功能,这意味着数据库首先将要进行的任何修改的描述写入磁盘日志文件,然后进行修改本身。这样做是为了确保如果出现任何问题,数据库系统可以备份到先前的 检查点(日志文件中已知没有损坏的位置)并重播事务,直到数据恢复到可使用状态。有关 Berkeley DB 日志文件的更多信息,请参阅 名为“管理磁盘空间”的部分。
但是,每朵玫瑰都有它的刺,因此我们必须注意 Berkeley DB 的一些已知局限性。首先,Berkeley DB 环境不可移植。您不能简单地将创建于 Unix 系统上的 Subversion 版本库复制到 Windows 系统上并期望它能正常工作。虽然 Berkeley DB 数据库格式的大部分是体系结构无关的,但环境的其他方面并非如此。其次,Subversion 使用 Berkeley DB 的方式不会在 Windows 95/98 系统上运行,如果您需要将版本库放在 Windows 机器上,请坚持使用 Windows 2000 或 Windows XP。此外,您永远不要将 Berkeley DB 版本库保留在网络共享上。虽然 Berkeley DB 承诺在满足特定规范的网络共享上正常运行,但几乎没有已知的共享实际满足所有这些规范。
最后,由于 Berkeley DB 是直接链接到 Subversion 的库,它比典型的关系型数据库系统更容易受到中断的影响。例如,大多数 SQL 系统都有一个专用的服务器进程来协调对所有表的访问。如果访问数据库的程序由于某种原因崩溃,数据库守护进程会注意到连接丢失并清理任何遗留的混乱。由于数据库守护进程是唯一访问表的进程,因此应用程序无需担心权限冲突。然而,Berkeley DB 并非如此。Subversion(以及使用 Subversion 库的程序)直接访问数据库表,这意味着程序崩溃可能会使数据库处于暂时不一致且无法访问的状态。发生这种情况时,管理员需要要求 Berkeley DB 恢复到检查点,这有点令人讨厌。除了崩溃的进程外,其他问题也会导致存储库“卡住”,例如程序在数据库文件的所有权和权限上发生冲突。因此,虽然 Berkeley DB 存储库非常快且可扩展,但最好由作为单个用户运行的单个服务器进程使用,例如 Apache 的 httpd 或 svnserve(参见 第 6. 章,服务器配置)— 而不是通过 file:///
或 svn+ssh://
URL 作为多个不同用户访问它。如果直接作为多个用户使用 Berkeley DB 存储库,请务必阅读 名为“支持多种存储库访问方法”的部分。
在 2004 年年中,第二种类型的存储库存储系统诞生了:它根本不使用数据库。FSFS 存储库将修订树存储在一个文件中,因此存储库的所有修订都可以在一个装满编号文件的子目录中找到。事务在单独的子目录中创建。完成后,将创建单个事务文件并将其移动到 revisions 目录,从而确保提交是原子的。由于修订文件是永久的且不变的,因此存储库也可以在“热”状态下备份,就像 Berkeley DB 存储库一样。
修订文件格式表示修订的目录结构、文件内容以及与其他修订树中文件的增量。与 Berkeley DB 数据库不同,此存储格式可以在不同的操作系统之间移植,并且对 CPU 架构不敏感。由于没有使用日志记录或共享内存文件,因此可以安全地通过网络文件系统访问存储库并在只读环境中对其进行检查。没有数据库开销也意味着整个存储库的大小会稍微小一些。
FSFS 也有不同的性能特征。提交包含大量文件的目录时,FSFS 使用 O(N) 算法追加条目,而 Berkeley DB 使用 O(N^2) 算法重写整个目录。另一方面,FSFS 将文件的最新版本写入为相对于早期版本的增量,这意味着签出最新树的速度比从 Berkeley DB HEAD 修订中获取全文要慢。FSFS 在完成提交时也有更长的延迟,在极端情况下会导致客户端在等待响应时超时。
然而,最重要的区别是 FSFS 在出现问题时无法“卡住”。如果使用 Berkeley DB 数据库的进程遇到权限问题或突然崩溃,则数据库将无法使用,直到管理员恢复它。如果使用 FSFS 存储库的进程发生相同的情况,则存储库不会受到影响。最糟糕的是,一些事务数据会留下来。
反对 FSFS 的唯一真正论据是它与 Berkeley DB 相比还比较不成熟。它还没有被使用或压力测试过那么多,因此关于速度和可扩展性的许多断言仅仅是:断言,基于合理的猜测。从理论上讲,它承诺为新管理员提供更低的入门门槛,并且不易出现问题。在实践中,只有时间才能证明一切。