使用Subversion进行版本控制

针对 Subversion 1.4(根据r2866编译)

Ben Collins-Sussman

Brian W. Fitzpatrick

C. Michael Pilato

本作品使用共同创造许可证,可以访问http://creativecommons.org/licenses/by/2.0/或发送邮件到Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.查看本许可证。

(TBA)


目录

前言
序言
读者
怎样阅读本书
本书约定
排版习惯
图标
本书的结构
本书是免费的
致谢
来自 Ben Collins-Sussman
来自 Brian W. Fitzpatrick
来自 C. Michael Pilato
Subversion是什么?
Subversion的历史
Subversion的特性
Subversion的架构
Subversion的组件
1. 基本概念
版本库
版本模型
文件共享的问题
锁定-修改-解锁 方案
拷贝-修改-合并 方案
Subversion实践
Subversion版本库URL
工作拷贝
修订版本
工作拷贝怎样跟踪版本库
混合修订版本的工作拷贝
总结
2. 基本使用
求助!
导入数据到你的版本库
svn import
推荐的版本库布局
初始化检出
禁用密码缓存
用其它身份认证
基本的工作周期
更新你的工作拷贝
修改你的工作拷贝
检查你的修改
取消本地修改
解决冲突(合并别人的修改)
提交你的修改
检验历史
产生历史修改列表
检查历史修改的详情
浏览版本库
获得旧的版本库快照
有时你只需要清理
总结
3. 高级主题
版本清单
修订版本关键字
版本日期
属性
为什么需要属性?
操作属性
属性和 Subversion 工作流程
自动设置属性
文件移植性
文件内容类型
文件的可执行性
行结束字符串
忽略未版本控制的条目
关键字替换
锁定
创建锁定
发现锁定
解除和偷窃锁定
锁定交流
外部定义
Peg和实施修订版本
网络模型
请求和响应
客户端凭证缓存
4. 分支与合并
什么是分支?
使用分支
创建分支
在分支上工作
分支背后的关键概念
在分支间复制修改
复制特定的修改
合并背后的关键概念
合并的最佳实践
常见用例
合并分支到另一分支
取消修改
找回删除的项目
常用分支模式
使用分支
标签
建立简单标签
建立复杂标签
分支维护
版本库布局
数据的生命周期
供方分支
常规的供方分支管理过程
svn_load_dirs.pl
总结
5. 版本库管理
Subversion 版本库的定义
版本库开发策略
规划你的版本库结构
决定在哪里与如何部署你的版本库
选择数据存储格式
创建和配置你的版本库
创建版本库
实现版本库钩子
Berkeley DB 配置
版本库维护
管理员的工具箱
修正提交消息
管理磁盘空间
Berkeley DB 恢复
版本库数据的移植
过滤版本库历史
版本库复制
版本库备份
总结
6. 服务配置
概述
选择一个服务器配置
svnserve服务器
svnserve使用SSH通道
Apache 的 HTTP 服务器
推荐
svnserve,一个自定义的服务器
调用服务器
内置的认证和授权
SSH 隧道
SSH 配置技巧
httpd,Apache的HTTP服务器
先决条件
基本的 Apache 配置
认证选项
授权选项
额外的糖果
基于路径的授权
支持多种版本库访问方法
7. 定制你的Subversion体验
运行配置区
配置区布局
配置和Windows注册表
配置选项
本地化
理解地区
Subversion对区域设置的支持
使用外置比较工具
外置 diff
外置 diff3
8. 嵌入Subversion
分层的库设计
版本库层
版本库访问层
客户端层
进入工作拷贝的管理区
条目文件
原始拷贝和属性文件
使用API
Apache可移植运行库
URL 和路径需求
使用 C 和 C++ 以外的语言
代码样例
9. Subversion 完全参考
Subversion 命令行客户端:svn
svn选项
svn子命令
svnadmin
svnadmin选项
svnadmin子命令
svnlook
svnlook选项
svnlook子命令
svnsync
svnsync选项
svnsync子命令
svnserve
svnserve选项
svnversion
mod_dav_svn
Subversion属性
版本控制的属性
未版本控制的属性
版本库钩子
A. Subversion 快速入门指南
安装 Subversion
快速指南
B. CVS用户的Subversion指南
版本号现在不同了
目录的版本
更多离线操作
区分状态和更新
状态
更新
分支和标签
元数据属性
解决冲突
二进制文件和行结束标记转换
版本化的模块
认证
迁移CVS版本库到Subversion
C. WebDAV和自动版本
什么是WebDAV ?
自动版本化
客户端交互性
独立的 WebDAV 应用程序
文件浏览器 WebDAV 扩展
WebDAV文件系统实现
D. 第三方工具
E. Copyright
索引

插图清单

1. Subversion的架构
1.1. 一个典型的客户/服务器系统
1.2. 需要避免的问题
1.3. 锁定-修改-解锁 方案
1.4. 拷贝-修改-合并 方案
1.5. 拷贝-修改-合并 方案(续)
1.6. 版本库的文件系统
1.7. 版本库
4.1. 分支与开发
4.2. 开始规划版本库
4.3. 版本库与复制
4.4. 一个文件的分支历史
8.1. 二维的文件和目录
8.2. 版本时间—第三维!

表格清单

1.1.
5.1.
6.1.
C.1.

范例清单

5.1. txn-info.sh(报告异常事务)
5.2. 镜像版本库的 pre-revprop-change 钩子
5.3. 镜像版本库的 start-commit 钩子
6.1. 匿名访问的配置实例。
6.2. 一个认证访问的配置实例。
6.3. 一个混合认证/匿名访问的配置实例。
6.4. 禁用所有的路径检查
7.1. 注册表条目(.reg)样本文件。
7.2. diffwrap.sh
7.3. diffwrap.bat
7.4. diff3wrap.sh
7.5. diff3wrap.bat
8.1. 使用版本库层
8.2. 使用 Python 处理版本库层
8.3. 一个Python状态爬虫

前言

Karl Fogel

芝加哥,2004年3月14日

