在Docker下搭建SVN服务

第一步:环境准备

拉取CentOS的镜像,利用Dockerfile文件

FROM centos:6  
MAINTAINER chenyuan

# RUN yum install -y java-1.7.0-openjdk.x86_64 java-1.7.0-openjdk-devel.x86_64
RUN yum -y install openssh-server openssh-clients httpd

# RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
RUN sed -i "s/#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config  
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key  
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key  
ENV SSH_PORT 22

# 添加测试用户admin,密码123456,并且将此用户添加到sudoers里  
RUN useradd admin  
RUN echo "admin:123456" | chpasswd  
RUN echo "admin   ALL=(ALL)       ALL" >> /etc/sudoers  

执行如下命令拉取我们需要的镜像,并且是预安装我们想要软件的镜像,所以Dockfile对于我们来说,就是一个配置文件,有了这个东西,我们在哪儿都能获取到相同环境的镜像。

# 进入Dockfile文件的目录
cd /Users/chenyuan/Tools/Docker  
➜  Docker  ll
total 8  
-rw-r--r--@ 1 chenyuan  staff   803B Jun  5 22:38 Dockerfile

# 执行
docker build -t centos6-svn .  

当我们安装完毕后,再看看我们的镜像列表

➜  Docker  docker images
REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE  
centos6-svn                    latest              394ea6ee9455        15 hours ago        330MB  
centos                         6                   70b5d81549ec        2 months ago        195MB  

我们就能看到2个镜像,一个是centos6-svn,另外一个是centos的镜像。那我们需要利用这个最基本的镜像启动3个容器来为我们模拟zzhangsanlisi的用户针对于一台SVN服务器操作搭建环境。

利用Docker来启动3台机器

#启动暴露端口的容器
docker run  -tid -p 5001:22 --name centos-svn-node1 394ea6ee9455  /usr/sbin/sshd -D  
docker run  -tid -p 5002:22 --name centos-svn-node2 394ea6ee9455  /usr/sbin/sshd -D  
docker run  -tid -p 5003:22 --name centos-svn-node3 394ea6ee9455  /usr/sbin/sshd -D  

解释一下,这行命令代表我需要利用ssh工具并且开启了5001这样的端口让外接去连接,并且取名字centos-svn-node1来区别开来。

如果完全启动后,我们看看容器列表具体的情况。

➜  ~  docker ps -a
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS                  NAMES  
54d68f2ff26b        394ea6ee9455        "/usr/sbin/sshd -D"   15 hours ago        Up 15 hours         0.0.0.0:5003->22/tcp   centos-svn-node3  
9da60e04d8f1        394ea6ee9455        "/usr/sbin/sshd -D"   15 hours ago        Up 15 hours         0.0.0.0:5002->22/tcp   centos-svn-node2  
0d376abb1d82        394ea6ee9455        "/usr/sbin/sshd -D"   15 hours ago        Up 15 hours         0.0.0.0:5001->22/tcp   centos-svn-node1  

我们可以通过iTerm或者CRT等其他的Shell工具来连接,前提是你们通过adduser获取passwd命令为每台机器添加好相应的用户或者设置好对应的密码。我这里图简单,我就直接用root用户来操作,生产上建议用不同的用户来管理好权限,这也是用Linux的一大优势所在。

image-20180606133001855

通过ifconfig命令找到对应的IP地址连接上去,我截图统一展示一下。

172.17.0.2 centos-svn-node1 # SVN服务器  
172.17.0.3 centos-svn-node2 # zhangsan节点  
172.17.0.4 centos-svn-node3 # lisi节点  

第二步:安装SVN服务端与客户端

我把核心的脚本贴出来,供大家参考:

# install svn, 在机器centos-svn-node1上面
yum install -y subversion  
yum install -y mod_dav_svn  

添加仓库

mkdir -p /data/svn  
svnadmin create /data/svn/repo1  
chown -R apache:apache /data/svn/repo1  

配置svnserve.conf

vi /data/svn/repo1/conf/svnserve.conf  
# 追加该内容到最后
[general]
anon-access = none  
auth-access = write  
password-db = passwd  
authz-db = authz  

