本文档旨在描述 Subversion 的 1.6.x 系列。如果您运行的是不同版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/,并查阅适合您 Subversion 版本的文档。
Subversion 的每个核心库都可以说是存在于三个主要层中的一个——存储库层、存储库访问 (RA) 层或客户端层(参见前言中的 图 1,“Subversion 的架构”)。我们将在稍后研究这些层,但首先,让我们简要总结一下 Subversion 的各种库。为了保持一致性,我们将使用它们没有扩展名的 Unix 库名称(libsvn_fs
、libsvn_wc
、mod_dav_svn
等)来引用这些库。
客户端程序的主要接口
树和字节流差异化例程
上下文差异化和合并例程
文件系统通用库和模块加载器
Berkeley DB 文件系统后端
原生文件系统 (FSFS) 后端
存储库访问通用库和模块加载器
本地存储库访问模块
WebDAV 存储库访问模块
另一个(实验性的)WebDAV 存储库访问模块
自定义协议存储库访问模块
存储库接口
各种有用的子例程
工作副本管理库
Apache 授权模块,用于通过 WebDAV 访问 Subversion 存储库
Apache 模块,用于将 WebDAV 操作映射到 Subversion 操作
在前面的列表中,“各种”一词只出现了一次,这是一个好兆头。Subversion 开发团队认真确保功能在正确的层级和库中。也许模块化设计的最大优势是,从开发人员的角度来看,它不那么复杂。作为开发人员,您可以快速形成一种“全局观”,使您可以轻松地确定某些功能的位置。
模块化的另一个好处是能够用一个完全新的库来替换一个给定的模块,而这个新的库实现了相同的 API,但不会影响代码库的其余部分。从某种意义上说,这已经在 Subversion 中发生了。libsvn_ra_local
、libsvn_ra_neon
、libsvn_ra_serf
和 libsvn_ra_svn
库都实现了相同的接口,它们都作为 libsvn_ra
的插件工作。所有这四个库都与存储库层通信——libsvn_ra_local
直接连接到存储库;其他三个库通过网络连接。libsvn_fs_base
和 libsvn_fs_fs
库是另一个以不同方式实现相同功能的库对——它们都是通用 libsvn_fs
库的插件。
客户端本身也突出了 Subversion 设计中模块化的优势。Subversion 的 libsvn_client
库是设计一个正常工作的 Subversion 客户端所需的大部分功能的一站式商店(参见 名为“客户端层”的部分)。因此,虽然 Subversion 发行版只提供 svn 命令行客户端程序,但一些第三方程序提供了各种形式的图形客户端 UI。这些 GUI 使用与库存命令行客户端相同的 API。这种类型的模块化在 Subversion 客户端和 IDE 集成的激增中发挥了重要作用,并由此扩展到 Subversion 本身的大量采用率。
当提到 Subversion 的存储库层时,我们通常指的是两个基本概念——版本化文件系统实现(通过 libsvn_fs
访问,并由其 libsvn_fs_base
和 libsvn_fs_fs
插件支持),以及围绕它的存储库逻辑(如 libsvn_repos
中实现的那样)。这些库为版本控制数据的各个修订版提供了存储和报告机制。从 Subversion 用户的角度来看,该层通过存储库访问层连接到客户端层,它是“线路另一端”的东西。
Subversion 文件系统不是在操作系统中安装的内核级文件系统(如 Linux ext2 或 NTFS),而是一个虚拟文件系统。它不是将“文件”和“目录”存储为真实文件和目录(您可以使用您最喜欢的 shell 程序浏览这些文件和目录),而是使用两种可用的抽象存储后端之一——Berkeley DB 数据库环境或平面文件表示。(要详细了解两种存储库后端,请参见 名为“选择数据存储”的部分)。开发社区甚至对 Subversion 的未来版本使用其他后端数据库系统(也许通过 Open Database Connectivity (ODBC) 等机制)表现出极大的兴趣。事实上,Google 在推出 Google 代码项目托管服务之前就做了类似的事情:他们于 2006 年年中宣布,其开源团队的成员编写了一个新的专有 Subversion 文件系统插件,该插件使用 Google 的超可扩展 Bigtable 数据库进行存储。
由 libsvn_fs
导出的文件系统 API 包含您对任何其他文件系统 API 所期望的功能——您可以创建和删除文件和目录,复制和移动它们,修改文件内容等等。它还具有一些不太常见的特性,例如能够在每个文件或目录上添加、修改和删除元数据(“属性”)。此外,Subversion 文件系统是一个版本化文件系统,这意味着当您对目录树进行更改时,Subversion 会记住您在进行这些更改之前树的外观。以及之前的更改。以及之前的更改。等等,一直追溯到版本化时间的起点(甚至更早),直到您第一次开始将内容添加到文件系统的那一刻。
您对树所做的所有修改都在 Subversion 提交事务的上下文中进行。以下是修改文件系统的简化通用例程
开始 Subversion 提交事务。
进行更改(添加、删除、属性修改等)。
提交您的事务。
提交事务后,您的文件系统修改将作为历史文物永久存储。每个这样的循环都会生成树的单个新修订版,并且每个修订版都将永远以“过去的样子”的不可变快照形式提供访问。
文件系统接口提供的大部分功能都处理对单个文件系统路径执行的操作。也就是说,从文件系统外部来看,描述和访问文件和目录的各个修订版的主要机制是通过使用路径字符串,例如 /foo/bar
,就像您通过您最喜欢的 shell 程序来寻址文件和目录一样。您可以通过将要使用的路径传递给正确的 API 函数来添加新文件和目录。您可以通过相同的方式查询有关它们的信息。
然而,与大多数文件系统不同,路径本身不足以标识 Subversion 中的文件或目录。将目录树视为一个二维系统,其中节点的兄弟节点表示一种左右运动,而进入节点的子目录则表示一种向下运动。 图 8.1,“文件和目录在二维空间中的表示” 显示了对树的典型表示,正是这样。
这里的区别在于,Subversion 文件系统有一个大多数文件系统都没有的第三个维度——时间![57] 在文件系统接口中,几乎每个具有 path
参数的函数也需要一个 root
参数。这个 svn_fs_root_t
参数描述了修订版或 Subversion 事务(它只是正在进行的修订版),并提供了理解修订版 32 中的 /foo/bar
与修订版 98 中存在的相同路径之间的区别所需的第三个维度上下文。 图 8.2,“版本化时间——第三个维度!” 显示了修订版历史作为 Subversion 文件系统宇宙的附加维度。
正如我们之前提到的,libsvn_fs
API 看起来和感觉起来与任何其他文件系统一样,只是它具有这个奇妙的版本化功能。它旨在供任何对版本化文件系统感兴趣的程序使用。并非巧合,Subversion 本身对该功能很感兴趣。但是,虽然文件系统 API 应该足以满足基本文件和目录版本化支持,但 Subversion 想要更多——这就是 libsvn_repos
的用武之地。
Subversion 存储库库 (libsvn_repos
) 位于(从逻辑上讲)libsvn_fs
API 之上,提供超出底层版本化文件系统逻辑的功能。它不会完全包装每个文件系统函数——只有文件系统活动的一般循环中的一些主要步骤才会被存储库接口包装。其中一些包括 Subversion 事务的创建和提交以及修订版属性的修改。这些特定事件由存储库层包装,因为它们与挂钩相关联。存储库挂钩系统与实现版本化文件系统没有直接关系,因此它位于存储库包装库中。
钩子机制只是将独立的仓库库从其余文件系统代码中抽象出来的众多原因之一。libsvn_repos
API 为 Subversion 提供了其他一些重要的工具。这些工具包括以下功能:
创建、打开、销毁 Subversion 仓库以及仓库中包含的文件系统,并对其执行恢复操作。
描述两个文件系统树之间的差异。
查询与一组文件在文件系统中被修改的所有(或部分)修订版相关联的提交日志消息。
生成文件系统的可读“转储”——文件系统中修订版的完整表示。
解析该转储格式,将转储的修订版加载到另一个 Subversion 仓库中。
随着 Subversion 的不断发展,仓库库将与文件系统库一起发展,以提供更多功能和可配置选项支持。
如果 Subversion 仓库层位于“线路的另一端”,那么仓库访问 (RA) 层就是线路本身。该层负责在客户端库和仓库之间传递数据,它包括libsvn_ra
模块加载器库、RA 模块本身(目前包括 libsvn_ra_neon
、libsvn_ra_local
、libsvn_ra_serf
和 libsvn_ra_svn
),以及一个或多个 RA 模块所需的任何其他库(例如 mod_dav_svn
Apache 模块或 libsvn_ra_svn
的服务器 svnserve)。
由于 Subversion 使用 URL 来标识其仓库资源,因此 URL 方案的协议部分(通常为 file://
、http://
、https://
、svn://
或 svn+ssh://
)用于确定哪个 RA 模块将处理通信。每个模块注册其知道如何““说””的协议列表,以便 RA 加载器可以在运行时确定为当前任务使用哪个模块。您可以通过运行 svn --version
来确定哪些 RA 模块可用于 Subversion 命令行客户端,以及它们声称支持哪些协议。
$ svn --version svn, version 1.6.0 compiled Mar 21 2009, 17:27:36 Copyright (C) 2000-2009 CollabNet. Subversion is open source software, see https://subversion.org.cn/ This product includes software developed by CollabNet (http://www.Collab.Net/). The following repository access (RA) modules are available: * ra_neon : Module for accessing a repository via WebDAV protocol using Neon. - handles 'http' scheme - handles 'https' scheme * ra_svn : Module for accessing a repository using the svn network protocol. - with Cyrus SASL authentication - handles 'svn' scheme * ra_local : Module for accessing a repository on local disk. - handles 'file' scheme * ra_serf : Module for accessing a repository via WebDAV protocol using serf. - handles 'http' scheme - handles 'https' scheme $
RA 层导出的公共 API 包含向仓库发送和接收版本化数据所需的功能。每个可用的 RA 插件都可以使用特定的协议执行该任务——libsvn_ra_neon
和 libsvn_ra_serf
使用 HTTP/WebDAV 与运行 mod_dav_svn
Subversion 服务器模块的 Apache HTTP 服务器进行通信(可选地使用 SSL 加密);libsvn_ra_svn
使用自定义网络协议与 svnserve 程序进行通信,等等。
对于那些希望使用其他协议访问 Subversion 仓库的人来说,这正是仓库访问层模块化的原因!开发人员只需编写一个新的库,该库在一侧实现 RA 接口,并在另一侧与仓库进行通信。您的新库可以使用现有的网络协议,也可以发明自己的协议。您可以使用进程间通信 (IPC) 调用,或者——让我们疯狂一下吧——您甚至可以实现基于电子邮件的协议。Subversion 提供 API;您提供创意。
在客户端方面,Subversion 工作副本是所有操作发生的地方。客户端库实现的大部分功能都是为了管理工作副本——包含文件和其他子目录的目录,作为对一个或多个仓库位置的本地可编辑的““镜像””——以及将更改传播到仓库访问层和从仓库访问层传播更改。
Subversion 的工作副本库 libsvn_wc
负责直接管理工作副本中的数据。为此,库将每个工作副本目录的管理信息存储在一个特殊的子目录中。这个名为 .svn
的子目录存在于每个工作副本目录中,并包含各种其他文件和目录,这些文件和目录记录状态并为管理操作提供一个私有工作区。对于熟悉 CVS 的人来说,这个 .svn
子目录与 CVS 工作副本中找到的 CVS
管理目录在用途上类似。有关 .svn
管理区域的更多信息,请参见本章后面名为“工作副本管理区域内部”的部分。
Subversion 客户端库 libsvn_client
承担最广泛的责任;它的任务是将工作副本库的功能与仓库访问层的功能混合在一起,然后为希望执行一般修订控制操作的任何应用程序提供最高级别的 API。例如,函数 svn_client_checkout()
以 URL 为参数。它将此 URL 传递给 RA 层,并与特定仓库打开一个经过身份验证的会话。然后,它要求仓库提供某个树,并将此树发送到工作副本库,然后工作副本库将完整的副本写入磁盘(.svn
目录和所有内容)。
客户端库旨在供任何应用程序使用。虽然 Subversion 源代码包含一个标准的命令行客户端,但使用客户端库编写任意数量的 GUI 客户端应该非常容易。Subversion 的新 GUI(或任何新的客户端)不需要是包含命令行客户端的笨拙包装器——它们可以通过 libsvn_client
API 完整访问命令行客户端使用的相同功能、数据和回调机制。实际上,Subversion 源代码树包含一个小型 C 程序(您可以在 tools/examples/minimal_client.c
中找到它),它举例说明了如何使用 Subversion API 创建一个简单的客户端程序。