一个差劲的常见问题列表(FAQ)总是充斥着作者渴望被问到的问题,而不是人们真正想要了解的问题。也许你曾经见过下面这样的问题:

Q:怎样使用Glorbosoft XYZ最大程度的提高团队生产率?

A:许多客户希望知道怎样利用我们革命性的专利办公套件最大程度的提高生产率。答案非常简单:首先,点击“文件” 菜单,找到“提高生产率”菜单项,然后…

类似的问题完全不符合FAQ的精神。没人会打电话给技术支持中心,询问“怎样提高生产率?”相反,人们经常询问一些非常具体的问题,像“怎样让日程系统提前两天而不是一天提醒相关用户?”等等。但是想象比发现真正的问题更容易。构建一个真实的问题列表需要持之以恒的、有组织的辛勤工作:跨越整个软件生命周期,追踪新提出的问题,监控反馈信息,所有的问题要整理成一个统一的、可查询的整体,并且能够真实的反映所有用户的感受。这需要耐心,如自然学家一样严谨的态度,没有浮华的假设,没有虚幻的断言—相反的,需要开放的视野和精确的记录。

我很喜欢这本书,因为它正是按照这种精神建立起来的,这种精神体现在本书的每一页中。这是作者与用户直接交流的结果。而这一切是源于Ben Collins-Sussman's对于Subversion常见问题邮件列表的研究。他发现人们总是在邮件列表中重复询问一些基本问题:使用subversion的一般程序是怎样的?分支与标签同其它版本控制系统的工作方式一样吗?我怎样知道某一处修改是谁做的?

日复一日看到相同问题的烦闷,促使Ben在2002年的夏天努力工作了一个月,撰写了一本Subversion手册,一本六十页厚的、涵盖了所有Subversion使用基础知识的手册。这本手册没有说明最终定稿的时间,但它随着Subversion的每个版本一起发布,帮助许多用户跨过学习之初的艰难。当O'Reilly和Associates决定出版一本完备的Subversion图书的时候,一条捷径浮出水面:扩充Subversion手册。

新书的三位合著者因而面临着一个不寻常的机会。从职责上讲,他们的任务是从一个目录和一些草稿为基础,自上而下的写一部专著。但事实上,他们的灵感源泉则来自一些具体的内容,稳定却难以组织。Subversion被数以千计的早期用户采用,这些用户提供了大量的反馈,不仅仅针对Subversion,还包括业已存在的文档。

在写这本书的过程里,Ben,Mike 和 Brian一直像鬼魂一样游荡在Subversion邮件列表和聊天室中,仔细的研究用户实际遇到的问题。监视这些反馈也是他们在CollabNet工作的一部分,这给他们撰写Subversion文档提供了巨大的便利。这本书建立在丰富的使用经验,而非在流沙般脆弱的想象之上,它结合了用户手册和FAQ的优点。初次阅读时,这种二元性的优势并不明显,按照顺序,从前到后,这本书只是简单的从头到尾描述了软件的细节。书中的内容包括一章概述,一章必不可少的快速指南,一章关于管理配置,一些高级主题,当然还包括命令参考手册和故障排除指南。而当你过一段时间之后,再次翻开本书查找一些特定问题的解决方案时,这种二元性才得以显现:这些生动的细节一定来自不可预测的实际用例的提炼,大多是源于用户的需要和视点。

当然,没人可以承诺这本书可以回答所有问题。尽管有时候一些前人提问的惊人一致性让你感觉是心灵感应;你仍有可能在社区的知识库里摔跤,空手而归。如果有这种情况,最好的办法是写明问题发送email到,作者还在那里关注着社区,不仅仅封面提到的三位,还包括许多曾经作出修正与提供原始材料的人。从社区的视角,帮你解决问题只是逐步的调整这本书,进一步调整Subversion本身以更合理的适合用户使用这样一个大工程的一个有趣的额外效用。他们渴望你的信息,不仅仅可以帮助你,也因为可以帮助他们。与Subversion这样活跃的自由软件项目一起,你并不孤单

让这本书将成为你的第一个伙伴。

序言

 

即使你能确认什么是完美,也不要让完美成为好的敌人,更何况你不能确认。因为落入过去陷阱的不悦,你会在设计时因为担心自己的缺陷而无所作为。

 
  --Greg Hudson

在开源软件世界,长久以来,并行版本系统(CVS)一直是版本控制工具的唯一选择。事实证明,这个选择不错。CVS的自由软件身份,无约束的处事态度,和对网络化操作的支持(网络使众多身处不同地方的程序员可以共享他们的工作成果),正符合了开源世界协作的精神,CVS和它半混乱状态的开发模式已成为开源文化的基石。

但是,CVS也并不是没有缺陷,而修正这些缺陷必定要耗费很大的精力。而Subversion则是以CVS继任者的面目出现的新型版本控制系统。Subversion的设计者们力图通过两方面的努力赢得CVS用户的青睐:保持开源系统的设计(以及“界面风格”)与CVS尽可能类似,同时尽力弥补CVS许多显著的缺陷。这些努力的结果使得从CVS迁移到Subversion不需要作出重大的变革,Subversion确实是非常强大、非常有用和非常灵活的工具。并且很重要的一点,几乎新开的开源项目都选择了Subversion替代CVS。

本书是为Subversion 1.4系列撰写的。在书中,我们尽力涵盖Subversion的所有内容。但是,Subversion有一个兴盛和充满活力的开发社区,已有许多新的特性和改进措施计划在Subversion新版本中实现,本书中讲述的命令和特性可能会有所变化。

读者

本书是为了那些在计算机领域有丰富知识,并且希望使用Subversion管理数据的人士准备的。尽管Subversion可以在多种不同的操作系统上运行,但其基本用户操作界面是基于命令行的,也就是我们将要在本书中讲述和使用的命令行工具(svn)。

出于一致性的考虑,本书的例子假定读者使用的是类Unix的操作系统,并且熟悉Unix和命令行界面。当然,svn程序也可以在入Microsoft Windows这样的非Unix平台上运行,除了一些微小的不同,如使用反斜线(\)代替正斜线(/)作为路径分隔符,在Windows上运行svn程序的输入和输出与在Unix平台上运行完全一致。

