最近由于项目的原因开始研究 SVN,虽说这是一款年代久远的工具,在经过了解后发现,即使在当今它还是有很多亮点的。
0x00 关于 SVN
SVN 的全称为 Subversion,是一个开放源代码的版本控制系统,SVN 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。
SVN 的架构:
0x01 基本概念
版本控制模型
SVN 以复制-修改-合并模型为主,但是对某些类型的文件仍然需要使用加锁-修改-解锁模型。复制-修改-合并模型要求文件是支持合并的—也就是说文件是基于行的文本文件(例如程序源代码文件),但是对二进制文件(例如图片和音频文件)来说,合并有冲突的修改几乎是不可能完成的。在这种情况下,串行地修改文件就显得非常有必要。如果没有串行访问,用户花费大量时间作出的修改很可能会被丢弃。
加锁-修改-解锁模型:
复制-修改-合并模型:
仓库
仓库是存放 SVN 的版本控制数据的中央位置,用户及其软件通过工作副本与仓库交互。SVN 实现仓库的方式与其他版本控制系统非常类似。与工作副本不同,一个 SVN 仓库是一个抽象的实体,可以被 SVN 的库和工具进行独占性地操作。
工作副本
工作副本(working copy)是仓库的特定版本数据在本地的副本,用户可以自由地对它进行操作。工作副本中除了存放被版本控制的文件外,还有用于跟踪文件和与服务器通信的元数据。对其他软件来说,工作副本只是一个普通的本地目录,所以即使它们不具备版本控制功能也可以对工作副本进行读写。SVN 的客户端工具负责管理工作副本,以及与仓库通信。
版本号
SVN 客户端将任意多的文件和目录的修改作为一个原子事务提交给仓库。原子事务的意思是要么所有的修改都被仓库接受,要么一个也没有。SVN 尽量保证即使是在程序崩溃,操作系统崩溃,网络断开和有其他 用户干扰的情况下,也能维持住原子性。
仓库每接受一次提交都会为文件系统树创建一个新状态,叫作一个版本号(revision)。每一个版本号都与一个独一无二的自然数相关联,后一个版本号都比前一个大一。新创建的仓库的初始版本号是 0,除了一个空的根目录外,什么也没有。
0x02 SVN 的工作副本
一个 SVN 工作副本是用户本地系统中的一个普通目录,用户可以按照自己的要求对存放在目录中的文件进行编辑,如果是源代码文件,用户也可以按照通常的方式对它们进行编译。工作副本是用户的私有工作空间:除非用户明确地要求 SVN,否则它不会让工作副本合并其他人的修改,也不会把用户的修改暴露给其他人。用户可以为同一个项目创建多个工作副本。
如果用户修改了工作副本中的文件,并且确认了修改是正确的,此时可以使用 SVN 提供的命令来”发布”修改(通过把修改保存到仓库中),于是项目中的其他人就可以看到你的修改。如果其他人也发布了他们的修改,SVN 也提供了命令把他们的修改合并到你的工作副本中(通过读取仓库)。
工作副本还会包含一些额外的文件,这些文件由 SVN 创建并维护,用于命令的正常运行。每一个工作副本中都有一个名为 .svn
的子目录,它是工作副本的管理目录。管理目录中的文件可以帮助 SVN 识别哪些文件含有未发布的修改,哪些文件是过时的。
工作副本的工作原理
SVN 为工作副本中的每一个文件记录两项信息:
- 版本号:这被称为文件的工作版本号(working revision)
- 时间戳:记录了本地文件最近一次被仓库更新是在什么时候
有了这些信息后,通过与仓库通信,SVN 就可以判断出 每一个工作文件处于以下 4 种状态中的哪一种:
- 当前未修改的:文件在工作副本中未被修改,并且在工作版本号之后还没有人提交过该文件的修改。对文件执行
svn commit
和svn update
都不会产生任何效果. - 当前已修改的:文件在工作副本中已被修改,并且在一次更新以来还没有人向仓库提交过该文件的修改。本地有未提交的修改,于是执行
svn commit
将会成功地把修改提交到仓库中,而 svn update 不会产生任何效果. - 过时未修改的:文件在工作副本中未被修改,但是在上一次更新之后有人往仓库提交了该文件的修改。为了让文件和最新版本保持同步,应该执行更新操作。对文件执行
svn commit
不会产生任何效果,执行svn update
将 把仓库中的最新修改合并到文件中. - 过时且已修改的:文件在本地工作副本和仓库都被修改了。对文件执行
svn commit
会由于文件已过时而失败。首先应该更新文件,命令svn update
试图 把仓库的修改合并到本地。如果 SVN 不能自动地以一种 合理的方式完成合并,就会把冲突交由用户来解决.
0x03 SVN 基本用法
往仓库中添加数据
命令 svn import
可以快速地向仓库中添加新文件或目录。svn import
不要求工作副本,新增的文件会马上提交到仓库中。使用该命令的典型情况是用户想要把一个已存在的目录添加到 SVN 仓库中,例如:
svn import SRC DST [-m MSG]
上面的例子把本地目录 mytree
中的内容添加到仓库的 some/project
目录中。注意,用户在导入前无需创建新目录——svn import
会自动完成这些工作。提交后,用户就可以在仓库中看到新增的文件和目录:
svn list SRC
创建工作副本
检出(checkout)仓库中的目录将会在用户的本地主机上创建一个该目录的工作副本。除非特意指定,否则这个副本将包含仓库最新版本的数据:
svn checkout SRC
更新工作副本
如果某个项目正在被多个工作副本修改,用户就需要更新自己本地的工作副本,以获取其他人提交的修改:
svn update
修改工作副本
工作副本支持的修改类型分为两种:文件修改(file changes)和目录修改(tree changes)。文件修改不需要告知 SVN,用户可以使用任意一种自己喜欢的工具来修改文件,SVN 可以自动检测到哪些文件发生了变化。目录修改涉及到目录结构的变化,例如添加和删除文件、重命名文件和目录、复制文件和目录。目录修改要使用 SVN 的命令完成。文件修改和目录修改只有在提交后才会更新到仓库中:
svn add FOO
svn delete FOO
svn copy FOO BAR
svn move FOO BAR
svn mkdir FOO
审核修改
在提交之前,应该查看一下自己到底修改了哪些东西。
查看修改的整体概述:
svn status [FOO]
查看修改的细节:
svn diff [FOO]
修正错误
SVN 提供了一种简便的方法来撤消工作副本中的修改,把文件或目录恢复到修改前的样子:
svn revert [-r REV] [FOO]
解决冲突
当一个用户正在修改文件时,其他人可能已经把自己的修改提交到了服务器上。为了防止在提交修改时,由于工作副本过旧导致提交失败,用户需要把其他人的修改更新到本地:
svn update
如果命令的执行结果有冲突产生:
svn resolve --accept [ACT] [FOO]
关于 ACT:
- base:使用上一次检出时版本
- mine-full:保留自己的修改
- theirs-full:保留从服务器收到的更新
提交修改
把工作副本的修改提交到仓库中,如果修改 被接受,其他用户就可以看到这些修改:
svn commit [-m MSG]
检查历史
从行的级别上查看修改的内容:
svn diff [-r REV]
查看和版本号绑定的日志消息,及其日期、作者、以及受影响的文件路径:
svn log [-r REV]
根据给定的版本号,输出文件在该版本下的内容:
svn cat [-r REV]
根据给定的版本号,查看该版本下的文件的每一行的最后一次修改信息:
svn annotate [-r REV]
根据给定的版本号,列出仓库在该版本下的文件与目录清单:
svn list [-r REV] [SRC]
0x04 SVN 与 Git 的区别
- SVN 仓库为集中式管理,而 Git 仓库为分布式管理
- SVN 的权限管理可精确到具体路径,而 Git 只能做到仓库级别
- SVN 可下载仓库下的某一子目录,而 Git 只能下载完整的仓库
- SVN 一个仓库可对应多个项目,而 Git 一个仓库对应一个项目
- SVN 的分支与标签基于文件目录,而 Git 则基于引用
- SVN 对文件的完整性依赖于服务端给出的校验码,而 Git 则基于自身的机制(通过 SHA-1 计算的 Commit ID)
参考资料
- http://svnbook.red-bean.com/nightly/zh/svn-book.html#svn.advanced.props.special.ignore

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。