1. 动手之前先想清楚为什么要搭 SVN 服务器不管你信不信这几年做版本管理很多人第一反应都是 GitSVN 在不少团队里已经被贴上了“老古董”的标签。但我在实际项目里碰到的真实情况是仍然有大量中小团队、外包项目、传统企业内部系统、甚至不少高校实验室一直在用 Subversion 做代码和文档的版本管理。原因很朴实——SVN 的集中式模型太直白了权限一配谁能看、谁能写清清楚楚审计追溯也简单对非纯技术同事比如测试、产品、文档协助角色来说学习成本比 Git 低太多。所以当你在 Ubuntu 上需要搭建一台 SVN 服务器时首先要明确的不是“装什么”而是“给谁用、用哪种协议、怎么管权限”。如果你只是想自己一个人本地开个版本库那svnadmin直接搞定如果要给几个人甚至几十人的团队用那就要考虑 svnserve 还是 Apache mod_dav_svn如果还牵扯到跨部门协作或者临时对外共享还得考虑 HTTP(S) 协议怎么配。这篇文章我会把 Ubuntu 下搭建 SVN 服务器的完整过程讲清楚沿着一条最常见也最稳的路径走先装好subversion再用svnserve提供访问服务然后配置用户与目录权限最后附上一堆我在生产环境里踩过的高频坑和排查思路。无论是纯新手还是有 Linux 基础的朋友照着做就能把服务跑起来。我这里假设你的环境是 Ubuntu 20.04 LTS 或更新的 22.04 LTS / 24.04 LTS操作用 root 或具备 sudo 权限的账号。不要拿一个刚装完、乱七八糟改过源和 PATH 的系统直接开干先把基础环境理清楚。2. 核心思路拆解CentOS 那套经验别直接搬如果你以前在 CentOS 上搭过 SVN你会发现 Ubuntu 上思路差不多但几个关键点完全不一样照搬会踩坑。第一个区别是软件包管理器和安装源。CentOS 用 yum/dnfUbuntu 用 apt。装 SVN 核心包很简单sudo apt update sudo apt install subversion但这里有个容易忽略的细节Ubuntu 的subversion包只包含服务器端和客户端工具并不自动安装 Apache 模块。如果你需要走 HTTP 协议访问要额外装apache2和libapache2-mod-svn。很多教程直接跳过多说一句导致用户死活打不开http://服务器IP/svn/那个页面查半天原因。我的建议是如果团队不大、纯内网用直接用svn://协议就够了根本不用上 Apache省内存省配置省心。第二个区别是目录结构与配置文件位置的差异不大但权限管理和系统默认策略有差异。Ubuntu 下 SVN 仓库默认没有固定的代码库根目录多数人习惯放在/home/svn或/var/svn你可以自己定。系统默认不会为 SVN 创建专门的运行用户如果你打算让多个协作者通过网络访问就不得不考虑文件系统层面的权限。第三个区别是防火墙规则。Ubuntu 默认通常没装 ufw 防火墙规则限制 3690 端口svnserve 默认端口但如果你之前开过 ufw 或云主机的安全组策略有收敛就必须自己放行否则外网永远连不上。这个点我在后面会仔细说。整体设计上我建议的小团队方案是一台 Ubuntu 服务器内网 IP 固定只跑 svnserve。仓库根目录用/var/svn里面按项目拆分多个仓库。用户认证用passwd文件目录权限用authz文件两者都是明文文本方便随手改。日常备份用svnadmin dump定时执行备份文件丢到独立磁盘或另一台机器。这套方案的好处是依赖少、迁移方便、配置直观。缺点是没有任何 Web 界面也没法走 HTTPS。如果以后需要 Web 浏览和 HTTPS再架一层 Apache 或迁移到 VisualSVN ServerWindows也不费劲。我见过太多人一上来就搭 Apache SVN 组合结果配置 mod_dav_svn、处理 SELinux 和 AppArmor 的权限限制花了一整天最后只为了“能有个网页看看代码”。不值当。先用最省事的方式跑起来按需升级才是正确的姿势。3. 实操全流程从装包到跑起第一个仓库3.1 安装软件包与创建仓库根目录把系统源更新之后直接安装sudo apt update sudo apt install subversion apache2 libapache2-mod-svn -y我这里把 Apache 相关的一起装了因为你后面很可能会想开 HTTP 访问。如果确定只走svn://可以只装subversion。装完检查版本svnserve --version svnadmin --version确认能输出版本号就说明装好了。然后创建仓库存放的根目录建一个专用的系统用户来管理 SVN 服务这一步很多人偷懒直接用 root后患无穷sudo mkdir -p /var/svn sudo useradd -r -s /usr/sbin/nologin svn sudo chown -R svn:svn /var/svn为什么要单独建svn用户因为 svnserve 默认以当前用户身份运行如果直接跑 root出问题就是 root 权限全盘失陷。用独立低权限用户运行即便仓库被爆破也只影响 SVN 目录。Chown 给 svn 用户之后后面所有仓库目录的操作都要注意属主别搞乱。3.2 创建第一个代码仓库仓库目录的创建不要自己mkdir再往里塞文件一定要用svnadmin create因为它会生成完整的仓库元数据结构、钩子目录、数据库配置等sudo svnadmin create /var/svn/project1 sudo chown -R svn:svn /var/svn/project1创建完之后看一下目录结构ls -la /var/svn/project1你会看到conf/、db/、hooks/、locks/等目录。conf/里面放着svnserve.conf、passwd、authz三个核心配置文件后面所有权限和用户管理都围绕它们展开。这里有个重要经验仓库目录一旦创建不要轻易挪动路径。SVN 仓库内部记录的是相对路径正常操作下移动没问题但/var/svn这个根目录如果从一台机器整体拷到另一台机器要注意版本兼容性svnadmin dump/load 方式迁移最稳。真实生产环境里我见过有人直接把整个/var/svn打包复制到新机器结果 svnserve 起来后各种诡异报错最后老实 dump 才解决。3.3 配置 svnserve.conf先让服务能跑起来编辑/var/svn/project1/conf/svnserve.conf这是仓库的“总开关”。我第一次搭的时候忽略了文件里很多行都有前置空格结果怎么改都不生效。这里提醒大家SVN 配置文件不允许行首有多余空格每项配置必须顶格写。sudo vim /var/svn/project1/conf/svnserve.conf核心配置如下[general] anon-access none auth-access write password-db passwd authz-db authz realm project1 [sasl] use-sasl false逐行解释anon-access none匿名用户不允许任何操作。如果这里设成read那任何人不用登录就能拉代码对大多数企业项目来说这是安全事故。auth-access write通过认证的用户拥有读写权限。具体写权限可以大到整个仓库还是只限某些目录后面靠 authz 精细控制。password-db passwd指向同目录下的passwd文件里面存用户名和密码。authz-db authz指向同目录下的authz文件用来配置目录级权限。realm project1这个名称会出现在客户端认证弹窗里也是 SVN 识别认证域的重要标识。多仓库部署时不同仓库的 realm 不要重名否则客户端切换仓库时会遇到“缓存凭证不匹配”的奇怪问题。3.4 配置 passwd添加用户账号编辑passwd文件格式极简就是“用户名 密码”[users] alice 123456 bob 654321 dev_lead admin123几个注意事项密码不要用纯数字或是太弱的口令svnserve 本身没有登录失败锁定和密码强度校验弱密码很容易被暴力破解。公司内部用的话建议密码复杂度拉高一点。不要用明文密码在团队里大范围传播。SVN 的 passwd 文件默认就是明文如果你实在很在意这一点考虑换 Apache mod_dav_svn HTTPS 的方案让密码走加密通道并且可以对接 LDAP。修改 passwd 后无需重启 svnserve下次认证就会自动生效。3.5 配置 authz目录级权限的精髓authz是 SVN 权限控制里最值钱的部分。它负责解决一个核心问题谁对哪个仓库的哪个目录有什么权限。编辑/var/svn/project1/conf/authz[groups] admins alice, dev_lead developers bob [/] * admins rw developers r [/trunk] developers rw解释一下[groups]定义用户组避免一条条给用户加权太啰嗦。[/]表示仓库根目录。* 注意等号右边是空格表示所有其他用户没有任何权限。admins rw表示 admins 组对整个仓库拥有读写权限。developers r表示 developers 组对根目录只有只读权限。然后单独给/trunk目录加了一条rw这样 developers 可以在主干提交但没法动/branches和/tags。这种“根目录收紧、目录级放开”的做法是生产环境里最常见的权限设计。要注意的是authz 的目录路径是不带仓库名的相对路径。如果服务器上有多个仓库[/]只作用于当前仓库 id 对应的这个库。跨仓库的话要写成[project1:/]、[project2:/trunk]这种格式。这块是 SVN 权限配置里最容易绕晕的地方后面我会专门展开说多仓库的配置写法。3.6 启动 svnserve 并验证服务仓库配置好了直接启动sudo svnserve -d -r /var/svn --log-file /var/log/svnserve.log参数说明-d后台守护进程模式运行。-r /var/svn以/var/svn为仓库根目录。这个参数极其关键因为它决定了仓库访问 URL。比如客户端访问svn://192.168.1.100/project1服务端就会在/var/svn下找project1这个仓库。--log-file日志输出路径强烈建议带上不然排查问题全靠猜。启动后用下面几条验证状态ps -ef | grep svnserve netstat -tlnp | grep 3690如果看到3690端口在监听那服务就已经跑起来了。再用客户端试一下svn list svn://192.168.1.100/project1 --username alice --password 123456能列出空仓库说明一切正常。如果这步在服务器本机执行没问题、但远程连不上下一步就是防火墙和网络的问题。3.7 配置防火墙与开机自启Ubuntu 上如果开了ufw执行sudo ufw allow 3690/tcp sudo ufw status云服务器用户还要记得去安全组里放行 3690 端口这个不能忘。我有一次在服务器上排查了一晚上连不上最后发现是云安全组根本没放行。svnserve 不像 Apache 那样自带 systemd 服务脚本所以需要手动配置开机自启。在/etc/systemd/system/svnserve.service创建如下内容[Unit] DescriptionSubversion server Afternetwork.target [Service] Typeforking ExecStart/usr/bin/svnserve -d -r /var/svn --log-file /var/log/svnserve.log ExecReload/bin/kill -HUP $MAINPID PIDFile/run/svnserve.pid Restarton-failure [Install] WantedBymulti-user.target然后sudo systemctl daemon-reload sudo systemctl enable svnserve.service sudo systemctl start svnserve.service注意Typeforking是因为 svnserve 的-d参数会让它 daemon 化systemd 会认为主进程退出、子进程在跑所以需要 PIDFile 来监控。如果觉得不好使更省事的方案是直接写 crontab 的reboot一条任务。但我个人还是推荐 systemd 的方式管理和排查都更正规。4. 多仓库架构与三种访问协议选型4.1 一个根目录管理多个项目实际工作中几乎没有只为一个项目搭 SVN 的情况。你很快就会发现需要在同一个 SVN 服务器上跑好几个仓库。比如sudo svnadmin create /var/svn/project1 sudo svnadmin create /var/svn/project2 sudo svnadmin create /var/svn/docs因为 svnserve 启动时指定了-r /var/svn所以客户端 URL 就分别是svn://IP/project1 svn://IP/project2 svn://IP/docs这种多仓库结构的管理核心在于 authz 文件的写法。每个仓库的conf/authz可以只管理自己仓库的权限但如果你希望统一管理所有仓库的权限可以单独维护一个全局 authz 文件然后在各仓库的svnserve.conf里通过authz-db /var/svn/authz指向它。全局 authz 文件的写法是[groups] admin alice dev1 bob [project1:/] admin rw dev1 r [project2:/] admin rw [docs:/] * r注意区分[/]表示当前仓库根目录[project1:/]表示指定仓库 project1 的根目录。如果只有[/]而没有加仓库名前缀那么多仓库场景下所有仓库共用一套权限这在某些场景下不是你想要的效果。4.2 svn:// 和 http(s):// 到底怎么选很多人纠结 svnserve 和 Apache 两种服务器方案我给你的实用建议是对比维度svnservesvn://Apachehttp://配置复杂度低三个文本文件搞定高要配 mod_dav_svn、认证模块性能足够中小团队使用高并发下略好HTTPS 加密不支持原生加密天然支持 SSL只读 Web 浏览不支持可以直接浏览器看代码与 LDAP / AD 集成比较麻烦官方有 mod_authz_svn 和 LDAP 模块日常维护成本低相对高我自己的结论是纯内网、人数少于 20无脑选 svnserve。它省去了一堆 TLS 证书、Apache 虚拟主机、目录权限的折腾效果稳稳的。如果你们公司强制要求所有账号统一走 LDAP 或 AD或者需要外网访问且对安全合规有要求那就一步到位选 Apache HTTPS。4.3 用 Apache 提供 HTTP 访问的简要补充如果你最终确实要用 Apache 方案核心步骤大概是这样sudo a2enmod dav dav_svn sudo vim /etc/apache2/mods-available/dav_svn.conf在dav_svn.conf里配置Location /svn DAV svn SVNParentPath /var/svn AuthType Basic AuthName Subversion Repository AuthUserFile /etc/apache2/conf-available/svn.htpasswd Require valid-user /Location然后创建用户文件sudo htpasswd -c /etc/apache2/conf-available/svn.htpasswd alice sudo systemctl restart apache2这时候浏览器访问http://IP/svn/project1/就能看到仓库目录列表了。这个方案的目录级权限在 Apache 侧是通过mod_authz_svn的配置块控制的可以复用前面写的 authz 文件思路。但注意它不是本文的主线如果你的场景没有硬性需求就先别在这上面纠结。5. 客户端配置与常见的“连不上”问题排查5.1 Linux 命令行客户端Ubuntu 桌面或开发机上一般也装了 subversion 客户端sudo apt install subversion检出svn checkout svn://192.168.1.100/project1/my_project --username alice提交cd my_project svn add new_file.txt svn commit -m 添加新文件更新到最新svn updateSVN 的工作副本机制和 Git 有本质区别——SVN 每次提交都是直接入库切换分支是svn switch不是 Git 的git checkout。如果你团队里新人有 Git 背景在这里会非常不适应。建议把这些基础命令贴到团队内部文档里能少很多事。5.2 Windows 端 TortoiseSVNWindows 用户一般会装 TortoiseSVN俗称“小乌龟”装完会有右键菜单。连接服务器时需要注意仓库 URL 填写svn://192.168.1.100/project1不是http://。首次连接会弹认证框填用户名和密码并且可以勾选“保存认证信息”。在资源管理器里点右键选择“SVN Checkout”而不是在浏览器里输入地址。如果 TortoiseSVN 首次连接就报“无法连接主机”先 ping 服务器 IP再确认 3690 端口通不通然后用 Telnet 测试telnet 192.168.1.100 36905.3 IDEA、VS Code 等 IDE 集成现在 JetBrains 全家桶IDEA、PyCharm自带的 SVN 插件已经很好用了。你只需要在 Settings → Version Control → Subversion 里配置 SVN 可执行文件路径然后把项目通过 VCS → Checkout from Version Control 填 URL 检出就行。作为服务器端管理员你基本上不需要在 IDE 侧再做什么额外动作。VS Code 则要装svn扩展然后调用系统里的 svn 命令行工具。这里面会牵扯到一个问题VS Code 内置终端能识别svn但图形界面那个 SVN 扩展可能要用到svn.exe的绝对路径如果你用的 Windows 但 svn 命令是通过 WSL 装的那 VS Code 扩展层面经常会卡住。建议 Windows 上做 SVN 客户端操作时直接单独装一个命令行 SVN 客户端或者 TortoiseSVN用系统路径里的 svn.exeVS Code 扩展就可以正常调用。5.4 “svn is not a working copy”这类工作副本问题这是一句非常高频的报错字面意思是“当前目录不是 SVN 工作副本”。出现它的原因其实并不复杂你在一个普通文件夹里执行了svn commit或svn update。.svn元数据目录被误删了。目录是从另一个地方复制过来的.svn里保存的 URL 还是原来的仓库地址而你直接在这个副本上做了操作。路径大小写问题Linux 下路径区分大小写Windows 不区分跨平台移动工作副本时偶尔也会踩坑。除了检查以上这些有一个比较实用的恢复操作是svn cleanup它能清理掉中断操作留下的锁。如果 cleanup 也没用就把当前目录重新 checkout 一份把本地修改用svn diff patch.diff导出再打到新工作副本里。你要是直接rm -rf重来但本地有一堆未提交的代码会很难受所以一定要先把 diff 导出来。5.5 “推送提示仓库不存在”的排查这个报错几乎 90% 是 URL 指向的仓库路径在服务器上不存在或者大小写不对。比如访问svn://IP/Project1而服务器上目录名是project1那就会报“仓库不存在”。记住 SVN 的仓库 URL 是区分大小写的。另一个可能原因是svnserve的-r参数根目录设错了。假设你启动命令是svnserve -d -r /var/svn客户端就不该访问svn://IP/var/svn/project1而是svn://IP/project1。这个错误我见太多新手上过当因为你从svn://IP/var/svn/project1访问时服务器会从根目录往下找var/svn/project1找到/var/svn/var/svn/project1自然不存在。记住-r后面的路径是什么URL 就从那条路径的下一级开始。5.6 忘了仓库存在、但能通却认证失败分几种情况passwd文件里没有这个用户。passwd文件里用户名多余空格比如alice 123456被写成了alice123456没有空格通常也可以。注意行首空格和两侧的空格处理方式不同。认证域 realm 和你客户端缓存的不一致。如果你之前连过另外一台 SVN 服务器且用户密码一样TortoiseSVN 可能缓存了旧域名的凭证弹框里显示的认证域会告诉你现在是哪个 realm清掉缓存或换一个用户名测试就知道是不是这个原因。svnserve.conf 里password-db没写对路径。如果password-db /var/svn/global_passwd就要确保那个文件存在而且格式正确。6. 权限实战精确控制不同目录的提交与只读6.1 经典目录结构下的权限设计一个规范的 SVN 仓库里通常会有trunk、branches、tags三个目录。基于这个结构可以设计出很细的权限策略。拿一个开发团队举例架构师 / 技术负责人所有目录全读全写。普通开发trunk 可读可写branches 可读可写但不能删除tags 只读。测试人员trunk 只读其他目录全部没有权限。产品 / 项目经理只读 trunk 和一个特定 docs 目录。authz 配置大概是[groups] architect alice devs bob, carol testers dave, erin pm frank [/] * architect rw [/trunk] devs rw testers r pm r [/branches] devs rw testers r [/tags] devs r testers r注意如果某个目录没有显式授权SVN 会继承上一级目录的权限。比如branches下开了devs rw但又想限制某人不能改某个分支可以在[/branches/feature001]下加一条更具体的配置比如devs r覆盖上一层的读写权限。6.2 关于“删权限”和“读权限”需要注意的细节SVN 权限模型里很容易被忽略的一点如果一个用户对某个目录只有r那么他能看到目录列表、能 checkout但不能提交。但如果你完全不给他任何权限他连这个目录存在都不知道。所以在给测试、产品这类角色开权限时如果业务上有“只让他们能看到但不让他们知道别的分支存在”的需求那就不要给根目录r而是只给具体某个子目录r。另一个细节是groupname引用组要在[groups]里先定义*表示所有人包括匿名用户但如果 svnserve.conf 里anon-access none匿名其实已经进不来了。$表示“为空”在文件末尾经常看到* 就是这一行的意思。很多人看到* $会困惑其实$是“空权限”的显式写法。6.3 修改权限后立即生效吗是的passwd和authz文件改完保存后就会生效不需要重启 svnserve。这算 SVN 的一个好处。批量调整权限时可以先备份再改然后用一个普通账号非管理员测试一下是否还能访问不该访问的地方。我之前的做法是加完权限后用不同账号分别跑一遍svn list svn://IP/仓库目录确认返回结果符合预期才收工。7. 数据备份与灾难恢复这件事千万别拖SVN 服务器最怕一件事硬盘挂了仓库全没了。别天真地以为代码都在每个人本地的 working copy 里SVN 工作副本没有完整的版本历史顶多只有最新一份或者部分旧文件。仓库没了历史就真没了。我在实际运维中吃过这种亏所以对备份特别敏感。最稳的离线备份方式就是svnadmin dump。对单个仓库sudo -u svn svnadmin dump /var/svn/project1 /backup/project1.dump恢复时sudo svnadmin create /var/svn/project1_restore sudo -u svn svnadmin load /var/svn/project1_restore /backup/project1.dump注意svnadmin dump默认只导仓库里当前所有版本如果想过滤某些路径用svnadmin dump配合--incremental或--revision参数做增量备份。定时备份可以直接写个脚本扔进 crontab#!/bin/bash DATE$(date %Y%m%d%H%M) svnadmin dump /var/svn/project1 /backup/project1_$DATE.dump find /backup -name *.dump -mtime 30 -delete备份策略上没有标准答案但我个人建议至少每日全量中小仓库没啥体积压力保留 30 天。如果仓库特别大再考虑周一全量周二到周日增量的方案。更重要的是备份文件务必放到另一台机器或另一块独立硬盘不然服务器整个挂了备份也在同一块盘上等于白做。有条件的话每季度手动做一次恢复演练确保 dump 文件真的能 load 回去。别问我为什么强调这个——我在演练时已经发现过备份文件损坏的情况不信你也试试。还有一点如果要用svnadmin hotcopy它适合做在线备份但注意它依赖底层 Berkeley DB 或 FSFS 存储格式的一致性。小团队没必要为这个纠结dump/load 足够可靠。8. 高频问题速查表与最终避坑心得8.1 问题速查表症状常见原因解决方法客户端连接超时或拒绝连接防火墙/安全组未放行 3690检查 ufw、云安全组放行 3690/tcp能 ping 通但连不上svnserve 未启动或启动参数错误检查ps -ef | grep svnserve确认-r参数正确提示仓库不存在URL 路径错误或大小写不匹配使用svn list svn://IP/仓库名逐个排查认证失败passwd 文件写错或者 realm 冲突检查两侧空格、用户名是否存在、realm 是否唯一commit 报 “is not a working copy”当前目录缺少 .svn 元数据svn cleanup或重新 checkout 并导回 diff文件没有绿勾图标TortoiseSVN 图标叠加缓存问题修改 TortoiseSVN 设置里的图标叠加状态或重启资源管理器修改 authz 后权限没变配置行有缩进或格式错误确认行首无空格使用[仓库名:/路径]格式客户端提交中文文件名乱码服务器默认编码问题换 Apache HTTPS 方式或者在客户端配置 UTF-88.2 更多实用的避坑心得第一目录规划一定要提前。我搭第一台 SVN 服务器时没想那么多仓库丢在/home/svn后来服务器要迁移路径和权限各种整不明白。第二台开始我统一放在/var/svn同时给svn独立用户后续做备份和迁移的脚本一次成型。这种规划层面的亏踩过一次就能记住。第二不要把 SVN 密码和系统密码混用。因为passwd是明文存储系统密码混用等于把系统后门开着。更稳妥的方案是用 Apache LDAP 对接公司统一认证但那就超出本文范围了。小团队先做到 SVN 密码独立、复杂度达标就够。第三每次修改配置文件之前记得先备份。SVN 的配置文件不会自动留版本记录cp svnserve.conf svnserve.conf.bak或者是用svn管理/var/svn/conf目录本身对SVN 是可以管理自己的配置目录的很多老手会这么做这样改崩了还能回滚别问我怎么知道的。第四svn switch和svn update的区别必须讲清楚。团队里有新人第一次从 Git 切到 SVN问得最多的就是“我在分支上改了代码怎么切回主干”如果你理解成 Git 的 checkout那很容易把分支的修改带进主干。正确姿势是先确认当前分支工作副本是干净的没有未提交的改动再执行svn switch切到目标路径。如果工作副本有未提交修改SVN 会直接拒绝切换这个机制其实是在保护你。第五谨慎清理仓库里的历史文件。SVN 不像 Git 那么容易改写历史即便用svnadmin dump配合某些工具强行清理代价也很大。所以权限严格控制比事后清理高效得多。真遇到某个路径下有大量不该出现的历史垃圾优先考虑新建一个干净的仓库把需要的内容重新导入而不是拼命清洗旧仓库。第六用 monitor 类工具盯着仓库规模。仓库体积会随着二进制文件和 tag 数量快速增长。用du -sh /var/svn/*定期看看如果发现某个仓库涨得异常快多半是有人把大二进制文件安装包、图片、视频提交进去了。SVN 不像 Git LFS 那样对二进制做特殊处理所以从一开始就约定“二进制文件不进 SVN”改为走对象存储或内部网盘能省太多事。我自己的经验是SVN 服务器跑起来容易想要长期稳定省心真正花时间的地方全在权限规划、目录结构和备份策略上。每次做完一个项目的版本库花十分钟把 authz 里的用户组、目录层级和备份脚本梳理一遍远比等到上线前手动补权限、被迫救数据要好。这也是我个人这几年运维 SVN 下来最深的一条体会——版本服务器的本质不是把代码存起来而是让合适的人在合适的路径上做合适的事。把这条想明白技术细节都是水到渠成的事。