大多数读者可能是那些需要跟踪代码变化的程序员或者系统管理员,这是Subversion最普遍的用途,因此这个场景贯穿于整本书的例子中。但是Subversion可以用来管理任何类型的数据:图像、音乐、数据库、文档等等。对于Subversion,数据就是数据而已。

本书假定读者从来没有使用过任何版本控制工具,同时,我们也努力使CVS用户能够轻松的投入到Subversion使用当中,不时会出现一些涉及CVS的内容,此外,在附录的一个章节中总结了Subversion和CVS的区别。

需要说明的是,所有源代码示例仅仅是例子而已。这些例子需要通过正确编译参数进行编译,在这里列举它们只是为了说明特定的场景,并非为了展示优秀的编码风格。

怎样阅读本书

技术书籍经常要面对这样两难的困境:是迎合自上至下的初学者,还是自下至上的初学者。一个自上至下的学习者会喜欢略读文档,得到对系统工作原理的总体看法;然后她才会开始实际使用软件。而一个自下至上的学习者,是“通过实践学习”的人,他们希望快速的开始使用软件,自己领会软件的使用,只在必要时读取相关章节。大多数图书会倾向于针对某一类读者,而本书毫无疑问倾向于自上至下的方法。(如果你阅读了本节,那你也一定是一个自上至下的学习者!)然而,如果你是自下至上的人,不要失望,本书以Subversion主题的广泛观察进行组织,每个章节都包含了大量可以尝试的详细实例。如果你希望马上开工,没有耐心等待,你可以看附录 A, Subversion 快速入门指南

本书适用于具有不同背景知识的各个层次的读者—从未使用过版本控制的新手到经验丰富的系统管理员都能够从本书中获益。根据基础的不同,某些的章节可能对某些读者更有价值。下面的内容可以看作是为不同类型的读者提供的“推荐阅读清单”:

资深系统管理员

假定你从前使用过版本控制,并且迫切需要建立起Subversion服务器并尽快运行起来,第 5 章 版本库管理第 6 章 服务配置将会告诉你如何建立起一个版本库,并将其在网络上发布。此后,依靠你的CVS使用经验,第 2 章 基本使用附录 B, CVS用户的Subversion指南将向你展示怎样使用Subversion客户端软件。

新用户

如果管理员已经为你准备好了Subversion服务,你所需要的是学习如何使用客户端。如果你没有使用版本控制系统(像CVS)的经验,那么第 1 章 基本概念第 2 章 基本使用是重要的入门教程,其中介绍了版本控制的重要思想。

高级用户

无论是用户还是管理员,项目终将会壮大起来。那时,就需要学习更多Subversion的高级功能,像如何使用分支和执行合并(第 4 章 分支与合并)、怎样使用Subversion的属性(第 3 章 高级主题)、怎样配制运行参数(第 7 章 定制你的Subversion体验)等等。这两章在学习的初期并不重要,但熟悉了基本操作之后还是非常有必要了解一下。

开发者

你应该已经很熟悉Subversion了,并且想扩展它或使用它的API开发新软件。第 8 章 嵌入Subversion将最适合你。

本书以参考材料作为结束—第 9 章 Subversion 完全参考是一部Subversion全部命令的详细指南,此外,在附录中还有许多很有意义的主题。阅读完本书后,这些章节将会是你经常查阅的内容。

本书约定

本节描述了本书中使用的各种约定。

排版习惯

等宽字体

用于命令,命令输出和选项

等宽字体

用于代码和文本中的可替换部分

斜体

用于文件和路径名

图标

注意

此图标表示旁边的文本内容需特别注意。

提示

此图标表示旁边的文本描述了一个有用的小技巧。

警告

此图标表示旁边的文本是警告信息。

本书的结构

以下是各个章节的内容介绍:

序言

回顾了Subversion的历史,描述了Subversion的特性、架构、组件。

第 1 章 基本概念

介绍了版本控制的基础知识及不同的版本模型,同时讲述了Subversion版本库,工作拷贝和修订版本的概念。

第 2 章 基本使用

引领你开始一个Subversion用户的工作。示范怎样使用Subversion获得、修改和提交数据。

第 3 章 高级主题

覆盖了许多普通用户最终要面对的复杂特性,例如版本化的元数据、文件锁定和peg修订版本。

第 4 章 分支与合并

讨论分支、合并与标签,包括最佳实践的介绍,常见用例的描述,怎样取消修改,以及怎样从一个分支转到另一个分支。

第 5 章 版本库管理

讲述Subversion版本库的基本概念,怎样建立、配置和维护版本库,以及哪些工具可以完成上述的工作。

第 6 章 服务配置

描述了如何配置Subversion服务器,以及三种访问版本库的方式,HTTPsvn协议和本地磁盘访问。这里也介绍了认证,授权与匿名访问的细节。

第 7 章 定制你的Subversion体验

研究了Subversion的客户端配置文件,对国际化字符的处理,以及Subversion如何与外置工具交互。

第 8 章 嵌入Subversion

介绍了Subversion的核心部件、Subversion的文件系统,以及程序员眼中的工作拷贝管理区域,展示了如何使用公共API编写Subversion应用程序。最重要的内容是,如何为Subversion的开发贡献力量。

第 9 章 Subversion 完全参考

以大量的实例,详细描述了svnsvnadminsvnlook的所有子命令。

附录 A, Subversion 快速入门指南

因为缺乏耐心,我们会立刻解释如何安装和使用Subversion,我们已经告诉你了。

附录 B, CVS用户的Subversion指南

详细比较了Subversion与CVS的异同,并针对如何消除多年使用CVS养成的坏习惯提出建议。内容包括Subversion修订版本号、版本化的目录、离线操作、updatestatus的对比、分支、标签、元数据、冲突处理和认证。

附录 C, WebDAV和自动版本

描述了WebDAV与DeltaV的细节,并介绍了如何将Subversion版本库作为可读/写的DAV共享装载。

