SvnKit
地址:https://svnkit.com/download.php
项目需要做了个简单的demo,可以进行基础操作。demo可以运行,但是更多高级更多实现需要自己在扩展。因为这个项目已经进入Thread.sleep :( ,开展新项目了 :(
功能
1.实现了几个基础操作
2.提供了日志操作
项目结构
首先我们先创建3个对象来为后面服务
package com.svn.model;
/**
* Svn账号信息
*
* @author Allen
* @date 2016年8月8日
*/
public class SvnAccountPojo implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String svnAccount;// svn账号
private String svnPassword;// svn密码
protected SvnAccountPojo() {
// TODO Auto-generated constructor stub
}
public SvnAccountPojo(String svnAccount, String svnPassword) {
super();
this.svnAccount = svnAccount;
this.svnPassword = svnPassword;
}
public String getSvnAccount() {
return svnAccount;
}
public void setSvnAccount(String svnAccount) {
this.svnAccount = svnAccount;
}
public String getSvnPassword() {
return svnPassword;
}
public void setSvnPassword(String svnPassword) {
this.svnPassword = svnPassword;
}
}
package com.svn.model;
/**
* Svn链接状态信息
*
* @author Allen
* @date 2016年8月8日
*/
public class SvnLinkPojo extends SvnAccountPojo {
/**
*
*/
private static final long serialVersionUID = 1L;
private String repoPath;// 库链接路径
public SvnLinkPojo(String repoPath, String svnAccount, String svnPassword) {
super(svnAccount, svnPassword);
this.repoPath = repoPath;
}
public SvnLinkPojo(String svnAccount, String svnPassword) {
super(svnAccount, svnPassword);
}
public String getRepoPath() {
return repoPath;
}
public void setRepoPath(String repoPath) {
this.repoPath = repoPath;
}
}
package com.svn.model;
import java.util.Date;
/**
* SVN资源库对象
*
* @author Allen
* @date 2016年8月8日
*/
public class SvnRepoPojo implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String commitMessage; // 提交信息
private Date date; // 提交日期
private String kind; // 提交方式 dir目录 file文件 none空 unknown 未知
private String name;// 目录名
private String repositoryRoot; // 资源库路径
private long revision; // 提交的svn版本号
private long size; // 提交的文件数
private String url; // 更变的目录地址
private String author;// 作者
private String state;// 状态
public String getCommitMessage() {
return commitMessage;
}
public void setCommitMessage(String commitMessage) {
this.commitMessage = commitMessage;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRepositoryRoot() {
return repositoryRoot;
}
public void setRepositoryRoot(String repositoryRoot) {
this.repositoryRoot = repositoryRoot;
}
public long getRevision() {
return revision;
}
public void setRevision(long revision) {
this.revision = revision;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
创建svn基础服务接口
package com.svn.inf.service;
/**
* svn主服务创建
*
* @author Allen
* @date 2016年8月8日
*/
public interface ISvnService {
/**
* 创建SNV版本库服务
*
* @author Allen
* @date 2016年8月11日
*/
public void createSVNRepository();
/**
* 关闭版本库容器,便于刷新重连等
*
* @author Allen
* @date 2016年8月11日
*/
public void closeRepo();
/**
* 创建svn客户操作服务
*
* @author Allen
* @date 2016年8月11日
*/
public void createSVNClientManager();
}
建立主功能接口
package com.svn.inf;
import java.io.File;
import java.util.List;
import com.svn.inf.service.ISvnDbLog;
import com.svn.inf.service.ISvnService;
import com.svn.model.SvnRepoPojo;
/**
* svn操作大全
*
* @author Allen
* @date 2016年8月8日
*/
public interface ISvn extends ISvnService {
/**
* 获取目标路径下版本库数据信息
*
* @param openPath
* 需要查看的版本库路径
* @return 版本库列表 {@link SvnRepoPojo}
* @author Allen
* @date 2016年8月11日
*/
public List getRepoCatalog(String openPath);
/**
* 检出到目录
*
* @param checkUrl
* 检出目标URL
* @param savePath
* 检出到本地路径
* @return true|false
* @author Allen
* @date 2016年8月11日
*/
public boolean checkOut(String checkUrl, String savePath);
/**
* 添加到版本库
*
* @see 先添加文件夹再添加文件
* @param paths
* 提交文件路径
* @param message
* 提交信息
* @param uLocks
* 是否解锁
* @param isvnLog
* 数据持久化接口 {@link ISvnDbLog}
* @return trun|false
* @author Allen
* @date 2016年8月11日
*/
public boolean add(String[] paths, String message, boolean uLocks, ISvnDbLog extends T> isvnLog);
/**
* 提交到版本库(所有写操作已在内部调用过COMMIT,自行调用则需要手动同步到DbLog)
*
* @param files
* 提交的文件路径
* @param message
* 提交信息
* @param uLocks
* 是否解锁
* @return 返回提交后版本号-1为提交失败
* @author Allen
* @date 2016年8月11日
*/
public Long commit(File[] files, String message, boolean uLocks);
/**
* 删除到版本库
*
* @see 先删除文件再删除文件夹
* @param paths
* 提交文件路径
* @param localDelete
*
* - 如果是true则在本地也删除此文件,false则只删除版本库中的此文件
* - 删除实体文件时要注意
* - 删除文件夹时其目录下所有内容都要提交到参数paths中,否则无法删除实体文件
*
* @param message
* 提交内容解释
* @param uLock
* 是否解锁
* @param isvnLog
* 数据持久化接口 {@link ISvnDbLog}
* @return true|false
* @author Allen
* @date 2016年8月11日
*/
public boolean delete(String[] paths, boolean localDelete, String message, boolean uLock, ISvnDbLog extends T> isvnLog);
/**
* 更新到版本库
*
* @param path
* 要更新的文件目录
* @param message
* 提交内容解释
* @param uLock
* 是否解锁
* @param isvnLog
* 数据持久化接口 {@link ISvnDbLog}
* @return true|false
* @author Allen
* @date 2016年8月11日
*/
public boolean update(String path, String message, boolean uLock, ISvnDbLog super T> isvnLog);
/**
* 比对目录下内容信息
*
* @see 返回delete,update文件列表
* @param file
* 待比对的目标文件路径
* @return 返回有差异的文件路径否则为null
* @author Allen
* @date 2016年8月11日
*/
public List diffPath(File file);
/**
* 清理目录
*
* @param file
* 待清理目录
* @return true|false
* @author Allen
* @date 2016年8月17日
*/
public boolean cleanUp(File file);
public boolean doLock();
public boolean unLock();
}
当然我们还有通用的工具接口
package com.svn.inf.service;
import java.io.File;
import java.util.List;
/**
* Svn功能组件
*
* @author Allen
* @date 2016年8月12日
*
*/
public interface ISvnCommon {
/**
* 组装file[]分离文件与文件夹
*
* @param files
* 待重组的文件列表
* @return 文件容器组index:0 文件夹 index:1 文件
* @author Allen
* @date 2016年8月11日
*/
public List> bindFile(File[] files);
/**
* 排序 父级 ->子级
*
* @param files
* 待排序文件数组
* @return 排序后文件数组
* @author Allen
* @date 2016年8月11日
*/
public File[] sortF_S(File[] files);
/**
* 排序 子级 ->父级
*
* @param files
* 待排序文件数组
* @return 排序后文件数组
* @author Allen
* @date 2016年8月11日
*/
public File[] sortS_F(File[] files);
/**
* 检查文件路径信息并组装到文件容器
*
* @param paths
* 待组装文件路径
* @return 文件数组
* @throws Exception
* 文件路径中有寻找不到的地址
* @author Allen
* @date 2016年8月11日
*/
public File[] checkFilePaths(String[] paths) throws Exception;
}
为了满足上级要求我们提供了db接口
package com.svn.inf.service;
import java.io.File;
import java.util.Date;
import java.util.List;
import com.svn.conf.SvnConfig;
/**
* 记录svn操作人
*
* @author Allen
* @date 2016年8月8日
*/
public interface ISvnDbLog {
/**
* 添加日志
*
* @param name
* 操作人账号
* @param dbType
* 数据类型{@link SvnConfig}
* @param versionId
* 版本号
* @param files
* 操作的文件组
* @return true|false
* @author Allen
* @date 2016年8月11日
*/
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files);
/**
* 获取日志
*
* @param name
* 操作人账号
* @param startTime
* 日志开始时间
* @param endTime
* 日志结束时间
* @return T 类型列表
* @author Allen
* @date 2016年8月11日
*/
public List super T> getLog(String name, Date startTime, Date endTime);
}
到上面为止我们的接口全部做好。这是一个java项目是希望做为一个工具jar的存在,demo中只做了一套基础的实现,按照这个思路根据不同需求可以慢慢扩展
工具类
其实这里自己写了个简单的错误匹配,主要是针对svnkit的errorCode转义,当然也可以用log4j等开源日志。不过毕竟是demo么 :)
package com.svn.tools;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
/**
* 控制台输出log信息
*
* @author Allen
* @date 2016年8月8日
*/
public class ConsoleLog {
protected boolean log = false;
/**
* 错误编码
*/
private String[] errorCode = { "E155010", "E200005", "E204899", "E200009", "E155015","E155004" };
/**
* 编码解释
*/
private String[] errorInfo = { "找不到文件或文件夹,其所在目录未被纳入版本控制的", "文件没有被纳入版本控制", "无法访问的目录", "文件没有被纳入版本控制", "svn冲突!","文件被锁定,可能是因为上次操作意外中断导致,请执行cleanup" };
/**
* 输出log到console
*
* @param msg
* 输出信息
*/
protected void log(String msg, String... content) {
if (log) {
StackTraceElement stack[] = (new Throwable()).getStackTrace();
String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
System.out.println(new StringBuffer("[").append(time).append("]").append(stack[1].getClassName()).append(".").append(stack[1].getMethodName()).append(" line:")
.append(stack[1].getLineNumber()));
System.out.println(new StringBuffer("[").append(time).append("]").append(msg));
}
}
protected void log(Exception e) {
StringBuffer sbf = new StringBuffer("【SVN-ERROR】");
for (int i = 0; i < errorCode.length; i++) {
if (e.getMessage().indexOf(errorCode[i]) != -1)
this.log(sbf.append(errorInfo[i]).toString());
}
}
}
这里主要是输出文本比对时差异信息
说实话版本冲突这里我svn是可以对列表进行更新或写操作的时候,批量返回错误信息,而svn提供的cmd入口及svnkit对其cmd命令的实现,都是提供的单条问题抛出,譬如我update一个文件目录,在第3,第5个文件会出现冲突,则接口在第三个文件直接就exception了,无法往下进行了。当然你可以递归从第四条再更新执行,最后把结果合并显示 :)
package com.svn.tools;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
/**
* Output输出到字符串
*
* @author Allen
* @date 2016年8月11日
*
*/
public final class StringOutputSteam extends OutputStream {
public List s;
public StringOutputSteam(List s) {
this.s = s;
}
@Override
public void write(int b) throws IOException {
}
@Override
public void write(byte b[], int off, int len) throws IOException {
String temp = new String(b);
String[] temps = temp.split("\r\n");
//System.out.println(temp);
boolean flag = false;
for (int i = 0; i < temps.length; i++) {
// 根据svn命令返回Index: xxxx(path)获取所有包含delete update的path获取
if (temps[i].split(" ")[0].equalsIgnoreCase("Index:")) {
String p = temps[i].substring(7, temps[i].length());
if (s.indexOf(p) == -1) {
s.add(p);
flag = true;
continue;
}
}
// 以Index:为开始点来判定svn diff返回的Log信息
// LogFormat:
// Index: path
// =============================
// xxxxxxx
// Index: path
// =============================
// xxxxxxx
if (flag) {
String state = "";
for (int j = i; j < temps.length; j++) {
if (temps[j].split(" ")[0].equalsIgnoreCase("Index:"))
break;
if (temps[j].split(" ")[0].equalsIgnoreCase("---"))
state = "冲突";
else if (state.equals(""))
state = "删除";
}
s.add(state);
flag = false;
}
}
}
}
这里是自定义的错误信息
package com.svn.conf;
/**
* 错误信息库
*
* @author Allen
* @date 2016年8月8日
*
*/
public final class ErrorVal {
public final static String SVNRepository_is_null = "版本库未被创建";
public final static String SVNClientManager = "";
public final static String SVNRepository_is_having = "版本库已存在 ,可以调用closeRepo进行[库服务清理]";
public final static String ISVNAuthenticationManager_is_null = "身份验证器还未被创建";
public final static String Path_no_having = "目标地址路径不存在";
public final static String Url_no_having = "目标库路径 不存在";
public final static String File_not_exist_repo = "文件所在目录没有被svn版本控制";
public final static String SvnConfig_is_null = "错误的配置";
public final static String Commit_error = "提交错误[==请确认文件是否已有其他版本操作,请检查文件是否不存在==]";
public final static String AddDbLog_error = "添加到数据日志错误";
public final static String File_Repo_no_having = "文件不存在版本库中";
public final static String Repo_Status_error = "版本状态错误";
public final static String Update_no_change = "没有变化的更新";
public final static String File_is_not_directory = "File不是一个目录";
}
这里不仅为小工厂提供了选项,还提供了对抽象db日志的数据设置
package com.svn.conf;
/**
* 系统参数设置
*
* @see 外部通过SvnConfig.xx来一个SvnConfig类型传入不同的配置
* @see 外部通过SvnConfig.xx.get来以String类型得到不同的配置
* @author Allen
* @date 2016年8月8日
*
*/
public class SvnConfig {
/**
* 选择非日志模式
*/
public static final SvnConfig normal = new SvnConfig("normal");
/**
* 选择console日志模式
*/
public static final SvnConfig log = new SvnConfig("log");
/**
* 数据存储类型Add
*/
public static final SvnConfig add = new SvnConfig("add");
/**
* 数据存储类型Update
*/
public static final SvnConfig update = new SvnConfig("update");
/**
* 数据存储类型Delete
*/
public static final SvnConfig delete = new SvnConfig("delete");
private String val;
public String getVal() {
return val;
}
private SvnConfig() {
}
private SvnConfig(String val) {
this.val = val;
}
}
实现类来了。。。拦不住的implements
提供了对通用工具接口的实现,为什么要那么复杂呢。因为例如add接口,必须要先提交最底层的文件夹目录从深层提交到高层,然后再提交其文件,以此类推,说白了就是你没有文件夹时无法建立文件的。
package com.svn.impl.service;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.svn.conf.ErrorVal;
import com.svn.inf.service.ISvnCommon;
import com.svn.tools.ConsoleLog;
/**
* {@link ISvnCommon}
*
* @author Allen
* @date 2016年8月12日
*
*/
public class SvnCommonImpl extends ConsoleLog implements ISvnCommon {
@Override
public List> bindFile(File[] files) {
List> fileList = new ArrayList>(1);
fileList.add(new ArrayList());
fileList.add(new ArrayList());
for (File f : files) {
if (f.isDirectory())
// 0是文件目录,先提交文件目录让再提交文件
fileList.get(0).add(f);
else
fileList.get(1).add(f);
}
for (int i = 0; i < fileList.size(); i++) {
if (fileList.get(i).size() == 0)
fileList.remove(i);
}
return fileList;
}
@Override
public File[] sortF_S(File[] files) {
Arrays.sort(files);
return files;
}
@Override
public File[] sortS_F(File[] files) {
files = sortF_S(files);
File[] f = new File[files.length];
for (int i = 0; i < files.length;) {
f[i] = files[files.length - (++i)];
}
return f;
}
@Override
public File[] checkFilePaths(String[] paths) throws Exception {
if (paths == null | paths.length == 0)
throw new Exception(ErrorVal.Path_no_having);
File[] files = new File[paths.length];
for (int i = 0; i < paths.length; i++) {
if (!(files[i] = new File(paths[i])).exists())
throw new Exception(ErrorVal.Path_no_having);
}
return files;
}
}
服务接口实现类,快乐的建立库容器
因为整个项目保持开闭原则,让你用的你随便用,不让你用的你就老实点:)
一次创建整个实例都能在开闭原则下快乐的使用各种创建好的实例/各种服务类
package com.svn.impl.service;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import com.svn.conf.ErrorVal;
import com.svn.inf.service.ISvnService;
/**
* {@link ISvnService}
*
* @author Allen
* @date 2016年8月8日
*/
public class SvnServiceImpl extends SvnCommonImpl implements ISvnService {
protected String svnAccount; // svn账号
protected String svnPassword;// svn密码
protected String svnRepoPath;// svn版本库根目录
protected boolean logStatus = false;// 日志状态
protected SVNRepository repository = null;// 版本库服务
protected ISVNAuthenticationManager authManager;// 身份验证器
protected SVNClientManager clientManager;// svn客户操作服务
/**
*
* @param account
* 账号
* @param password
* 密码
* @param logStatus
* 是否开启日志状态(默认false)
* @param repoPath
* svn库根目录
*
*/
public SvnServiceImpl(String account, String password, boolean logStatus, String repoPath) {
this.svnAccount = account;
this.svnPassword = password;
this.svnRepoPath = repoPath;
super.log = logStatus;
}
@Override
public void createSVNRepository() {
try {
if (repository != null)
throw new Exception(ErrorVal.SVNRepository_is_having);
// 创建库连接
SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(this.svnRepoPath));
super.log("创建版本库连接");
// 身份验证
this.authManager = SVNWCUtil.createDefaultAuthenticationManager(this.svnAccount, this.svnPassword.toCharArray());
super.log("创建身份验证");
// 创建身份验证管理器
repository.setAuthenticationManager(authManager);
this.repository = repository;
super.log("设置版本库身份验证");
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void closeRepo() {
if (repository == null)
try {
throw new NullPointerException(ErrorVal.SVNRepository_is_null);
} catch (Exception e) {
e.printStackTrace();
}
else {
repository.closeSession();
repository = null;
super.log("关闭版本库");
}
}
@Override
public void createSVNClientManager() {
if (authManager == null)
try {
throw new NullPointerException(ErrorVal.ISVNAuthenticationManager_is_null);
} catch (Exception e) {
e.printStackTrace();
}
clientManager = SVNClientManager.newInstance(SVNWCUtil.createDefaultOptions(true), authManager);
super.log("创建svn客户操作服务");
}
}
其实这里本来还想写个池服务。。不过时间关系就没写。毕竟demo没有后续跟进计划了。以后有机会再补
package com.svn.impl.service;
import com.svn.impl.service.SvnCommonImpl;
import com.svn.inf.service.ISvnService;
/**
* {@link ISvnService} 池的实现
*
* @author Allen
* @date 2016年8月8日
*/
public class SvnServicePoolImpl extends SvnCommonImpl implements ISvnService {
@Override
public void createSVNRepository() {
// TODO Auto-generated method stub
}
@Override
public void closeRepo() {
// TODO Auto-generated method stub
}
@Override
public void createSVNClientManager() {
// TODO Auto-generated method stub
}
}
这里..这里就是业务实现了,其实就是对svnkit进行了一个封装,让他更符合项目要求
package com.svn.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import com.svn.conf.ErrorVal;
import com.svn.conf.SvnConfig;
import com.svn.impl.service.SvnServiceImpl;
import com.svn.inf.ISvn;
import com.svn.inf.service.ISvnDbLog;
import com.svn.inf.service.ISvnService;
import com.svn.model.SvnRepoPojo;
import com.svn.tools.StringOutputSteam;
/**
*
* {@link ISvnService}
*
* @author Allen
* @date 2016年8月8日
*
*/
public class SvnBaseImpl extends SvnServiceImpl implements ISvn {
public SvnBaseImpl(String account, String password, boolean logStatus, String repoPath) {
super(account, password, logStatus, repoPath);
}
@SuppressWarnings("unchecked")
@Override
public List getRepoCatalog(String openPath) {
try {
if (repository == null)
throw new Exception(ErrorVal.SVNRepository_is_null);
Collection entries = repository.getDir(openPath, -1, null, (Collection) null);
List svns = new ArrayList();
Iterator it = entries.iterator();
while (it.hasNext()) {
SVNDirEntry entry = it.next();
SvnRepoPojo svn = new SvnRepoPojo();
svn.setCommitMessage(entry.getCommitMessage());
svn.setDate(entry.getDate());
svn.setKind(entry.getKind().toString());
svn.setName(entry.getName());
svn.setRepositoryRoot(entry.getRepositoryRoot().toString());
svn.setRevision(entry.getRevision());
svn.setSize(entry.getSize() / 1024);
svn.setUrl(openPath.equals("") ? new StringBuffer("/").append(entry.getName()).toString() : new StringBuffer(openPath).append("/").append(entry.getName()).toString());
svn.setAuthor(entry.getAuthor());
svn.setState(svn.getKind() == "file" ? null : "closed");
svns.add(svn);
}
super.log("获得版本库文件信息");
return svns;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
return null;
} catch (Exception e) {
super.log(e);
e.printStackTrace();
return null;
}
}
@Override
public boolean checkOut(String checkUrl, String savePath) {
SVNUpdateClient updateClient = clientManager.getUpdateClient();
updateClient.setIgnoreExternals(false);
try {
if (savePath == null || savePath.trim().equals(""))
throw new Exception(ErrorVal.Path_no_having);
else if (checkUrl == null || checkUrl.trim().equals(""))
throw new Exception(ErrorVal.Url_no_having);
File save = new File(savePath);
if (!save.isDirectory())
save.mkdir();
updateClient.doCheckout(SVNURL.parseURIEncoded(checkUrl), save, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, false);
super.log("检出版本库信息");
return true;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 此实现自动对Add后的数据进行提交
*/
@Override
public boolean add(String[] paths, String message, boolean uLocks, ISvnDbLog extends T> isvnLog) {
try {
File[] files = checkFilePaths(paths);
files = sortF_S(files);
SVNStatus status;
List targetList = new ArrayList();
List> fileList = bindFile(files);
for (int i = 0; i < fileList.size(); i++) {
for (File f : fileList.get(i)) {
if ((status = clientManager.getStatusClient().doStatus(f, true, true)) != null && status.getContentsStatus() != SVNStatusType.STATUS_UNVERSIONED
&& status.getContentsStatus() != (SVNStatusType.STATUS_NONE))
continue;
else if (f.isFile()) {
clientManager.getWCClient().doAdd(f, true, false, true, SVNDepth.fromRecurse(true), false, false, true);
targetList.add(f);
super.log("添加文件到提交队列");
} else if (f.isDirectory()) {
// SVNDepth.empty 保证不递归文件夹下文件
clientManager.getWCClient().doAdd(f, false, false, false, SVNDepth.EMPTY, false, false, false);
targetList.add(f);
super.log("添加文件夹到提交队列");
}
}
}
long versionId = commit(targetList.toArray(new File[targetList.size()]), message, uLocks);
if (versionId == -1)
throw new Exception(ErrorVal.Commit_error);
if (!isvnLog.addLog(this.svnAccount, SvnConfig.add, versionId, files))
throw new Exception(ErrorVal.AddDbLog_error);
return true;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public Long commit(File[] files, String message, boolean uLocks) {
try {
if (files.length == 0) {
super.log("无效的提交信息");
return -1l;
}
long versionId = clientManager.getCommitClient().doCommit(files, uLocks, message, null, null, false, false, SVNDepth.INFINITY).getNewRevision();
super.log("提交队列中预处理的操作操作 => 版本号: " + versionId);
return versionId;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return -1l;
}
@Override
public boolean delete(String[] paths, boolean localDelete, String message, boolean uLock, ISvnDbLog extends T> isvnLog) {
try {
File[] files = checkFilePaths(paths);
files = sortS_F(files);
SVNStatus status = null;
{
List targetList = new ArrayList();
List> fileList = bindFile(files);
for (int i = fileList.size() - 1; i >= 0; i--) {
for (File f : fileList.get(i)) {
if ((status = clientManager.getStatusClient().doStatus(f, true, true)) == null)
throw new Exception(ErrorVal.File_Repo_no_having);
else if (status.getContentsStatus() != SVNStatusType.STATUS_NORMAL)
throw new Exception(ErrorVal.Repo_Status_error + status.getContentsStatus().toString());
else {
clientManager.getWCClient().doDelete(f, false, localDelete, false);
if (f.isFile())
super.log("添加文件到删除队列");
else
super.log("添加文件夹到删除队列");
targetList.add(f);
}
}
}
long versionId = commit(targetList.toArray(new File[targetList.size()]), message, uLock);
if (versionId == -1)
throw new Exception(ErrorVal.Commit_error);
if (!isvnLog.addLog(this.svnAccount, SvnConfig.delete, versionId, files))
throw new Exception(ErrorVal.AddDbLog_error);
}
return true;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean update(String path, String message, boolean uLocks, ISvnDbLog super T> isvnLog) {
try {
File[] files = checkFilePaths(new String[] { path });
// diffPath(files);
long[] l = clientManager.getUpdateClient().doUpdate(files, SVNRevision.HEAD, SVNDepth.INFINITY, true, false);
super.log("更新文件到操作队列");
long versionId = l[0];// commit(files, message, uLocks);
if (versionId == -1)
throw new Exception(ErrorVal.Update_no_change);
if (!isvnLog.addLog(this.svnAccount, SvnConfig.update, versionId, files))
throw new Exception(ErrorVal.AddDbLog_error);
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public List diffPath(File file) {
try {
if (file == null || !file.exists())
throw new Exception(ErrorVal.Path_no_having);
// 获取SVNDiffClient
SVNDiffClient diffClient = clientManager.getDiffClient();
diffClient.setIgnoreExternals(false);
StringOutputSteam os = new StringOutputSteam(new ArrayList());
diffClient.doDiff(new File[] { file }, SVNRevision.HEAD, SVNRevision.BASE, null, SVNDepth.INFINITY, true, os, null);
super.log("比对库路径");
return os.s;
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean cleanUp(File file) {
try {
if (!file.exists())
throw new Exception(ErrorVal.Path_no_having);
else if (!file.isDirectory())
throw new Exception(ErrorVal.File_is_not_directory);
clientManager.getWCClient().doCleanup(file, false, false, false, true, true, true);
super.log("清理完成");
} catch (SVNException e) {
super.log(e);
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean doLock() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean unLock() {
// TODO Auto-generated method stub
return false;
}
}
实现代码比较多。不要慌
仔细看看加断点看看
加锁解锁还有一些接口的高级应用并没有深入去写,因为毕竟是demo。上级觉得技术上是可以搞定这个功能的,虽然因为其他原因项目阉割了 :( 所以也没有后续进行深层开发
https://svnkit.com/documentation.html 这个是svnkit 文档,不过文档比svnkit.jar版本要老 :((
关于参数中
ISvnDbLog super T> isvnLog
可以让使用者提供一个ISvnDbLog的实现,来进行日志保存,无论是db还是file
最后我们提供一个简单纺织小工厂
package com.svn.factory;
import com.svn.conf.ErrorVal;
import com.svn.conf.SvnConfig;
import com.svn.impl.SvnBaseImpl;
import com.svn.inf.ISvn;
import com.svn.model.SvnLinkPojo;
/**
* DemoSvn主体
*
* @author Allen
* @date 2016年8月8日
*/
public final class DemoSvn {
SvnLinkPojo svnLink;
public SvnLinkPojo getSvnLink() {
return svnLink;
}
/**
* 私有构造
*/
public DemoSvn() {
}
public DemoSvn(String svnAccount, String svnPassword, String repoPath) {
this.svnLink = new SvnLinkPojo(repoPath, svnAccount, svnPassword);
}
/**
* 获取SVN操作
*
* @param val
* default 不设置日志状态 log 开启console日志状态
* @throws 没有操作匹配
* @return {@link ISvn}
*/
public ISvn execute(SvnConfig val) throws Exception {
ISvn is = null;
if (val == null)
throw new Exception(ErrorVal.SvnConfig_is_null);
switch (val.getVal()) {
case "normal":
is = new SvnBaseImpl(svnLink.getSvnAccount(), svnLink.getSvnPassword(), false, svnLink.getRepoPath());
break;
case "log":
is = new SvnBaseImpl(svnLink.getSvnAccount(), svnLink.getSvnPassword(), true, svnLink.getRepoPath());
break;
default:
throw new Exception(ErrorVal.SvnConfig_is_null);
}
return is;
}
}
来一个测试函数吧
其实创建版本库,操作对象,初始化实例,close都可以做的更简洁,然后做到事务中,切面管理.. :)
package com.svn;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Date;
import com.svn.conf.SvnConfig;
import com.svn.factory.DemoSvn;
import com.svn.inf.ISvn;
import com.svn.inf.service.ISvnDbLog;
import com.svn.model.SvnRepoPojo;
/**
* 测试类
*
* @author Allen
* @date 2016年8月8日
*/
public class exmple {
String account = "allen";
String password = "123456";
String path = "http://192.168.1.xx/svn/abc";
String targetHead = "e:/测试";
ISvn svn;
/**
* 样例
*
* @throws Exception
* @author Allen
* @date 2016年8月12日
*/
private void testCore() throws Exception {
// 初始化实例
DemoSvn ts = new DemoSvn(account, password, path);
// 获得操作对象
this.svn = ts.execute(SvnConfig.log);
// 得到版本库信息
svn.createSVNRepository();
// 得到基础操作对象
svn.createSVNClientManager();
/** 测试--Start-- **/
testGetRepo();
testCheckOut();
testAdd();
testDel();
testCleanUp();
testUpdate();
testDiff();
/** 测试 --End-- **/
// 关闭库容器
svn.closeRepo();
}
/**
* 获得版本路径文件信息
*
* @author Allen
* @date 2016年8月12日
*/
private void testGetRepo() {
print(svn.getRepoCatalog(""));
}
/**
* 检出到本地路径
*
* @author Allen
* @date 2016年8月12日
*/
private void testCheckOut() {
svn.checkOut("xxx", targetHead);
}
/**
* 添加文件到svn
*
* @author Allen
* @date 2016年8月12日
*/
private void testAdd() {
String[] strs = new String[] { targetHead + "/4/a/", targetHead + "/4/", targetHead + "/4/a/b/", targetHead + "/4/a/b/b.txt" };
svn.add(strs, "haha", false, new ISvnDbLog() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("Add 到 DB 了");
return true;
}
@Override
public List getLog(String name, Date startTime, Date endTime) {
System.out.println("get 到 log 了");
return null;
}
});
}
/**
* 删除文件到svn
*
* @author Allen
* @date 2016年8月12日
*/
private void testDel() {
String[] strs = new String[] { targetHead + "/4/a/", targetHead + "/4/", targetHead + "/4/a/b/", targetHead + "/4/a/b/b.txt" };
svn.delete(strs, true, "haha", false, new ISvnDbLog() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("del 到 DB 了");
return true;
}
@Override
public List getLog(String name, Date startTime, Date endTime) {
System.out.println("get 到 log 了");
return null;
}
});
}
/**
* 更新文件到svn
*
* @author Allen
* @date 2016年8月12日
*/
private void testUpdate() {
String strs = targetHead + "/4/a/b/";
svn.update(strs, "哈哈", false, new ISvnDbLog() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("update 到 DB 了");
return true;
}
@Override
public List getLog(String name, Date startTime, Date endTime) {
System.out.println("get 到 log 了");
return null;
}
});
}
/**
* 测试库比对
*
* @author Allen
* @date 2016年8月12日
*/
private void testDiff() {
String[] strs = new String[] { targetHead + "/4/a/b/" };
List s = svn.diffPath(new File(strs[0]));
for (String t : s)
System.out.println(t);
}
private void testCleanUp() {
String[] strs = new String[] { targetHead + "/4/a/b/" };
svn.cleanUp(new File(strs[0]));
}
/**
* 打印当前版本库路径目录
*/
private void print(List paramList) {
System.out.print("commitMessage ");
System.out.print("\t\t date \t ");
System.out.print("\t kind \t ");
System.out.print("\t name \t ");
System.out.print("\t repositoryRoot \t ");
System.out.print("\t revision \t ");
System.out.print("\t size \t ");
System.out.print("\t url \t ");
System.out.print("\t author \t ");
System.out.println("\t state \t ");
Collections.sort(paramList, new Comparator() {
@Override
public int compare(SvnRepoPojo o1, SvnRepoPojo o2) {
return o1.getName().compareTo(o2.getName());
}
});
for (SvnRepoPojo pojo : paramList) {
System.out.print("\t" + pojo.getCommitMessage() + "\t");
System.out.print("\t" + pojo.getDate().getTime() + "\t");
System.out.print("\t" + pojo.getKind() + "\t");
System.out.print("\t" + pojo.getName() + "\t");
System.out.print("\t" + pojo.getRepositoryRoot() + "\t");
System.out.print("\t" + pojo.getRevision() + "\t");
System.out.print("\t" + pojo.getSize() + "\t");
System.out.print("\t" + pojo.getUrl() + "\t");
System.out.print("\t" + pojo.getAuthor() + "\t");
System.out.print("\t" + pojo.getState() + "\t");
System.out.print("\r\n");
}
}
public static void main(String[] args) throws Exception {
new exmple().testCore();
}
}
就这样吧。demo而已看个用法和思路。具体优化及细节也没有时间去做了,毕竟是demo :)
下载地址
http://download.csdn.net/detail/crazyzxljing0621/9755958