AbyssalSwamp  ActivaUser
» Guest:  Register | Login | 会员列表

RSS subscription to this AbyssalSwamp  

Previous thread Next thread
     
Title: java 获取svn日志信息_SVNKit --记一次获取SVN提交信息的经历  
 
Phoenix001





UID 305208
Digest 0
Points 0
Posts
码币MB 0 Code
黄金 0 Catty
钻石 0 Pellet
Permissions 10
Register 2019-5-27
Status offline
java 获取svn日志信息_SVNKit --记一次获取SVN提交信息的经历

前言

在工作中,遇到一个需求,需要监控研发人员每天提交的代码行数和代码bug数,关于代码的bug可以使用源码质量扫描工具sonar解决,但是对于svn每天提交的代码记录,哪种方式获取,对以后的数据筛选、存储、展示更有利,值得深思,后来,查阅资料,了解到了SVNKit 这个工具。

介绍

SVNKit (JavaSVN) 是一个纯 Java 的 SVN 客户端库,使用 SVNKit 无需安装任何 SVN 的客户端,支持各种操作系统。 这不是一个开源的类库,但你可以免费使用。它是Jar包形式,很好的可以引入到你的java工程中。

引入

对于maven工程,在项目的pom中加入相关依赖:

org.tmatesoft.svnkit

svnkit

1.8.14

运行示例

deeb4cafbab2

统计代码总提交行数.png

deeb4cafbab2

每次的提交信息.png

获取固定时间段内的提交记录,例如文件路径

说明:SVNLogEntry 这个实体类,保存了获取到的所有svn提交日志信息,包含,提交的文件,版本号,提交注释,提交人等,可以对SVNLogEntry进行过滤处理,然后对数据进行存储,用于展示。完整源码参考如下:

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import org.tmatesoft.svn.core.ISVNLogEntryHandler;

import org.tmatesoft.svn.core.SVNException;

import org.tmatesoft.svn.core.SVNLogEntry;

import org.tmatesoft.svn.core.SVNURL;

import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;

import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;

import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;

import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;

import org.tmatesoft.svn.core.io.SVNRepository;

import org.tmatesoft.svn.core.io.SVNRepositoryFactory;

import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class ModerOption {undefined

//svn地址

private static String url = "http://xxxx:xxx/svn/project/anka/trunk/Anka";

private static SVNRepository repository = null;

public void filterCommitHistory() throws Exception {undefined

DAVRepositoryFactory.setup();

SVNRepositoryFactoryImpl.setup();

FSRepositoryFactory.setup();

try {undefined

repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));

}

catch (SVNException e) {undefined

e.printStackTrace();

}

// 身份验证

ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager("svn用户名","svn密码");

repository.setAuthenticationManager(authManager);

// 过滤条件

final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

final Date begin = format.parse("2019-04-13");

final Date end = format.parse("2019-05-14");

final String author = ""; //过滤提交人

long startRevision = 0;

long endRevision = -1;//表示最后一个版本

final List history = new ArrayList();

//String[] 为过滤的文件路径前缀,为空表示不进行过滤

repository.log(new String[]{""},

startRevision,

endRevision,

true,

true,

new ISVNLogEntryHandler() {undefined

@Override

public void handleLogEntry(SVNLogEntry svnlogentry)

throws SVNException {undefined

//依据提交时间进行过滤

if (svnlogentry.getDate().after(begin)

&& svnlogentry.getDate().before(end)) {undefined

// 依据提交人过滤

if (!"".equals(author)) {undefined

if (author.equals(svnlogentry.getAuthor())) {undefined

fillResult(svnlogentry);

}

} else {undefined

fillResult(svnlogentry);

}

}

}

public void fillResult(SVNLogEntry svnlogentry) {undefined

//getChangedPaths为提交的历史记录MAP key为文件名,value为文件详情

history.addAll(svnlogentry.getChangedPaths().keySet());

}

});

for (String path : history) {undefined

System.out.println(path);

}

}

public static void main(String[] args) throws Exception {undefined

ModerOption test = new ModerOption();

test.filterCommitHistory();

}

}

统计提交文件的代码行数

思路:由于提交的文件,都会跟随着提交的形式,例如:增加,修改,删除等。这里统计增加和修改的文件,通过svn diff功能统计增加和修改的代码行数,源码参考如下:

import java.io.BufferedReader;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.FileReader;

import java.io.IOException;

import java.io.OutputStream;

import java.nio.charset.Charset;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Date;

import java.util.List;

import java.util.Map;

import java.util.Random;

import java.util.Set;

import org.tmatesoft.svn.core.ISVNLogEntryHandler;

import org.tmatesoft.svn.core.SVNDirEntry;