附录 D, 第三方工具

讨论一些支持和使用Subversion的工具,包括其它客户端工具,版本库浏览工具等。

本书是免费的

本书最初是作为Subversion项目的文档并由Subversion的开发者开始撰写的,后来成为一个独立的项目并进行了重写。与Subversion相同,它始终按免费许可证(见附录 E, Copyright)发布。事实上,本书是在公众的关注中写出来的,最初是Subversion项目的一部分,这有两种含义:

  • 总可以在Subversion的版本库里找到本书的最新版本。

  • 可以任意分发或修改本书—它在免费许可证的控制之下,你的唯一限制是必须保留正确的最初作者。当然,与其独自发布私有版本,不如向Subversion开发社区提供反馈和修正信息。

本书的在线主页在http://svnbook.red-bean.com,有许多志愿的翻译工作。在网站上,你可以找到许多本书最新快照和标签版本的链接,也可以访问到本书的Subversion版本库(存放了DocBook XML源文件)。我们欢迎反馈—也愿意接受鼓励。请将所有的评论、抱怨和对本书源文件的补丁发送到。本书的中文版主要是由Subversion中文站的志愿者翻译的,可以在http://www.subversion.org.cn/看到本书的最新版本和其他资料,也要感谢i18n-zh的朋友的一些支持。

致谢

没有Subversion就不可能有(即使有也没什么价值)这本书。所以作者衷心感谢Brian Behlendorf和CollabNet,他们独到的眼光开创了这个充满冒险但雄心勃勃的开源项目;Jim Blandy贡献了Subversion最初的名字和设计—我们爱你,Jim。还有Karl Fogel,一个好朋友和伟大的社区领袖。[1]

感谢O'Reilly和我们的编辑Linda Mui和Tatiana对我们的耐心和支持。

最后,我们要感谢数不清的曾经为本书作出贡献的人们,他们进行了非正式的审阅,并给出了大量建议和修改意见。虽然无法列出一个完整的列表,但本书的完整和正确离不开他们:David Anderson, Jani Averbach, Ryan Barrett, Francois Beausoleil, Jennifer Bevan, Matt Blais, Zack Brown, Martin Buchholz, Brane Cibej, John R. Daily, Peter Davis, Olivier Davy, Robert P. J. Day, Mo DeJong, Brian Denny, Joe Drew, Nick Duffek, Ben Elliston, Justin Erenkrantz, Shlomi Fish, Julian Foad, Chris Foote, Martin Furter, Dave Gilbert, Eric Gillespie, David Glasser, Matthew Gregan, Art Haas, Eric Hanchrow, Greg Hudson, Alexis Huxley, Jens B. Jorgensen, Tez Kamihira, David Kimdon, Mark Benedetto King, Andreas J. Koenig, Nuutti Kotivuori, Matt Kraai, Scott Lamb, Vincent Lefevre, Morten Ludvigsen, Paul Lussier, Bruce A. Mah, Philip Martin, Feliciano Matias, Patrick Mayweg, Gareth McCaughan, Jon Middleton, Tim Moloney, Christopher Ness, Mats Nilsson, Joe Orton, Amy Lyn Pilato, Kevin Pilch-Bisson, Dmitriy Popkov, Michael Price, Mark Proctor, Steffen Prohaska, Daniel Rall, Jack Repenning, Tobias Ringstrom, Garrett Rooney, Joel Rosdahl, Christian Sauer, Larry Shatzer, Russell Steicke, Sander Striker, Erik Sjoelund, Johan Sundstroem, John Szakmeister, Mason Thomas, Eric Wadsworth, Colin Watson, Alex Waugh, Chad Whitacre, Josef Wolf, Blair Zajac, 以及整个Subversion社区。

来自 Ben Collins-Sussman

感谢我的妻子Frances,在好几个月里,我一直在对你说:“但是亲爱的,我还在为这本书工作”,此外还有,“但是亲爱的,我还在处理邮件”。我不知道她为什么会如此耐心!她是我完美的平衡点。

感谢我的家人对我的鼓励,无论他们是否真的对我的课题感兴趣。(你知道的,一个人说 “哇,你正在写一本书?”,然后当他知道你是写一本计算机书时,那种惊讶就变得没有那么多了。)

感谢我身边让我富有的朋友,不要那样看我—你们知道你们是谁。

感谢父母对我的低级格式化,和难以置信的角色典范 ,感谢儿子给我机会传承这些东西。

来自 Brian W. Fitzpatrick

非常非常感谢我的妻子Marie的理解,支持和最重要的耐心。感谢引导我学会UNIX编程的兄弟Eric,感谢我的母亲和祖母的支持,对我在圣诞夜里埋头工作的理解。

Mike和Ben:与你们一起工作非常快乐,Heck,我们在一起工作很愉快!

感谢所有在Subversion和Apache软件基金会的人们给我机会与你们在一起,没有一天我不从你们那里学到知识。

最后,感谢我的祖父,他一直跟我说“自由等于责任”,我深信不疑。

来自 C. Michael Pilato

特别感谢Amy,我最好的朋友和9年里不可思议的妻子,因为她的爱和耐心支持,因为她提供的深夜工作,因为她对我强加给她的版本控制过程的优雅忍受 。不要担心,甜心—你会立刻成为TortoiseSVN巫师!

Gavin,或许现在本书的很多词你还不能读出来,但是当你最终能够书写我们所说的疯狂语言时,希望你会为你的父亲感到骄傲,就像他对你一样。

Aidan, Daddy luffoo et ope Aiduh yike contootoo as much as Aiduh yike batetball, base-ball, et bootball. [2]

妈妈和爸爸,感谢你们的支持和热情,岳父岳母,以同样的理由感谢你们,还要感谢你们难以置信的女儿。

向你们致敬,Shep Kendall,为我打开了通向计算机世界的大门;Ben Collins Sussman,我在开源世界的导师;Karl Fogel—你我的.emacs;Greg Stain,让我在困境中知道怎样编程;Brain Fitzpatrick—同我分享他的写作经验。所有我曾经从你们那里获得知识的人—尽管又不断忘记。

