本文字档尚在撰写中,内容可能随时变更,且可能无法准确描述 Apache™ Subversion® 软件的任何已发布版本。将此页面设为书签或将其推荐给其他人可能不是一个明智的选择。请访问 https://svnbook.subversion.org.cn/ 获取本书的稳定版本。

分层库设计

Subversion 的每个核心库都存在于三个主要层中的一个——仓库层、仓库访问 (RA) 层或客户端层(参见前言中的 图 1,“Subversion 的架构”)。我们将在稍后详细介绍这些层,但首先,让我们简要总结一下 Subversion 的各个库。为了保持一致性,我们将使用库的无扩展名 Unix 库名来引用这些库(libsvn_fslibsvn_wcmod_dav_svn 等)。

libsvn_client

客户端程序的主要接口

libsvn_delta

树和字节流差异化例程

libsvn_diff

上下文差异化和合并例程

libsvn_fs

文件系统通用组件和模块加载器

libsvn_fs_base

Berkeley DB 文件系统后端

libsvn_fs_fs

本地文件系统 (FSFS) 后端

libsvn_ra

仓库访问通用组件和模块加载器

libsvn_ra_local

本地仓库访问模块

libsvn_ra_serf

另一个(实验性的)WebDAV 仓库访问模块

libsvn_ra_svn

自定义协议仓库访问模块

libsvn_repos

仓库接口

libsvn_subr

各种有用的子例程

libsvn_wc

工作副本管理库

mod_authz_svn

Apache 授权模块,用于通过 WebDAV 访问 Subversion 仓库

mod_dav_svn

Apache 模块,用于将 WebDAV 操作映射到 Subversion 操作

前面列表中“杂项”一词只出现了一次,这是一个好兆头。Subversion 开发团队非常认真地确保功能位于正确的层和库中。也许模块化设计最大的优势是,从开发者的角度来看,它缺乏复杂性。作为开发者,你可以快速构建那种“大图”,这让你能够相对轻松地确定某些功能的位置。

模块化的另一个好处是能够用一个全新的库替换现有模块,该库实现相同的 API 而不影响代码库的其余部分。在某种意义上,这已经在 Subversion 中发生了。libsvn_ra_locallibsvn_ra_serflibsvn_ra_svn 库都实现了相同的接口,它们都作为插件工作到 libsvn_ra 中。所有三个库都与仓库层通信——libsvn_ra_local 直接连接到仓库;其他库通过网络连接。libsvn_fs_baselibsvn_fs_fs 库是另一个库对,它们以不同的方式实现相同的功能——两者都是对公共 libsvn_fs 库的插件。

客户端本身也突出了 Subversion 设计中模块化的优势。Subversion 的 libsvn_client 库是设计 Subversion 工作客户端所需的大部分功能的一站式商店(参见 名为“客户端层”的部分)。因此,虽然 Subversion 发行版只提供了 svn 命令行客户端程序,但几个第三方程序提供了各种形式的图形客户端 UI。这些 GUI 使用与库存命令行客户端相同的 API。这种模块化在可用的 Subversion 客户端和 IDE 集成的激增中发挥了重要作用,并进而导致了 Subversion 本身的巨大采用率。

仓库层

在提到 Subversion 的仓库层时,我们通常指的是两个基本概念——版本化文件系统实现(通过 libsvn_fs 访问,并由其 libsvn_fs_baselibsvn_fs_fs 插件支持)以及围绕它封装的仓库逻辑(如在 libsvn_repos 中实现的)。这些库为版本控制数据的各种修订版提供了存储和报告机制。此层通过仓库访问层连接到客户端层,从 Subversion 用户的角度来看,它是“线路另一端”的东西。

Subversion 文件系统不是要在操作系统中安装的内核级文件系统(如 Linux ext2 或 NTFS),而是一个虚拟文件系统。它不是将“文件”和“目录”存储为实际文件和目录(你可以使用你最喜欢的 shell 程序遍历这些文件和目录),而是使用两个可用的抽象存储后端之一——Berkeley DB 数据库环境或平面文件表示。(要详细了解这两个仓库后端,请参见 关于文件系统…。)开发社区对在 Subversion 的未来版本中使用其他后端数据库系统(也许通过 Open Database Connectivity (ODBC) 等机制)也产生了相当大的兴趣。事实上,在推出 Google Code 项目托管服务之前,Google 就做了类似的事情:他们在 2006 年年中宣布,其开源团队的成员编写了一个新的专有 Subversion 文件系统插件,该插件使用 Google 的超可扩展 Bigtable 数据库进行存储。

