这篇文章旨在较为系统地整理SVN的用法。如果读者希望快速上手,可以直接阅读SVN生命周期部分。
SVN简介
SVN全称为Apache Subversion,是一个开源的版本控制系统。
SVN中的一些概念
- Repository(源代码库):源代码统一存放的地方。
- Checkout(提取):当你手上没有源代码的时候,你需要从repository checkout一份。
- Commit(提交):当你已经修改了代码,你就需要Commit到repository。
- Update (更新):当你已经Checkout了一份源代码, Update一下你就可以和Repository上的源代码同步,你手上的代码就会有最新的变更。
日常开发的流程通常为:Update(获得最新的代码)=>作出自己的修改并调试成功=> Commit(大家就可以看到你的修改了) 。
值得注意的是,SVN管理源代码是以行为单位的。所以如果两个程序员同时修改了同一个文件,只要没有修改同一行,SVN就可以合并这两个改动。如果是同一行则会提示Conflict,需要手动解决冲突。
SVN的主要功能
- 目录版本控制:SVN通过一个“虚拟”的版本管控文件系统增加了对目录变动的支持。
- 真实的版本历史:和CVS相比,支持了对目录或文件的增加(add)、删除(delete)、复制(copy)和重命名(rename)。所有新增的文件都从一个新的、干净的版本开始。
- 自动提交:commit是原子操作,不是全部更新就是完全不更新。
- 纳入版本控管的元数据:每个文件与目录所附有的属性也接受管控,如同文件内容一样。
- 选择不同的网络层:SVN可以作为一个扩展模块嵌入Apache HTTP服务器中;也可以使用轻量级的独立SVN服务器,并自定义通信协议。另外还支持:身份认证, 授权, 在线压缩, 以及文件库浏览等功能。
- 一致的数据处理方式:SVN使用二进制差异算法,所以在对二进制文件的支持和对文本文件的支持一样好。
- 有效分支(branch)与标签(tag):SVN通过复制项目来建立分支和标签,类似与硬连接(hard-link),所以只会花费固定的少量时间。
- Hackability:SVN没有历史包袱、便于维护。
安装
- Windows:https://sourceforge.net/projects/win32svn/
- CentOS:
yum install subversion - Ubuntu:
apt-get install subversion - MacOS:
brew install subversion(需要先安装homebrew)
其中CentOS和MacOS通常自带Subversion,可通过svn --version命令检查已安装版本。
SVN生命周期
创建版本库=>检出=>更新=>执行变更=>复查变化=>修复错误=>解决冲突=>提交更改
- 创建版本库(create):大多情况下只会执行一次。
- 检出(checkout):从版本库创建一个工作副本,作为开发者私人的工作空间。
- 更新(update):讲工作副本与版本库进行同步。
- 执行变更(commit):对项目的编辑会被添加到待变更列表中,直到执行commit才会成为版本库的一部分。
- 复查变化(status):在commit之前复查是很好的习惯,status会列出工作副本中的改动,可以使用diff来查看详细变动。
- 修复错误(revert):重置对工作副本的修改(部分或者全部),会销毁待变更列表并恢复工作副本到原始状态。
- 解决冲突(mrege、resolve):merge会自动处理可以安全合并的部分,resolve用来帮助用户找出并处理冲突。
- 提交更改(commit):提交前必须讲文件/目录添加(add)到待变更列表中。通常需要提供一个注释来说明为什么进行这些改动。
SVN启动模式
手动新建版本库目录
mkdir /opt/svn使用svn命令创建版本库
svnadmin create /opt/svn/project_repo使用senserve启动服务
svnserve -d -r 目录 --listen-port 端口号--listen-port指定了svn监听端口(默认3690)-r决定了版本库访问的方式:在project_repo下为单库svnserve方式
svnserve -d -r /opt/svn/project_repoauthz配置文件 1
2
3
4
5
6[groups]
admin=user1
dev=user2
[/]
@admin=rw
user2=r使用类似这样的URL:
svn://192.168.0.1/即可访问project_repo版本库在svn下则为多库svnserve方式
svnserve -d -r /opt/svnauthz配置文件 1
2
3
4
5
6
7
8
9
10[groups]
admin=user1
dev=user2
[project_repo:/]
@admin=rw
user2=r
[project_repo01:/]
@admin=rw
user2=r如果此时你还用
[/],则表示所有库的根目录,同理,[/src]表示所有库的根目录下的src目录。
使用类似这样的URL:svn://192.168.0.1/project_repo即可访问project_repo版本库。
配置SVN版本库
进入/opt/svn/project_repo/conf目录 修改默认配置文件。
svn服务配置文件svnserve.conf
该文件仅由一个[general]配置段组成。
1 | [general] |
- anon-access: 非鉴权用户访问版本库的权限。”write”表示可读可写,”read”表示只读,”none”表示无访问权限。 缺省值:read。
- auth-access: 鉴权用户(authentication user)访问版本库的权限。”write”表示可读可写,”read”表示只读,”none”表示无访问权限。 缺省值:write。
- authz-db: 权限配置文件名,通过该文件可以实现以路径为基础的访问控制。 除非指定绝对路径,否则文件位置为相对conf目录的相对路径。 缺省值:authz。
- password-db: 用户名口令文件名。 除非指定绝对路径,否则文件位置为相对conf目录的相对路径。缺省值为passwd。
- realm: 版本库的认证域,即在登录时提示的认证域名称。若两个版本库的认证域相同,建议使用相同的用户名口令数据文件。缺省值为一个UUID(Universal Unique IDentifier,全局唯一标示)。
用户名口令文件passwd
用户名口令文件由svnserve.conf的配置项password-db指定,缺省为conf目录中的passwd。该文件仅由一个[users]配置段组成。
1 | [users] |
[users]配置段的配置行格式如下:<用户名> = <口令>
权限配置文件
权限配置文件由svnserve.conf的配置项authz-db指定,缺省为conf目录中的authz。该配置文件由一个[groups]配置段和若干个版本库路径权限段组成。
1 | [groups] |
[groups]配置段的配置行格式如下:<用户名> = <口令>
版本库路径权限段的段名格式如下:[<版本库名>:<路径>]
本例是使用svnserve -d -r /opt/svn以多库svnserve方式启动SVN,所以URL为svn://192.168.0.1/project_repo01。
SVN检出操作
1 | svn checkout svn://svn.server.com/svn/project_repo --username=user01 |
检出成功后在当前目录下生成project_repo副本目录。
SVN解决冲突
使用以下命令查看更改
svn diff1
2
3
4
5
6
7Index: HelloWorld.html
===================================================================
--- HelloWorld.html (revision 5)
+++ HelloWorld.html (working copy)
@@ -1,2 +1 @@
-HelloWorld! http://www.svn.com/
+HelloWorld! http://www.svn.com/!尝试使用下面的命令来提交他的更改
svn commit -m "change HelloWorld.html first"1
2
3
4root@hostname:~/svn/project_repo01/trunk# svn commit -m "change HelloWorld.html first"
Sending HelloWorld.html
Transmitting file data .svn: E160028: Commit failed (details follow):
svn: E160028: File '/trunk/HelloWorld.html' is out of date此时,HelloWorld.html 已经被 user02 修改并提交到了仓库,所以提交失败了。
为了避免两人的代码被互相覆盖在提交更改之前必须先更新工作副本。更新工作副本
svn update1
2
3
4
5
6
7
8
9
10
11root@hostname:~/svn/project_repo01/trunk# svn update
Updating '.':
C HelloWorld.html
Updated to revision 6.
Conflict discovered in file 'HelloWorld.html'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
(mc) my side of conflict, (tc) their side of conflict,
(s) show all options: mc
Resolved conflicted state of 'HelloWorld.html'
Summary of conflicts:
Text conflicts: 0 remaining (and 1 already resolved)在上面的例子中选择了”mc”,代表以本地的文件为主。你也可以使用其选项对冲突的文件进行不同的操作。
默认是更新到最新的版本,我们也可以指定更新到哪个版本
svn update -r6工作副本和仓库同步完毕后即可安全地提交
svn commit -m "change HelloWorld.html second"1
2
3
4root@hostname:~/svn/project_repo01/trunk# svn commit -m "change HelloWorld.html second"
Sending HelloWorld.html
Transmitting file data .
Committed revision 7.
SVN提交操作
查看工作副本中的状态
1 | root@hostname:~/svn/project_repo01/trunk# svn status |
此时 readme的状态为?,说明它还未加到版本控制中。
将文件readme加到版本控制,等待提交到版本库。
1 | root@hostname:~/svn/project_repo01/trunk# svn add readme |
查看工作副本中的状态
1 | root@hostname:~/svn/project_repo01/trunk# svn status |
此时 readme的状态为A,它意味着这个文件已经被成功地添加到了版本控制中。
为了把 readme 存储到版本库中,使用 commit -m 加上注释信息来提交。
如果你忽略了 -m 选项, SVN会打开一个可以输入多行的文本编辑器来让你输入提交信息。
1 | root@hostname:~/svn/project_repo01/trunk# svn commit -m "SVN readme." |
现在 readme 被成功地添加到了版本库中,并且修订版本号自动增加了1。
SVN版本回退
回退未提交的修改
1 | root@hostname:~/svn/project_repo01/trunk# svn status |
使用一下命令回退
1 | root@hostname:~/svn/project_repo01/trunk# svn revert readme |
回退后的状态为
1 | root@hostname:~/svn/project_repo01/trunk# svn status |
进行 revert 操作之后,readme 文件恢复了原始的状态。 revert 操作不单单可以使单个文件恢复原状, 而且可以使整个目录恢复原状。恢复目录用 -R 命令,如下
1 | svn revert -R trunk |
回退已提交的修改
为了消除一个旧版本,我们必须撤销旧版本里的所有更改然后提交一个新版本。这种操作叫做 reverse merge。
首先,找到仓库的当前版本,现在是版本 22,我们要撤销回之前的版本,比如版本 21。
1 | svn merge -r 22:21 readme |
SVN查看历史信息
通过svn命令可以根据时间或修订号去除过去的版本,或者某一版本所做的具体的修改。以下四个命令可以用来查看svn 的历史:
- svn log: 用来展示svn 的版本作者、日期、路径等等。
- svn diff: 用来显示特定修改的行级详细信息。
- svn cat: 取得在特定版本的某文件显示在当前屏幕。
- svn list: 显示一个目录或某一版本存在的文件。
svn log
基本使用方法
1
svn log
可以显示所有的信息。
如果只想查看某一个文件的版本修改信息,可以使用
1
svn log filename
如果只希望查看特定的某两个版本之间的信息,可以加上
-r 6:8。 其中6和8分别代表开始的版本和结束的版本如果希望得到目录的信息,加上
-v。如果希望显示限定N条记录的目录信息,加上
-l N。
svn diff
用来检查历史修改的详情。
检查本地修改
如果不带任何参数,它将会比较你的工作文件与缓存在 .svn 的”原始”拷贝。1
svn diff
比较工作拷贝与版本库
比较你的工作拷贝和版本库中版本号为 3 的文件 rule.txt。1
svn diff -r 3 rule.txt
比较版本库与版本库
通过-r(revision)传递两个通过冒号分开的版本号,这两个版本会进行比较。
比较 svn 工作版本中版本号2和3的这个文件的变化。1
svn diff -r 2:3 rule.txt
svn cat
如果只是希望检查一个过去版本,不希望查看他们的区别,可使用svn cat。
1 | svn cat -r 版本号 rule.txt |
这个命令会显示在该版本号下的该文件内容。
svn list
svn list可以在不下载文件到本地目录的情况下来察看目录中的文件。
1 | svn list svn://192.168.0.1/project_repo |
SVN分支
Branch 选项会给开发者创建出另外一条线路。当有人希望开发进程分开成两条不同的线路时,这个选项会非常有用。
比如项目 demo 下有两个小组,svn 下有一个 trunk 版。
由于客户需求突然变化,导致项目需要做较大改动,此时项目组决定由小组 1 继续完成原来正进行到一半的工作(某个模块),小组 2 进行新需求的开发。
那么此时,我们就可以为小组2建立一个分支,分支其实就是 trunk 版(主干线)的一个copy版,不过分支也是具有版本控制功能的,而且是和主干线相互独立的,当然,到最后我们可以通过(合并)功能,将分支合并到 trunk 上来,从而最后合并为一个项目。
创建分支
1
root@hostname:~/svn/project_repo01#svn copy trunk/ branches/my_branch
提交分支
1
root@hostname:~/svn/project_repo01#svn commit -m "add my_branch"
切换分支
1
root@hostname:~/svn/project_repo01# cd branches/my_branch/
提交分支中的修改
1
root@hostname:~/svn/project_repo01/branches/my_branch# svn add newfile
1
root@hostname:~/svn/project_repo01/branches/my_branch# svn commit -m "add newfile"
合并分支
切换到 trunk,执行 svn update,然后将 my_branch 分支合并到 trunk 中。1
root@hostname:~/svn/project_repo01/trunk# svn merge ../branches/my_branch/
提交主干
将合并好的 trunk 提交到版本库中。1
root@hostname:~/svn/project_repo01/trunk# svn commit -m "add newfile"
SVN标签
版本管理系统支持 tag 选项,通过使用 tag 的概念,我们可以给某一个具体版本的代码一个更加有意义的名字。
Tags 即标签主要用于项目开发中的里程碑,比如开发到一定阶段可以单独一个版本作为发布等,它往往代表一个可以固定的完整的版本。
使用一下命令在本地工作副本创建一个 tag
1 | root@hostname:~/svn/project_repo01# svn copy trunk/ tags/v1.0 |
TortoiseSVN的使用
具有图形界面的SVN。
由于我目前主要在Mac上工作,而且GUI的工具也比较好上手,故在此略过了。
如有需要可以参考SVN教程