创建用户

htpasswd -c /data/svn/repo1/conf/passwd zhangsan # 创建文件,并且创建zhangsan用户  
htpasswd /data/svn/repo1/conf/passwd lisi #创建lisi用户  

赋值权限

vi /data/svn/repo1/conf/authz  
# 追加该内容到最后
[repo1:/]
zhangsan = rw  
lisi = r  
* =

配置HTTPD

cd /etc/httpd/conf.d/  
# 追加该内容到最后
LoadModule dav_svn_module modules/mod_dav_svn.so  
LoadModule authz_svn_module modules/mod_authz_svn.so

<Location /repo1>  
DAV svn  
SVNPath /data/svn/repo1  
Authtype Basic  
AuthName "My Repository"  
AuthzSVNAccessFile /data/svn/repo1/conf/authz  
AuthUserFile /data/svn/repo1/conf/passwd  
Require valid-user  
</Location>  

启动httpd的服务

service httpd restart  

继续在centos-svn-node2centos-svn-node3上安装SVN的客户端。

yum install -y subversion  

在这里基本上就把一个SVN的环境给搭建完毕了。

温馨提示,这里当时是用CentOS7搭建的,但是会一直报错:Failed to get D-Bus connection: Operation not permitted,这个错误一直未能找到很好的解决方案,才降级到CentOS6。如果大家知道好的解决方案,也可以告诉我。

第三步:SVN实操

拉取代码

mkdir -p /root/repo_client  
svn co --username zhangsan http://172.17.0.2:80/repo1 repo_client  

提交一点点代码到仓库里去,看看服务端是如何保存我们的文件的。

help

第一个命令,也是最最重要的一个命令

[root@9da60e04d8f1 demo001]# svn help
usage: svn <subcommand> [options] [args]  
Subversion command-line client, version 1.6.11.  
Type 'svn help <subcommand>' for help on a specific subcommand.  
Type 'svn --version' to see the program version and RA modules  
  or 'svn --version --quiet' to see just the version number.

Most subcommands take file and/or directory arguments, recursing  
on the directories.  If no arguments are supplied to such a  
command, it recurses on the current directory (inclusive) by default.

Available subcommands:  
   add
   blame (praise, annotate, ann)
   cat
   changelist (cl)
   checkout (co)
   cleanup
   commit (ci)
   copy (cp)
   delete (del, remove, rm)
   diff (di)
   export
   help (?, h)
   import
   info
   list (ls)
   lock
   log
   merge
   mergeinfo
   mkdir
   move (mv, rename, ren)
   propdel (pdel, pd)
   propedit (pedit, pe)
   propget (pget, pg)
   proplist (plist, pl)
   propset (pset, ps)
   resolve
   resolved
   revert
   status (stat, st)
   switch (sw)
   unlock
   update (up)

Subversion is a tool for version control.  
For additional information, see http://subversion.tigris.org/  
add/commit (ci)

添加文件到svn版本中

[root@9da60e04d8f1 repo_client]# svn add demo001/
A         demo001  
A         demo001/A.txt  
[root@9da60e04d8f1 repo_client]# svn st
A       demo001  
A       demo001/A.txt  
[root@9da60e04d8f1 repo_client]# svn commit -m 'init'
Adding         demo001  
Adding         demo001/A.txt  
Transmitting file data .  
Committed revision 1.  

在这里,我们的代码就已经提交到服务器上面去了,但是我们知道服务器上文件是如何保存的吗?

# /data/svn/repo1 仓库地址的目录详情
drwxr-xr-x 2 apache apache 4096 Jun 12 11:00 conf  
drwxr-xr-x 3 apache apache 4096 Jun 12 10:47 dav  
drwxr-sr-x 6 apache apache 4096 Jun 12 10:55 db  
-r--r--r-- 1 apache apache    2 Jun  5 14:48 format
drwxr-xr-x 2 apache apache 4096 Jun  5 14:48 hooks  
drwxr-xr-x 2 apache apache 4096 Jun  5 14:48 locks  
-rw-r--r-- 1 apache apache  229 Jun  5 14:48 README.txt