import org.tmatesoft.svn.core.SVNException;

import org.tmatesoft.svn.core.SVNLogEntry;

import org.tmatesoft.svn.core.SVNLogEntryPath;

import org.tmatesoft.svn.core.SVNNodeKind;

import org.tmatesoft.svn.core.SVNProperties;

import org.tmatesoft.svn.core.SVNURL;

import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;

import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;

import org.tmatesoft.svn.core.io.SVNRepository;

import org.tmatesoft.svn.core.io.SVNRepositoryFactory;

import org.tmatesoft.svn.core.wc.SVNDiffClient;

import org.tmatesoft.svn.core.wc.SVNLogClient;

import org.tmatesoft.svn.core.wc.SVNRevision;

import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class SvnkitDemo {undefined

private String userName = "svn用户名";

private String password = "svn密码";

//svn地址

private String urlString = "http://xxx:xxx/svn/project/anka/trunk/Anka";

boolean readonly = true;

private String tempDir = System.getProperty("java.io.tmpdir");

private DefaultSVNOptions options = SVNWCUtil.createDefaultOptions( readonly );

private Random random = new Random();

private SVNRepository repos;

private ISVNAuthenticationManager authManager;

public SvnkitDemo() {undefined

try {undefined

init();

} catch (SVNException e) {undefined

// TODO Auto-generated catch block

e.printStackTrace();

}

}

public void init() throws SVNException{undefined

authManager = SVNWCUtil.createDefaultAuthenticationManager(new File(tempDir+"/auth"), userName, password.toCharArray());

options.setDiffCommand("-x -w");

repos = SVNRepositoryFactory.create(SVNURL

.parseURIEncoded(urlString));

repos.setAuthenticationManager(authManager);

System.out.println("init completed");

}

/**获取一段时间内,所有的commit记录

* @param st 开始时间

* @param et 结束时间

* @return

* @throws SVNException

*/

public SVNLogEntry[] getLogByTime(Date st, Date et) throws SVNException{undefined

long startRevision = repos.getDatedRevision(st);

long endRevision = repos.getDatedRevision(et);

@SuppressWarnings("unchecked")

Collection logEntries = repos.log(new String[]{""}, null,

startRevision, endRevision, true, true);

SVNLogEntry[] svnLogEntries = logEntries.toArray(new SVNLogEntry[0]);

return svnLogEntries;

}

/**获取版本比较日志,并存入临时文件

* @param startVersion

* @param endVersion

* @return

* @throws SVNException

* @throws IOException

*/

public File getChangeLog(long startVersion, long endVersion) throws SVNException, IOException{undefined

SVNDiffClient diffClient = new SVNDiffClient(authManager, options);

diffClient.setGitDiffFormat(true);

File tempLogFile = null;

OutputStream outputStream = null;

String svnDiffFile = null;

do {undefined

svnDiffFile = tempDir + "/svn_diff_file_"+startVersion+"_"+endVersion+"_"+random.nextInt(10000)+".txt";

tempLogFile = new File(svnDiffFile);

} while (tempLogFile != null && tempLogFile.exists());

try {undefined

tempLogFile.createNewFile();

outputStream = new FileOutputStream(svnDiffFile);

diffClient.doDiff(SVNURL.parseURIEncoded(urlString),

SVNRevision.create(startVersion),

SVNURL.parseURIEncoded(urlString),

SVNRevision.create(endVersion),

org.tmatesoft.svn.core.SVNDepth.UNKNOWN, true, outputStream);

} catch (Exception e) {undefined

e.printStackTrace();

}finally {undefined

if(outputStream!=null)

try {undefined

outputStream.close();

} catch (IOException e) {undefined

e.printStackTrace();

}

}

return tempLogFile;

}

/**分析变更的代码,统计代码增量

* @param file

* @return

* @throws Exception

*/

public int staticticsCodeAdd(File file) throws Exception{undefined

System.out.println("开始统计修改代码行数");

FileReader fileReader = new FileReader(file);

BufferedReader in = new BufferedReader(fileReader);

int sum = 0;

String line = null;

StringBuffer buffer = new StringBuffer(1024);

boolean start = false;

while((line=in.readLine()) != null){undefined

if(line.startsWith("Index:")){undefined

if(start){undefined

ChangeFile changeFile = parseChangeFile(buffer);

int oneSize = staticOneFileChange(changeFile);

System.out.println("filePath="+changeFile.getFilePath()+" changeType="+changeFile.getChangeType()+" addLines="+oneSize);

sum += oneSize;

buffer.setLength(0);

}

start = true;

}

buffer.append(line).append(' ');

}

if(buffer.length() > 0){undefined

ChangeFile changeFile = parseChangeFile(buffer);

int oneSize = staticOneFileChange(changeFile);

System.out.println("filePath="+changeFile.getFilePath()+" changeType="+changeFile.getChangeType()+" addLines="+oneSize);

sum += oneSize;

}

in.close();

fileReader.close();

boolean deleteFile = file.delete();

System.out.println("-----delete file-----"+deleteFile);

return sum;

}

/**统计单个文件的增加行数,(先通过过滤器,如文件后缀、文件路径等等),也可根据修改类型来统计等,这里只统计增加或者修改的文件

* @param changeFile

* @return

*/

public int staticOneFileChange(ChangeFile changeFile){undefined

char changeType = changeFile.getChangeType();

if(changeType == 'A'){undefined

return countAddLine(changeFile.getFileContent());

}else if(changeType == 'M'){undefined

return countAddLine(changeFile.getFileContent());

}

return 0;

}

/**解析单个文件变更日志

* @param str

* @return

*/

public ChangeFile parseChangeFile(StringBuffer str){undefined

int index = str.indexOf(" @@");

if(index > 0){undefined

String header = str.substring(0, index);

String[] headers = header.split(" ");

String filePath = headers[0].substring(7);

char changeType = 'U';

boolean oldExist = !headers[2].endsWith("(nonexistent)");

boolean newExist = !headers[3].endsWith("(nonexistent)");

if(oldExist && !newExist){undefined

changeType = 'D';

}else if(!oldExist && newExist){undefined

changeType = 'A';

}else if(oldExist && newExist){undefined

changeType = 'M';

}

int bodyIndex = str.indexOf("@@ ")+3;

String body = str.substring(bodyIndex);

ChangeFile changeFile = new ChangeFile(filePath, changeType, body);

return changeFile;

}else{undefined

String[] headers = str.toString().split(" ");

String filePath = headers[0].substring(7);

ChangeFile changeFile = new ChangeFile(filePath, 'U', null);

return changeFile;

}

}

/**通过比较日志,统计以+号开头的非空行

* @param content

* @return

*/

public int countAddLine(String content){undefined

int sum = 0;

if(content !=null){undefined

content = ' ' + content +' ';

char[] chars = content.toCharArray();

int len = chars.length;

//判断当前行是否以+号开头

boolean startPlus = false;

//判断当前行,是否为空行(忽略第一个字符为加号)

boolean notSpace = false;

for(int i=0;i

char ch = chars;

if(ch ==' '){undefined

//当当前行是+号开头,同时其它字符都不为空,则行数+1

if(startPlus && notSpace){undefined

sum++;

notSpace = false;

}

//为下一行做准备,判断下一行是否以+头

if(i < len-1 && chars[i+1] == '+'){undefined

startPlus = true;

//跳过下一个字符判断,因为已经判断了

i++;

}else{undefined

startPlus = false;

}

}else if(startPlus && ch > ' '){//如果当前行以+开头才进行非空行判断

notSpace = true;

}

}

}

return sum;

}

/**统计一段时间内代码增加量

* @param st

* @param et

* @return

* @throws Exception

*/

public int staticticsCodeAddByTime(Date st, Date et) throws Exception{undefined

int sum = 0;

SVNLogEntry[] logs = getLogByTime(st, et);

if(logs.length > 0){undefined

long lastVersion = logs[0].getRevision()-1;

for(SVNLogEntry log:logs){undefined

File logFile = getChangeLog(lastVersion, log.getRevision());

int addSize = staticticsCodeAdd(logFile);

sum+=addSize;

lastVersion = log.getRevision();

}

}

return sum;

}

/**获取某一版本有变动的文件路径

* @param version

* @return

* @throws SVNException

*/

static List result = new ArrayList<>();

public List getChangeFileList(long version) throws SVNException{undefined

SVNLogClient logClient = new SVNLogClient( authManager, options );

SVNURL url = SVNURL.parseURIEncoded(urlString);

String[] paths = { "." };

SVNRevision pegRevision = SVNRevision.create( version );

SVNRevision startRevision = SVNRevision.create( version );

SVNRevision endRevision = SVNRevision.create( version );

boolean stopOnCopy = false;

boolean discoverChangedPaths = true;

long limit = 9999l;

ISVNLogEntryHandler handler = new ISVNLogEntryHandler() {undefined

/**

* This method will process when doLog() is done

*/

@Override

public void handleLogEntry( SVNLogEntry logEntry ) throws SVNException {undefined

System.out.println( "Author: " + logEntry.getAuthor() );

System.out.println( "Date: " + logEntry.getDate() );

System.out.println( "Message: " + logEntry.getMessage() );

System.out.println( "Revision: " + logEntry.getRevision() );

System.out.println("-------------------------");

Map maps = logEntry.getChangedPaths();

Set> entries = maps.entrySet();

for(Map.Entry entry : entries){undefined

//System.out.println(entry.getKey());

SVNLogEntryPath entryPath = entry.getValue();

result.add(entryPath);

System.out.println(entryPath.getType()+" "+entryPath.getPath());

}

}

};

// Do log

try {undefined

logClient.doLog( url, paths, pegRevision, startRevision, endRevision, stopOnCopy, discoverChangedPaths, limit, handler );

}

catch ( SVNException e ) {undefined

System.out.println( "Error in doLog() " );

e.printStackTrace();

}

return result;

}

/**获取指定文件内容

* @param url svn地址

* @return

*/

public String checkoutFileToString(String url){//"", -1, null

try {undefined

SVNDirEntry entry = repos.getDir("", -1, false, null);

int size = (int)entry.getSize();

ByteArrayOutputStream outputStream = new ByteArrayOutputStream(size);

SVNProperties properties = new SVNProperties();

repos.getFile("", -1, properties, outputStream);

String doc = new String(outputStream.toByteArray(),Charset.forName("utf-8"));

return doc;

} catch (SVNException e) {undefined

e.printStackTrace();

}

return null;

}

/**列出指定SVN 地址目录下的子目录

* @param url

* @return

* @throws SVNException

*/

public List listFolder(String url){undefined

if(checkPath(url)==1){undefined

try {undefined

Collection list = repos.getDir("", -1, null, (List)null);

List dirs = new ArrayList(list.size());

dirs.addAll(list);

return dirs;

} catch (SVNException e) {undefined

e.printStackTrace();

}

}

return null;

}

/**检查路径是否存在

* @param url

* @return 1:存在 0:不存在 -1:出错

*/

public int checkPath(String url){undefined

SVNNodeKind nodeKind;

try {undefined

nodeKind = repos.checkPath("", -1);

boolean result = nodeKind == SVNNodeKind.NONE ? false : true;

if(result) return 1;

} catch (SVNException e) {undefined

e.printStackTrace();

return -1;

}

return 0;

}

public static void main(String[] args) throws ParseException {undefined

SvnkitDemo demo = new SvnkitDemo();

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

Date now = format.parse("2019-04-06");

Date twoDayAgo = format.parse("2019-05-14");

try {undefined

int sum = demo.staticticsCodeAddByTime(now, twoDayAgo);

System.out.println("sum="+sum);

demo.getChangeFileList(128597L);

demo.getChangeFileList(128599L);

demo.getChangeFileList(128621L);

} catch (Exception e) {undefined

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class ChangeFile {undefined

private String filePath;

private String fileType;

/**A表示增加文件,M表示修改文件,D表示删除文件,U表示末知

*

*/

private Character changeType;

private String fileContent;

public ChangeFile() {undefined

}

public ChangeFile(String filePath) {undefined

this.filePath = filePath;

this.fileType = getFileTypeFromPath(filePath);

}

public ChangeFile(String filePath, Character changeType, String fileContent) {undefined

this.filePath = filePath;

this.changeType = changeType;

this.fileContent = fileContent;

this.fileType = getFileTypeFromPath(filePath);

}

public String getFilePath() {undefined

return filePath;

}

public void setFilePath(String filePath) {undefined

this.filePath = filePath;

}

public String getFileType() {undefined

return fileType;

}

public void setFileType(String fileType) {undefined

this.fileType = fileType;

}

public Character getChangeType() {undefined

return changeType;

}

public void setChangeType(Character changeType) {undefined

this.changeType = changeType;

}

public String getFileContent() {undefined

return fileContent;

}

public void setFileContent(String fileContent) {undefined

this.fileContent = fileContent;

}

private static String getFileTypeFromPath(String path) {undefined

String FileType = "";

int idx = path.lastIndexOf(".");

if (idx > -1) {undefined

FileType = path.substring(idx + 1).trim().toLowerCase();

}

return FileType;

}

}

这次svn提交信息的获取经历,就记录这些,希望对有需要的朋友有所帮助。

分享,也是自我回顾的开始。
相关资源:java调用svn获取历史方法_java获取svn提交记录,java执行svn命令...
————————————————
版权声明:本文为CSDN博主「C大调a小调」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_29023079/article/details/114777509







笔记网址:https://blog.csdn.net/weixin_29023079/article/details/114777509

2022-3-12 12:24#1
View profile  Blog  Send a short message  Top
     


  Printable version | Recommend to a friend | Subscribe to topic | Favorite topic  


 


All times are GMT+8, and the current time is 2026-1-14 01:31 Clear informations ->sessions/cookies - Contact Us - CAFFZ - ZAKE