最后,对所有为我展现完美卓越创造力的人们—感谢。

Subversion是什么?

Subversion是一个自由/开源的版本控制系统。也就是说,在Subversion管理下,文件和目录可以超越时空。也就是Subversion允许你数据恢复到早期版本,或者是检查数据修改的历史。正因为如此,许多人将版本控制系统当作一种神奇的“时间机器”。

Subversion的版本库可以通过网络访问,从而使用户可以在不同的电脑上进行操作。从某种程度上来说,允许用户在各自的空间里修改和管理同一组数据可以促进团队协作。因为修改不再是单线进行,开发速度会更快。此外,由于所有的工作都已版本化,也就不必担心由于错误的更改而影响软件质量—如果出现不正确的更改,只要撤销那一次更改操作即可。

某些版本控制系统本身也是软件配置管理(SCM)系统,这种系统经过精巧的设计,专门用来管理源代码树,并且具备许多与软件开发有关的特性—比如,对编程语言的支持,或者提供程序构建工具。不过Subversion并不是这样的系统。它是一个通用系统,可以管理任何类型的文件集。对你来说,这些文件这可能是源程序—而对别人,则可能是一个货物清单或者是数字电影。

Subversion的历史

早在2000年,CollabNet, Inc. (http://www.collab.net)就开始寻找CVS替代产品的开发人员。CollabNet提供了一个名为CollabNet企业版(CEE)的协作软件套件。这个软件套件的一个组成部分就是版本控制系统。尽管CEE在最初采用了CVS作为其版本控制系统,但是CVS的局限性从一开始就很明显,CollabNet知道,迟早要找到一个更好的替代品。遗憾的是,CVS已经成为开源世界事实上的标准,很大程度上是因为没有更好的替代品,至少是没有可以自由使用的替代品。所以CollabNet决定从头编写一个新的版本控制系统,这个系统保留CVS的基本思想,但是要修正其中错误和不合理的特性。

2000年2月,他们联系到Open Source Development with CVS(Coriolis, 1999)的作者Karl Fogel,并且询问他是否希望为这个新项目工作。巧合的是,当时Karl正在与朋友Jim Blandy讨论设计一个新的版本控制系统。1995年时,他们两人曾经开办了一个提供CVS支持的公司Cyclic Software,尽管他们最终卖掉了公司,但还是天天使用CVS进行日常工作。使用CVS时的挫折促使Jim认真的思考如何管理版本化的数据,并且他当时不仅使用了“Subversion”这个名字,并且已经完成了Subversion版本库的最初设计。所以当CollabNet提出邀请的时候,Karl马上同意为这个项目工作,同时Jim也找到了他的雇主—Red Hat软件公司—允许他到这个项目工作,并且没有限定最终的期限。CollabNet雇佣了Karl和Ben Collins Sussman,详细设计工作从三月开始,在Behlendorf 、CollabNet、Jason Robbins和Greg Stein(当时是一个独立开发者,活跃在WebDAV/DeltaV系统规范制订工作中)恰到好处的激励下,Subversion很快吸引了许多活跃的开发者,结果是许多对CVS有过失望经历的人很乐于为这个项目做些事情。

最初的设计小组设定了简单的开发目标。他们不想在版本控制方法学中开垦处女地,他们只是希望修正CVS。他们决定Subversion应符合CVS的特性,并保留相同的开发模型,但不再重复CVS的一些显著缺陷。尽管Subversion并不需要成为CVS的完全替代品,但它应该与CVS保持足够的相似性,以使CVS用户可以轻松的转移到Subversion上。

经过14个月的编码,2001年8月31日,Subversion能够“自己管理自己”了,开发者停止使用CVS保存Subversion的代码,而使用Subversion本身。

虽然CollabNet启动了这个项目,并且一直提供了大量的工作支持(它为一些全职的Subversion开发者提供薪水),但Subversion像其它许多开源项目一样,被松散的、透明的规则管理着,这样的规则激励着知识界的精英们。CollabNet的版权许可证完全符合Debian的自由软件方针。也就是说,任何人都可以根据自己的意愿自由的下载、修改和重新发布Subversion,不需要CollabNet或其他人的授权。

Subversion的特性

在讲解Subversion为版本控制领域带来的特性时,我们会经常通过Subversion对CVS的改进进行说明。如果不熟悉CVS,了解所有Subversion的特性会有一定的困难。而如果根本就不熟悉版本控制,你就只有干瞪眼的份儿了。因此,最好首先阅读一下第 1 章 基本概念,这一章简单介绍了一些版本控制的基本思想和概念。

Subversion支持:

版本化的目录

CVS只能跟踪单个文件的变更历史,但是Subversion实现的“虚拟”版本化文件系统则可以跟踪目录树的变更。在Subversion中,文件目录都是版本化的。

真实的版本历史

由于只能跟踪单个文件的变更,CVS无法支持如文件拷贝和改名这些常见的操作—这些操作改变了目录的内容。同样,在CVS中,一个目录下的文件只要名字相同即拥有相同的历史,即使这些同名文件在历史上毫无关系。而在Subversion中,可以对文件或目录进行增加、拷贝和改名操作,也解决了同名而无关的文件之间的历史联系问题。

原子提交

一系列相关的更改,要么全部提交到版本库,要么一个也不提交。这样用户就可以将相关的更改组成一个逻辑整体,防止出现只有部分修改提交到版本库的情况。

版本化的元数据

每一个文件和目录都有自己的一组属性—键和它们的值。可以根据需要建立并存储任何键/值对。和文件本身的内容一样,属性也在版本控制之下。

可选的网络层

Subversion在版本库访问的实现上具有较高的抽象程度,利于人们实现新的网络访问机制。Subversion可以作为一个扩展模块嵌入到Apache之中。这种方式在稳定性和交互性方面有很大的优势,可以直接使用服务器的成熟技术—认证、授权和传输压缩等。此外,Subversion自身也实现了一个轻型的,可独立运行的服务器软件。这个服务器使用了一个自定义协议,可以轻松的用SSH封装。

一致的数据操作

Subversion用一个二进制差异算法描述文件的变化,对于文本(可读)和二进制(不可读)文件其操作方式是一致的。这两种类型的文件压缩存储在版本库中,而差异信息则在网络上双向传递。

高效的分支和标签操作

在Subversion中,分支与标签操作的开销与工程的大小无关。Subversion的分支和标签操作用只是一种类似于硬链接的机制拷贝整个工程。因而这些操作通常只会花费很少且相对固定的时间。

可修改性

Subversion没有历史负担,它以一系列优质的共享C程序库的方式实现,具有定义良好的API。这使得Subversion非常容易维护,和其它语言的互操作性很强。

Subversion的架构

图 1 “Subversion的架构”给出了Subversion设计总体上的“俯视图”。

图 1. Subversion的架构


图中的一端是保存所有版本数据的Subversion版本库,另一端是Subvesion的客户程序,管理着所有版本数据的本地影射(称为“工作拷贝”),在这两极之间是各种各样的版本库访问(RA)层,某些使用电脑网络通过网络服务器访问版本库,某些则绕过网络服务器直接访问版本库。

Subversion的组件

安装好的Subversion由几个部分组成,下面将简单的介绍一下这些组件。下文的描述或许过于简略,不易理解,但不用担心—本书后面的章节中会用更多的内容来详细阐述这些组件。

svn

命令行客户端程序。

svnversion

此工具用来显示工作拷贝的状态(用术语来说,就是当前项目的修订版本)。

svnlook

直接查看Subversion版本库的工具。

svnadmin

建立、调整和修复Subversion版本库的工具。

svndumpfilter

过滤Subversion版本库转储数据流的工具。

mod_dav_svn

Apache HTTP服务器的一个插件,使版本库可以通过网络访问。

svnserve

一个单独运行的服务器程序,可以作为守护进程或由SSH调用。这是另一种使版本库可以通过网络访问的方式。

svnsync

一个通过网络增量镜像版本库的程序。

如果已经正确完成了Subversion的安装,我们就可以开始我们的学习之旅了。在后面的两章中,我们将讲解如何使用Subversion的客户端程序svn



[1] 噢,还要感谢Karl为了本书所付出的辛勤工作。

[2] 翻译:爸爸希望你会像喜欢篮球、棒球和足球一样喜欢计算机。(不是很明显吗?)

基本概念

本章主要为那些不熟悉版本控制技术的入门者提供一个简单扼要的、非系统的介绍。我们将从版本控制的基本概念开始,随后阐述Subversion的独特理念,并演示一些使用Subversion的例子。

虽然我们在本章中以分享程序源代码作为例子,但是记住Subversion可以管理任何类型的文件集—它并非是程序员专用的。

版本库

Subversion是一个“集中式”的信息共享系统。版本库是Subversion的核心部分,是数据的中央仓库。版本库以典型的文件和目录结构形式文件系统树来保存信息。任意数量的客户端连接到Subversion版本库,读取、修改这些文件。客户端通过写数据将信息分享给其他人,通过读取数据获取别人共享的信息。图 1.1 “一个典型的客户/服务器系统”展示了这种系统:

图 1.1. 一个典型的客户/服务器系统


这有什么意义吗?说了这么多,Subversion听起来和一般的文件服务器没什么不同。事实上,Subversion的版本库的确一种文件服务器,但不是“一般”的文件服务器。Subversion版本库的特别之处在于,它会记录每一次改变:每个文件的改变,甚至是目录树本身的改变,例如文件和目录的添加、删除和重新组织。

一般情况下,客户端从版本库中获取的数据是文件系统树中的最新数据。但是客户端也具备查看文件系统树以前任何一个状态的能力。举个例子,客户端有时会对一些历史性问题感兴趣,比如“上星期三时的目录结构是什么样的?”或者“谁最后一个修改了这个文件,都修改了什么?”这些都是版本控制系统的核心问题:设计用来记录和跟踪数据变化的系统。

版本模型

版本控制系统的核心任务是实现协作编辑和数据共享,但是不同的系统使用不同的策略实现这个目的。我们有许多理由要去理解这些策略的区别,首先,如果你遇到了其他类似Subversion的系统,可以帮助你比较现有的版本控制系统。此外,可以帮助你更有效的使用Subversion,因为Subversion本身支持不同的工作方式。

文件共享的问题

所有的版本控制系统都需要解决这样一个基础问题:怎样让系统允许用户共享信息,而不会让他们因意外而互相干扰?版本库里意外覆盖别人的更改非常的容易。

考虑图 1.2 “需要避免的问题”的情景,我们有两个共同工作者,Harry和Sally,他们想同时编辑版本库里的同一个文件,如果首先Harry保存它的修改,过了一会,Sally可能凑巧用自己的版本覆盖了这些文件,Harry的更改不会永远消失(因为系统记录了每次修改),但Harry所有的修改不会出现在Sally新版本的文件中,所以Harry的工作还是丢失了—至少是从最新的版本中丢失了—而且可能是意外的,这就是我们要明确避免的情况!

图 1.2. 需要避免的问题


锁定-修改-解锁 方案

许多版本控制系统使用锁定-修改-解锁机制解决这种问题,在这样的模型里,在一个时间段里版本库的一个文件只允许被一个人修改。首先在修改之前,Harry要“锁定”住这个文件,锁定很像是从图书馆借一本书,如果Harry锁住这个文件,Sally不能做任何修改,如果Sally想请求得到一个锁,版本库会拒绝这个请求。在Harry结束编辑并且放开这个锁之前,她只可以阅读文件。Harry解锁后,就要换班了,Sally得到自己的轮换位置,锁定并且开始编辑这个文件。图 1.3 “锁定-修改-解锁 方案”描述了这样的解决方案。

图 1.3. 锁定-修改-解锁 方案


锁定-修改-解锁模型有一点问题就是限制太多,经常会成为用户的障碍:

  • 锁定可能导致管理问题。有时候Harry会锁住文件然后忘了此事,这就是说Sally一直等待解锁来编辑这些文件,她在这里僵住了。然后Harry去旅行了,现在Sally只好去找管理员放开锁,这种情况会导致不必要的耽搁和时间浪费。

  • 锁定可能导致不必要的线性化开发。如果Harry编辑一个文件的开始,Sally想编辑同一个文件的结尾,这种修改不会冲突,设想修改可以正确的合并到一起,他们可以轻松的并行工作而没有太多的坏处,没有必要让他们轮流工作。

  • 锁定可能导致错误的安全状态。假设Harry锁定和编辑一个文件A,同时Sally锁定并编辑文件B,如果A和B互相依赖,这种变化是必须同时作的,这样A和B不能正确的工作了,锁定机制对防止此类问题将无能为力—从而产生了一种处于安全状态的假相。很容易想象Harry和Sally都以为自己锁住了文件,而且从一个安全,孤立的情况开始工作,因而没有尽早发现他们不匹配的修改。锁定经常成为真正交流的替代品

拷贝-修改-合并 方案

Subversion,CVS和一些版本控制系统使用拷贝-修改-合并模型,在这种模型里,每一个客户联系项目版本库建立一个个人工作拷贝—版本库中文件和目录的本地映射。用户并行工作,修改各自的工作拷贝,最终,各个私有的拷贝合并在一起,成为最终的版本,这种系统通常可以辅助合并操作,但是最终要靠人工去确定正误。

这是一个例子,Harry和Sally为同一个项目各自建立了一个工作拷贝,工作是并行的,修改了同一个文件A,Sally首先保存修改到版本库,当Harry想去提交修改的时候,版本库提示文件A已经过期,换句话说,A在他上次更新之后已经更改了,所以当他通过客户端请求合并版本库和他的工作拷贝之后,碰巧Sally的修改和他的不冲突,所以一旦他把所有的修改集成到一起,他可以将工作拷贝保存到版本库,图 1.4 “拷贝-修改-合并 方案”图 1.5 “拷贝-修改-合并 方案(续)”展示了这一过程。

图 1.4. 拷贝-修改-合并 方案


图 1.5. 拷贝-修改-合并 方案(续)


但是如果Sally和Harry的修改交迭了该怎么办?这种情况叫做冲突,这通常不是个大问题,当Harry告诉他的客户端去合并版本库的最新修改到自己的工作拷贝时,他的文件A就会处于冲突状态:他可以看到一对冲突的修改集,并手工的选择保留一组修改。需要注意的是软件不能自动的解决冲突,只有人可以理解并作出智能的选择,一旦Harry手工的解决了冲突—也许需要与Sally讨论—它可以安全的把合并的文件保存到版本库。

拷贝-修改-合并模型感觉有一点混乱,但在实践中,通常运行的很平稳,用户可以并行的工作,不必等待别人,当工作在同一个文件上时,也很少会有交迭发生,冲突并不频繁,处理冲突的时间远比等待解锁花费的时间少。

最后,一切都要归结到一条重要的因素:用户交流。当用户交流贫乏,语法和语义的冲突就会增加,没有系统可以强制用户完美的交流,没有系统可以检测语义上的冲突,所以没有任何证据能够承诺锁定系统可以防止冲突,实践中,锁定除了约束了生产力,并没有做什么事。

Subversion实践

是时候从抽象转到具体了,在本小节,我们会展示一个Subversion真实使用的例子。

Subversion版本库URL

正如我们在整本书里描述的,Subversion使用URL来识别Subversion版本库中的版本化资源,通常情况下,这些URL使用标准的语法,允许服务器名称和端口作为URL的一部分:

$ svn checkout http://svn.example.com:9834/repos
…

但是Subversion处理URL的一些细微的不同之处需要注意,例如,使用file:访问方法的URL(用来访问本地版本库)必须与习惯一致,可以包括一个localhost服务器名或者没有服务器名:

$ svn checkout file:///path/to/repos
…
$ svn checkout file://localhost/path/to/repos
…

同样,在Windows平台下使用file://模式时需要使用一个非正式的“标准”语法来访问本机上不在同一个磁盘分区中的版本库。下面的任意一个URL路径语法都可以工作,其中的X表示版本库所在的磁盘分区:

C:\> svn checkout file:///X:/path/to/repos
…
C:\> svn checkout "file:///X|/path/to/repos"
…

在第二个语法里,你需要使用引号包含整个URL,这样竖线字符才不会被解释为管道。当然,也要注意URL使用普通的斜线而不是Windows本地(不是URL)的反斜线。

注意

也必须意识到Subversion的file: URL不能在普通的web服务器中工作。当你尝试在web服务器查看一个file:的URL时,它会通过直接检测文件系统读取和显示那个位置的文件内容,但是Subversion的资源存在于虚拟文件系统(见“版本库层”一节)中,你的浏览器不会理解怎样读取这个文件系统。

最后,必须注意Subversion的客户端会根据需要自动编码URL,这一点和一般的web浏览器一样,举个例子,如果一个URL包含了空格或是一个字符编码大于128的ASCII字符:

$ svn checkout "http://host/path with space/project/españa"

…Subversion会回避这些不安全字符,并且会像你输入了这些字符一样工作:

$ svn checkout http://host/path%20with%20space/project/espa%C3%B1a

如果URL包含空格,一定要使用引号,这样你的脚本才会把它做一个单独的svn参数。

工作拷贝

你已经阅读过了关于工作拷贝的内容;现在我们要讲一讲客户端怎样建立和使用它。

一个Subversion工作拷贝是你本地机器上的一个普通目录,保存着一些文件,你可以任意的编辑文件,而且如果是源代码文件,你可以像平常一样编译,你的工作拷贝是你的私有工作区,在你明确的做了特定操作之前,Subversion不会把你的修改与其他人的合并,也不会把你的修改展示给别人,你甚至可以拥有同一个项目的多个工作拷贝。

当你在工作拷贝作了一些修改并且确认它们工作正常之后,Subversion提供了一个命令可以“发布”你的修改给项目中的其他人(通过写到版本库),如果别人发布了各自的修改,Subversion提供了手段可以把这些修改与你的工作目录进行合并(通过读取版本库)。

工作副本也包括一些由 Subversion 创建并维护的额外文件,用来协助执行命令。通常情况下,你的工作副本的每个文件夹都有一个以 .svn 为名的文件夹,也被叫做工作副本的管理目录,这个目录里的文件能够帮助 Subversion 识别哪些文件做过修改,哪些文件相对于别人的工作已经过期。

一个典型的Subversion的版本库经常包含许多项目的文件(或者说源代码),通常每一个项目都是版本库的子目录,在这种布局下,一个用户的工作拷贝往往对应版本库的的一个子目录。

举一个例子,你的版本库包含两个软件项目,paintcalc。每个项目在它们各自的顶级子目录下,见图 1.6 “版本库的文件系统”

图 1.6. 版本库的文件系统


为了得到一个工作拷贝,你必须检出check out)版本库的一个子树,(术语“check out”听起来像是锁定或者保留资源,实际上不是,只是简单的得到一个项目的私有拷贝),举个例子,你检出 /calc,你可以得到这样的工作拷贝:

