2.2.3 "复制―修改―合并"方案
Subversion、CVS以及其他一些版本控制系统使用"复制―修改―合并"模型来代替锁定。在这种模型中,每一个用户的客户端软件从中央仓库创建出一份个人的工作副本――仓库中文件和目录的本地映射。之后,用户就可以并行工作,修改手中的私有副本。最后,这些私有副本合并成为一个全新的版本。版本控制系统常常需要合并,但是最终,操作者本身必须负责让合并工作正确进行。
这里有一个例子。Harry 和 Sally 都从仓库中复制出一份同一个项目的工作副本。他们同步工作,并且同时对工作副本中的同一个文件 A 作修改。Sally 先将她的修改保存到仓库中。当 Harry 也准备保存他的修改时,仓库会提示说他的文件是"过时"的。换句话说,在他最后复制文件 A 之后,文件 A 不知何时已经发生了变化。所以,Harry 要求他的客户端将仓库中的新的变化"合并"到他的工作副本中。很有可能,Sally 的修改并没有覆盖掉他所作的工作。这样,一旦他将两部分修改集成到一起,Harry 就可以将他的工作副本保存到仓库中。图2.4 "复制―修改―合并方案"和图2.5 "复制―修改―合并方案(续)"描述了这个过程。
图2.4 "复制―修改―合并方案"
图2.5 "复制―修改―合并方案(续)"
但是,如果 Sally 的修改会覆盖掉 Harry 的工作怎么办?这种情况叫做"冲突(conflict)",却也称不上是什么问题。当 Harry 要求他的客户端软件合并仓库中的最新修改到工作副本时,文件 A 被标记为冲突状态。这时,他可以看到相互冲突的两份修改,并且手动选择如何处理。注意,软件并不能自动解决冲突。只有人本身才有能力理解和做出合理的选择。一旦 Harry 手动解决了那些可能导致覆盖的修改(也许这个过程中需要和 Sally 协商一下),他就可以安全地将合并后的文件保存到仓库中了。
复制―修改―合并模型也许听起来有一点混乱,但是实际上,它工作地非常的稳定。用户可以并行工作,完全不需要等待其他人。当他们在同一个文件上工作时,其实大多数的并行修改都不会覆盖对方,冲突不会常常发生。用于解决冲突的时间远远少于锁定系统所带来的时间浪费。
最终,我们将所有的问题归结为一个关键因素:用户交流。如果用户很少交流,不论是语法的还是语义的冲突都会增加。没有哪个系统可以让用户完美地交流,也没有哪个系统可以自动检查出语义上的冲突。所以,不要被那种锁定系统可以解决冲突的虚假承诺所麻痹,事实上,锁定系统除了限制生产力之外一无是处。
2.3 实际中的 Subversion
现在是让我们从抽象概念过渡到具体实践的时候了。在这一节中,我们会演示一个真实的例子。
2.3.1 工作副本
在上文中,我们已经知道了工作副本这个概念了。在这里,我们来演示一下如何使用 Subversion 的客户端来创建和使用工作副本。
一个 Subversion 的工作副本其实就是本地系统中的一个普通的文件目录树。你可以使用任何方式来编辑这些文件。如果是源代码文件的话,你也可以像通常情况那样去编译它们。工作副本是你的私人工作区。如果你不明确的要求,Subversion 绝不会合并其他人的修改,也不会让其他人看到你做的修改。
在你已经修改完工作副本中的文件,并且确信修改正确后,就可以将这些修改公开给同一个项目中的其他工作人员。Subversion 提供了将文件写入仓库的命令。通过这种方式来达到公布你的修改的目的。如果其他人公布了他们的修改,也可以使用 Subversion 提供的命令来合并修改(通过从仓库中读出文件)。
工作副本中也包含一些额外的文件。它们是由 Subversion 创建和维护的,用来辅助完成这些命令。最典型的情况是,每一个目录都包含一个叫做 .svn 的子目录。该目录也被称为"工作副本管理目录"。在管理目录中的那些文件可以帮助 Subversion 来识别哪些文件包含没有发布的修改,以及哪些文件已经过期了。
通常,一个 Subversion 的仓库会包含几个项目,而每一个项目都是仓库的目录的一个子目录。这样,用户的工作副本也就对应于仓库中一个特定的子目录。
例如,假设我们有一个包含两个软件项目的仓库。它们分别叫做 paint 和 calc。每一个项目都在它们自己的顶层子目录下。如图2.6 "仓库的文件系统"所示。
图2.6 "仓库的文件系统"
为了获得一份工作副本,我们必须先"check out"仓库中的某一个子目录。(check out 这个词听起来有点像是要锁定什么资源,但事实上不是,它只是简单的创建一份工作副本而已)。例如,我们来 check out 一个 /calc 项目的工作副本:
$ svn checkout http://svn.example.com/repos/calc
A calc
A calc/Makefile
A calc/integer.c
A calc/button.c
$ ls -A calc
Makefile integer.c button.c .svn/
上面以字母 A 开头的行表明了 Subversion 已经为我们的工作副本添加了一些东西。现在,我们拥有了仓库中 /calc 目录的一份私有副本。另外还有一个额外的目录 .svn 用来保存 Subversion 需要使用的额外信息。我们前面已经提到过它了。
(开始)
Repository URLs
Subversion 的仓库可以通过多种方式来访问,例如本地磁盘或者是网络协议。然而,一个仓库的位置总是以 URL 的方式出现。表2-1描述了不同的访问方法所对应的 URL 样式。
表2-1 仓库存取 URLs
┌──────────────────────────────────┐