libsvn_fs 导出的文件系统 API 包含你对任何其他文件系统 API 都会期望的功能——你可以创建和删除文件和目录、复制和移动它们、修改文件内容等等。它还有一些不太常见的特性,例如能够在每个文件或目录上添加、修改和删除元数据(“属性”)。此外,Subversion 文件系统是一个版本化文件系统,这意味着当你对目录树进行更改时,Subversion 会记住更改之前树的样子。以及之前的更改。以及之前的更改。以及如此往后,一直到(并且超出)你第一次开始向文件系统添加东西的那一刻。

你在树上进行的所有修改都是在 Subversion 提交事务的上下文中完成的。以下是修改文件系统的简化通用例程

  1. 开始 Subversion 提交事务。

  2. 进行更改(添加、删除、属性修改等)。

  3. 提交你的事务。

提交事务后,你的文件系统修改将永久存储为历史记录。每个这样的循环都会生成一个新的树修订版,每个修订版都将永远作为一个不可变的快照来访问,展示了“事物曾经的样子”。

文件系统接口提供的大部分功能都处理对单个文件系统路径进行的操作。也就是说,从文件系统外部来看,描述和访问文件和目录各个修订版的主要机制是使用路径字符串,例如 /foo/bar,就像你在使用你最喜欢的 shell 程序来寻址文件和目录一样。你可以通过将要创建的文件和目录的路径传递给正确的 API 函数来添加新文件和目录。你可以通过相同的机制查询有关它们的信息。

但是,与大多数文件系统不同,路径本身不足以识别 Subversion 中的文件或目录。将目录树视为二维系统,其中节点的兄弟节点表示左右运动,导航到节点的子目录表示向下运动。 图 8.1,“文件和目录的二维表示” 展示了树的典型表示,正是如此。

图 8.1. 文件和目录的二维表示

Files and directories in two dimensions

这里的区别是,Subversion 文件系统有一个很酷的三维,而大多数文件系统都没有——时间![77] 在文件系统接口中,几乎每个具有 path 参数的函数也期望一个 root 参数。这个 svn_fs_root_t 参数描述了一个修订版或一个 Subversion 事务(它只是一个正在进行的修订版),并提供了理解修订版 32 中的 /foo/bar 与修订版 98 中的相同路径之间区别所需的三维上下文。 图 8.2,“版本控制时间——第三维度!” 将修订版历史记录显示为 Subversion 文件系统宇宙的附加维度。

图 8.2. 版本控制时间——第三维度!

Versioning time—the third dimension!

正如我们前面提到的,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_locallibsvn_ra_serflibsvn_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.8.0-dev (under development)
   compiled Jan  8 2013, 11:45:25 on i686-pc-linux-gnu

Copyright (C) 2013 The Apache Software Foundation.
This software consists of contributions made by many people;
see the NOTICE file for more information.
Subversion is open source software, see https://subversion.org.cn/

The following repository access (RA) modules are available:

* 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_serf 使用 HTTP/WebDAV(可选地使用 SSL 加密)与运行 mod_dav_svn Subversion 服务器模块的 Apache HTTP 服务器通信;libsvn_ra_svn 使用自定义网络协议与 svnserve 程序通信;等等。

对于那些希望使用其他协议访问 Subversion 存储库的人来说,这正是存储库访问层模块化的原因!开发人员只需编写一个新的库,该库在一端实现 RA 接口,并在另一端与存储库进行通信。您的新库可以使用现有的网络协议,也可以自己发明。您可以使用进程间通信 (IPC) 调用,或者 - 让我们疯狂一下,可以吗? - 您甚至可以实现基于电子邮件的协议。Subversion 提供 API;您提供创意。

客户端层

在客户端方面,Subversion 工作副本是所有操作发生的地方。客户端库实现的大部分功能都存在于管理工作副本的唯一目的 - 充满文件和其他子目录的目录,作为对一个或多个存储库位置的本地、可编辑反映 - 以及将更改传播到存储库访问层和从存储库访问层传播到存储库访问层。

Subversion 的工作副本库,libsvn_wc,直接负责管理工作副本中的数据。为了完成此操作,该库将工作副本的管理信息存储在一个特殊的子目录中。这个名为 .svn 的子目录存在于每个工作副本中,并包含各种其他文件和目录,这些文件和目录记录状态并为管理操作提供私有工作区。对于熟悉 CVS 的人来说,这个 .svn 子目录与 CVS 工作副本中找到的 CVS 管理目录在目的上类似。

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 来创建一个简单的客户端程序。



[77] 我们理解这对于长期以来一直认为时间实际上是第四维度的科幻迷来说可能是一个震惊,我们对我们对不同理论的主张造成的任何情绪创伤表示歉意。

TortoiseSVN 官方中文版 1.14.7 发布