Subversion 项目:构建更好的 CVS
如果您从事任何类型的开源项目,您可能使用过 CVS。您可能还记得第一次学习如何通过网络匿名检出源代码树,您的第一次提交或学习如何查看 CVS 差异。然后,命运攸关的一天到来了:您问您的朋友如何重命名文件。“你不能”,是回复。
“什么?你是什么意思?” 你问。
“好吧,您可以从仓库中删除该文件,然后在新名称下重新添加它。”
“是的,但是那样就没有人知道它已被重命名了。”
“让我们致电 CVS 管理员。她可以手动编辑仓库的 RCS 文件,并有可能使事情正常运作。”
“什么?”
“顺便说一句,也不要尝试删除目录。”
你翻了翻白眼,呻吟起来。如此简单的任务怎么会如此困难?
毫无疑问,CVS 已经发展成为开源社区的标准软件配置管理 (SCM) 系统,这是理所当然的。CVS 是自由软件,并具有出色的非锁定开发模型,允许数百名分散的程序员进行协作。事实上,有人可能会说,如果没有 CVS,像 Freshmeat 或 SourceForge 这样的站点是否能够像现在这样蓬勃发展是值得怀疑的。CVS 及其半混乱的开发模型已成为开源文化的重要组成部分。
那么 CVS 有什么问题呢?因为它在底层使用了 RCS 存储系统,所以 CVS 只能跟踪文件内容,而不能跟踪树结构。因此,用户无法在不丢失历史记录的情况下复制、移动或重命名项目。树的重新排列始终是难看的服务器端调整。
RCS 后端无法有效地存储二进制文件,并且分支和标记操作可能会变得非常缓慢。CVS 也低效地使用网络;许多用户对长时间的等待感到恼火,因为文件差异仅在一个方向(从服务器到客户端,而不是从客户端到服务器)发送,并且二进制文件始终以完整形式传输。
从开发人员的角度来看,CVS 代码库是历史“hack”层层叠加的结果。(请记住,CVS 最初是驱动 RCS 的 shell 脚本的集合。)这使得代码难以理解、维护或扩展。例如,CVS 的网络能力基本上是附加上去的。它从未被设计为原生的客户端/服务器系统。
纠正 CVS 的问题是一项艰巨的任务,我们在这里只列出了一些常见的抱怨。
1995 年,Karl Fogel 和 Jim Blandy 创立了 Cyclic Software 公司,旨在为 CVS 提供商业支持和改进。Cyclic 发布了第一个启用网络的 CVS 公共版本(由 Cygnus 软件贡献)。1999 年,Karl Fogel 出版了一本关于 CVS 及其支持的开源开发模型的书 (cvsbook.red-bean.com)。Karl 和 Jim 长期以来一直在谈论编写 CVS 的替代品;Jim 甚至起草了一个新的理论仓库设计。最后,在 2000 年 2 月,Collabnet (www.collab.net) 的 Brian Behlendorf 向 Karl 提供了一份全职工作,以编写 CVS 的替代品。Karl 组建了一个团队,工作于 5 月开始。
该团队确定了一些简单的目标:Subversion 将被设计为 CVS 的功能替代品。它将完成 CVS 所做的一切,保留相同的开发模型,同时修复 CVS 设计(缺乏设计)中的缺陷。现有的 CVS 用户将是目标受众;任何 CVS 用户都应该能够轻松开始使用 Subversion。任何其他奖励功能都被认为是次要的(至少在 1.0 版本发布之前)。
在撰写本文时,原始团队已经编码了一年多,我们有许多优秀的志愿者贡献者。(Subversion,像 CVS 一样,是一个开源项目。)
以下是您应该对 Subversion 感到兴奋的一些原因的快速概述
真正的复制和重命名:Subversion 仓库根本不使用 RCS 文件;相反,它实现了一个虚拟的版本化文件系统,可以跟踪随时间变化的树结构(如下所述)。文件 和 目录都是版本化的。终于有了真正的客户端 mv 和 cp 命令,它们的行为正如您所想。
原子提交:提交要么完全进入仓库,要么完全不进入。
高级网络层:Subversion 网络服务器是 Apache,客户端和服务器之间使用 WebDAV(2) 进行通信。(请参阅下面的“Subversion 设计”部分。)
更快的网络访问:二进制差异算法用于在 双向 存储和传输增量,无论文件是文本类型还是二进制类型。
文件系统属性:每个文件或目录都附加了一个不可见的哈希表。您可以发明和存储任何任意的键/值对:所有者、权限、图标、应用程序所有者、MIME 类型、个人笔记等。这是用户的通用功能。属性是版本化的,就像文件内容一样。并且某些属性是自动检测的,例如文件的 MIME 类型(不再需要记住使用 -kb 开关)。
可扩展和可 hack:Subversion 没有历史包袱;它被设计和实现为一组具有明确定义的 API 的共享 C 库。这使得 Subversion 非常易于维护,并且可以被其他应用程序和语言使用。
轻松迁移:Subversion 命令行客户端与 CVS 非常相似;开发模型是相同的,因此 CVS 用户应该可以轻松地进行切换。cvs2svn 仓库转换器的开发正在进行中。
它是免费的:Subversion 是在 Apache/BSD 风格的开源许可证下发布的。
Subversion 采用模块化设计;它被实现为一组 C 库。每一层都有明确定义的用途和接口。通常,代码流从图表的顶部开始并向下流动——每一层都为其上层提供接口(参见图 1)。让我们简要浏览一下这些层,从底部开始。
Subversion 文件系统不是用户安装在操作系统中的内核级文件系统(如 Linux ext2 fs)。相反,它指的是 Subversion 仓库的设计。仓库建立在数据库之上,目前是 Berkeley DB,因此是一组 .db 文件。但是,一个库访问这些文件并导出一个 C API,该 API 模拟文件系统,特别是版本化的文件系统。
这意味着编写程序来访问仓库就像针对其他文件系统 API 进行编写一样:您可以像往常一样打开文件和目录进行读取和写入。主要区别在于,当写入此特定文件系统时,它永远不会丢失数据;旧版本的文件和目录始终作为历史工件保存。
CVS 的后端 (RCS) 按文件存储修订号,而 Subversion 则对整个树进行编号。每次对仓库的原子提交都会创建一个全新的文件系统树,并单独标记一个全局修订号。已更改的文件和目录将被重写(旧版本将被备份并存储为与最新版本的差异),而未更改的条目将通过共享存储机制指向。这就是仓库能够版本化树结构而不仅仅是文件内容的方式。
最后,应该提到的是,使用像 Berkeley DB 这样的数据库立即提供了 Subversion 需要的其他优秀功能:数据完整性、原子写入、可恢复性和热备份。有关更多信息,请参见 www.sleepycat.com。
Subversion 处处都带有 Apache 的印记。在其核心,客户端使用了 Apache 可移植运行时 (APR) 库。事实上,这意味着 Subversion 客户端应该可以在 Apache httpd 运行的任何地方编译和运行。目前,此列表包括所有 UNIX 版本、Win32、BeOS、OS/2、Mac OS X,甚至可能是 NetWare。
但是,Subversion 不仅仅依赖于 APR;Subversion 服务器本身就是 Apache httpd。为什么选择 Apache?最终,该决定是为了避免重新发明轮子。Apache 是一个经过时间考验的开源服务器进程,可以用于严肃的用途,但仍然是可扩展的。它可以承受高网络负载。它可以在许多平台上运行,并且可以通过防火墙运行。它能够使用多种不同的身份验证协议。它可以进行网络流水线和缓存。通过使用 Apache 作为服务器,Subversion 可以免费获得所有这些功能。为什么要从头开始呢?
Subversion 使用 WebDAV 作为其网络协议。DAV(分布式创作和版本控制)本身就是一个完整的讨论 (www.webdav.org),但简而言之,它是 HTTP 的扩展,允许通过 Web 读取/写入文件和进行版本控制。Subversion 项目希望搭上对该协议日益增长的支持浪潮;Win32、Mac OS 和 GNOME 的所有最新文件浏览器都已支持此协议。随着时间的推移,互操作性(有望)将变得越来越有价值。
对于只想访问本地磁盘上的 Subversion 仓库的用户,客户端也可以做到这一点;不需要网络。仓库访问 (RA) 层是一个抽象 API,由 DAV 和本地访问 RA 库实现。这是编写“库化”版本控制系统的一个特定好处;与 CVS 相比,这是一个巨大的优势,CVS 对于本地与网络仓库访问具有两条非常不同且难以维护的代码路径。想为 Subversion 编写一个新的网络协议吗?只需编写一个新的库来实现 RA API 即可。
在客户端,Subversion 工作副本库在特殊的 /SVN 子目录中维护管理信息,其目的类似于 CVS 工作副本中发现的 /CVS 管理目录。
然而,瞥一眼典型的 /SVN 目录,会发现比通常情况更多一点的东西。entries 文件包含 XML,用于描述工作副本目录的当前状态(并且基本上服务于 CVS 的 Entries、Root 和 Repository 文件的组合目的)。但是,存在的其他项目(在 /CVS 中找不到)包括版本化属性(上面“Subversion 功能”部分中提到的元数据)的存储位置以及每个文件的原始版本的私有缓存。后一个功能提供了报告本地修改并在无需网络访问的情况下进行还原的能力。身份验证数据也存储在 /SVN 中,而不是在单个类似 .cvspass 的文件中。
Subversion 客户端库承担着最广泛的责任。它的工作是将工作副本库的功能与仓库访问库的功能混合在一起,然后为任何希望执行通用版本控制操作的应用程序提供最高级别的 API。
例如,C 例程 svn_client_checkout() 接受一个 URL 作为参数。它将此 URL 传递给仓库访问库,并与特定仓库打开经过身份验证的会话。然后,它向仓库请求某个树,并将此树发送到工作副本库,然后工作副本库将完整的工作副本写入磁盘(/SVN 目录和所有内容)。
客户端库旨在供任何应用程序使用。虽然 Subversion 源代码包含一个标准的命令行客户端,但在客户端库之上编写任意数量的 GUI 客户端应该很容易。希望这些 GUI 最终会证明比当前的一批 CVS GUI 应用程序要好得多,后者只不过是 CVS 命令行客户端周围的脆弱包装器。
此外,适当的 SWIG 绑定 (www.swig.org) 应该使 Subversion API 可用于任何数量的语言:Java、Perl、Python、Guile 等等。为了颠覆 CVS,无处不在会有所帮助。
Subversion 1.0 版本的发布目前计划在 2002 年初进行。在 1.0 版本发布之后,Subversion 计划增加诸如 i18n 支持、智能合并、更好的变更集操作、客户端插件和改进的服务器管理功能等功能。愿望清单上还有各种各样的想法,例如分布式、复制仓库。
Subversion 常见问题解答中的最后一点想法:“我们(尚未)尝试在 SCM 系统中开辟新天地,也没有尝试模仿所有 SCM 系统中最好的功能。我们正在努力取代 CVS。”
如果在三年内 Subversion 被广泛认为是开源社区的标准 SCM 系统,那么该项目将获得成功。但未来仍然不明朗。最终,Subversion 将必须凭借其自身的技术优势赢得这个地位。欢迎补丁。
电子邮件:sussman@red-bean.com
Ben Collins-Sussman 曾在政府、学术和商业机构担任程序员和系统管理员 11 年。Ben 目前在 Collabnet(Subversion 的主要赞助商)工作,并在芝加哥剧院界兼职担任作曲家。他的主页位于 www.red-bean.com/sussman。