本手册是为了描述 Subversion 1.1 而编写的。如果您运行的是更新版本的 Subversion,我们强烈建议您访问 https://svnbooks.subversion.org.cn/ 并参阅适合您 Subversion 版本的版本。
目录
Subversion 仓库是任何数量项目的版本化数据的中央存储库。因此,它成为管理员可以提供的全部关爱和关注的明显候选对象。虽然仓库通常是一个低维护项目,但了解如何正确配置和维护它非常重要,这样可以避免潜在问题,并安全地解决实际问题。
在本章中,我们将讨论如何创建和配置 Subversion 仓库。我们还将讨论仓库维护,包括使用 svnlook 和 svnadmin 工具(这些工具随 Subversion 提供)。我们将解决一些常见问题和错误,并提供一些有关如何在仓库中排列数据的建议。
如果您计划以版本控制数据的用户的身份访问 Subversion 仓库(即,通过 Subversion 客户端),则可以完全跳过本章。但是,如果您是或希望成为 Subversion 仓库管理员,[12] 您一定要关注本章。
在深入了解仓库管理的更广泛主题之前,让我们进一步定义什么是仓库。它看起来像什么?感觉如何?它喜欢喝热的还是冰的茶,加糖吗?加柠檬吗?作为管理员,您将需要从逻辑角度(处理如何在仓库内部表示数据)和物理角度(仓库在非 Subversion 工具方面的外观和行为)来了解仓库的构成。以下部分将从非常高的层面介绍一些这些基本概念。
从概念上讲,Subversion 仓库是一系列目录树。每个树都是您仓库中版本化的文件和目录在某个时间点的样子的一张快照。这些快照是由于客户端操作而创建的,称为修订版本。
每个修订版本都是从一个事务树开始的。当进行提交时,客户端会构建一个 Subversion 事务来镜像其本地更改(加上自客户端提交过程开始以来可能对仓库进行的任何其他更改),然后指示仓库将该树存储为序列中的下一个快照。如果提交成功,事务将有效地提升为一个新的修订版本树,并被分配一个新的修订版本号。如果提交由于某种原因失败,则事务将被销毁,并且客户端会收到失败通知。
更新的工作方式类似。客户端会构建一个临时事务树来镜像工作副本的状态。然后,仓库将该事务树与请求的修订版本处的修订版本树(通常是最新的或“最年轻的”树)进行比较,并发送回信息,通知客户端需要进行哪些更改才能将其工作副本转换为该修订版本树的副本。更新完成后,将删除临时事务。
使用事务树是永久更改仓库版本化文件系统的唯一方法。但是,了解事务的生命周期是完全灵活的这一点很重要。对于更新,事务是临时树,会立即被销毁。对于提交,事务将转换为永久修订版本(如果提交失败,则会删除)。在发生错误或 bug 时,可能会有一个事务意外地留在仓库中(实际上没有影响任何东西,但仍然占用空间)。
从理论上讲,将来有一天,整个工作流应用程序可能会围绕对事务生命周期的更细粒度控制而展开。可以想象一个系统,在这个系统中,每个要成为修订版本的事务在客户端完成对其更改的描述到仓库后会处于静止状态。这将使每个新的提交都可以由其他人(可能是经理或工程 QA 团队)进行审查,他们可以选择将事务提升为修订版本,或者中止它。
Subversion 仓库中的事务和修订版本可以附加属性。这些属性是通用的键值映射,通常用于存储与其附加的树有关的信息。这些属性的名称和值存储在仓库的文件系统中,与您树数据的其余部分一起。
修订版本和事务属性对于将与树相关联的信息(不严格与该树中的文件和目录相关联的信息)相关联很有用,这些信息不是由客户端工作副本管理的。例如,当在仓库中创建一个新的提交事务时,Subversion 会向该事务添加一个名为svn:date的属性,它是一个日期戳,表示创建事务的时间。到提交过程完成时,事务已被提升为永久修订版本,该树也已获得一个属性来存储修订版本作者的用户名(svn:author)和一个属性来存储附加到该修订版本的日志消息(svn:log).
修订版本和事务属性是 非版本化属性,因为它们被修改时,它们以前的值会被永久丢弃。此外,虽然修订版本树本身是不可变的,但附加到这些树的属性不是。您可以在将来的任何时间添加、删除和修改修订版本属性。如果您提交了一个新的修订版本,并且后来意识到您的日志消息中存在一些错误信息或拼写错误,您只需用新的、更正后的日志消息替换svn:log属性的值。
从 Subversion 1.1 开始,有两种在 Subversion 仓库中存储数据的方法。一种类型的仓库将所有内容存储在 Berkeley DB 数据库中;另一种类型的仓库将数据存储在普通平面文件中,使用自定义格式。因为 Subversion 开发人员经常将仓库称为“版本化文件系统”,所以他们习惯于将后一种类型的仓库称为 FSFS:也就是说,它是一个使用本机操作系统文件系统来存储数据的版本化文件系统实现。
创建仓库时,管理员必须决定它将使用 Berkeley DB 还是 FSFS。每种方法都有其优点和缺点,我们将在稍后进行描述。这两种后端都没有比另一种更“官方”,访问仓库的程序与这种实现细节无关。程序不知道仓库是如何存储数据的;它们只通过仓库 API 查看修订版本和事务树。
以下表格对 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 以一种无法在 Windows 95/98 系统上运行的方式使用 Berkeley DB,如果您需要将仓库放在 Windows 计算机上,请坚持使用 Windows 2000 或 Windows XP。此外,您永远不要将 Berkeley DB 仓库放在网络共享上。虽然 Berkeley DB 承诺在满足特定规范的网络共享上正常运行,但几乎没有已知的共享实际上满足所有这些规范。
最后,由于 Berkeley DB 是直接链接到 Subversion 的库,因此它比典型的关系型数据库系统更容易受到中断的影响。例如,大多数 SQL 系统都有一个专用的服务器进程来协调对所有表的访问。如果访问数据库的程序由于某种原因崩溃,数据库守护进程会注意到连接丢失并清理任何遗留的混乱。而且,由于数据库守护进程是访问表的唯一进程,因此应用程序无需担心权限冲突。然而,Berkeley DB 并非如此。Subversion(以及使用 Subversion 库的程序)直接访问数据库表,这意味着程序崩溃可能会使数据库处于暂时不一致、无法访问的状态。发生这种情况时,管理员需要要求 Berkeley DB 还原到检查点,这有点烦人。除了进程崩溃之外,其他一些事情也会导致存储库“卡住”,例如程序在数据库文件的所有权和权限上发生冲突。因此,虽然 BerkeleyDB 存储库速度很快且可扩展性强,但最好由单个服务器进程以一个用户身份运行,例如 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 相比还比较稚嫩。它还没有被使用或压力测试过很多次,因此关于速度和可扩展性的许多断言只是断言,基于良好的猜测。理论上,它承诺为新的管理员提供较低的入门门槛,并且不易出现问题。在实践中,只有时间才能证明一切。