$ svn checkout http://svn.example.com/repos/calc
A    calc/Makefile
A    calc/integer.c
A    calc/button.c
Checked out revision 56.

$ ls -A calc
Makefile  integer.c  button.c  .svn/

列表中的A表示Subversion增加了一些条目到工作拷贝,你现在有了一个/calc的个人拷贝,有一个附加的目录—.svn—保存着前面提及的Subversion需要的额外信息。

假定你修改了button.c,因为.svn目录记录着文件的修改日期和原始内容,Subversion可以告诉你已经修改了文件,然而,在你明确告诉它之前,Subversion不会将你的改变公开,将改变公开的操作被叫做提交(committing,或者是checking in)修改到版本库。

将你的修改发布给别人,你可以使用Subversion的提交(commit)命令。

$ svn commit button.c -m "Fixed a typo in button.c."
Sending        button.c
Transmitting file data .
Committed revision 57.

这时你对button.c的修改已经提交到了版本库,其中包含了关于此次提交的日志信息(例如是修改了拼写错误)。如果其他人取出了/calc的一个工作拷贝,他们会看到这个文件最新的版本。

假设你有个合作者,Sally,她和你同时取出了/calc的一个工作拷贝,你提交了你对button.c的修改,Sally的工作拷贝并没有改变,Subversion只在用户要求的时候才改变工作拷贝。

