Subverion仓库迁移知识点整理

最近在工作上提出了一个需求,需要将各个团队的Subversion仓库进行集中管理。也就是需要在一台机器上的一个仓库里面导入上百个已经存在的部分仓库。在这里简单的总结一下这一块的工作。工作的重点主要涉及以下两个方面:

  • 根据SVNUrl、SVNUserName、SVNPassword信息把远程仓库的子目录内容dump到本地
  • 在本地创建子目录,将dumpFile读取到本地仓库的固定路径下

dump远程仓库内容到本地

Subversion 1.7 引入了一个新工具, svnrdump. 它提供了较为特殊的功能, 本质上就是 svnadmin dumpsvnadmin load (见 the section called “使用 svnadmin 迁移仓库数据”) 的跨网络版本.
svnrdump dump 从一个远程仓库转储数据, 打印到标准输出; svnrdump load 从标准输入读取转储数据, 加载到一个远程仓库上. svnrdump 可以像 svnadmin dump 那样生成增量转储, 甚至可以只转储仓库的某个子目录, svnadmin 却无法做到这一点.
svnadmin 与 svnrdump 之间 最关键的区别在于后者不需要直接访问仓库, svnrdump 使用与 Subversion 客户端相同的仓库访问 (Repository Access, 简称 RA) 协议完成操作 的远程执行, 因此用户可能需要提供认证证书, 除此之外, 远程交互可能还会受到 Subversion 服务器的授权限制. ——Subversion的官方手册

通过以上文档,可以发现子目录跨网络的svnrdump命令符合我们的需求,我们可以通过该命令实现将远程仓库的子目录数据保存到本地仓库,其具体命令如下:

svnrdummp dump https://url/to/remote/repository/sub/directory > tempFile --trust-server-cert --non-interactive --username *** --password ***

--trust-server-cert:Used with --non-interactive to accept any unknown SSL server certificates without prompting.
--non-interactive:In the case of an authentication failure or insufficient credentials, prevents prompting for credentials (e.g., username or password). This is useful if you're running Subversion inside an automated script and it's more appropriate to have Subversion fail than to prompt for more information.

可能碰到的问题

hostName Not Match

可能碰到如下错误:

svnrdump:E175002: Unable to connect to a repository at URL 'http://***'
svnrdump:E175002: OPTIONS of 'http://***': could not resolve hostname '***': Host not found (http://***)

当客户端收到服务器证书时, 它需要验证服务器身份的真实性, OpenSSL 完成验证的方法是检查服务器证书的签发人, 也就是 证书颁发机构 (certificate authority, 简称 CA). 如果 OpenSSL 无法自动信任 CA, 或者是发生的错误 (例如认证超时或主机名不匹配), 那么 Subversion 客户端工具将询问用户是否要信任服务器的证书:
$ svn list https://host.example.com/repos/project

Error validating server certificate for 'https://host.example.com:443':

  • The certificate is not issued by a trusted authority. Use the
    fingerprint to validate the certificate manually!
  • The certificate hostname does not match.
    Certificate information:
  • Hostname: host.example.com
  • Valid: from Jan 30 19:23:56 2004 GMT until Jan 30 19:23:56 2006 GMT
  • Issuer: CA, example.com, Sometown, California, US
  • Fingerprint: 7d:e1:a9:34:33:39:ba:6a:e9:a5:c4:22:98:7b:76:5c:92:a0:9c:7b
    (R)eject, accept (t)emporarily or accept (p)ermanently?

用户可能会在网页浏览器看到相同的对话框 (浏览器只是一个 HTTP 客户端), 如果选择 p, Subversion 将把 服务器证书缓存在本地的 auth/ 目录内, 你的用户名和密码也缓存在这里 (见 the section called “缓存证书”), 今后再次 连接服务器时, 将会自动信任证书. ——Subversion客户端SSL证书管理

假如用户碰到如上的错误,说明你使用的svnUrl中的前缀和Subversion服务器端返回的证书中的hostname不同,这种情况只能修改hosts文件、svnUrl,保证svnUrl的前缀和证书中的hostname相同,同时在hosts文件中将前缀和访问的ip做映射。

svnurl转码出现问题

如果不对svnUrl进行处理,直接放到命令行中执行可能会有各种问题。在这里使用cn.hutool.core.net.URLEncoder类对svnurl进行编码处理,但是需要考虑svnurl已经被处理过的情况,这种情况下需要将斜线、冒号、点号、百分号替换回来,具体代码如下:

URLEncoder urlEncoder = new URLEncoder();
Charset charset = Charset.forname("utf8");
svnUrl = urlEncoder.encode(svnUrl, charset);
svnUrl = svnUrl.replace("%2F","/").replace("%3A",":").replace("%2E",".").replace("%25","%");

增量导入的问题

再实际的导入过程中,如果每次导入失败都进行重新导入,势必会在仓库中造成很多冗余的数据。这种情况一般选择在错误的地方进行增量导入。需要记住上一次出错的版本号,下一次从该版本号进行dump(出错意味着这一次commit失败了,所以下一次从出错的版本号进行dump)。

svnrdump dump /remote/url -r startNum:endNum --incremental > dumpFile --trust-server-cert --non-interactive --username XXX --password XXX

除了记住上一次出错的版本号,还需要查找远程仓库总的版本号,使用svn info命令即可:

svn info /remote/repository/url --trust-server-cert --non-interactive --username XXX --password XXX

将dumpFile导入到本地仓库

在导入到本地仓库之前,你必须先创建好本地仓库的路径,才能将dumpFile导入到你指定的路径下面,创建本地仓库子路径的命令如下(注意-m参数中尽量不要有空格,空格可能导致在某些执行命令行的工具包中出问题):

svn mkdir --parents -m 'makeSubversion' file:///base/repository/path/to/sub/directory

创建子目录后,将dumpFile Load到本地仓库中即可,使用如下命令:

svnadmin load /repository/path --parent-dir /subdirectory/path < dumpFile

可能碰到的问题

缺失路径的问题

svnadmin: File not found: transaction 'XXX' , path '/repository/to/sub/directory/dirA'

一般来说这种问题很少碰到,具体什么原因导致这种问题也暂时没有搞清楚,主要的解决方法是在导入出错后,使用svn mkdir手动创建/repository/to/sub/directory目录,然后再导入。或者再失败的版本号进行增量导入(推荐)。

空格导致的错误

error: type 'svnrdump help' for usage.

这种错误是由于svnUrl中存在空格导致的,需要对svnUrl进行url编码,参考上一章节中的SvnUrl转码问题

如何获取控制台信息

对于在java程序中运行命令行命令,输出日志中的信息尤为重要。一旦调用第三方的命令,一切都显得不可控,目前在项目中通过java.lang.Process类进行处理,具体代码如下:

Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c",command}, null, null);
InputStream inputStream = process.getInputStream();
try(InputStreamReader isr = new InputStreamReader(inputStream);
         BufferedReader br = new BufferedReader(isr)){
    String line = null;
    while((line = br.readline()) != null ){
       //写日志   
    }
} catch (IOException e){
    //写错误日志
}
int exitVal = process.waitFor();
int (exitVal == 1){
  //说明命令行执行出错
}

需要注意的是,如果写出的日志信息太多,会导致进程阻塞从而无法看到日志输出。这种情况可以另外启动一个线程用于专门读取日志输出流。具体代码如下所示:

Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c",command}, null, null);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "Error");
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OutPut");

public class StreamGobbler extends Thread {
    InputStream is;
    String type;
    public StreamGobbler(InputStream is, String type){
       this.is = is;
       this.type = type; 
   }
 
   public void run() throws Exception{
       try(InputStreamReader isr = new InputStreamReader(is);
              BufferedReader br = new BuggeredReader(isr)){
              String line = null;
              while((line = br.readline) != null) {
                  if(type.equals("Error")){
                        //输出带Error字样的日志
                  } else {
                        //输出带Debug字样的日志
                  }
              }           
              }
      }
}
原文地址:https://www.cnblogs.com/mrnx2004/p/10495939.html