Subversion 实战
工作副本
你已经阅读过了关于工作副本的内容,现在我们要讲一讲客户端怎样建立和使用它。
一个 Subversion 工作副本是你本地机器一个普通的目录,保存着一些文件,你可以任意的编辑文件,而且如果是源代码文件,你可以像平常一样编译,你的工作副本是你的私有工作区,在你明确的做了特定操作之前,Subversion 不会把你的修改与其他人的合并,也不会把你的修改展示给别人。
当对工作副本中的文件做了一些更改并确认他们能够正常工作后,Subversion 提供将这些更改公布给同项目的其他人员的命令(通过写入版本库)。如果其他人公布他们的更改,Subversion 提供将这些更改合并到工作副本的命令(通过读取本版本库)。
Subversion还会在工作副本中创建并管理一些额外的文件,这些文件将用于协助执行命令。特别地,你的工作副本中会包含一个名为.svn
的子目录,即该工作副本的管理目录。管理目录中的文件帮助Subversion识别哪些文件包含未发布的更改,哪些文件由于他人的更改已过期。早于1.7版本的Subversion会在工作副本中每一级目录下包含名为.svn
的管理子目录。1.7版本开始, Subversion 启用了全新的策略:每个工作副本只有一个管理子目录,该目录是工作副本根目录的直接子级。
一个典型的 Subversion 的版本库经常包含许多项目的文件(或者说源代码),通常每一个项目都是版本库的子目录,在这种安排下,一个用户的工作副本往往对应版本库的的一个子目录。
举一个例子,你的版本库包含两个软件项目。
图 2.6. 版本库的文件系统
换句话说,版本库的根目录包含两个子目录: paint
和 calc
。
要获得工作副本,必须通过 检出 版本库中的某个子树。(术语 检出 听起来好像会锁定和保留资源,但实际上不是这样; 它只是给项目创建了一个私有的副本。)
假设你对button.c
做出了修改。由于.svn
目录记录了文件的修改日期和原始内容,Subversion得以告知使用者已经更改了这个文件。然而, Subversion 并不会将你的更改展示给全部用户,除非你明确指出要这么做。推送你做出的更改这一举动通常被称作提交修改(或者检入修改)到版本库。
发布你的修改给别人,可以使用 Subversion 的提交命令。
这时你对 button.c
的修改已经提交到了版本库,如果其他人取出了 /calc
的一个工作副本,他们会看到这个文件最新的版本。
设你有个合作者,Sally,她和你同时取出了 /calc
的一个工作副本,你提交了你对 button.c
的修改,Sally 的工作副本并没有改变,Subversion 只在用户要求的时候才改变工作副本。
要使项目最新,Sally 可以要求 Subversion 更新她的工作副本,通过使用更新命令,可以将你和所有其他人在她上次更新之后的修改合并到她的工作副本。
注意,Sally 不必指定要更新的文件,Subversion 利用 .svn
以及版本库的进一步信息决定哪些文件需要更新。
版本库的 URL
Subversion 可以通过多种方式访问-本地磁盘访问,或各种各样不同的网络协议,但一个版本库地址永远都是一个 URL,URL 方案反映了访问方法。
表 2.1. 版本库访问 URL
方案 | 访问方法 |
---|---|
file:// | 直接版本库访问(本地磁盘或者网络磁盘)。 |
http:// | 通过 WebDAV 协议访问支持 Subversion 的 Apache 服务器。 |
https:// | 与 http:// 相似,但是用 SSL 加密。 |
svn:// | 通过未认证的 TCP/IP 自定义协议访问 svnserve 服务器。 |
svn+ssh:// | 通过认证并加密的 TCP/IP 自定义协议访问 svnserve 服务器。 |
对于大多数情况,Subversion 的 URL 使用标准格式,允许服务器名称和端口作为 URL 的一部分明确的指出来。files://
访问方式一般用于本地访问,尽管它可以使用 UNC 路径来引用网络主机。因此 URL 使用这种格式 file://hostname/path/to/repos
。对于本机而言,URL 中的 hostname
部分必须省略或者使用 localhost
。就是因为这个原因,本机路径通常含有 3 个斜线,file:///path/to/repos
。
同样,在 Windows 平台上使用 file://
方案的用户需要使用一种非官方的 “标准” 协议格式来访问那些位于本机其他分区,即与客户端当前所处分区不同的版本库。如下两种格式的 URL 路径是有效的,其中X
代表版本库目录所在分区的盘符:
- file:///X:/path/to/repos
- ...
- file:///X|/path/to/repos
- ...
注意 URL 使用普通的斜杠,而不是 Windows 本地(非 URL)形式的路径。
你可以通过网络共享访问一个FSFS版本库,但由于很多原因不建议你这样做。
你会授予所有用户写权限,这样他们就有可能会意外删除或破坏版本库文件系统。
不是所有的网络文件共享协议都支持 Subversion 需要的文件锁定,所以你会发现你的版本库被毁了。有一天你会发现你的版本库微妙地崩溃
你必须设置正确的访问许可。SAMBA在这方面尤其困难
如果有人安装新版客户端升级了版本库格式,那么其他人将无法访问该版本库,直到他们也升级到新版客户端。
修订版本
svn commit 操作可以作为一个原子事务操作发布任意数量文件和目录的修改。在你的工作副本中,你可以改变文件内容,创建、删除、改名和复制文件和目录,然后作为一个整体提交。
在版本库中,每次提交被当作一次原子事务操作: 要么所有的改变发生,要么都不发生,Subversion 努力保持原子性以应对程序错误、系统错误、网络问题和其他用户行为。
每当版本库接受了一个提交,文件系统进入了一个新的状态,叫做版本,每个版本被赋予一个独一无二的自然数,一个比一个大,初始修订号是 0,只创建了一个空目录,没有任何内容。
可以形象的把版本库看作一系列树,想象有一组版本号,从 0 开始,从左到右,每一个修订号有一个目录树挂在它下面,每一个树好像是一次提交后的版本库“快照”。
图 2.7. 版本库
全局版本号
不同于其它版本控制系统,Subversion 的版本号是针对整个目录树的,而不是单个文件。每一个版本号代表了一次提交后版本库整个目录树的特定状态,另一种理解是版本 N 代表版本库已经经过了 N 次提交。当 Subversion 用户讨论“foo.c
的版本 5”时,他们的实际指得是“在版本 5 时的foo.c
”。需要注意的是,一个文件的版本 N 和 M 并不表示它必然不同。
需要特别注意的是,工作副本并不一定对应版本库中的单一版本,他们可能包含多个版本的文件。举个例子,你从版本库检出一个工作副本,最新的版本是 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 不做任何事情。
- 本地已修改且是当前的
工作副本已经修改,从基准版本之后没有修改提交到版本库。本地修改没有提交,因此 commit 会成功的提交,update 不做任何事情。
- 本地未修改且过时
这个文件在工作副本没有修改,但在版本库中已经修改了。这个文件应当更新到最新公共版本。commit 不做任何事情,update 将会更新工作副本到最新的版本。
- 本地已修改且过时
这个文件在工作副本和版本库中都被修改了。提交 该文件将会因为 过时 而失败。该文件应该先更新; 更新 命令将会尝试合并公共更改和本机更改。如果 Subversion 不能顺利的自动完成合并,则需要用户解决冲突。