SVN在服务器端的存储方式和客户端是不一样的,所以在服务器端是看不到源文件的。服务器端有两种存储方式FSFS和BDB,目前默认都是FSFS。

在这里就不细说了,参考连接:https://www.zhihu.com/question/46768239

checkout (co)

检出代码到本地

mkdir -p /root/repo_client  
svn co --username lisi http://172.17.0.2:80/repo1 repo_client

[root@54d68f2ff26b ~]# svn co --username lisi http://172.17.0.2:80/repo1 repo_client
A    repo_client/demo001  
A    repo_client/demo001/A.txt  
Checked out revision 1.  
update (up)
[root@54d68f2ff26b repo_client]# svn up
G    demo001/A.txt  
Updated to revision 7.  

更新到某一个指定版本

[root@9da60e04d8f1 demo001]# svn up -r r3 A.txt 
U    A.txt  
Updated to revision 3.  
[root@9da60e04d8f1 demo001]# svn st
[root@9da60e04d8f1 demo001]# svn cat A.txt 
AAAAAAAAAA  
BBB  
[root@9da60e04d8f1 demo001]# svn up -r r7 A.txt 
U    A.txt  
Updated to revision 7.  
[root@9da60e04d8f1 demo001]# svn cat A.txt 
AAAAAAAAAA  
CCC  
CCVCCCCCCCC  
DDDD  
status

查看本地文件的状态

[root@9da60e04d8f1 demo001]# svn st
?       B.txt # 未加入版本
A       C.txt # 第一次加入版本  
D       D.txt # 已经删除  
M       A.txt # 编辑文件  

查看文件目录层级的状态情况

[root@54d68f2ff26b repo_client]# svn st -v .
                 7        7 zhangsan     .
                 7        7 zhangsan     demo001
                 7        3 zhangsan     demo001/B.txt
M                7        3 zhangsan     demo001/C.txt  
M                7        7 zhangsan     demo001/A.txt  
log

查看提交日志信息

[root@9da60e04d8f1 demo001]# svn log
------------------------------------------------------------------------
r2 | lisi | 2018-06-12 11:16:40 +0000 (Tue, 12 Jun 2018) | 1 line

add D  
------------------------------------------------------------------------
r1 | zhangsan | 2018-06-12 10:47:14 +0000 (Tue, 12 Jun 2018) | 1 line

init  
------------------------------------------------------------------------

查看版本之间某个用户提交的日志集合

[root@9da60e04d8f1 repo_client]# svn log -r r1:r3  | grep lisi
r2 | lisi | 2018-06-12 11:16:40 +0000 (Tue, 12 Jun 2018) | 1 line  

查看某个版本具体的信息

[root@9da60e04d8f1 repo_client]# svn log -r r1 -v
------------------------------------------------------------------------
r1 | zhangsan | 2018-06-12 10:47:14 +0000 (Tue, 12 Jun 2018) | 1 line  
Changed paths:  
   A /demo001
   A /demo001/A.txt

init  
------------------------------------------------------------------------

查看某一个文件

[root@9da60e04d8f1 demo001]# svn log B.txt 
------------------------------------------------------------------------
r3 | zhangsan | 2018-06-12 11:19:53 +0000 (Tue, 12 Jun 2018) | 1 line

op  
------------------------------------------------------------------------
diff (di)

查看文件不同

[root@9da60e04d8f1 demo001]# svn diff A.txt 
Index: A.txt  
===================================================================
--- A.txt       (revision 3)
+++ A.txt       (working copy)
@@ -1,2 +1,3 @@
 AAAAAAAAAA
-BBB
+
+CCCCCCCCC

比较一个文件从某个版本到另外一个版本的区别

[root@9da60e04d8f1 demo001]# svn diff -r r1:r3 A.txt 
Index: A.txt  
===================================================================
--- A.txt       (revision 1)
+++ A.txt       (revision 3)
@@ -1 +1,2 @@
 AAAAAAAAAA
+BBB
lock/unlock/info