要使项目最新,Sally可以要求Subversion更新她的工作备份,通过使用更新(update)命令,将结合你和所有其他人在她上次更新之后的改变到她的工作拷贝。

$ pwd
/home/sally/calc

$ ls -A 
.svn/ Makefile integer.c button.c

$ svn update
U    button.c
Updated to revision 57.

svn update命令的输出表明Subversion更新了button.c的内容,注意,Sally不必指定要更新的文件,subversion利用.svn以及版本库的进一步信息决定哪些文件需要更新。

修订版本

一个svn commit操作可以作为一个原子事务操作发布任意数量文件和目录的修改,在你的工作拷贝里,你可以改变文件内容、删除、改名以及拷贝文件和目录,然后作为一个原子事务一起提交。

原子事务”的意思是:要么所有的改变发生,要么都不发生,Subversion努力保持原子性以应对程序错误、系统错误、网络问题和其他用户行为。

每当版本库接受了一个提交,文件系统进入了一个新的状态,叫做一次修订(revision),每一个修订版本被赋予一个独一无二的自然数,一个比一个大,初始修订号是0,只创建了一个空目录,没有任何内容。

图 1.7 “版本库”可以更形象的描述版本库,想象有一组修订号,从0开始,从左到右,每一个修订号有一个目录树挂在它下面,每一个树好像是一次提交后的版本库“快照”。

