能在一天之内把整个开发部的SVN地址从旧IP迁到新IP,其实不是个复杂活,但你如果没搞清楚SVN地址到底存在哪里、用什么命令改、改了之后哪些人还要跟着动,那这件事可能从五分钟变成一下午。最近我又处理了一轮SVN服务迁移,把所有要改的点和踩过的坑整理出来,写这篇给准备做“SVN地址改为IP”的朋友一份能直接照做的操作手册。先说结论:如果只是服务器换了IP,仓库本身还是同一个,那客户端工作副本里记的URL要改,你只需要一个命令,就是svn relocate。但如果你遇到的是“换了IP也换了仓库”“服务端绑定了旧IP”或者“几十个人每人手里好几个工作副本”这类情况,那就要分场景处理了。下面我按“原理→标准操作→批量操作→报错排查”的顺序说。1. 改SVN地址前,先弄明白URL被存放在哪里很多人一上来就在项目目录里翻.svn文件夹,或者直接全局搜索旧IP字符串,觉得把某个文件里的地址换掉就行。这样做不是完全没道理,但很容易翻车。因为SVN的地址不是存在某个“配置文件”里,而是被写在工作副本自己的元数据中,不同版本的SVN存法还不一样。1.1 工作副本元数据:URL、Repository Root和UUID当你执行过一次svn checkout svn://192.168.1.10/repos/trunk后,这条URL会被记录到工作副本的元数据里。SVN 1.7之前,每个目录下都有自己的.svn/entries文件来记录这类信息;1.7之后改成了集中式管理,只有工作副本根目录有一个.svn目录,里面核心文件是wc.db,一个SQLite数据库。你可以先在自己的项目里执行一下svn info,看输出:$ svn info Path: . Working Copy Root Path: /home/me/project URL: svn://192.168.1.10/repos/trunk Relative URL: ^/trunk Repository Root: svn://192.168.1.10/repos Repository UUID: 3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e Revision: 128 Node Kind: directory这里有几个字段要分清:URL:当前这个工作副本目录在服务器上的完整路径。如果你在trunk目录下执行,URL就是仓库根加/trunk。Repository Root:版本库的根地址,不管你在哪个子目录里看到的都是同一个值,比如svn://192.168.1.10/repos。Repository UUID:这个版本库的全局唯一标识。同一份版本库,不管复制到哪台服务器,UUID都不变;只有用svnadmin create新建的仓库才会有不同的UUID。改IP这件事,本质上改的是URL里的服务器IP前缀,也就是把svn://192.168.1.10/repos这个Repository Root改成svn://192.168.1.20/repos,而后面/trunk、/branch/xxx这些相对路径要保持原样。1.2 客户端和服务端是两个层面“SVN地址改为IP”可以发生在两个地方,别混在一起:客户端层面:每个工作副本里记录的仓库地址需要更新,否则提交、更新时SVN会去连旧IP,报“无法连接”。服务端层面:如果SVN服务(比如svnserve、Apache SVN模块)启动配置里绑定了旧IP,那就算客户端地址改对了,新IP也连不上服务。我遇到过一种情况:svnserve的启动脚本里加了一个--listen-host 192.168.1.10参数,服务器IP换了以后服务还在跑,但监听地址还是旧的,那客户端改完新IP照样连不上。所以排查时一定要两边都看。1.3 先判断该relocate还是直接重新checkout不是所有“换IP”都能用relocate解决,我建议先列一张对照表理清自己的场景:场景新旧仓库UUID是否一致推荐做法同一个版本库,只是服务器IP变了一致svn relocate 新地址版本库整体迁移,通过dump/load或直接套文件复制一致(正常迁移时保持)svn relocate 新地址服务器上换成了全新的空版本库不一致重新checkout,或在确认目录结构一致时用--ignore-repos-uuid仓库目录结构大改,比如根目录从/trunk变成/main/trunk不确定重新checkout更省事判断UUID是否一致很简单,去服务器上找仓库路径执行svnlook uuid /path/to/repos,和你本地svn info里的UUID对比一下。如果一致,说明还是同一份版本库,relocate是安全的;如果不一致,说明你面对的是一个全新仓库,relocate要谨慎。2. 标准操作:用 svn relocate 把地址改到新IP确认场景没问题之后,就可以动手改地址了。SVN官方给的标准做法就是relocate,它的作用是“让本地工作副本记住新的仓库地址”,并不会下载或删除任何文件。2.1 命令行核心用法:新旧版本参数差异如果你用的是比较老的SVN客户端(1.6及更早),命令是:svn switch --relocate 旧地址 新地址这个写法在SVN 1.7之后被废弃,到了更高版本基本不能再用了。现在主流的写法是:svn relocate 新地址注意,新版relocate不需要你提供旧地址,它会自动读取工作副本里记录的Repository Root,然后把这个前缀替换成新地址。比如:cd /home/me/project # 方式一:给完整URL,推荐,能减少歧义 svn relocate svn://192.168.1.20/repos/trunk # 方式二:只给Repository Root svn relocate svn://192.168.1.20/repos执行成功后一般没有任何输出,你再用svn info确认:$ svn info | grep -E 'URL:|Repository Root:|Repository UUID:' URL: svn://192.168.1.20/repos/trunk Repository Root: svn://192.168.1.20/repos Repository UUID: 3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e这个操作非常快,因为它在本地改的只是一条元数据,不会去服务器上拉任何东西。即使服务器暂时还没就绪,你也可以先把本地工作副本的地址改掉,等服务好了再update。2.2 TortoiseSVN图形界面里的RelocateWindows上用TortoiseSVN的人很多,操作路径是:在工作副本目录上右键,菜单里找到TortoiseSVN → Relocate…弹窗里会显示当前的From URL(自动带出工作副本里记录的URL),你在To URL那一栏填入新地址点击OK,完成之后我一般会马上做一次SVN Update验证一下,能正常拉取到服务器日志就说明地址改对了。如果你的TortoiseSVN版本菜单里找不到“Relocate”,也可能是工作副本版本太旧,先用TortoiseSVN → Cleanup…或者升级客户端版本再试。这个功能不会消失,只是有时候藏在子菜单里。2.3 relocate背后发生了什么,以及什么时候别用它relocate只是一个“改记录”的动作,它不会比较本地文件和服务器文件的差异,也不会下载任何版本历史。所以你会发现它执行得特别快,这不是没生效,而是它本来就不需要跟服务器交互。也正因为如此,工作副本里如果有未提交的修改,relocate不会动它们,这些修改在本地保留,之后commit时提交到新地址对应的仓库。听起来很方便,但危险也在这里:如果你新IP对应的是一个全新的空仓库,relocate之后第一次update,SVN会把本地这些文件当成“应该由服务器管理,但服务器上不存在”的东西,于是会出现大量删除或冲突。这种场景别用relocate,老老实实重新checkout,本地改动用补丁或手工迁移。所以我的实践经验是:执行relocate前,至少先看一眼svn status,确认没有特别多的未提交改动。如果有,要么先提交,要么至少备份一份diff,免得改完地址后仓库状态对不上。2.4 换服务器IP时服务端同步要改什么客户端改完了,还要确认服务端没有绑定旧IP。svnserve(Linux):常见启动方式有两种。直接命令行启动时写svnserve -d -r /data/svn,这是监听所有网卡的3690端口,服务器换IP后不用改配置;但如果启动了--listen-host 192.168.1.10这种参数,就必须改成新IP。用systemd管理时,去查/etc/systemd/system/svnserve.service里的ExecStart,改完执行systemctl daemon-reload && systemctl restart svnserve。Apache mod_dav_svn:如果SVN地址是http://IP/svn这种,检查Apache配置里有没有用<VirtualHost 192.168.1.10:80>这种写法绑死旧IP。改成*:80或者新IP都行。另外如果配置里写了ServerName或重定向到旧域名,也要一并更新,否则客户端访问新IP时会收到301跳转。VisualSVN Server(Windows):打开管理控制台,进入服务器属性里的Network标签,看一下绑定的IP或者端口,换了IP后改过来,重启服务。云主机:Linux服务器自身的防火墙要看,云控制台的安全组规则也要看。SVN默认端口是3690/TCP,只服务端监听还不够,入方向规则必须放行。我很多次排查到最后发现是安全组没加端口。3. 批量换IP:几十个工作副本怎么快速收编如果只有一两个项目,手动relocate没问题。但开发团队往往每个人电脑里有好几个检出目录,有些老项目很久没动了,只会在某天需要提交时才被想起来。这种时候需要一个批量处理方案。3.1 自动脚本定位并relocate所有工作副本SVN 1.7以上的工作副本只在根目录有一个.svn文件夹,子目录里不会有。所以写脚本寻找所有带.svn的目录,基本上就是找到了各个工作副本的根目录。下面这个脚本适合Linux/macOS环境,假设我们统一的旧仓库根是svn://192.168.1.10/repos,新地址是svn://192.168.1.20/repos:#!/bin/bash OLD_ROOT="svn://192.168.1.10/repos" NEW_ROOT="svn://192.168.1.20/repos" WORK_BASE="/data/workspace" find "$WORK_BASE" -maxdepth 4 -type d -name ".svn" -print0 | while IFS= read -r -d '' svn_dir; do wc_root="$(dirname "$svn_dir")" echo "处理工作副本: $wc_root" (cd "$wc_root" && svn info --show-item repos-root) (cd "$wc_root" && svn relocate "$NEW_ROOT") if [ $? -eq 0 ]; then echo " -> 成功: $wc_root" else echo " -> 失败: $wc_root" fi done这里为什么要先打印旧的repos-root?因为脚本不一定覆盖了所有机器,有些工作副本可能本来就是另一个服务器地址,如果你盲目把所有工作副本都relocate到新IP,反而会把原来连其他仓库的副本改坏。所以脚本里加一个判断:只有当svn info --show-item repos-root等于旧地址时才执行relocate。Windows上用PowerShell也可以写类似逻辑,核心思路都一样:遍历目录、判断.svn、执行relocate。批量改完以后,至少抽几个项目做一次svn log -l 1或者svn update,确认能连上新服务器。3.2 保留域名、只改hosts的过渡方案有一种情况可以不动任何工作副本:如果客户端地址原本是域名而不是IP,比如svn://svn.company.com/repos,而服务器换成了新IP,你可以临时在每台机器上改hosts文件,把svn.company.com解析到新IP。Windows hosts文件路径是C:\Windows\System32\drivers\etc\hosts,Linux/macOS是/etc/hosts,加一行:192.168.1.20 svn.company.com这样SVN客户端再去访问svn.company.com时,会直接走新IP,工作副本里的地址一个字都不用改。这个方案非常适合紧急恢复,比如早上大家都等着提交代码,域名解析还没更新,改hosts能让所有人先连上。但它只是临时手段。hosts只对本机生效,不是所有人所有机器都会去改;而且等DNS正式切换到新IP后,这些hosts里的旧映射可能变成隐患。所以后续仍然建议找时间用relocate把工作副本的统一地址收敛到正式域名或新IP。3.3 最不该做的操作:直接改wc.db既然SVN 1.7把地址存到了.svn/wc.db这个SQLite数据库里,有人就想到直接改数据库。实际上也有人这么干,而且表面看确实能把svn info里的URL改成新地址,但这属于给自己埋雷。wc.db里不只存一条Repository Root字段,还关联了很多内部缓存、节点状态和操作记录。你手动改数据库后,SVN客户端某些操作可能正常,但换到另一个命令可能直接报错,常见的像E155037、管理工作区状态不一致之类的问题,最后只能靠重新checkout来收拾烂摊子。一句话:官方提供了svn relocate就是干这个的,不要绕路去改wc.db。如果已经改了,也别慌,再用正确的relocate命令改回来,只要地址是对的,状态可以慢慢调整。4. 换地址后现场最容易翻车的5类报错这部分是我实际帮别人处理问题时遇到频率最高的几类,每条都附上排查思路。4.1 连不上主机:端口、防火墙和监听地址报错示例:svn: E170013: Unable to connect to a repository at URL 'svn://192.168.1.20/repos' svn: E730050: Can't connect to host '192.168.1.20': No route to host这是最基础的一类。按顺序排查:服务器上确认svnserve进程在跑:ps aux | grep svnserve确认它在监听3690端口:ss -tlnp | grep 3690确认没有绑定旧IP:看svnserve启动参数里的--listen-host客户端这台机器到服务器的3690端口是否通:Linux执行nc -vz 192.168.1.20 3690,Windows执行Test-NetConnection 192.168.1.20 -Port 3690注意,很多服务器上ping通不代表SVN端口通,因为防火墙规则可能只放行了ICMP没放行TCP。端口不通时优先看本机防火墙规则和云主机安全组。4.2 路径不存在或目录结构不匹配报错示例:svn: E160005: Target path 'svn://192.168.1.20/repos/trunk' does not exist这种一般是relocate时给的路径写得不对。比如旧仓库根是svn://192.168.1.10/repos,新服务器上仓库父目录叫svn://192.168.1.20/svn,那你要relocate到svn://192.168.1.20/svn/trunk,而不是原样把repos换成IP。处理办法:先svn list svn://192.168.1.20/svn看一下新服务器上到底有什么目录,再决定relocate的URL。千万别闭着眼睛照搬旧地址。4.3 UUID不匹配:是换IP还是换仓库,必须分清楚报错示例:svn: E170013: Unable to connect to a repository at URL... svn: E160024: Repository UUID '3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e' doesn't match expected uuid 'a8d4a22e-1c70-4f7e-9b74-6d25a23b4f91'这个报错说明:你relocate的新地址,对应的是一个完全不同的仓库。虽然IP换了,但服务器给的是一个全新版本库,UUID自然不一致。处理思路:如果你确认新仓库里应该就是旧仓库的内容(迁移过的),那多半是迁移方式有问题,比如你用svnadmin create新建了一个空仓库,然后只把文件复制过去而不是用svnadmin dump/load恢复。正确迁移要保留UUID。如果你确定就是故意换了一个新仓库,且它的目录结构跟旧仓库一致,可以使用relocate的--ignore-repos-uuid参数强制绑定:svn relocate --ignore-repos-uuid svn://192.168.1.20/repos/trunk但这是一条危险参数。它只是让工作副本不再校验UUID,不等于让新旧两份代码自动同步。relocate之后做update,服务器会按新仓库的版本号来调整工作副本,本地很多文件可能变成删除或冲突状态。所以用这个参数之前要做好重新checkout或手动合并的准备。4.4 HTTPS/SVN+SSH协议带来的附加问题换成IP地址后,如果新地址用了https://,证书问题基本绕不开。常规SSL证书是按域名签发的,IP地址通常没有对应的证书(除非用IP证书,但申请和使用都比较麻烦),所以TortoiseSVN或者命令行客户端会报证书验证失败。临时办法是首次连接时手动接受证书,或者命令行加--trust-server-cert,但我不建议长期依赖这种方式。如果SVN服务在内网,用svn://协议就简单很多,不需要证书;如果必须要走HTTPS,最好还是保持域名地址并同步更新证书,而不是用IP来配HTTPS。SVN+SSH协议类似,换IP后SSH客户端会提示host key变了,需要确认并更新known_hosts。同事如果用了自动化脚本连接,这个问题会特别明显。4.5 外部定义、钩子脚本和提交后版本错乱这个坑比较隐蔽,等你relocate成功、大家能正常提交了,它才在背后爆发。如果你的项目里配置了svn:externals属性,那relocate只管了主工作副本的地址,外部引用的URL还是旧的,需要手动重新设置属性。例如:svn propset svn:externals "libs svn://192.168.1.20/repos/libs/trunk" .另外,如果服务器上的post-commit钩子脚本里写死了旧IP(比如自动往某一台机器部署代码、推送通知),换IP后这些钩子会失效。改完SVN服务端地址,最好把仓库hooks目录下的脚本也检查一遍。还有一点,relocate本身不会改变你的版本号,但如果新仓库是全新的,那提交之后版本号会从1开始,跟旧仓库的历史版本号完全对不上。这对开发团队可能无所谓,但如果有人依赖旧版本号做发布记录或问题回归,要提前沟通。最后再分享一个我自己的操作习惯:改地址前,不管多么着急,先花一分钟在本地执行svn info,把旧的Repository Root和UUID记下来;再到服务器上执行svnlook uuid /path/to/repos看一眼新仓库的UUID。这两个值一对比,你基本就知道自己是该直接relocate、加--ignore-repos-uuid还是干脆重新checkout了。很多人改半天改不明白,问题就出在没分清“只是换了IP”和“其实换了个仓库”这两件事。
能在一天之内把整个开发部的SVN地址从旧IP迁到新IP,其实不是个复杂活,但你如果没搞清楚SVN地址到底存在哪里、用什么命令改、改了之后哪些人还要跟着动,那这件事可能从五分钟变成一下午。最近我又处理了一轮SVN服务迁移,把所有要改的点和踩过的坑整理出来,写这篇给准备做“SVN地址改为IP”的朋友一份能直接照做的操作手册。先说结论:如果只是服务器换了IP,仓库本身还是同一个,那客户端工作副本里记的URL要改,你只需要一个命令,就是svn relocate。但如果你遇到的是“换了IP也换了仓库”“服务端绑定了旧IP”或者“几十个人每人手里好几个工作副本”这类情况,那就要分场景处理了。下面我按“原理→标准操作→批量操作→报错排查”的顺序说。1. 改SVN地址前,先弄明白URL被存放在哪里很多人一上来就在项目目录里翻.svn文件夹,或者直接全局搜索旧IP字符串,觉得把某个文件里的地址换掉就行。这样做不是完全没道理,但很容易翻车。因为SVN的地址不是存在某个“配置文件”里,而是被写在工作副本自己的元数据中,不同版本的SVN存法还不一样。1.1 工作副本元数据:URL、Repository Root和UUID当你执行过一次svn checkout svn://192.168.1.10/repos/trunk后,这条URL会被记录到工作副本的元数据里。SVN 1.7之前,每个目录下都有自己的.svn/entries文件来记录这类信息;1.7之后改成了集中式管理,只有工作副本根目录有一个.svn目录,里面核心文件是wc.db,一个SQLite数据库。你可以先在自己的项目里执行一下svn info,看输出:$ svn info Path: . Working Copy Root Path: /home/me/project URL: svn://192.168.1.10/repos/trunk Relative URL: ^/trunk Repository Root: svn://192.168.1.10/repos Repository UUID: 3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e Revision: 128 Node Kind: directory这里有几个字段要分清:URL:当前这个工作副本目录在服务器上的完整路径。如果你在trunk目录下执行,URL就是仓库根加/trunk。Repository Root:版本库的根地址,不管你在哪个子目录里看到的都是同一个值,比如svn://192.168.1.10/repos。Repository UUID:这个版本库的全局唯一标识。同一份版本库,不管复制到哪台服务器,UUID都不变;只有用svnadmin create新建的仓库才会有不同的UUID。改IP这件事,本质上改的是URL里的服务器IP前缀,也就是把svn://192.168.1.10/repos这个Repository Root改成svn://192.168.1.20/repos,而后面/trunk、/branch/xxx这些相对路径要保持原样。1.2 客户端和服务端是两个层面“SVN地址改为IP”可以发生在两个地方,别混在一起:客户端层面:每个工作副本里记录的仓库地址需要更新,否则提交、更新时SVN会去连旧IP,报“无法连接”。服务端层面:如果SVN服务(比如svnserve、Apache SVN模块)启动配置里绑定了旧IP,那就算客户端地址改对了,新IP也连不上服务。我遇到过一种情况:svnserve的启动脚本里加了一个--listen-host 192.168.1.10参数,服务器IP换了以后服务还在跑,但监听地址还是旧的,那客户端改完新IP照样连不上。所以排查时一定要两边都看。1.3 先判断该relocate还是直接重新checkout不是所有“换IP”都能用relocate解决,我建议先列一张对照表理清自己的场景:场景新旧仓库UUID是否一致推荐做法同一个版本库,只是服务器IP变了一致svn relocate 新地址版本库整体迁移,通过dump/load或直接套文件复制一致(正常迁移时保持)svn relocate 新地址服务器上换成了全新的空版本库不一致重新checkout,或在确认目录结构一致时用--ignore-repos-uuid仓库目录结构大改,比如根目录从/trunk变成/main/trunk不确定重新checkout更省事判断UUID是否一致很简单,去服务器上找仓库路径执行svnlook uuid /path/to/repos,和你本地svn info里的UUID对比一下。如果一致,说明还是同一份版本库,relocate是安全的;如果不一致,说明你面对的是一个全新仓库,relocate要谨慎。2. 标准操作:用 svn relocate 把地址改到新IP确认场景没问题之后,就可以动手改地址了。SVN官方给的标准做法就是relocate,它的作用是“让本地工作副本记住新的仓库地址”,并不会下载或删除任何文件。2.1 命令行核心用法:新旧版本参数差异如果你用的是比较老的SVN客户端(1.6及更早),命令是:svn switch --relocate 旧地址 新地址这个写法在SVN 1.7之后被废弃,到了更高版本基本不能再用了。现在主流的写法是:svn relocate 新地址注意,新版relocate不需要你提供旧地址,它会自动读取工作副本里记录的Repository Root,然后把这个前缀替换成新地址。比如:cd /home/me/project # 方式一:给完整URL,推荐,能减少歧义 svn relocate svn://192.168.1.20/repos/trunk # 方式二:只给Repository Root svn relocate svn://192.168.1.20/repos执行成功后一般没有任何输出,你再用svn info确认:$ svn info | grep -E 'URL:|Repository Root:|Repository UUID:' URL: svn://192.168.1.20/repos/trunk Repository Root: svn://192.168.1.20/repos Repository UUID: 3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e这个操作非常快,因为它在本地改的只是一条元数据,不会去服务器上拉任何东西。即使服务器暂时还没就绪,你也可以先把本地工作副本的地址改掉,等服务好了再update。2.2 TortoiseSVN图形界面里的RelocateWindows上用TortoiseSVN的人很多,操作路径是:在工作副本目录上右键,菜单里找到TortoiseSVN → Relocate…弹窗里会显示当前的From URL(自动带出工作副本里记录的URL),你在To URL那一栏填入新地址点击OK,完成之后我一般会马上做一次SVN Update验证一下,能正常拉取到服务器日志就说明地址改对了。如果你的TortoiseSVN版本菜单里找不到“Relocate”,也可能是工作副本版本太旧,先用TortoiseSVN → Cleanup…或者升级客户端版本再试。这个功能不会消失,只是有时候藏在子菜单里。2.3 relocate背后发生了什么,以及什么时候别用它relocate只是一个“改记录”的动作,它不会比较本地文件和服务器文件的差异,也不会下载任何版本历史。所以你会发现它执行得特别快,这不是没生效,而是它本来就不需要跟服务器交互。也正因为如此,工作副本里如果有未提交的修改,relocate不会动它们,这些修改在本地保留,之后commit时提交到新地址对应的仓库。听起来很方便,但危险也在这里:如果你新IP对应的是一个全新的空仓库,relocate之后第一次update,SVN会把本地这些文件当成“应该由服务器管理,但服务器上不存在”的东西,于是会出现大量删除或冲突。这种场景别用relocate,老老实实重新checkout,本地改动用补丁或手工迁移。所以我的实践经验是:执行relocate前,至少先看一眼svn status,确认没有特别多的未提交改动。如果有,要么先提交,要么至少备份一份diff,免得改完地址后仓库状态对不上。2.4 换服务器IP时服务端同步要改什么客户端改完了,还要确认服务端没有绑定旧IP。svnserve(Linux):常见启动方式有两种。直接命令行启动时写svnserve -d -r /data/svn,这是监听所有网卡的3690端口,服务器换IP后不用改配置;但如果启动了--listen-host 192.168.1.10这种参数,就必须改成新IP。用systemd管理时,去查/etc/systemd/system/svnserve.service里的ExecStart,改完执行systemctl daemon-reload && systemctl restart svnserve。Apache mod_dav_svn:如果SVN地址是http://IP/svn这种,检查Apache配置里有没有用<VirtualHost 192.168.1.10:80>这种写法绑死旧IP。改成*:80或者新IP都行。另外如果配置里写了ServerName或重定向到旧域名,也要一并更新,否则客户端访问新IP时会收到301跳转。VisualSVN Server(Windows):打开管理控制台,进入服务器属性里的Network标签,看一下绑定的IP或者端口,换了IP后改过来,重启服务。云主机:Linux服务器自身的防火墙要看,云控制台的安全组规则也要看。SVN默认端口是3690/TCP,只服务端监听还不够,入方向规则必须放行。我很多次排查到最后发现是安全组没加端口。3. 批量换IP:几十个工作副本怎么快速收编如果只有一两个项目,手动relocate没问题。但开发团队往往每个人电脑里有好几个检出目录,有些老项目很久没动了,只会在某天需要提交时才被想起来。这种时候需要一个批量处理方案。3.1 自动脚本定位并relocate所有工作副本SVN 1.7以上的工作副本只在根目录有一个.svn文件夹,子目录里不会有。所以写脚本寻找所有带.svn的目录,基本上就是找到了各个工作副本的根目录。下面这个脚本适合Linux/macOS环境,假设我们统一的旧仓库根是svn://192.168.1.10/repos,新地址是svn://192.168.1.20/repos:#!/bin/bash OLD_ROOT="svn://192.168.1.10/repos" NEW_ROOT="svn://192.168.1.20/repos" WORK_BASE="/data/workspace" find "$WORK_BASE" -maxdepth 4 -type d -name ".svn" -print0 | while IFS= read -r -d '' svn_dir; do wc_root="$(dirname "$svn_dir")" echo "处理工作副本: $wc_root" (cd "$wc_root" && svn info --show-item repos-root) (cd "$wc_root" && svn relocate "$NEW_ROOT") if [ $? -eq 0 ]; then echo " -> 成功: $wc_root" else echo " -> 失败: $wc_root" fi done这里为什么要先打印旧的repos-root?因为脚本不一定覆盖了所有机器,有些工作副本可能本来就是另一个服务器地址,如果你盲目把所有工作副本都relocate到新IP,反而会把原来连其他仓库的副本改坏。所以脚本里加一个判断:只有当svn info --show-item repos-root等于旧地址时才执行relocate。Windows上用PowerShell也可以写类似逻辑,核心思路都一样:遍历目录、判断.svn、执行relocate。批量改完以后,至少抽几个项目做一次svn log -l 1或者svn update,确认能连上新服务器。3.2 保留域名、只改hosts的过渡方案有一种情况可以不动任何工作副本:如果客户端地址原本是域名而不是IP,比如svn://svn.company.com/repos,而服务器换成了新IP,你可以临时在每台机器上改hosts文件,把svn.company.com解析到新IP。Windows hosts文件路径是C:\Windows\System32\drivers\etc\hosts,Linux/macOS是/etc/hosts,加一行:192.168.1.20 svn.company.com这样SVN客户端再去访问svn.company.com时,会直接走新IP,工作副本里的地址一个字都不用改。这个方案非常适合紧急恢复,比如早上大家都等着提交代码,域名解析还没更新,改hosts能让所有人先连上。但它只是临时手段。hosts只对本机生效,不是所有人所有机器都会去改;而且等DNS正式切换到新IP后,这些hosts里的旧映射可能变成隐患。所以后续仍然建议找时间用relocate把工作副本的统一地址收敛到正式域名或新IP。3.3 最不该做的操作:直接改wc.db既然SVN 1.7把地址存到了.svn/wc.db这个SQLite数据库里,有人就想到直接改数据库。实际上也有人这么干,而且表面看确实能把svn info里的URL改成新地址,但这属于给自己埋雷。wc.db里不只存一条Repository Root字段,还关联了很多内部缓存、节点状态和操作记录。你手动改数据库后,SVN客户端某些操作可能正常,但换到另一个命令可能直接报错,常见的像E155037、管理工作区状态不一致之类的问题,最后只能靠重新checkout来收拾烂摊子。一句话:官方提供了svn relocate就是干这个的,不要绕路去改wc.db。如果已经改了,也别慌,再用正确的relocate命令改回来,只要地址是对的,状态可以慢慢调整。4. 换地址后现场最容易翻车的5类报错这部分是我实际帮别人处理问题时遇到频率最高的几类,每条都附上排查思路。4.1 连不上主机:端口、防火墙和监听地址报错示例:svn: E170013: Unable to connect to a repository at URL 'svn://192.168.1.20/repos' svn: E730050: Can't connect to host '192.168.1.20': No route to host这是最基础的一类。按顺序排查:服务器上确认svnserve进程在跑:ps aux | grep svnserve确认它在监听3690端口:ss -tlnp | grep 3690确认没有绑定旧IP:看svnserve启动参数里的--listen-host客户端这台机器到服务器的3690端口是否通:Linux执行nc -vz 192.168.1.20 3690,Windows执行Test-NetConnection 192.168.1.20 -Port 3690注意,很多服务器上ping通不代表SVN端口通,因为防火墙规则可能只放行了ICMP没放行TCP。端口不通时优先看本机防火墙规则和云主机安全组。4.2 路径不存在或目录结构不匹配报错示例:svn: E160005: Target path 'svn://192.168.1.20/repos/trunk' does not exist这种一般是relocate时给的路径写得不对。比如旧仓库根是svn://192.168.1.10/repos,新服务器上仓库父目录叫svn://192.168.1.20/svn,那你要relocate到svn://192.168.1.20/svn/trunk,而不是原样把repos换成IP。处理办法:先svn list svn://192.168.1.20/svn看一下新服务器上到底有什么目录,再决定relocate的URL。千万别闭着眼睛照搬旧地址。4.3 UUID不匹配:是换IP还是换仓库,必须分清楚报错示例:svn: E170013: Unable to connect to a repository at URL... svn: E160024: Repository UUID '3f235f6b-9d2c-4a9e-9c1e-9f2a1b2c3d4e' doesn't match expected uuid 'a8d4a22e-1c70-4f7e-9b74-6d25a23b4f91'这个报错说明:你relocate的新地址,对应的是一个完全不同的仓库。虽然IP换了,但服务器给的是一个全新版本库,UUID自然不一致。处理思路:如果你确认新仓库里应该就是旧仓库的内容(迁移过的),那多半是迁移方式有问题,比如你用svnadmin create新建了一个空仓库,然后只把文件复制过去而不是用svnadmin dump/load恢复。正确迁移要保留UUID。如果你确定就是故意换了一个新仓库,且它的目录结构跟旧仓库一致,可以使用relocate的--ignore-repos-uuid参数强制绑定:svn relocate --ignore-repos-uuid svn://192.168.1.20/repos/trunk但这是一条危险参数。它只是让工作副本不再校验UUID,不等于让新旧两份代码自动同步。relocate之后做update,服务器会按新仓库的版本号来调整工作副本,本地很多文件可能变成删除或冲突状态。所以用这个参数之前要做好重新checkout或手动合并的准备。4.4 HTTPS/SVN+SSH协议带来的附加问题换成IP地址后,如果新地址用了https://,证书问题基本绕不开。常规SSL证书是按域名签发的,IP地址通常没有对应的证书(除非用IP证书,但申请和使用都比较麻烦),所以TortoiseSVN或者命令行客户端会报证书验证失败。临时办法是首次连接时手动接受证书,或者命令行加--trust-server-cert,但我不建议长期依赖这种方式。如果SVN服务在内网,用svn://协议就简单很多,不需要证书;如果必须要走HTTPS,最好还是保持域名地址并同步更新证书,而不是用IP来配HTTPS。SVN+SSH协议类似,换IP后SSH客户端会提示host key变了,需要确认并更新known_hosts。同事如果用了自动化脚本连接,这个问题会特别明显。4.5 外部定义、钩子脚本和提交后版本错乱这个坑比较隐蔽,等你relocate成功、大家能正常提交了,它才在背后爆发。如果你的项目里配置了svn:externals属性,那relocate只管了主工作副本的地址,外部引用的URL还是旧的,需要手动重新设置属性。例如:svn propset svn:externals "libs svn://192.168.1.20/repos/libs/trunk" .另外,如果服务器上的post-commit钩子脚本里写死了旧IP(比如自动往某一台机器部署代码、推送通知),换IP后这些钩子会失效。改完SVN服务端地址,最好把仓库hooks目录下的脚本也检查一遍。还有一点,relocate本身不会改变你的版本号,但如果新仓库是全新的,那提交之后版本号会从1开始,跟旧仓库的历史版本号完全对不上。这对开发团队可能无所谓,但如果有人依赖旧版本号做发布记录或问题回归,要提前沟通。最后再分享一个我自己的操作习惯:改地址前,不管多么着急,先花一分钟在本地执行svn info,把旧的Repository Root和UUID记下来;再到服务器上执行svnlook uuid /path/to/repos看一眼新仓库的UUID。这两个值一对比,你基本就知道自己是该直接relocate、加--ignore-repos-uuid还是干脆重新checkout了。很多人改半天改不明白,问题就出在没分清“只是换了IP”和“其实换了个仓库”这两件事。