加锁一个目录/解锁一个目录

[root@9da60e04d8f1 demo001]# svn lock -m "lock command" A.txt 
'A.txt' locked by user 'zhangsan'.

[root@9da60e04d8f1 demo001]# svn info A.txt 
Path: A.txt  
Name: A.txt  
URL: http://172.17.0.2:80/repo1/demo001/A.txt  
Repository Root: http://172.17.0.2:80/repo1  
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa  
Revision: 6  
Node Kind: file  
Schedule: normal  
Last Changed Author: lisi  
Last Changed Rev: 6  
Last Changed Date: 2018-06-13 06:35:18 +0000 (Wed, 13 Jun 2018)  
Text Last Updated: 2018-06-13 06:35:52 +0000 (Wed, 13 Jun 2018)  
Checksum: 4ca0a5d1e30df75db6fee65ca9a4e0f1  
# 有关锁的相关信息
Lock Token: opaquelocktoken:62e86583-1638-4252-9719-b33f092b50c8  
Lock Owner: zhangsan  
Lock Created: 2018-06-13 06:37:09 +0000 (Wed, 13 Jun 2018)  
Lock Comment (1 line):  
lock command

[root@9da60e04d8f1 demo001]# svn unlock A.txt 
'A.txt' unlocked.  

如果另外的用户去操作这个文件,会报错误。

[root@54d68f2ff26b demo001]# svn commit -m "update the lock file"
Sending        demo001/A.txt  
Transmitting file data .svn: Commit failed (details follow):  
svn: Server sent unexpected return value (423 Locked) in response to PUT request for '/repo1/!svn/wrk/8e7d8d12-c4c6-4ba4-8bc0-fc3ec2c2e522/demo001/A.txt'  

当取消到锁以后,就可以继续提交数据了。但是加锁的时候,数据需要全部已经提交才行。

delete (del, remove, rm)

删除文件的命令

[root@54d68f2ff26b demo001]# svn st
M       C.txt  
[root@54d68f2ff26b demo001]# svn rm C.txt 
svn: Use --force to override this restriction  
svn: 'C.txt' has local modifications  
[root@54d68f2ff26b demo001]# svn rm --force C.txt 
D         C.txt  

对于有修改的文件也想删除掉的话,我们就只需要添加一个--force就好了。

list (ls)

查看服务端的文件列表

[root@54d68f2ff26b demo001]# svn list http://172.17.0.2:80/repo1
demo001/  
[root@54d68f2ff26b demo001]# svn list http://172.17.0.2:80/repo1/demo001
A.txt  
B.txt  
C.txt  
revert

还原本地文件,保持与服务端一致

[root@54d68f2ff26b demo001]# svn st
M       C.txt  
M       A.txt  
[root@54d68f2ff26b demo001]# svn revert -R .
Reverted 'C.txt'  
Reverted 'A.txt'  
[root@54d68f2ff26b demo001]# svn st
copy (cp)/move (mv, rename, ren)

移动文件或者重命名文件

[root@54d68f2ff26b repo_client]# svn cp demo001 demo002
A         demo002  
[root@54d68f2ff26b repo_client]# svn mv demo002 demo003
A         demo003  
D         demo002/B.txt  
D         demo002/A.txt  
D         demo002  

其操作就是copy一份,然后把之前的删除掉。

mkdir

创建一个目录

[root@9da60e04d8f1 repo_client]# svn mkdir testcase
A         testcase  
cleanup

如果出现被锁住或者其他情况,可以用cleanup命令来清除

[root@9da60e04d8f1 repo_client]# svn cleanup
switch (sw)

切换工作空间

[root@54d68f2ff26b test_switch]# svn info
Path: .  
URL: http://172.17.0.2:80/repo1/demo001  
Repository Root: http://172.17.0.2:80/repo1  
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa  
Revision: 10  
Node Kind: directory  
Schedule: normal  
Last Changed Author: lisi  
Last Changed Rev: 8  
Last Changed Date: 2018-06-13 07:37:43 +0000 (Wed, 13 Jun 2018)

