java中使用svnkit实现文件的版本管理
一、引入svnKit依赖
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>1.9.3</version>
</dependency>
二、初始化仓库工厂类
本文参考来自于svnkit官网
在使用svn客户端进行各种操作时第一步需要初始化仓库工厂
/*
* Initializes the library to work with a repository via
* different protocols.
*/
private static void setupLibrary() {
/*
* For using over http:// and https://
*/
DAVRepositoryFactory.setup();
/*
* For using over svn:// and svn+xxx://
*/
SVNRepositoryFactoryImpl.setup();
/*
* For using over file:///
*/
FSRepositoryFactory.setup();
}
二、使用svnkit创建本地存储仓库
try {
String tgtPath = "d:/modelFiles/repos";
SVNURL tgtURL = SVNRepositoryFactory.createLocalRepository( new File( tgtPath ), true , false );
} catch ( SVNException e ) {
//handle exception
}
执行完这一段代码之后,我们可以在本地目录下发现以下目录结构
三、svn基本原子操作
private static SVNClientManager ourClientManager;
private static ISVNEventHandler myCommitEventHandler;
private static ISVNEventHandler myUpdateEventHandler;
private static ISVNEventHandler myWCEventHandler;
/*
* Puts directories and files under version control scheduling them for addition
* to a repository. They will be added in a next commit. Like 'svn add PATH'
* command. It's done by invoking
*
* SVNWCClient.doAdd(File path, boolean force,
* boolean mkdir, boolean climbUnversionedParents, boolean recursive)
*
* which takes the following parameters:
*
* path - an entry to be scheduled for addition;
*
* force - set to true to force an addition of an entry anyway;
*
* mkdir - if true doAdd(..) creates an empty directory at path and schedules
* it for addition, like 'svn mkdir PATH' command;
*
* climbUnversionedParents - if true and the parent of the entry to be scheduled
* for addition is not under version control, then doAdd(..) automatically schedules
* the parent for addition, too;
*
* recursive - if true and an entry is a directory then doAdd(..) recursively
* schedules all its inner dir entries for addition as well.
*/
// 将文件纳入版本控制
private static void addEntry(File wcPath) throws SVNException {
ourClientManager.getWCClient().doAdd(wcPath, false, false, false, true);
}
/*
* Updates a working copy (brings changes from the repository into the working copy).
* Like 'svn update PATH' command; It's done by invoking
*
* SVNUpdateClient.doUpdate(File file, SVNRevision revision, boolean recursive)
*
* which takes the following parameters:
*
* file - a working copy entry that is to be updated;
*
* revision - a revision to which a working copy is to be updated;
*
* recursive - if true and an entry is a directory then doUpdate(..) recursively
* updates the entire directory, otherwise - only child entries of the directory;
*/
// 将本地文件更新到指定版本,前提是本地有该文件的其他版本
private static long update(File wcPath,
SVNRevision updateToRevision, boolean isRecursive)
throws SVNException {
SVNUpdateClient updateClient = ourClientManager.getUpdateClient();
/*
* sets externals not to be ignored during the update
*/
updateClient.setIgnoreExternals(false);
/*
* returns the number of the revision wcPath was updated to
*/
return updateClient.doUpdate(wcPath, updateToRevision, isRecursive);
}
/*
* Checks out a working copy from a repository. Like 'svn checkout URL[@REV] PATH (-r..)'
* command; It's done by invoking
*
* SVNUpdateClient.doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision,
* SVNRevision revision, boolean recursive)
*
* which takes the following parameters:
*
* url - a repository location from where a working copy is to be checked out;
*
* dstPath - a local path where the working copy will be fetched into;
*
* pegRevision - an SVNRevision representing a revision to concretize
* url (what exactly URL a user means and is sure of being the URL he needs); in other
* words that is the revision in which the URL is first looked up;
*
* revision - a revision at which a working copy being checked out is to be;
*
* recursive - if true and url corresponds to a directory then doCheckout(..) recursively
* fetches out the entire directory, otherwise - only child entries of the directory;
*/
// svn检出操作,即从svn仓库拉取指定版本的代码
private static long checkout(SVNURL url,
SVNRevision revision, File destPath, boolean isRecursive)
throws SVNException {
SVNUpdateClient updateClient = ourClientManager.getUpdateClient();
/*
* sets externals not to be ignored during the checkout
*/
updateClient.setIgnoreExternals(false);
/*
* returns the number of the revision at which the working copy is
*/
return updateClient.doCheckout(url, destPath, revision, revision, isRecursive);
}
/*
* Committs changes in a working copy to a repository. Like
* 'svn commit PATH -m "some comment"' command. It's done by invoking
*
* SVNCommitClient.doCommit(File[] paths, boolean keepLocks, String commitMessage,
* boolean force, boolean recursive)
*
* which takes the following parameters:
*
* paths - working copy paths which changes are to be committed;
*
* keepLocks - if true then doCommit(..) won't unlock locked paths; otherwise they will
* be unlocked after a successful commit;
*
* commitMessage - a commit log message;
*
* force - if true then a non-recursive commit will be forced anyway;
*
* recursive - if true and a path corresponds to a directory then doCommit(..) recursively
* commits changes for the entire directory, otherwise - only for child entries of the
* directory;
*/
// svn提交操作,将本地文件提交到svn仓库
private static SVNCommitInfo commit(File wcPath, boolean keepLocks, String commitMessage)
throws SVNException {
/*
* Returns SVNCommitInfo containing information on the new revision committed
* (revision number, etc.)
*/
return ourClientManager.getCommitClient().doCommit(new File[] { wcPath }, keepLocks,
commitMessage, false, true);
}
/*
* Imports an unversioned directory into a repository location denoted by a
* destination URL (all necessary parent non-existent paths will be created
* automatically). This operation commits the repository to a new revision.
* Like 'svn import PATH URL (-N) -m "some comment"' command. It's done by
* invoking
*
* SVNCommitClient.doImport(File path, SVNURL dstURL, String commitMessage, boolean recursive)
*
* which takes the following parameters:
*
* path - a local unversioned directory or singal file that will be imported into a
* repository;
*
* dstURL - a repository location where the local unversioned directory/file will be
* imported into; this URL path may contain non-existent parent paths that will be
* created by the repository server;
*
* commitMessage - a commit log message since the new directory/file are immediately
* created in the repository;
*
* recursive - if true and path parameter corresponds to a directory then the directory
* will be added with all its child subdirictories, otherwise the operation will cover
* only the directory itself (only those files which are located in the directory).
*/
// 本地目录初始提交到svn仓库
private static SVNCommitInfo importDirectory(File localPath, SVNURL dstURL, String commitMessage, boolean isRecursive) throws SVNException{
/*
* Returns SVNCommitInfo containing information on the new revision committed
* (revision number, etc.)
*/
return ourClientManager.getCommitClient().doImport(localPath, dstURL, commitMessage, isRecursive);
}
/*
* Schedules directories and files for deletion from version control upon the next
* commit (locally). Like 'svn delete PATH' command. It's done by invoking
*
* SVNWCClient.doDelete(File path, boolean force, boolean dryRun)
*
* which takes the following parameters:
*
* path - an entry to be scheduled for deletion;
*
* force - a boolean flag which is set to true to force a deletion even if an entry
* has local modifications;
*
* dryRun - set to true not to delete an entry but to check if it can be deleted;
* if false - then it's a deletion itself.
*/
// svn删除操作
private static void delete(File wcPath, boolean force) throws SVNException {
ourClientManager.getWCClient().doDelete(wcPath, force, false);
}
// svn删除整个工作目录操作
public static SVNCommitInfo deleteDir(ISVNEditor editor , String dirPath ) throws SVNException {
editor.openRoot( -1 );
editor.deleteEntry( dirPath , -1 );
//Closes the root directory.
editor.closeDir( );
return editor.closeEdit( );
}
// 创建指定版本分支
private static SVNCommitInfo copy(SVNURL srcURL, SVNURL dstURL,SVNRevision revision,
boolean isMove, String commitMessage) throws SVNException {
/*
* SVNRevision.HEAD means the latest revision.
* Returns SVNCommitInfo containing information on the new revision committed
* (revision number, etc.)
*/
SVNCopySource[] sources = {new SVNCopySource(null, revision, srcURL)};
return ourClientManager.getCopyClient().doCopy(sources , dstURL, false, false, true, commitMessage, null);
}
/*
* Called recursively to obtain all entries that make up the repository tree
* repository - an SVNRepository which interface is used to carry out the
* request, in this case it's a request to get all entries in the directory
* located at the path parameter;
*
* path is a directory path relative to the repository location path (that
* is a part of the URL used to create an SVNRepository instance);
*
*/
// 打印指定版本下的文件列表信息
public static void listEntries(SVNRepository repository, String path, Long version)
throws SVNException {
/*
* Gets the contents of the directory specified by path at the latest
* revision (for this purpose -1 is used here as the revision number to
* mean HEAD-revision) getDir returns a Collection of SVNDirEntry
* elements. SVNDirEntry represents information about the directory
* entry. Here this information is used to get the entry name, the name
* of the person who last changed this entry, the number of the revision
* when it was last changed and the entry type to determine whether it's
* a directory or a file. If it's a directory listEntries steps into a
* next recursion to display the contents of this directory. The third
* parameter of getDir is null and means that a user is not interested
* in directory properties. The fourth one is null, too - the user
* doesn't provide its own Collection instance and uses the one returned
* by getDir.
*/
Collection entries = repository.getDir(path, version, null,
(Collection) null);
Iterator iterator = entries.iterator();
while (iterator.hasNext()) {
SVNDirEntry entry = (SVNDirEntry) iterator.next();
System.out.println("/" + (path.equals("") ? "" : path + "/")
+ entry.getName() + " (author: '" + entry.getAuthor()
+ "'; revision: " + entry.getRevision() + "; date: " + entry.getDate() + ")");
/*
* Checking up if the entry is a directory.
*/
if (entry.getKind() == SVNNodeKind.DIR) {
listEntries(repository, (path.equals("")) ? entry.getName()
: path + "/" + entry.getName());
}
}
}
四、通过原子方法实现简单svn相应操作
// svn 工作目录
private static String copyPath = "C:/modelFiles/copy/";
// svn 仓库目录
private static String reposPath= "C:/modelFiles/repos/";
// 临时文件存放目录
private static String tmpPath= "C:/modelFiles/tmp/";
// 初始提交本地copyPath下文件夹到svn仓库
public static long commitFile(String dirName) throws SVNException {
File sourcePath = new File(copyPath+dirName);
SVNURL targetURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName);
committedRevision = importDirectory(sourcePath, targetURL, "上传模型文件夹 "+dirName, true).getNewRevision();
return committedRevision;
}
// 检出指定版本到指定目录
// 其中 SVNRevision 为版本对象,可以通过SVNRevision.create(1) 形式创建版本id为1的版本对象
// svn版本从1开始往后迭代,为长整型,0表示最新版本用 SVNRevision.HEAD表示,其等同于 SVNRevision.create(0)
public static void checkOut(String dirName,String basePath,SVNRevision svnRevision) throws SVNException {
SVNURL repository = SVNURL.parseURIEncoded("file:///"+reposPath);
File wcDir = new File(basePath+"/"+dirName);
checkout(repository.appendPath(dirName,false), svnRevision, wcDir, true);
}
// svn工作目录下新增文件夹或文件后提交至svn仓库
public static long commitNewFile(String dirName,String newDir) throws SVNException {
// svn工作区目录
File wcDir = new File(copyPath+dirName);
// svn工作目录下新增的文件夹
File NewDir = new File(copyPath+newDir);
// 为了防止当前不是最新版本先从svn仓库更新下最新代码,其实也可以不进行这一步操作
update(wcDir, SVNRevision.HEAD, true);
// 将当前新增的目录加入版本控制
addEntry(NewDir);
// 提交至svn仓库
long newRevision = commit(wcDir, false,
"在 " + dirName + " 目录下新增 " + newDir)
.getNewRevision();
log.info("在 "+dirName+" 目录下新增 "+newDir);
return newRevision;
}
// 删除svn工作目录下某个文件夹并提交svn仓库
public static long deleteFile(String dirName,String delDir) throws SVNException {
File wcDir = new File(copyPath+dirName);
File DelDir = new File(copyPath+delDir);
delete(DelDir, false);
long newRevision = commit(wcDir, false,
"从 " + dirName + " 目录下删除 " + delDir)
.getNewRevision();
log.info("从 "+dirName+" 目录下删除 "+delDir);
//删除后更新下
update(wcDir, SVNRevision.HEAD, true);
return newRevision;
}
// svn删除整个工作目录操作
public static void deleteFileFromRepos(String dirName){
try {
SVNRepository svnRepository = FSRepositoryFactory.create(SVNURL.parseURIEncoded("file:///" + reposPath));
ISVNEditor editor = svnRepository.getCommitEditor( "删除目录 "+dirName , null );
try {
deleteDir(editor, dirName);
} catch ( SVNException svne ) {
editor.abortEdit( );
throw svne;
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @description 创建指定版本的分支
*/
public static Long createBranchByCopy(String dirName,Long revisionNum) {
try {
SVNURL srcURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName);
SVNURL dstURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName+"-分支");
SVNCommitInfo copyInfo = copy(
srcURL,
dstURL,
SVNRevision.create(revisionNum),
true,
" 创建分支:" + dirName+"-分支"
);
checkOut(getBranchName(dirName+"-分支",copyPath);
return copyInfo.getNewRevision();
} catch (SVNException e) {
log.info("创建分支失败:"+e.getMessage());
throw new RuntimeException(e);
}
}
// 获取当前模型仓库的最新版本编号
public static long getLatestRevision() throws SVNException {
SVNRepository svnRepository = FSRepositoryFactory.create(SVNURL.parseURIEncoded("file:///" + reposPath));
return svnRepository.getLatestRevision();
}