图 1.7. 版本库


需要特别注意的是,工作拷贝并不一定对应版本库中的单个修订版本,他们可能包含多个修订版本的文件。举个例子,你从版本库检出一个工作拷贝,最近的修订号是4:

calc/Makefile:4
     integer.c:4
     button.c:4

此刻,工作目录与版本库的修订版本4完全对应,然而,你修改了button.c并且提交之后,假设没有别的提交出现,你的提交会在版本库建立修订版本5,你的工作拷贝会是这个样子的:

calc/Makefile:4
     integer.c:4
     button.c:5

假设此刻,Sally提交了对integer.c的修改,建立修订版本6,如果你使用svn update来更新你的工作拷贝,你会看到:

calc/Makefile:6
     integer.c:6
     button.c:6

Sally对integer.c的改变会出现在你的工作拷贝,你对button.c的改变还在,在这个例子里,Makefile在4、5、6修订版本都是一样的,但是Subversion会把他的Makefile的修订号设为6来表明它是最新的,所以你在工作拷贝顶级目录作一次干净的更新,会使得所有内容对应版本库的同一修订版本。

工作拷贝怎样跟踪版本库

对于工作拷贝的每一个文件,Subversion在管理区域.svn/记录两项关键的信息:

  • 工作文件所作为基准的修订版本(叫做文件的工作修订版本)和

  • 一个本地拷贝最后更新的时间戳。

给定这些信息,通过与版本库通讯,Subversion可以告诉我们工作文件是处于如下四种状态的那一种:

未修改且是当前的

文件在工作目录里没有修改,在工作修订版本之后没有修改提交到版本库。svn commit操作不做任何事情,svn update不做任何事情。

本地已修改且是当前的

在工作目录已经修改,从基本修订版本之后没有修改提交到版本库。本地修改没有提交,因此svn commit会成功提交,svn update不做任何事情。

未修改且不是当前的了

这个文件在工作目录没有修改,但在版本库中已经修改了。这个文件最终将更新到最新版本,成为当时的公共修订版本。svn commit不做任何事情,svn update将会取得最新的版本到工作拷贝。

本地已修改且不是最