使用svnkit对svn新增代码统计、代码查询

java 专栏收录该内容
16 篇文章 0 订阅

引入包:svnkit

https://mvnrepository.com/artifact/org.tmatesoft.svnkit/svnkit

<!-- https://mvnrepository.com/artifact/org.tmatesoft.svnkit/svnkit -->
<dependency>
    <groupId>org.tmatesoft.svnkit</groupId>
    <artifactId>svnkit</artifactId>
    <version>1.8.14</version>
</dependency>

代码demo

  1. package com.yy.svn;
  2. import java.io.BufferedReader;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.FileOutputStream;
  6. import java.io.FileReader;
  7. import java.io.IOException;
  8. import java.io.OutputStream;
  9. import java.nio.charset.Charset;
  10. import java.util.ArrayList;
  11. import java.util.Collection;
  12. import java.util.Date;
  13. import java.util.List;
  14. import java.util.Map;
  15. import java.util.Random;
  16. import java.util.Set;
  17. import org.tmatesoft.svn.core.ISVNLogEntryHandler;
  18. import org.tmatesoft.svn.core.SVNDirEntry;
  19. import org.tmatesoft.svn.core.SVNException;
  20. import org.tmatesoft.svn.core.SVNLogEntry;
  21. import org.tmatesoft.svn.core.SVNLogEntryPath;
  22. import org.tmatesoft.svn.core.SVNNodeKind;
  23. import org.tmatesoft.svn.core.SVNProperties;
  24. import org.tmatesoft.svn.core.SVNURL;
  25. import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
  26. import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
  27. import org.tmatesoft.svn.core.io.SVNRepository;
  28. import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
  29. import org.tmatesoft.svn.core.wc.SVNDiffClient;
  30. import org.tmatesoft.svn.core.wc.SVNLogClient;
  31. import org.tmatesoft.svn.core.wc.SVNRevision;
  32. import org.tmatesoft.svn.core.wc.SVNWCUtil;
  33. public class SvnkitDemo {
  34. private String userName = "user";
  35. private String password = "password";
  36. private String urlString = "svn url";
  37. boolean readonly = true;
  38. private String tempDir = System.getProperty("java.io.tmpdir");
  39. private DefaultSVNOptions options = SVNWCUtil.createDefaultOptions( readonly );
  40. private Random random = new Random();
  41. private SVNRepository repos;
  42. private ISVNAuthenticationManager authManager;
  43. public SvnkitDemo() {
  44. try {
  45. init();
  46. } catch (SVNException e) {
  47. // TODO Auto-generated catch block
  48. e.printStackTrace();
  49. }
  50. }
  51. public void init() throws SVNException{
  52. ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(new File(tempDir+"/auth"), userName, password.toCharArray());
  53. options.setDiffCommand("-x -w");
  54. repos = SVNRepositoryFactory.create(SVNURL
  55. .parseURIEncoded(urlString));
  56. repos.setAuthenticationManager(authManager);
  57. System.out.println("init completed");
  58. }
  59. /**获取一段时间内,所有的commit记录
  60. * @param st 开始时间
  61. * @param et 结束时间
  62. * @return
  63. * @throws SVNException
  64. */
  65. public SVNLogEntry[] getLogByTime(Date st, Date et) throws SVNException{
  66. long startRevision = repos.getDatedRevision(st);
  67. long endRevision = repos.getDatedRevision(et);
  68. @SuppressWarnings("unchecked")
  69. Collection<SVNLogEntry> logEntries = repos.log(new String[]{""}, null,
  70. startRevision, endRevision, true, true);
  71. SVNLogEntry[] svnLogEntries = logEntries.toArray(new SVNLogEntry[0]);
  72. return svnLogEntries;
  73. }
  74. /**获取版本比较日志,并存入临时文件
  75. * @param startVersion
  76. * @param endVersion
  77. * @return
  78. * @throws SVNException
  79. * @throws IOException
  80. */
  81. public File getChangeLog(long startVersion, long endVersion) throws SVNException, IOException{
  82. SVNDiffClient diffClient = new SVNDiffClient(authManager, options);
  83. diffClient.setGitDiffFormat(true);
  84. File tempLogFile = null;
  85. OutputStream outputStream = null;
  86. String svnDiffFile = null;
  87. do {
  88. svnDiffFile = tempDir + "/svn_diff_file_"+startVersion+"_"+endVersion+"_"+random.nextInt(10000)+".txt";
  89. tempLogFile = new File(svnDiffFile);
  90. } while (tempLogFile != null && tempLogFile.exists());
  91. try {
  92. tempLogFile.createNewFile();
  93. outputStream = new FileOutputStream(svnDiffFile);
  94. diffClient.doDiff(SVNURL.parseURIEncoded(urlString),
  95. SVNRevision.create(startVersion),
  96. SVNURL.parseURIEncoded(urlString),
  97. SVNRevision.create(endVersion),
  98. org.tmatesoft.svn.core.SVNDepth.UNKNOWN, true, outputStream);
  99. } catch (Exception e) {
  100. e.printStackTrace();
  101. }finally {
  102. if(outputStream!=null)
  103. try {
  104. outputStream.close();
  105. } catch (IOException e) {
  106. e.printStackTrace();
  107. }
  108. }
  109. return tempLogFile;
  110. }
  111. /**分析变更的代码,统计代码增量
  112. * @param file
  113. * @return
  114. * @throws Exception
  115. */
  116. public int staticticsCodeAdd(File file) throws Exception{
  117. System.out.println("开始统计修改代码行数");
  118. FileReader fileReader = new FileReader(file);
  119. BufferedReader in = new BufferedReader(fileReader);
  120. int sum = 0;
  121. String line = null;
  122. StringBuffer buffer = new StringBuffer(1024);
  123. boolean start = false;
  124. while((line=in.readLine()) != null){
  125. if(line.startsWith("Index:")){
  126. if(start){
  127. ChangeFile changeFile = parseChangeFile(buffer);
  128. int oneSize = staticOneFileChange(changeFile);
  129. System.out.println("filePath="+changeFile.getFilePath()+" changeType="+changeFile.getChangeType()+" addLines="+oneSize);
  130. sum += oneSize;
  131. buffer.setLength(0);
  132. }
  133. start = true;
  134. }
  135. buffer.append(line).append('\n');
  136. }
  137. if(buffer.length() > 0){
  138. ChangeFile changeFile = parseChangeFile(buffer);
  139. int oneSize = staticOneFileChange(changeFile);
  140. System.out.println("filePath="+changeFile.getFilePath()+" changeType="+changeFile.getChangeType()+" addLines="+oneSize);
  141. sum += oneSize;
  142. }
  143. in.close();
  144. fileReader.close();
  145. boolean deleteFile = file.delete();
  146. System.out.println("-----delete file-----"+deleteFile);
  147. return sum;
  148. }
  149. /**统计单个文件的增加行数,(先通过过滤器,如文件后缀、文件路径等等),也可根据修改类型来统计等,这里只统计增加或者修改的文件
  150. * @param changeFile
  151. * @return
  152. */
  153. public int staticOneFileChange(ChangeFile changeFile){
  154. char changeType = changeFile.getChangeType();
  155. if(changeType == 'A'){
  156. return countAddLine(changeFile.getFileContent());
  157. }else if(changeType == 'M'){
  158. return countAddLine(changeFile.getFileContent());
  159. }
  160. return 0;
  161. }
  162. /**解析单个文件变更日志
  163. * @param str
  164. * @return
  165. */
  166. public ChangeFile parseChangeFile(StringBuffer str){
  167. int index = str.indexOf("\n@@");
  168. if(index > 0){
  169. String header = str.substring(0, index);
  170. String[] headers = header.split("\n");
  171. String filePath = headers[0].substring(7);
  172. char changeType = 'U';
  173. boolean oldExist = !headers[2].endsWith("(nonexistent)");
  174. boolean newExist = !headers[3].endsWith("(nonexistent)");
  175. if(oldExist && !newExist){
  176. changeType = 'D';
  177. }else if(!oldExist && newExist){
  178. changeType = 'A';
  179. }else if(oldExist && newExist){
  180. changeType = 'M';
  181. }
  182. int bodyIndex = str.indexOf("@@\n")+3;
  183. String body = str.substring(bodyIndex);
  184. ChangeFile changeFile = new ChangeFile(filePath, changeType, body);
  185. return changeFile;
  186. }else{
  187. String[] headers = str.toString().split("\n");
  188. String filePath = headers[0].substring(7);
  189. ChangeFile changeFile = new ChangeFile(filePath, 'U', null);
  190. return changeFile;
  191. }
  192. }
  193. /**通过比较日志,统计以+号开头的非空行
  194. * @param content
  195. * @return
  196. */
  197. public int countAddLine(String content){
  198. int sum = 0;
  199. if(content !=null){
  200. content = '\n' + content +'\n';
  201. char[] chars = content.toCharArray();
  202. int len = chars.length;
  203. //判断当前行是否以+号开头
  204. boolean startPlus = false;
  205. //判断当前行,是否为空行(忽略第一个字符为加号)
  206. boolean notSpace = false;
  207. for(int i=0;i<len;i++){
  208. char ch = chars[i];
  209. if(ch =='\n'){
  210. //当当前行是+号开头,同时其它字符都不为空,则行数+1
  211. if(startPlus && notSpace){
  212. sum++;
  213. notSpace = false;
  214. }
  215. //为下一行做准备,判断下一行是否以+头
  216. if(i < len-1 && chars[i+1] == '+'){
  217. startPlus = true;
  218. //跳过下一个字符判断,因为已经判断了
  219. i++;
  220. }else{
  221. startPlus = false;
  222. }
  223. }else if(startPlus && ch > ' '){//如果当前行以+开头才进行非空行判断
  224. notSpace = true;
  225. }
  226. }
  227. }
  228. return sum;
  229. }
  230. /**统计一段时间内代码增加量
  231. * @param st
  232. * @param et
  233. * @return
  234. * @throws Exception
  235. */
  236. public int staticticsCodeAddByTime(Date st, Date et) throws Exception{
  237. int sum = 0;
  238. SVNLogEntry[] logs = getLogByTime(st, et);
  239. if(logs.length > 0){
  240. long lastVersion = logs[0].getRevision()-1;
  241. for(SVNLogEntry log:logs){
  242. File logFile = getChangeLog(lastVersion, log.getRevision());
  243. int addSize = staticticsCodeAdd(logFile);
  244. sum+=addSize;
  245. lastVersion = log.getRevision();
  246. }
  247. }
  248. return sum;
  249. }
  250. /**获取某一版本有变动的文件路径
  251. * @param version
  252. * @return
  253. * @throws SVNException
  254. */
  255. public List<SVNLogEntryPath> getChangeFileList(long version) throws SVNException{
  256. List<SVNLogEntryPath> result = new ArrayList<>();
  257. SVNLogClient logClient = new SVNLogClient( authManager, options );
  258. SVNURL url = SVNURL.parseURIEncoded(urlString);
  259. String[] paths = { "." };
  260. SVNRevision pegRevision = SVNRevision.create( version );
  261. SVNRevision startRevision = SVNRevision.create( version );
  262. SVNRevision endRevision = SVNRevision.create( version );
  263. boolean stopOnCopy = false;
  264. boolean discoverChangedPaths = true;
  265. long limit = 9999l;
  266. ISVNLogEntryHandler handler = new ISVNLogEntryHandler() {
  267. /**
  268. * This method will process when doLog() is done
  269. */
  270. @Override
  271. public void handleLogEntry( SVNLogEntry logEntry ) throws SVNException {
  272. System.out.println( "Author: " + logEntry.getAuthor() );
  273. System.out.println( "Date: " + logEntry.getDate() );
  274. System.out.println( "Message: " + logEntry.getMessage() );
  275. System.out.println( "Revision: " + logEntry.getRevision() );
  276. System.out.println("-------------------------");
  277. Map<String, SVNLogEntryPath> maps = logEntry.getChangedPaths();
  278. Set<Map.Entry<String, SVNLogEntryPath>> entries = maps.entrySet();
  279. for(Map.Entry<String, SVNLogEntryPath> entry : entries){
  280. //System.out.println(entry.getKey());
  281. SVNLogEntryPath entryPath = entry.getValue();
  282. result.add(entryPath);
  283. System.out.println(entryPath.getType()+" "+entryPath.getPath());
  284. }
  285. }
  286. };
  287. // Do log
  288. try {
  289. logClient.doLog( url, paths, pegRevision, startRevision, endRevision, stopOnCopy, discoverChangedPaths, limit, handler );
  290. }
  291. catch ( SVNException e ) {
  292. System.out.println( "Error in doLog() " );
  293. e.printStackTrace();
  294. }
  295. return result;
  296. }
  297. /**获取指定文件内容
  298. * @param url svn地址
  299. * @return
  300. */
  301. public String checkoutFileToString(String url){//"", -1, null
  302. try {
  303. SVNDirEntry entry = repos.getDir("", -1, false, null);
  304. int size = (int)entry.getSize();
  305. ByteArrayOutputStream outputStream = new ByteArrayOutputStream(size);
  306. SVNProperties properties = new SVNProperties();
  307. repos.getFile("", -1, properties, outputStream);
  308. String doc = new String(outputStream.toByteArray(),Charset.forName("utf-8"));
  309. return doc;
  310. } catch (SVNException e) {
  311. e.printStackTrace();
  312. }
  313. return null;
  314. }
  315. /**列出指定SVN 地址目录下的子目录
  316. * @param url
  317. * @return
  318. * @throws SVNException
  319. */
  320. public List<SVNDirEntry> listFolder(String url){
  321. if(checkPath(url)==1){
  322. try {
  323. Collection<SVNDirEntry> list = repos.getDir("", -1, null, (List<SVNDirEntry>)null);
  324. List<SVNDirEntry> dirs = new ArrayList<SVNDirEntry>(list.size());
  325. dirs.addAll(list);
  326. return dirs;
  327. } catch (SVNException e) {
  328. e.printStackTrace();
  329. }
  330. }
  331. return null;
  332. }
  333. /**检查路径是否存在
  334. * @param url
  335. * @return 1:存在 0:不存在 -1:出错
  336. */
  337. public int checkPath(String url){
  338. SVNNodeKind nodeKind;
  339. try {
  340. nodeKind = repos.checkPath("", -1);
  341. boolean result = nodeKind == SVNNodeKind.NONE ? false : true;
  342. if(result) return 1;
  343. } catch (SVNException e) {
  344. e.printStackTrace();
  345. return -1;
  346. }
  347. return 0;
  348. }
  349. public static void main(String[] args) {
  350. SvnkitDemo demo = new SvnkitDemo();
  351. Date now = new Date();
  352. Date twoDayAgo = new Date(now.getTime()-2*24*3600000);
  353. try {
  354. // int sum = demo.staticticsCodeAddByTime(now, twoDayAgo);
  355. // System.out.println("sum="+sum);
  356. demo.getChangeFileList(487837L);
  357. } catch (Exception e) {
  358. // TODO Auto-generated catch block
  359. e.printStackTrace();
  360. }
  361. }
  362. }
  363. class ChangeFile {
  364. private String filePath;
  365. private String fileType;
  366. /**A表示增加文件,M表示修改文件,D表示删除文件,U表示末知
  367. *
  368. */
  369. private Character changeType;
  370. private String fileContent;
  371. public ChangeFile() {
  372. }
  373. public ChangeFile(String filePath) {
  374. this.filePath = filePath;
  375. this.fileType = getFileTypeFromPath(filePath);
  376. }
  377. public ChangeFile(String filePath, Character changeType, String fileContent) {
  378. this.filePath = filePath;
  379. this.changeType = changeType;
  380. this.fileContent = fileContent;
  381. this.fileType = getFileTypeFromPath(filePath);
  382. }
  383. public String getFilePath() {
  384. return filePath;
  385. }
  386. public void setFilePath(String filePath) {
  387. this.filePath = filePath;
  388. }
  389. public String getFileType() {
  390. return fileType;
  391. }
  392. public void setFileType(String fileType) {
  393. this.fileType = fileType;
  394. }
  395. public Character getChangeType() {
  396. return changeType;
  397. }
  398. public void setChangeType(Character changeType) {
  399. this.changeType = changeType;
  400. }
  401. public String getFileContent() {
  402. return fileContent;
  403. }
  404. public void setFileContent(String fileContent) {
  405. this.fileContent = fileContent;
  406. }
  407. private static String getFileTypeFromPath(String path) {
  408. String FileType = "";
  409. int idx = path.lastIndexOf(".");
  410. if (idx > -1) {
  411. FileType = path.substring(idx + 1).trim().toLowerCase();
  412. }
  413. return FileType;
  414. }
  415. }