Subversion实战

是时候从抽象回到具体了。这一节,我们将演示Subversion的使用实例。

工作副本

你已经对工作副本有了些了解,现在我们要演示如何用Subversion客户程序创建和使用工作副本。

一个Subversion的工作副本是你本地系统上的一个普通的目录树,包含一系列文件。客户随他所愿来编辑这些文件, 如果他们是源码文件,你可以用通常的方法来编译程序。你的工作副本是你的私人工作区。Subversion永远不会自行加入别人的改动,或把你的改动给别人,除非你明确的让它这么做。

在你对你的工作副本中的一些文件作了改动,并且验证了它们工作正常后,Subversion提供给你命令来把你的改动“发布”给其他和你一起为你的项目工作的人(通过写到资料库中)。如果别人发布了他们的修改,Subversion也提供命令给你来把这些改动合并到你的工作目录(通过从资料库读取)。

工作副本也包含有一些由Subversion创建和维护的额外文件,Subversion利用这些文件来完成命令。具体点说,你的工作副本中的每个目录包含一个名字为.svn的子目录,也被称为工作副本管理目录。管理目录中的文件帮助Subversion识别那些文件包含未发布的修改,那些文件相对于别人的工作已经过时了。

一个典型的Subversion资料库常会保存多个项目的文件(或源码)。通常,在资料库的目录树中每个项目都是一个子目录。按这种按排,一个用户的工作目录通常对应于资料库中一个特定的子树。

例如,假如你有一个资料库,其中有两个软件项目,paintcalc。每个项目都位于自己的顶级子目录下,如图 2.6 “资料库的文件系统”所示。

图 2.6. 资料库的文件系统

资料库的文件系统

要得到一个工作副本,你必须检出资料库的某个子树(名词“检出”可能听起来有点像锁定或保留资源,其实不然,它仅仅是为你创建了这个项目的私有拷贝)。例如, 如果你检出/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需要的额外信息,像前面提到的那样。

假设你对button.c作了修改。由于.svn目录记住了文件的修改日期和原料的内容,所以Subversion能辨别出你修改了文件。然而,Subversion不会公布你的修改,除非你明确让它这么做。发布你的修改这一行为,一般称为提交(或检入)修改到资料库。

要把你的修改发布给别人,你要使用Subversion的commit命令:

$ svn commit button.c
Sending        button.c
Transmitting file data .
Committed revision 57.

现在你对button.c的修改已经被提交到资料库,如果其他用户检出/calc的一个工作副本 ,他们将在最近的文件版本中看到你的修改 。

假设你有一个合作者,Sally,她和你同时检出了/calc的一个工作副本。当你提交你对 button.c的修改到资料库时,Sally的工作副本并没有被修改。Subversion只会在用户的要求下修改工作副本。

要让她的项目跟上,Sally要让Subversion来更新他的工作副本,这需要Subversion的update命令。这个命令会把你的修改合并到她的工作副本,还有其他在她检出后的修改。

$ pwd
/home/sally/calc

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

$ svn update
U button.c

svn update命令的输出说明Subversion更新了 button.c的内容。注意Sally不需要指明要更新那些文件。Subversion利用.svn目录和资料库中的信息来决定那些文件需要被更新。

修订版(Revision)

一次svn commit操作能把对任意个数的文件和目录作的所有改动以一个原子事务发布。在你的工作副本里,你可以改变文件内容,创建,删除,重命名和复制文件和目录,然后把所有这些修改作为一个单元提交。

在资料库中,每次提交都被看作一个原子事务:要么所有的提交修改发生,要么全不发生。Subversion在碰到程序崩溃,系统崩溃,网络问题和其他用户的动作时也会努力维持原子性。

资料库在每次接到一次提交请求时,会创建一个文件树的新状态,称为一个修订版(revision)。每一个修订版都被赋予一个唯一的自然数,比上一个修订版大一。对新创建的资料库,初始的修订版被编号为0,这个修订版仅仅是一个空的根目录,没有任何内容。

图 2.7 “资料库”展示了一个形象化的看待资料库的好办法。想象一个修订版号码的数列,从0开始,从左到右延伸。每个修订版号码有一个文件树挂在下面,每个文件树都是一次提交后的资料库的一个“快照”。

图 2.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中都是一样的,但是Subversoin会把你工作副本中的Makefile with revision 6 to 标记为修订版6,以表明它仍然是最新的。因此,在你对工作副本做了一个完整的更新后,它会仍然精确的对应于资料库中的一个修订版。

工作副本如何跟踪资料库

对工作副本中的每一个文件,Subversion在 .svn/管理区中记录了两方面必要的信息:

  • 你的工作副本基于那一个修订版(称为这个文件的工作修订版),以及

  • 一个时间戳,用来记录本地副本被资料库更新的最后时间。

有了这些信息,靠和资料库交互,Subversion能分辨一个工作文件处在下面四个状态中的那个:

未修改,是最新的

在工作副本中这个文件没被修改,从它的工作修订版以来还没有修改提交到资料库。对这个文件作svn commit,什么都不会发生。对这个文件作svn update也什么都不会发生。

在本地修改了,是最新的

这个文件在工作目录里已经被修改了,但从它的工作修订版以来还没有新修改提交到资料库。本地有尚未提交到资料库的修改,因而对这个文件作svn commit,会把你的修改成功的发布,对这个文件作svn update什么都不会发生。

未修改,已经过时了

在工作副本中这个文件没被修改,但在资料库它已经被修改了。最终它应该被更新,以使它和公共修订版保持同步。对这个文件作svn commit,什么都不会发生。对这个文件作 svn update将把最新的修改调入你的工作副本。

在本地修改了,已经过时了

这个文件在工作目录和资料库中都已经被修改了。对这个文件作svn commit,会由于 “文件已过时”而失败。这个文件应该首先被更新,对这个文件作svn update,会首先尝试把公共的修改合并到本地的修改中。如果Subversion无法比较合理的自动完成合并,它会把冲突留给用户来解决。

听起来这种方式好像有很多东西要跟踪,但是 svn status 命令能给你展示你工作副本中任何条目的状态。关于这个命令的更多信息,参见svn status”一节

混合修订版的局限

作用一个基本原则,Subversion尽可能做到灵活。一个特别的灵活性是工作副本可以包含混合的修订版号。

首先,为什么这种灵活性要被看作是特性而不是负担的原因可能不那么明显。在完成对资料库的一次提交后,新提交的文件和目录比工作副本里的其他文件处于更新的工作修订版。这看起来有点乱。像前面演示的那样,工作副本总可以运行 svn update命令来使自己只有一个工作修订版。为什么有些人故意想要混合的工作修订版?

如果你的项目足够复杂,你会发现有时强制 “回溯”你的部分的工作副本是很有用的;你将在第三章学会如何做。可能你想测试一个字模块的一个较早的版本,这个子模块包含在一个子目录里,或者可能你想在最新文件树的环境下检查一个文件的一系列先前的版本。

不管你如何利用你工作副本中的混合修订版,这个灵活性都有一些局限。

首先,如果一个文件或目录不是完全最新的话,你没法提交对它们的删除。如果资料库中存在一个这个文件的新版本,你删除的企图会被驳回,以免意外毁掉了你还没有看到的修改。

其次,如果一个目录不是最新的,你无法提交对它的元数据的修改。在第六章,你将学到给一个条目附加 “属性”。一个目录的工作修订版定义了一个特殊的条目和属性集合, 因而,提交对一个过时目录的属性修改可能破坏你还没看到的属性。