Linux - 版本控制系统SVN
0. 摘要
本文通过搭建SVN多版本库为例,介绍SVN的使用。
SVN是一个集中式版本控制系统,在服务端部署中央版本库,所有开发人员客户端连接到中央版本库进行代码的提交和更新。
Apache Subversion 通常被缩写成 SVN,是一个开放源代码的版本控制系统,Subversion 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。
SVN相对于的RCS、CVS,采用了分支管理系统,它的设计目标就是取代CVS。互联网上免费的版本控制服务多基于Subversion。
1. SVN简介
1.1 SVN属于集中式版本控制系统
1.2 SVN工作流程及常用术语
1.2.1 工作流程
1.2.2 常用术语
版本库 :或称为源代码库(repository),SVN服务端存放各版本代码的仓库。
检出 (checkout):程序员工作的第一步,从服务端版本库中拉取一份代码到本机上,这个动作称为检出。
工作副本 :程序员检出到本机上的那份代码,称之为工作副本,该副本受版本库控制。
更新 (update):程序员将版本库中的代码更新到本机上。
提交 (commit):程序员将本机上的代码提交到版本库中。
版本 :每向版本库中提交一次,就产生一个版本。
版本号:记录版本的编号。
无版本控制:程序员自己的工作副本目录下,有些文件并没有纳入版本控制中,这些文件称为无版本控制。
增加(add):工作副本中将无版本控制的文件纳入版本控制的过程。
修改:工作副本中修改纳入版本控制的文件。
常规:常规指一种状态,当工作副本目录下的文件版本与.svn目录记录的版本一致时,该文件处于常规状态。
1.2.3 检出(checkout)与导出(export)的区别
从版本库中检出到本机的工作副本中,不但包含项目代码,还有一个隐藏文件.svn,该文件记录了最近一次版本库的状态。工作副本中所有文件的变化也记录于该文件中,这就是工作副本中的文件受版本控制的原因。而导出仅仅是把项目代码从版本库中复制下来,导出的文件不受版本控制。
2. SVN服务端的安装、配置、启动
2.1 安装SVN
CentOS7上安装SVN,程序包名是subversion,该程序包包含服务端和客户端命令。
$ sudo yum install subversion
服务端命令
svnadmin:用于管理版本库,例如版本库的创建、备份、恢复。
svnserve:用于启动SVN服务。
客户端命令
svn:客户端所有的操作都使用该命令。
2.2 配置SVN
2.2.1 规划仓库的组织方式
subversion安装好以后,首先需要创建存放代码的版本库,SVN支持创建多个版本库。通常有两种组织代码的方式
a. 一个仓库存放一个项目
repo1/ trunk/ branches/ tags/ repo2/ trunk/ branches/ tags/
b. 一个仓库存放多个项目
repo/ project1 trunk/ branchs/ tags/ project2 trunk/ branchs/ tags/
2.2.2 创建多版本库
此处演示创建三个版本库,并且三个版本库使用相同的权限(authz)和密码(passwd)配置文件。
# 创建存放版本库的根目录 $ sudo mkdir /opt/svn # 此处我使用的是普通用户,为了方便,将版本库根目录的属主、属组修改为当前用户。 $ sudo chown ec2-user.ec2-user /opt/svn # 在/opt/svn目录下创建三个版本库business,csa,sys $ svnadmin create /opt/svn/business $ svnadmin create /opt/svn/csa $ svnadmin create /opt/svn/sys # 此时/opt/svn目录下出现三个子目录business、csa、sys,每个子目录是一个独立的版本库 $ ls /opt/svn/ business csa sys
2.2.3 修改版本库配置文件
创建好的3个版本库目录下都有各自的配置文件
# 配置文件存放于各版本库根目录下的conf文件夹中,总共3个文件 $ ls /opt/svn/business/conf/ authz passwd svnserve.conf
authz:对SVN用户和组访问版本库进行授权
passwd:存放用户名和密码
svnserve.conf:svn服务的相关配置,包括authz、passwd文件所在路径的配置。
a. 此处3个版本库使用共同的authz、passwd文件,在版本库根目录下创建一个目录conf存放共用的这些文件。并修改3个版本库的svnserve.conf配置文件,将路径指向次文件。
# 创建存放共用配置文件passwd, authz的目录 $ mkdir /opt/svn/conf $ cp /opt/svn/business/conf/{passwd,authz} /opt/svn/conf/ # 编辑其中一个仓库的配置文件 $ vim /opt/svn/business/conf/svnserve.conf [general] #匿名用户权限,read:更新代码权限,write:提交代码权限,none:无权限。 anon-access = none # 授权用户的权限,详细权限配置在authz文件中。 auth-access = write # 密码文件路径 password-db = ../../conf/passwd # 权限配置文件路径 authz-db = ../../conf/authz # 其他两个仓库使用相同的配置 $ cp /opt/svn/business/conf/svnserve.conf /opt/svn/csa/conf/ $ cp /opt/svn/business/conf/svnserve.conf /opt/svn/sys/conf/
b. 添加SVN用户和密码
$ cat /opt/svn/conf/passwd [users] # 格式为:username = password,并且行前不能有空格。 zb = 123 zy = 123 xc = 123
c. 授权用户
$ cat /opt/svn/conf/authz # 创建组,便于统一授权,格式:groupname = user1,user2,... [groups] admin = zb developer = zy,xc # 对SVN根目录下的所有仓库进行授权 [/] @admin = rw #组名前加@符号,不加为用户名。 # 对指定的仓库进行授权,格式[repository:/] [business:/] @developer = rw [csa:/] @developer = rw [sys:/] @developer = r zy = rw # 仅对仓库下的目录进行授权,格式[repository:/path] [sys:/branchs] @developer = rw
2.3 启动SVN,并设定为开机自启动
2.3.1 多版本库启动仓库有两种方式,
1. 不同的版本库使用不同的端口: svnserve -d -r /xxx/SVN_ROOT/REPOSITORY --listen-port PORT
2. 多个版本库使用同一个端口: svnserve -d -r /xxx/SVN_ROOT/
# -d:启动为守护进程,-r:指定SVN版本库根目录
$ svnserve -d -r /opt/svn
2.3.2 开机自启动
将启动命令添加到/etc/rc.d/rc.local文件中,并修改该文件为可执行。
$ sudo vim /etc/rc.d/rc.local svnserve -d -r /opt/svn $ sudo chmod a+x /etc/rc.d/rc.local
3. SVN客户端的使用
3.1 Windows图形客户端
Windows系统中常用的客户端工具是TortoiseSVN,下载链接:https://tortoisesvn.net/downloads.html,安装好TortoiseSVN后,并没有启动图标,而是将命令集成到了右键菜单上,如下图所示。
a. 新建3个目录,将3个版本库分别检出到对应目录下。
b. 进入business目录,检出business版本库,右键单击 SVN Checkout,输入版本库URL:snv://HOST:PORT/REPOSITORY,检出的目录保持默认。
c. 输入在配置文件中授权的用户及密码
d. 检出后再工作副本目录下,生成一个隐藏文件.svn,该文件记录了最近一次版本库中的文件状态及工作副本中所有文件的变化。
其他版本库的检出操作一样,更多的操作单击右键即可知晓,本文重点讲述命令行下SVN的使用。
3.2 命令行客户端
在命令行中,SVN客户端命令是 svn ,其拥有众多的子命令,用于实现不同的功能,使用 svn help 查看所有子命令,下面介绍常用的子命令。
$ svn help ... Available subcommands: add blame (praise, annotate, ann) cat changelist (cl) checkout (co) cleanup commit (ci) ...
3.2.1 svn常用子命令
a. 检出工作副本,开发的第一步是从版本库中检出最新的工作副本于本机上。
语法: svn checkout|co svn://HOST[:PORT]/REPOSITORY [WCPATH] [--username USER] [--password PASSWD] ,若server端启动时未带仓库路径,则此处检出语法为: svn checkout|co svn://HOST[:PORT] [WCPATH] [--username USER] [--password PASSWD]
$ svn checkout svn://13.231.178.30/business business_copy --username zb Password for 'zb': #输入密码 Store password unencrypted (yes/no)? yes #是否在本机上保存密码 # 在当前目录下生成工作副本business_copy,及其子目录下.svn隐藏文件 $ ls business_copy/ -a . .. .svn
b. 按照常用的代码组织方式,在工作副本中创建三个目录trunk(主版本)、branchs(分支)、tags(阶段性发布的版本)
创建目录语法: svn mkdir PATH [--parents]
提交代码语法: svn commit|ci [PATH...] -m 'MESSAGE'
$ cd business_copy/ # svn mkdir命令创建的目录直接添加到版本控制中,使用linux命令创建的目录,还要使用svn add命令才添加到版本控制中 $ svn mkdir trunk branchs tags A trunk A branchs A tags # 向版本库中提交代码 $ svn commit -m 'add trunk branchs tags' Adding branchs Adding tags Adding trunk Committed revision 1.
c. 通常我们只对其中一个分支进行开发,进入turnk分支,随意创建一些文件做测试
添加文件到版本控制中: svn add [PATH...|*] [--force] [--non-recursive]
查看工作副本中文件状态: svn status [PATH...]
?: 无版本控制
D: 已标记为删除
M: 已被修改过
A: 已标记为添加
R: 文件替换,比如删除后创建了同名文件
C: 文件存在冲突
!: 文件缺失,直接使用linux命令删除
$ ls branchs tags trunk $ cd trunk/ $ echo 'Hello World' > index.html # 查看文件状态 $ svn status index.html ? index.html # 将文件添加到版本控制中 $ svn add index.html A index.html # 提交代码 $ svn commit -m 'add index.html'
d. 继续添加一些文件,并修改index.html
svn diff命令可以用来比较工作副本中的文件与.svn记录的最新版本的差别,也可以用来比较不同版本中文件的差别,语法: svn diff|di [-r N:M] [PATH...]
将工作副本更新为版本库中的最新版本: svn update|up ,将工作副本更新为历史版本: svn update|up [-r N] [PATH...]
# 添加一些测试文件 $ echo 'Just a test' > about.html $ echo 'Some test content' > content.html $ svn mkdir jss css A jss A css $ touch css/a.css $ touch jss/a.jss # index中追加内容 $ echo 'Something so beautiful' >> index.html # 所有文件追加到版本控制中 $ svn add * --force A about.html A content.html A css/a.css A jss/a.jss # 比较工作副本中index.html和.svn记录的最新版本的差别 $ svn diff index.html Index: index.html =================================================================== --- index.html (revision 2) +++ index.html (working copy) @@ -1 +1,2 @@ Hello World +Something so beautiful $ svn commit -m 'add some fiels' # 从版本库中更新代码到工作副本中 $ svn update Updating '.': At revision 3. # 比较 版本3和版本2中index.html文件的差别 $ svn diff -r r3:r1 index.html Index: index.html =================================================================== --- index.html (revision 3) +++ index.html (revision 1) @@ -1,2 +0,0 @@ -Hello World -Something so beautiful
e. 查看版本库信息
语法: svn info [PATH...]
# 查看版本库信息 $ svn info Path: . Working Copy Root Path: /home/zhubiao/business_copy URL: svn://13.231.178.30/business/trunk Repository Root: svn://13.231.178.30/business Repository UUID: ea4cdeee-e36c-4b55-bada-911a124e2e4f Revision: 3 Node Kind: directory Schedule: normal Last Changed Author: zb Last Changed Rev: 3 Last Changed Date: 2018-09-16 19:18:45 +0800 (Sun, 16 Sep 2018) # 查看具体文件的信息 $ svn info index.html Path: index.html Name: index.html Working Copy Root Path: /home/zhubiao/business_copy URL: svn://13.231.178.30/business/trunk/index.html Repository Root: svn://13.231.178.30/business Repository UUID: ea4cdeee-e36c-4b55-bada-911a124e2e4f Revision: 3 Node Kind: file Schedule: normal Last Changed Author: zb Last Changed Rev: 3 Last Changed Date: 2018-09-16 19:18:45 +0800 (Sun, 16 Sep 2018) Text Last Updated: 2018-09-16 19:14:53 +0800 (Sun, 16 Sep 2018) Checksum: 4fef81a4d13f740b6eb72923f3031bcdde6f0923
f. 查看版本提交日志
$ svn log ------------------------------------------------------------------------ r3 | zb | 2018-09-16 19:18:45 +0800 (Sun, 16 Sep 2018) | 1 line add some fiels ------------------------------------------------------------------------ r2 | zb | 2018-09-16 18:59:07 +0800 (Sun, 16 Sep 2018) | 1 line add index.html ------------------------------------------------------------------------ r1 | zb | 2018-09-16 18:42:32 +0800 (Sun, 16 Sep 2018) | 1 line add trunk branchs tags ------------------------------------------------------------------------
g. 删除工作副本中的文件
语法: svn delete|del|remove|rm [PATH...] [--force]
# 从版本控制中删除jss $ svn delete jss --force D jss D jss/a.jss $ svn commit -m 'delete jss' Deleting jss
h. 有时工作副本中的文件修改错误了,想还原为.svn记录的最新版本,使用命令: svn revert [-R] PATH...|* ,注意不是还原为版本库中的最新版本,而是还原为工作副本中.svn所记录的最新版本。
$ echo 'Maybe the lien is Wrong.' >> index.html $ cat index.html Hello World Something so beautiful Maybe the lien is Wrong. # 还原为.svn记录的最新版本 $ svn revert index.html Reverted 'index.html' $ cat index.html Hello World Something so beautiful
i. 列出工作副本目录下处于版本控制中的文件
语法: svn list [-R|--recursive] [-v
$ svn list -R -v 3 zb Sep 16 19:18 ./ 3 zb 12 Sep 16 19:18 about.html 3 zb 18 Sep 16 19:18 content.html 3 zb Sep 16 19:18 css/ 3 zb 0 Sep 16 19:18 css/a.css 3 zb 35 Sep 16 19:18 index.html 3 zb Sep 16 19:18 jss/ 3 zb 0 Sep 16 19:18 jss/a.jss
3.2.2 与服务端版本库交换的一些命令
上面介绍的命令多数是在工作副本中执行的,下面介绍一些可以直接操作版本库的命令
a. 查看版本库中的文件
$ svn cat svn://13.231.178.30/business/trunk/index.html Hello World Something so beautiful
b. copy命令,copy有四种工作方式
WC -> WC: copy and schedule for addition (with history)
WC -> URL: immediately commit a copy of WC to URL
URL -> WC: check out URL into WC, schedule for addition
URL -> URL: complete server-side copy; used to branch and tag
语法: svn copy|cp SRC... DST [-m "MESSAGE"] ,向版本库中复制文件必须添加注释-m "MESSAGE",如同commit
# 工作副本内部复制:WC -> WC $ svn cp index.html org.html # 工作副本复制到版本库中:WC -> URL $ svn cp org.html svn://13.231.178.30/business/trunk/org.html -m "copy org.html" # 版本库复制到工作副本:URL -> WC $ svn cp svn://13.231.178.30/business/trunk/org.html org2.html # 版本库内部复制:URL -> URL $ svn cp svn://13.231.178.30/business/trunk/org.html svn://13.231.178.30/business/org3.html -m 'copy org3.html'
4. 回退历史版本
当前版本出现错误,需要回退历史版本,使用 svn merge 命令,回退版本步骤如下:
4.1 更新工作副本为最新版本
# 若工作副本中有未提交的要么使用svn revert回退为.svn记录的最新版本,要么先提交。
# 更新工作副本
$ svn update
4.2 查看版本提交日志,确定当前版本和要回退的版本。
$ svn log ------------------------------------------------------------------------ r5 | zb | 2018-09-16 20:06:30 +0800 (Sun, 16 Sep 2018) | 1 line copy org.html ------------------------------------------------------------------------ r4 | zb | 2018-09-16 19:37:30 +0800 (Sun, 16 Sep 2018) | 1 line delete jss ------------------------------------------------------------------------ r3 | zb | 2018-09-16 19:18:45 +0800 (Sun, 16 Sep 2018) | 1 line add some fiels ------------------------------------------------------------------------ r2 | zb | 2018-09-16 18:59:07 +0800 (Sun, 16 Sep 2018) | 1 line add index.html ------------------------------------------------------------------------ r1 | zb | 2018-09-16 18:42:32 +0800 (Sun, 16 Sep 2018) | 1 line add trunk branchs tags ------------------------------------------------------------------------
4.3 回退版本,此处回退到r2版本
# -r:当前版本:回退版本,记住后面有个“.”号,代表当前工作目录,不能缺少。 $ svn merge -r 5:2 . # 提交 $ svn commit -m 'Revert r5 -> r2'
5. 备份、迁移
有时需要更换SVN所在的服务器,此时需要将老服务器上的SVN版本库迁移到新服务器上,迁移步骤如下:
5.1 停止老服务器上的SVN服务
# 查看svnserve进程 $ ps -ef | grep svn ec2-user 12870 1 0 04:23 ? 00:00:00 svnserve -d -r /opt/svn # 结束进程 $ kill -9 12870
5.2 备份版本库
语法: svnadmin dump /xxx/REPO_ROOT/REPOSITORY [-r N:M] > BACKUP_FILE ,-r指定要备份的版本,未指定则备份所有版本。
# 备份版本库business到文件 repo_busienss.bk中 $ svnadmin dump /opt/svn/business > repo_busienss.bk * Dumped revision 0. * Dumped revision 1. * Dumped revision 2. * Dumped revision 3. * Dumped revision 4. * Dumped revision 5. * Dumped revision 6. * Dumped revision 7.
5.3 准备新服务器
安装subervison,并创建好版本库business
$ yum -y install subversion $ mkdir /opt/svn -p $ svnadmin create /opt/svn/business
5.4 导入版本库
语法: svnadmin load /xxx/REPO_ROOT/REPOSITORY < BACKUP_FILE
# svnadmin load /opt/svn/business < repo_busienss.bk <<< Started new transaction, based on original revision 1 * adding path : branchs ... done. * adding path : tags ... done. * adding path : trunk ... done. ------- Committed revision 1 >>> <<< Started new transaction, based on original revision 2 * adding path : trunk/index.html ... done. ------- Committed revision 2 >>> ...
5.5 复制配置文件
配置文件并没有备份下来,需要单独备份和恢复,或另行配置。