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<SvnRepoPojo> 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 <T> 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
* <ul>
* <li>如果是true則在本地也刪除此文件,false則只刪除版本庫中的此文件</li>
* <li>刪除實體文件時要注意</li>
* <li>刪除文件夾時其目錄下所有內容都要提交到<b>參數paths</b>中,否則無法刪除實體文件</li>
* </ul>
* @param message
* 提交內容解釋
* @param uLock
* 是否解鎖
* @param isvnLog
* 數據持久化接口 {@link ISvnDbLog}
* @return true|false
* @author Allen
* @date 2016年8月11日
*/
public <T> 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 <T> 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<String> 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 文件容器組</br>index:0 文件夾</br> index:1 文件
* @author Allen
* @date 2016年8月11日
*/
public List<List<File>> bindFile(File[] files);
/**
* 排序</br> 父級 ->子級
*
* @param files
* 待排序文件數組
* @return 排序後文件數組
* @author Allen
* @date 2016年8月11日
*/
public File[] sortF_S(File[] files);
/**
* 排序</br> 子級 ->父級
*
* @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<T> {
/**
* 添加日誌
*
* @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<String> s;
public StringOutputSteam(List<String> 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 外部通過<b>SvnConfig.xx</b>來一個<b>SvnConfig</b>類型傳入不同的配置
* @see 外部通過<b>SvnConfig.xx.get</b>來以<b>String</b>類型得到不同的配置
* @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<List<File>> bindFile(File[] files) {
List<List<File>> fileList = new ArrayList<List<File>>(1);
fileList.add(new ArrayList<File>());
fileList.add(new ArrayList<File>());
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<SvnRepoPojo> getRepoCatalog(String openPath) {
try {
if (repository == null)
throw new Exception(ErrorVal.SVNRepository_is_null);
Collection<SVNDirEntry> entries = repository.getDir(openPath, -1, null, (Collection<SVNDirEntry>) null);
List<SvnRepoPojo> svns = new ArrayList<SvnRepoPojo>();
Iterator<SVNDirEntry> 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 <T> boolean add(String[] paths, String message, boolean uLocks, ISvnDbLog<? extends T> isvnLog) {
try {
File[] files = checkFilePaths(paths);
files = sortF_S(files);
SVNStatus status;
List<File> targetList = new ArrayList<File>();
List<List<File>> 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 <T> 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<File> targetList = new ArrayList<File>();
List<List<File>> 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 <T> 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<String> 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<String>());
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<String>() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("Add 到 DB 了");
return true;
}
@Override
public List<String> 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<String>() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("del 到 DB 了");
return true;
}
@Override
public List<String> 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<String>() {
@Override
public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
System.out.println("update 到 DB 了");
return true;
}
@Override
public List<String> 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<String> 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<SvnRepoPojo> 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<SvnRepoPojo>() {
@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