本文档旨在描述 Apache™ Subversion® 的 1.7.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。
由于 Subversion 仓库的整体设计以及它所依赖的技术的简单性,创建和配置仓库是一项相当简单的任务。您需要做出一些初步的决定,但任何给定 Subversion 仓库设置中涉及的实际工作都非常基础,如果您发现自己要设置多个这样的东西,则会趋向于无脑重复。
不过,您需要事先考虑一些事项,例如
您希望在您的仓库(或仓库)中存储哪些数据,以及这些数据将如何组织?
您的仓库将位于何处,以及如何访问它?
您需要哪些类型的访问控制和仓库事件报告?
您想使用哪种可用的数据存储类型?
在本节中,我们将尝试帮助您回答这些问题。
虽然 Subversion 允许您在不丢失任何信息的情况下移动版本化的文件和目录,甚至提供将整个版本化历史记录集从一个仓库移动到另一个仓库的方法,但这样做可能会极大地破坏经常访问仓库并期望事物位于特定位置的人的工作流程。因此,在创建新仓库之前,请尝试展望未来;在将数据置于版本控制之下之前,请提前计划。通过有意识地 “布局” 您的仓库或仓库及其版本化的内容,您可以避免许多未来的麻烦。
让我们假设您作为仓库管理员,将负责为多个项目提供版本控制系统支持。您的第一个决定是使用单个仓库来存储多个项目,还是为每个项目提供一个单独的仓库,或者在这两者之间进行一些折衷。
为多个项目使用单个仓库有很多好处,最明显的是没有重复维护。单个仓库意味着只有一套钩子程序,一个需要定期备份的东西,一个需要在 Subversion 发布不兼容的新版本时进行转储和加载的东西,等等。此外,您可以在项目之间轻松移动数据,而不会丢失任何历史版本信息。
使用单一仓库的缺点是,不同的项目可能对仓库事件触发器有不同的需求,例如需要将提交通知邮件发送到不同的邮件列表,或者对构成合法提交的定义不同。当然,这些问题并非不可克服——只是意味着所有钩子脚本都必须对仓库的布局敏感,而不是假设整个仓库都与单一组人员相关联。此外,请记住,Subversion 使用仓库全局修订号。虽然这些数字没有特别的魔力,但有些人仍然不喜欢这样一个事实,即使他们的项目最近没有进行任何更改,仓库的最新修订号也会不断增加,因为其他项目正在积极添加新的修订版本。[39]
也可以采用折中的方法。例如,可以根据项目之间的关联程度对项目进行分组。您可以在每个仓库中拥有几个仓库,其中包含少量项目。这样,可能需要共享数据的项目可以轻松地做到这一点,并且随着新修订版本添加到仓库中,至少开发人员知道这些新修订版本至少与使用该仓库的所有人相关联。
在决定如何组织项目与仓库的关系后,您可能需要考虑仓库本身的目录层次结构。由于 Subversion 使用常规目录副本进行分支和标记(参见 第 4 章,分支和合并),Subversion 社区建议您为每个项目根目录选择一个仓库位置——包含与该项目相关数据的“最顶层”目录——然后在该根目录下创建三个子目录:trunk
,表示进行主要项目开发的目录;branches
,用于创建主开发线的各种命名分支的目录;以及 tags
,它是一组创建的树快照,可能被销毁,但从不更改。[40]
例如,您的仓库可能如下所示
/
calc/
trunk/
tags/
branches/
calendar/
trunk/
tags/
branches/
spreadsheet/
trunk/
tags/
branches/
…
请注意,每个项目根目录在仓库中的位置并不重要。如果您每个仓库只有一个项目,那么将每个项目根目录放在其各自仓库的根目录下是合乎逻辑的。如果您有多个项目,您可能希望将它们按组排列在仓库中,例如将具有相似目标或共享代码的项目放在同一个子目录中,或者只是按字母顺序对它们进行分组。这样的安排可能看起来像这样
/
utils/
calc/
trunk/
tags/
branches/
calendar/
trunk/
tags/
branches/
…
office/
spreadsheet/
trunk/
tags/
branches/
…
以您认为合适的方式布局您的仓库。Subversion 不会期望或强制执行特定的布局——在其眼中,目录就是目录。最终,您应该选择满足在其中工作的项目的用户的需求的仓库安排。
为了完全公开,我们将提到另一种非常常见的布局。在此布局中,trunk
、tags
和 branches
目录位于仓库的根目录中,您的项目位于这些目录下的子目录中,如下所示
/
trunk/
calc/
calendar/
spreadsheet/
…
tags/
calc/
calendar/
spreadsheet/
…
branches/
calc/
calendar/
spreadsheet/
…
这种布局并没有什么特别错误,但它可能对您的用户来说并不直观。特别是在拥有许多用户的庞大、多项目的情况下,这些用户可能只熟悉仓库中的一个或两个项目。但是,将项目作为分支兄弟的方法往往会淡化项目个性,而将重点放在整个项目集作为一个单一实体。不过,这是一个社会问题。我们喜欢我们最初建议的安排,纯粹出于实际原因——当有一个单一的仓库路径保存单个项目的整个历史记录(过去、现在、标记和分支)时,更容易询问(或修改或迁移到其他地方)单个项目的整个历史记录,并且仅此项目。
在创建您的 Subversion 仓库之前,您需要回答的一个显而易见的问题是它将放在哪里。这与涉及如何访问仓库(通过 Subversion 服务器或直接访问)、由谁访问(您公司防火墙后面的用户或整个开放互联网上的用户)、您将在 Subversion 周围提供哪些其他服务(仓库浏览界面、基于电子邮件的提交通知等)、您的数据备份策略等等。
我们在第 6 章,服务器配置中介绍了服务器选择和配置,但我们在这里想简要说明的是,对这些其他问题的答案可能会对您决定存储库的位置产生影响。例如,某些部署场景可能需要从多台计算机通过远程文件系统访问存储库,在这种情况下(正如您将在下一节中看到的那样),您对存储库后端数据存储的选择实际上不是选择,因为只有可用的后端之一在这种情况下才能工作。
介绍 Subversion 的每种可能的部署方式既不可能,也不在本书的范围之内。我们只是鼓励您使用这些页面和其他资源作为参考材料来评估您的选择,并提前计划。
Subversion 为每个存储库使用的底层数据存储类型提供了两种选择——通常称为 “后端” 或,有点令人困惑的是,“(版本化的)文件系统”——。一种数据存储类型将所有内容保存在 Berkeley DB (或 BDB) 数据库环境中;使用这种类型的存储库通常被称为 “BDB 支持的”。另一种类型将数据存储在普通平面文件中,使用自定义格式。Subversion 开发人员已经习惯于将这种后一种数据存储机制称为 FSFS[41]——一种版本化的文件系统实现,它直接使用本机操作系统文件系统——而不是通过数据库库或其他抽象层——来存储数据。
表 5.1,“存储库数据存储比较” 对 Berkeley DB 和 FSFS 存储库进行了比较概述。
表 5.1. 存储库数据存储比较
类别 | 功能 | Berkeley DB | FSFS |
---|---|---|---|
可靠性 | 数据完整性 | 在正确部署的情况下,非常可靠;Berkeley DB 4.4 带来了自动恢复功能 | 较旧的版本有一些很少见,但会破坏数据的错误 |
对中断的敏感度 | 非常敏感;崩溃和权限问题会导致数据库“卡住”,需要进行日志恢复操作 | 相当不敏感 | |
可访问性 | 可从只读挂载访问 | 否 | 是 |
平台无关存储 | 否 | 是 | |
可通过网络文件系统访问 | 通常情况下,否 | 是 | |
组权限处理 | 对用户 umask 问题敏感;最好只由一个用户访问 | 绕过 umask 问题 | |
可扩展性 | 仓库磁盘使用情况 | 更大(尤其是如果日志文件没有被清除) | 更小 |
修订树的数量 | 数据库;没有问题 | 一些较旧的原生文件系统在单个目录中包含数千个条目时扩展性不好 | |
包含许多文件的目录 | 更慢 | 更快 | |
性能 | 检出最新修订版 | 没有明显的差异 | 没有明显的差异 |
大型提交 | 总体上更慢,但成本在提交的整个生命周期内分摊 | 总体上更快,但最终化延迟可能会导致客户端超时 |
这两种后端类型各有优缺点。它们都没有比另一个更“官方”,尽管从 Subversion 1.2 开始,较新的 FSFS 是默认的数据存储。两者都足够可靠,可以用来存储您的版本化数据。但正如您在表 5.1, “仓库数据存储比较”中看到的那样,FSFS 后端在支持的部署场景方面提供了更多灵活性。更多的灵活性意味着您需要更努力地寻找错误部署它的方法。这些原因——再加上不使用 Berkeley DB 意味着系统中少了一个组件——在很大程度上解释了为什么今天几乎每个人在创建新仓库时都使用 FSFS 后端。
幸运的是,大多数访问 Subversion 仓库的程序都对使用哪种后端数据存储一无所知。您甚至不必坚持最初选择的数据存储——如果您以后改变主意,Subversion 提供了将仓库数据迁移到使用不同后端数据存储的另一个仓库的方法。我们将在本章后面详细介绍这一点。
以下小节将更详细地介绍可用的后端数据存储类型。
在 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——如果您需要将一个 BDB 支持的仓库放在 Windows 机器上,请坚持使用 Windows 2000 或更高版本。
虽然 Berkeley DB 承诺在满足特定规范的网络共享上正确运行,[42] 但大多数网络文件系统类型和设备实际上 不 满足这些要求。在任何情况下,您都不能允许位于网络共享上的 BDB 支持的仓库被该共享的多个客户端同时访问(这通常是将仓库放在网络共享上的主要目的)。
![]() |
警告 |
---|---|
如果您尝试在不兼容的远程文件系统上使用 Berkeley DB,结果将不可预测——您可能会立即看到神秘的错误,或者可能要几个月后才能发现您的存储库数据库已微妙地损坏。对于需要存放在网络共享上的存储库,您应该认真考虑使用 FSFS 数据存储。 |
最后,由于 Berkeley DB 是直接链接到 Subversion 的库,因此它比典型的关系数据库系统更容易受到中断的影响。例如,大多数 SQL 系统都有一个专用的服务器进程来调解对所有表的访问。如果访问数据库的程序由于某种原因崩溃,数据库守护进程会注意到连接丢失并清理所有遗留的混乱。由于数据库守护进程是唯一访问表的进程,因此应用程序无需担心权限冲突。然而,Berkeley DB 并非如此。Subversion(以及使用 Subversion 库的程序)直接访问数据库表,这意味着程序崩溃可能会使数据库处于暂时不一致、不可访问的状态。发生这种情况时,管理员需要要求 Berkeley DB 还原到检查点,这有点令人讨厌。除了进程崩溃之外,其他因素也会导致存储库“卡住”,例如程序在数据库文件的所有权和权限上发生冲突。
![]() |
注意 |
---|---|
Berkeley DB 4.4(在 Subversion 1.4 及更高版本中)使 Subversion 能够自动且透明地恢复需要恢复的 Berkeley DB 环境。当 Subversion 进程连接到存储库的 Berkeley DB 环境时,它会使用一些进程会计机制来检测以前进程的任何不干净断开连接,执行任何必要的恢复,然后继续执行,就好像什么都没发生一样。这并不能完全消除存储库卡住的情况,但它确实大大减少了从这些情况中恢复所需的人工干预。 |
虽然 Berkeley DB 存储库速度很快,可扩展性也很好,但最好由单个服务器进程以单个用户身份运行,例如 Apache 的 httpd 或 svnserve(参见 第 6 章,服务器配置),而不是通过 file://
或 svn+ssh://
URL 以多个不同用户身份访问它。如果您以多个用户身份直接访问 Berkeley DB 存储库,请务必阅读 本章后面名为“支持多种存储库访问方法”的部分。
2004 年年中,出现了第二种类型的存储库存储系统,它根本不使用数据库。FSFS 存储库将与修订相关的更改存储在一个文件中,因此所有存储库的修订都可以在一个包含编号文件的子目录中找到。事务在单独的子目录中创建为单个文件。完成后,事务文件将被重命名并移动到修订目录,从而保证提交是原子的。由于修订文件是永久的且不会改变,因此存储库也可以在 “热” 状态下备份,就像 BDB 支持的存储库一样。
FSFS 修订文件描述了修订的目录结构、文件内容以及与其他修订树中文件的差异。与 Berkeley DB 数据库不同,这种存储格式可在不同的操作系统之间移植,并且不受 CPU 架构的影响。由于没有使用日志记录或共享内存文件,因此可以安全地通过网络文件系统访问存储库,并在只读环境中对其进行检查。数据库开销的减少也意味着存储库的总体大小会略小。
FSFS 也有不同的性能特征。当提交包含大量文件的目录时,FSFS 可以更快地追加目录条目。另一方面,FSFS 在完成提交时会有更长的延迟,因为它执行了 BDB 后端在提交生命周期内分摊的任务,这在极端情况下会导致客户端在等待响应时超时。
然而,最重要的区别是 FSFS 在出现问题时不会卡死。如果使用 Berkeley DB 数据库的进程遇到权限问题或突然崩溃,数据库可能会处于不可用状态,直到管理员恢复它。如果使用 FSFS 存储库的进程发生相同的场景,存储库根本不会受到影响。最糟糕的情况是,一些事务数据会遗留下来。