[root@54d68f2ff26b test_switch]# svn sw http://172.17.0.2:80/repo1/demo003
At revision 10.

[root@54d68f2ff26b test_switch]# svn info
Path: .  
URL: http://172.17.0.2:80/repo1/demo003  
Repository Root: http://172.17.0.2:80/repo1  
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa  
Revision: 10  
Node Kind: directory  
Schedule: normal  
Last Changed Author: lisi  
Last Changed Rev: 9  
Last Changed Date: 2018-06-13 08:08:29 +0000 (Wed, 13 Jun 2018)  
cat

通过svn查看文件

[root@9da60e04d8f1 demo001]# svn cat A.txt 
AAAAAAAAAA  
CCC  
CCVCCCCCCCC  
DDDD  
changelist (cl)

工作拷贝下面的修改文件分类,可以灵活的使用,做分类提交或者防止部分文件被提交等。

[root@9da60e04d8f1 demo001]# svn cl one A.txt B.txt 
Path 'A.txt' is now a member of changelist 'one'.  
Path 'B.txt' is now a member of changelist 'one'.  
[root@9da60e04d8f1 demo001]# svn cl two C.txt 
Path 'C.txt' is now a member of changelist 'two'.  
[root@9da60e04d8f1 demo001]# svn cl three D.txt 
Path 'D.txt' is now a member of changelist 'three'.  
[root@9da60e04d8f1 demo001]# svn cl
svn: Try 'svn help' for more info  
svn: Not enough arguments provided  
[root@9da60e04d8f1 demo001]# svn st

--- Changelist 'one':
M       B.txt  
M       A.txt

--- Changelist 'three':
M       D.txt

--- Changelist 'two':
M       C.txt  

提交某一个changelist对应的文件

[root@9da60e04d8f1 repo_client]# svn ci -m "add changelist" --changelist one
Sending        demo001/A.txt  
Sending        demo001/B.txt  
Transmitting file data ..  
Committed revision 13.  
import/export

import将本地的文件导入到服务端去

[root@54d68f2ff26b ~]# svn import -m "add new project" projectname http://172.17.0.2:80/repo1/projectname
Adding         projectname/release  
Adding         projectname/trunk  
Adding         projectname/tag  
Adding         projectname/branch

Committed revision 11.  
[root@54d68f2ff26b ~]# svn list http://172.17.0.2:80/repo1
demo001/  
demo003/  
projectname/  
testcase/  

export导出一个干净的不带.svn文件夹的目录树

[root@54d68f2ff26b ~]# svn export http://172.17.0.2:80/repo1/testcase
A    testcase  
Exported revision 11.  
[root@54d68f2ff26b ~]# cd testcase/
[root@54d68f2ff26b testcase]# svn info
svn: '.' is not a working copy  
[root@54d68f2ff26b testcase]# 
merge/resolve/resolved

模拟一个生产发版本的例子来处理merge操作,因为用UI界面时间花费的太多太多,用命令行操作速度会快很多倍。

背景:已经在分支branch_20180613_001上做了一些开发,但是由于发布晚于了别人,其他团队代码已经merge到了trunk代码上,现在需要将以最新的代码作为base分支,然后把自己的改动merge回来,作为发布分支。

创建分支

[root@9da60e04d8f1 branch]# svn cp -m "create branch" http://172.17.0.2:80/repo1/projectname/trunk http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_001
Committed revision 15.

[root@9da60e04d8f1 branch]# svn cp -m "create branch" http://172.17.0.2:80/repo1/projectname/trunk http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002
Committed revision 16.  
[root@9da60e04d8f1 branch]# svn list http://172.17.0.2:80/repo1/projectname/branch
branch_20180613_001/  
branch_20180613_002/  
[root@9da60e04d8f1 branch]# 

分支branch_20180613_001修改后提前上线,合并到了trunk分支上去了。

现在分支branch_20180613_001branch_20180613_002都对同样的文件有了冲突了,肯定需要在branch合并后在发布,拉取第三个分支作为发布分支branch_20180613_004

第一步:获取分支branch_20180613_002最早的一个版本

