环境准备
首先通过 wsl 安装一个 Ubuntu 环境,
shell
wsl --install -d Ubuntu24.04 --name Ubuntu-SVN --web-download
安装 SVN
输入以下命令安装 subversion,
shell
sudo apt install -y subversion
执行结束后,检测安装是否成功,
shell
# 检测svn版本号
svn --version
# 检测svnserve版本号
svnserve --version
创建仓库
使用 svnadmin 命令创建一个仓库,该仓库的位置可自定义,
shell
## 创建仓库目录
sudo mkdir -p /var/svn
## 使用svnadmin创建仓库
sudo svnadmin create /var/svn/repository
## 提升db文件夹的权限
sudo chmod -R 777 /var/svn/repository/db
编辑 svnserve.conf 文件,将以下内容的注释去掉,
plain
[general]
## 控制未认证用户的权限,none-不可读不可写,read-只读,write-读写
anon-access = none
## 控制认证用户的权限,none-不可读不可写,read-只读,write-读写
auth-access = write
## 设置用户密码的文件
password-db = passwd
## 设置用户权限的文件
authz-db = authz
## 设置仓库的名称
realm = repository
配置用户和权限
新增用户
编辑仓库目录下的 password 文件,将需要添加的用户新增到 [user] 内容中,
shell
sudo vi /var/svn/repository/config/password
plain
### This file is an example password file for svnserve.
### Its format is similar to that of svnserve.conf. As shown in the
### example below it contains one section labelled [users].
### The name and password for each user follow, one account per line.
[users]
zhangsan = 123456
一个用户占一行,参考示例,每个用户为一个键值对,键为用户名,值为密码。修改完成后保存退出。
新增权限
编辑仓库目录下的 authz 文件,
shell
sudo vi /var/svn/repository/config/authz
plain
### As shown below each section defines authorizations for the path and
### (optional) repository specified by the section name.
### The authorizations follow. An authorization line can refer to:
### - a single user,
### - a group of users defined in a special [groups] section,
### - an alias defined in a special [aliases] section,
### - all authenticated users, using the '$authenticated' token,
### - only anonymous users, using the '$anonymous' token,
### - anyone, using the '*' wildcard.
###
### A match can be inverted by prefixing the rule with '~'. Rules can
### grant read ('r') access, read-write ('rw') access, or no access
### ('').
[aliases]
# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average
[groups]
# harry_and_sally = harry,sally
# harry_sally_and_joe = harry,sally,&joe
# [/foo/bar]
# harry = rw
# &joe = r
# * =
# [repository:/baz/fuz]
# @harry_and_sally = rw
# * = r
[/]
zhangsan = rw
基于路径的访问控制
svnserve 可以通过 [repos-name:path] 或 [path] 对仓库的文件夹或文件进行访问控制。repos-name 为仓库的名称,当指定了 repos-name 时会在指定的仓库生效,不指定时会在所有仓库的相同路径生效。
基于 svnserve 服务时可以不在意仓库名称,因为权限文件一般都在每个仓库中,不需要跨仓库访问。
基于 httpd 和 Apache 时,节名只有这两种形式: 如果使用了配置指令
AuthzSVNAccessFile, 则要么是[repos-name:path], 要么是[path]。如果使用了配置指令AuthzSVNReposRelativeAccessFile指定了每个仓库的访问权限配置文件, 则只能使用[path]这种形式。
例如在示例文件中,用户 harry 对所有仓库的 /foo/bar 路径下的文件具备读写权限。同时,用户 harry 在用户组 harry_and_sally 组中,对 repository 库下的 /baz/fuz 路径下的文件具备读写权限。
当一个用户具备多个路径权限时,遵循以下原则,优先匹配更详细的路径,再匹配他的父目录,相同的路径优先匹配指定仓库名的权限。
在下面的示例中,用户 harry 拥有对 calc 仓库 /branches/calc/bug-142 目录下所有文件的读写权限,但除了 secret 目录。
plain
[calc:/branches/calc/bug-142]
harry = rw
sally = r
[calc:/branches/calc/bug-142/secret]
harry =
分组权限
可在 [groups] 区域对用户进行分组,每一行为一组,分配权限时在组名前要添加@符。例如:
plain
[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = harry, sally, joe, frank, jane
[calc:/projects/calc]
@calc-developers = rw
[paint:/projects/paint]
jane = r
@paint-developers = rw
同一用户在不同分组时,权限是会进行叠加的,例如,用户 jane 在 paint-developers 组中,对 paint 库的 /projects/paint 目录具有读写权限,且单独对该目录声明了一个读权限,因此通过叠加,用户 jane 对该目录还是拥有读写权限。
通配符
可以通过*表示所有的用户,对所有的用户进行赋权。例如,
plain
[/]
## 所有用户对所有仓库的根目录下的文件拥有读权限
* = r
使用 $authenticated 表示所有的认证用户,$anonymous 表示所有未认证用户。例如,
plain
[calendar:/projects/calendar]
$anonymous = r
$authenticated = rw
使用~表示排除某些用户,例如,
plain
[calendar:/projects/calendar]
~$authenticated = r
~$anonymous = rw
该效果与上一个例子相同。
别名
对于较长的用户名可以对该用户名起一个别名,放在 [aliases] 区域内。例如,
plain
[aliases]
harry = CN=Harold Hacker,OU=Engineers,DC=red-bean,DC=com
sally = CN=Sally Swatterbug,OU=Engineers,DC=red-bean,DC=com
joe = CN=Gerald I. Joseph,OU=Engineers,DC=red-bean,DC=com
...
某些认证系统仅支持相对较短的用户名, 例如
<font style="color:rgb(0, 0, 0);">harry</font>,<font style="color:rgb(0, 0, 0);">sally</font>,<font style="color:rgb(0, 0, 0);">joe</font>这样简短的名字, 而其他一些认证系统---例如那些使用了 LDAP 和 SSL 的认证系统一却支持更复杂的用户名, 例如在一个使用了 LDAP 的认证系统中, Harry 的用户名可以是<font style="color:rgb(0, 0, 0);">CN=Harold Hacker,OU=Engineers,DC=red-bean,DC=com</font>. 如果在访问权限配置文件里出现这些用户名, 文件将变得非常臃肿, 这些又长又晦涩的用户名还很容易写错.
启动 SVN
执行以下命令启动 svnserve,
shell
sudo svnserve -d -r /var/svn
参数说明:
-d:调用守护进程
-r:指定仓库运行的位置,如果不指定,那么需要在访问时在路径中写出仓库的绝对路径,例如:svn://127.0.0.1/var/svn/repository才可以访问到仓库,当指定了目录后直接写仓库的名称即可,即:svn://127.0.0.1/repository,这样更加安全。
--listen-port:可选,指定访问端口,默认为 3690。
--listen-host:可选,指定监听的 IP,默认会监听所有 IP。
需要停止服务时,可以查看进程并杀死进程再重新启动。
shell
## 查找进程PID
ps -ef | grep "svnserve"
## 结束进程
sudo kill -9 xxx
SVN 仓库迁移
导出
使用 svnadmin dump 命令导出仓库为一个二进制文件,
shell
## 查看当前版本号
svnloop youngest myrepository
## 导出当前仓库
svnadmin dump myrepository > myrepository.dump
导出的文件会比仓库本身的文件要大,这是因为每个文件的每个版本在转储文件中都是全文本表示。这是最快速和最简单的方式。
可以使用 <font style="color:rgb(0, 0, 0);">-r</font> 参数来指定要导出的版本范围,例如,
shell
## 导出23号版本的仓库
svnadmin dump myrepos -r 23 > rev-23.dumpfile
## 导出100-200号版本的仓库
svnadmin dump myrepos -r 100:200 > revs-100-200.dumpfile
这里需要注意的是,指定版本号时会与第一个版本进行对比,生成一个完整的仓库。因此,导出时会把仓库的第一个版本作为转储的第一个版本。
当使用了 --incremental 参数时,svnadmin 会把转储的第一个版本号与前一个版本号作比较,跟转储范围中剩下的其他 版本号那样,只输出在版本号中被修改了的内容。根据这个特性,可以分批次导出一个较大的仓库,
shell
svnadmin dump myrepos -r 0:1000 > dumpfile1
svnadmin dump myrepos -r 1001:2000 --incremental > dumpfile2
svnadmin dump myrepos -r 2001:3000 --incremental > dumpfile3
再依次将转储文件导入到新的仓库中。
导入
使用 svnadmin load 命令即可将转储文件导入到指定的新仓库中,
shell
svnadmin load newrepos < oldrepos.dump
从 Windows 系统仓库导入 Linux 系统
在 Windows 系统中可以通过界面转储仓库文件,

选择 [ Export repository dump ],点击 [ Next ] 进入下一步,

选择文件的存储路径和文件名称,如果需要压缩转储文件可以勾选 [ Compress dump using GZIP ],点击 [ Export ] 导出文件。

将转储的文件拷贝至 Linux 服务器,使用 svnadmin load 进行导入。
注意:不要使用 PowerShell 命令导出仓库,可能会出现编码异常。
httpd 搭建版本管理服务器
httpd 安装
当前使用的环境是 Ubuntu 24.04,使用以下命令安装 apache2,
在 Ubuntu 环境下,安装包的名字是 apache2,CentOS 环境是 httpd。详情可查看官方文档的安装说明,
https://httpd.apache.org/docs/2.4/install.html
https://ubuntu.com/server/docs/how-to/web-services/install-apache2/
shell
## 安装 apache2
sudo apt install apache2 libapache2-mod-svn -y
libapache2-mod-svn 为 subversion 对 httpd 的支持模块,安装完成后自动启用该模块,不需要再单独启用。
使用以下命令检查 httpd 是否安装成功,
shell
## 检查 apache2 的版本号
dugh@Ubuntu-Dev:~$ apache2ctl -v
Server version: Apache/2.4.58 (Ubuntu)
Server built: 2026-05-05T13:22:45
或者在浏览器中直接访问 http://172.20.1.168/ 能够直接打印 Apache 首页,则表示安装成功。
使用以下命令检查 subversion 模块是否安装成功并启用,
shell
## 查看httpd支持的模块
dugh@Ubuntu-Dev:~$ apache2ctl -M | grep "svn"
dav_svn_module (shared)
authz_svn_module (shared)
能够打印 dav_svn_module 说明 httpd 已支持 subversion 模块。
踩坑:
如果直接使用 apache2 命令的话,会打印 AH00111: Config variable ${APACHE_RUN_DIR} is not defined,该命令不会加载 envvars 的环境变量。在 apache2.conf 中也有描述文本,
plain
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
基础配置
安装完成 libapache2-mod-svn 后,会在 /etc/apache2/mods-enabled 目录下生成一个 dav_svn.conf 的配置文件,我们编辑该文件,代理仓库。
shell
## 编辑 dav_svn.conf 文件
sudo vi /etc/apache2/mods-enabled/dav_svn.conf
在文件中添加以下内容,
plain
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
</Location>
这里建议将仓库文件添加到 apache2 的默认用户和用户组上,使得 apache2 可以有权限访问仓库目录,
shell
sudo chown -R www-data:www-data /var/lib/svn
浏览器直接访问 http://hostname/svn/repository 就能够访问 repository 仓库的内容。
这里需要注意一点,如果创建的仓库名称是中文的,需要在 dav_svn.conf 中添加 SVNUseUTF8 配置,该配置要添加在 Location 标签外,否则 apache2 无法启动,
plain
# 设置为UTF-8编码
SVNUseUTF8 On
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
</Location>
如果不设置,当访问中文仓库时,会报 Can't convert string from 'UTF-8' to native encoding 错误。
身份认证
Basic 认证
我们采用 apache2 提供的基础认证功能,为 dav_svn.conf 添加以下内容,
plain
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
# 设置apache2的认证模式为Basic认证
AuthType Basic
# 设置认证的名称,可以随意命名
AuthName "Subversion repository"
# 设置Basic认证的提供方式,默认值为file,可以不添加
AuthBasicProvider file
# 设置用户文件的位置
AuthUserFile /etc/svn-auth.htpasswd
</Location>
用户文件我们需要通过 htpasswd 命令进行创建,
shell
# 创建harry用户,文件位置为/etc/svn-auth.htpasswd
dugh@Ubuntu-Dev:~$ sudo htpasswd -c -m /etc/svn-auth.htpasswd lisi
New password:
Re-type new password:
Adding password for user lisi
-c 创建 passwdfile。如果 passwdfile 已经存在,它将被重写并截断。此选项不能与 -n 选项结合使用。
-m 对密码使用 MD5 哈希。这是默认值(从版本 2.2.18 开始)。如果需要使用其他的加密方式,可以参照官方文档进行选择。
如果需要修改某个用户的密码或在当前配置文件中新增某个用户,则只需要去掉 -c 命令,重新输入密码即可。
Digest 认证
Digest 认证会比 Basic 认证更加安全,具体描述可查看官方文档。
修改 dav_svn.conf 文件,
plain
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
# 设置apache2的认证模式为Digest认证
AuthType Digest
# 设置认证的名称,可以随意命名
AuthName "Subversion repository"
# 设置Digest认证的提供方式,默认值为file,可以不添加
AuthDigestProvider file
# 设置用户文件的位置
AuthUserFile /etc/svn-auth.htdigest
</Location>
在使用 Digest 认证时,需要先启用 mod_auth_digest模块。
shell
## 启用digest模块
sudo a2enmod auth_digest
## 查看已启用的模块
apache2ctl -M | grep "digest"
用户文件可以通过 htdigest 命令生成,
shell
## 生成用户文件
sudo htdigest -c /etc/svn-auth.htdigest "Subversion repository" zhangsan
-c 创建 passwdfile。如果 passwdfile 已经存在,则先删除它。如果要修改用户的密码可以去掉 -c 参数。
权限认证
在 7.3 的案例中虽然设置了身份认证,当是通过浏览器访问时还是可以直接访问。所以可以在 dav_svn.conf 中添加 Require 配置,该配置可以要求所有访问的用户都需要进行身份认证。如果需要限制请求类型,则可以使用 Limit 和 LimitExecpt 标签限制或排除某一类请求的身份认证。
plain
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
# 设置apache2的认证模式为Basic认证
AuthType Basic
# 设置认证的名称,可以随意命名
AuthName "Subversion repository"
# 设置Basic认证的提供方式,默认值为file,可以不添加
AuthBasicProvider file
# 设置用户文件的位置
AuthUserFile /etc/svn-auth.htpasswd
# 限制请求类型的身份认证
#<LimitExcept GET PROPFIND OPTIONS REPORT>
# 所有请求都需要进行身份认证
Require valid-user
#</LimitExcept>
</Location>
apache 也可以像 svnserver 一样进行权限认证,这里需要添加以下配置,
plain
# 设置访问的路径
<Location /svn>
# Uncomment this to enable the repository
DAV svn
# 如果只是指定单一的仓库,可以使用SVNPath配置
#SVNPath /var/lib/svn/repository
# 如果在该目录下有多个仓库
SVNParentPath /var/lib/svn
# 在根路径上列出所有的仓库信息(为了安全不建议这么操作)
#SVNListParentPath on
# 设置apache2的认证模式为Basic认证
AuthType Basic
# 设置认证的名称,可以随意命名
AuthName "Subversion repository"
# 设置Basic认证的提供方式,默认值为file,可以不添加
AuthBasicProvider file
# 设置用户文件的位置
AuthUserFile /etc/svn-auth.htpasswd
# 设置SVN权限认证文件的相对位置
#AuthzSVNReposRelativeAccessFile authz
# 设置SVN权限认证文件的绝对位置
AuthzSVNAccessFile /var/lib/svn/authz
# 限制请求类型的身份认证
#<LimitExcept GET PROPFIND OPTIONS REPORT>
# 所有请求都需要进行身份认证
Require valid-user
#</LimitExcept>
</Location>
subversion 提供了两个配置项,AuthzSVNAccessFile 和 AuthzSVNReposRelativeAccessFile,这两个配置的区别是 AuthzSVNAccessFile 指定的文件会作用于所有的仓库,所有仓库的权限都会依赖该文件;而 AuthzSVNReposRelativeAccessFile 指定了相对于每个仓库 conf 目录下的权限文件,这样可以给不同的仓库匹配属于自己的权限文件。
认证文件可以参考 4.2 章节的内容进行编写。
启用 SSL
使用 http 协议可能存在被监听的风险,因此可以开启 apache 的 ssl 功能,使用 https 访问仓库。首先要启用 SSL 模块(默认已经安装)。
shell
sudo a2ensite default-ssl
sudo a2enmod ssl
执行完成后,重启 apache2 服务,检查该功能是否启动成功,
shell
apache2ctl -M | grep "ssl"
要启用 SSL 功能,还需要 ssl-cert 包的支持,安装该功能,
shell
sudo apt install ssl-cert -y
当前使用的是 WSL 的 Ubuntu 环境,系统已安装了 ssl-cert,因此不需要再安装,
shell
dugh@Ubuntu-Dev:~$ apt search ssl-cert
Sorting... Done
Full Text Search... Done
ssl-cert/noble,now 1.1.2ubuntu1 all [installed,automatic]
simple debconf wrapper for OpenSSL
安装成功后会在 /etc/ssl/private 目录生成对应 hostname 的证书密钥以及对应的证书。只需要在 /etc/apache2/sites-available/default-ssl.conf 文件中检查一下 SSLCertificateKeyFile 和 SSLCertificateFile 的值是否正确即可。
plain
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
如果需要重新创建证书,执行以下命令,
shell
make-ssl-cert /usr/share/ssl-cert/ssleay.cnf /path/to/cert-file.crt
使用浏览器访问 https://172.20.1.168/svn/repository/ 就可以正常访问了。