[root@54d68f2ff26b branch_20180613_002]# pwd
/root/workspace/branch_20180613_002
[root@54d68f2ff26b branch_20180613_002]# svn log --stop-on-copy
------------------------------------------------------------------------
r27 | lisi | 2018-06-13 15:15:03 +0000 (Wed, 13 Jun 2018) | 1 line

add  
------------------------------------------------------------------------
r16 | zhangsan | 2018-06-13 10:58:06 +0000 (Wed, 13 Jun 2018) | 1 line

create branch  

第二步:试运行merge动作

# 切换到 branch_20180613_004目录
[root@9da60e04d8f1 branch_20180613_004]# svn merge --dry-run -r16:head http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002
--- Merging r17 through r27 into '.':
   C C.txt
C    A.txt  
Summary of conflicts:  
  Text conflicts: 1
  Tree conflicts: 1

通过--dry-run后,大概知道这次合并有多少个冲突,冲突是需要人为去处理的。然后去掉--dry-run的参数,真正执行合并动作。

[root@9da60e04d8f1 branch_20180613_004]# svn merge -r16:head http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002          
--- Merging r17 through r27 into '.':
   C C.txt
C    A.txt  
--- Recording mergeinfo for merge of r17 through r27 into '.':
 U   .
Conflict discovered in file 'A.txt'.  
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,  
        (mc) my side of conflict, (tc) their side of conflict,
        # 选择p暂存,后面再处理
        (s) show all options: p 
Tree conflict on 'C.txt'  
   > local file obstruction, incoming file add upon merge
Select: (r) mark resolved, (p) postpone, (q) quit resolution, (h) help: p  
Summary of conflicts:  
  Text conflicts: 1
  Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]# svn st
 M      .
C       A.txt  
?       A.txt.merge-left.r16
?       A.txt.merge-right.r27
?       A.txt.working
      C C.txt
      >   local file obstruction, incoming file add upon merge
Summary of conflicts:  
  Text conflicts: 1
  Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]# 

在这个过程中,系统会自动合并一些文件,如果是它处理不了的,就会中断命令窗口,让我们选择。这里对于冲突的文件会4个文件,我详细的描述一下。

# 冲突文件描述
C       A.txt #冲突文件的样子  
?       A.txt.merge-left.r16 #r16代表该分支服务端最早的样子
?       A.txt.merge-right.r27 #r27代表该分支服务端最后的样子
?       A.txt.working #代表本地空间最新的样子

通过人工处理后,然后标记其已经解决冲突。看看A.txt里面的内容

iAAAAAAAAAAAAAA  
<<<<<<< .working #代表本地空间最新的样子  
RRRRBBBBAAAAAAAAAAAABBBBBBBB  
CCCCCCCCC  
======= # 分割符号
B22222222222BBBBBBBBBBBi  
CCCCCCCCC  
>>>>>>> .merge-right.r27#r27代表该分支服务端最后的样子

解决冲突后,我决定的内容是:

iAAAAAAAAAAAAAA  
B22222222222BBBBBBBBBBBi  
CCCCCCCCC  

然后继续

[root@9da60e04d8f1 branch_20180613_004]# svn st
 M      .
M       A.txt  
      C C.txt
      >   local file obstruction, incoming file add upon merge
Summary of conflicts:  
  Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]# svn resolve --accept working C.txt 
Resolved conflicted state of 'C.txt'  
[root@9da60e04d8f1 branch_20180613_004]# svn st
 M      .
M       A.txt  

针对于--accept有如下的参数

--accept ARG             : specify automatic conflict resolution source
                             ('base', 'working', 'mine-conflict',
                             'theirs-conflict', 'mine-full', 'theirs-full')

最后再把代码用commit提交掉。

上面的这些只是一个svn的入门,其实还有很多地方都有待去学习,比如更底层的原理等。

参考链接:

1.https://www.kancloud.cn/i281151/svn

2.https://www.howtoforge.com/tutorial/how-to-setup-a-svn-server-on-centos-6/

3.https://www.cnblogs.com/jhj117/p/6256349.html

等等。