基于SVNkit的项目代码贡献量统计
2017-02-14 15:11
281 查看
SVNkit
SVNKit (JavaSVN) 是一个纯 Java 的 SVN 客户端库,使用 SVNKit 无需安装任何 SVN 的客户端,支持各种操作系统。思路
checkout 仓库筛选需要统计的文件
遍历每个文件的修改版本
获取文件的修改版本和各版本的修改作者
比对各版本相对前一个版本的代码变化数据
修改成员的代码贡献的各个参数
实现
环境准备
LineNumber.java
一个用于记录某个成员的共享贡献指标的bean类/** * 各成员的代码贡献的各项指标<br/> * 当前统计:新增代码、和修改行数 */ class LineNumber{ // 作者 String name; // 新增行数 long add = 0l; // 修改行数 long modify = 0l; // 总数 long sum = 0l; /** * 构造器,必须传入该记录所属作者 * @param name 作者姓名 */ public LineNumber(String name) { this.name = name; } /** * 设置新增行数,并在总数加上新增行数 * @param add 新增行数 */ public void setAdd(long add) { this.add += add; this.sum += add; } /** * 设置修改行数,并在总数加上修改行数 * @param add 修改行数 */ public void setModify(long modify) { this.modify += modify; this.sum += modify; } // getter 和 toString public long getAdd() { return add; } public long getModify() { return modify; } public long getSum() { return sum; } @Override public String toString() { return "LineNumber{" + "name='" + name + '\'' + ", add=" + add + ", modify=" + modify + ", sum=" + sum + '}'; } }
SVNkitUtil.java
基于svnkit的统计功能核心实现,本初代码仅初始化该工具类的基础属性/** * Created by zhao.wu on 2017/2/13. * svn 代码统计工具 */ public class SvnKitUtils { static { // 开启SVN的 http和https的访问方式 DAVRepositoryFactory.setup(); } // svn各种客户端的管理工具,使用svnkit-hl接口进行开发 private SVNClientManager ourClientManager; // 仓库的统计开始版本,默认为1 private long defaultPreVersion = 1; /** * 初始化工具类,为工具类连接仓库设置认证信息 * @param username 连接用户名 * @param password 连接密码 */ public SvnKitUtils(String username, String password) { ISVNOptions options = SVNWCUtil.createDefaultOptions(true); ourClientManager =SVNClientManager.newInstance((DefaultSVNOptions) options, username, password); } // getter 和 setter public long getDefaultPreVersion() { return defaultPreVersion; } public void setDefaultPreVersion(long defaultPreVersion) { this.defaultPreVersion = defaultPreVersion; } // 其他实现,如checkout等 }
checkOut的实现
当前的统计功能设计一切均是基于本地工作目录中的代码进行操作,然后计算相关指标。所以检出功能为重中之重的核心功能,好在是svnkit的hl接口中提供了这个方法,可以直接使用。/** * 从仓库检出代码到工作目录下 * @param repositoryUrl 仓库地址 * @param workCopyPath 本地工作路径 * @return 当前最新版本号 */ public long checkOut(String repositoryUrl, String workCopyPath) throws SVNException { SVNURL svnurl = SVNURL.parseURIEncoded(repositoryUrl); File wcDir = new File(workCopyPath); wcDir.mkdirs(); SVNUpdateClient updateClient = ourClientManager.getUpdateClient(); updateClient.setIgnoreExternals(false); return updateClient.doCheckout(svnurl, wcDir, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, true); }
筛选需要统计的文件
通过上边的checkout方法可将目标仓库的最新代码检出到本地工作目录,我们可以在本地工作目录中获取需要统计的文件名和路径等信息。一般情况下,我们仅能统计某一些文本文件,其他的一些安装包等非文本文件是无法进行统计。所以,我们需要对其进行筛选。/** * 递归遍历文件夹下指定格式的文件,并返回 * @param rootFolder 遍历的文件根目录 * @param filters 指定的文件类型,如:new String[]{".java", ".html", ".css", ".js", ".jsp", ".properties"} * @return 目录下所有为指定类型的文件列表 */ private List<File> scanFolder(File rootFolder, String[] filters){ List<File> paths = new ArrayList<File>(); File[] childFiles = rootFolder.listFiles(); for (File childFile : childFiles) { if(childFile.isDirectory()){ paths.addAll(scanFolder(childFile, filters)); }else { for (String filter : filters) { if(childFile.getName().endsWith(filter)){ paths.add(childFile); } } } } return paths; }
获取某一个文件的修改版本
该方法为统计某一个文件贡献量的文件,循环调用该方法即可完成某个仓库的代码贡献量。该方法线程不安全,切记不可在多线程中调用统一实例的该方法。/** * 获取文件的所有更新版本,并遍历 * @param file 需要遍历的文件 * @param authors 用于保存项目作者和其指标的键值对 */ private void computeDiff(final File file, final Map<String, LineNumber> authors) throws SVNException, IOException { long preVersion = defaultPreVersion; final Map<Long, String> versions = new HashMap<Long, String>(); ourClientManager.getLogClient().doLog(new File[]{file}, SVNRevision.create(defaultPreVersion), SVNRevision.HEAD, true, true, 1000000l, new ISVNLogEntryHandler() { public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { Map<String, SVNPropertyValue> meta = logEntry.getRevisionProperties().asMap(); String value = meta.get("svn:author").getString(); if(value != null){ if(!authors.containsKey(value)){ authors.put(value, new LineNumber(value)); } } versions.put(logEntry.getRevision(), value); } }); // 遍历计算 compute(file, versions, authors); }
遍历各版本的的差异,计算每次修改的代码贡献量
/** * 遍历文件修改版本<br/> * 遍历修正版本,生成diff文件 * @param file 本次统计文件 * @param versions 文件的修订版本与修订作者的键值对 * @param authors 作者与作者贡献指标键值对 */ private void compute(File file, Map<Long, String> versions, Map<String, LineNumber> authors) throws IOException, SVNException { File out = new File("temp"); List<String> changeList = new ArrayList<String>(); long preVersion = defaultPreVersion; for (Map.Entry<Long, String> longStringEntry : versions.entrySet()) { ourClientManager.getDiffClient().doDiff(file,SVNRevision.HEAD, SVNRevision.create(preVersion),SVNRevision.create(longStringEntry.getKey()),SVNDepth.INFINITY,true, new FileOutputStream(out), changeList); computeFileLineNum(out, authors.get(longStringEntry.getValue())); preVersion = longStringEntry.getKey(); } }
计算某次更新记录的代码贡献量
该功能是与某个成员的贡献量指标息息相关的,决定具体统计哪些指标,当前方法仅可统计新增代码量和修改代码量。/** * 统计该修改版本的各项指标。<br/> * 使用BufferedReader遍历文件的每一行数据, * 然后判断该行代码属于指标属性, * 最后对该作者对应的指标积进行相关操作 * @param file 版本修改文件(svn的diff文件) * @param lineNumber 该作者的指标对象 */ private void computeFileLineNum(File file, LineNumber lineNumber) throws IOException { int addCount = -1;//需要减去首行‘++++’开始的一行 int removeCount = -1;//需要减去首行‘----’开始的一行 BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String line = null; // 遍历diff文件,计算指标 while((line = reader.readLine())!=null) { if(line.startsWith("+")){ addCount++; }else if(line.startsWith("-")) { removeCount ++; } } reader.close(); // 设置作者指标 lineNumber.setModify(removeCount); lineNumber.setAdd(addCount-removeCount); }
统计某一个仓库中代码贡献量
该方法是综合上述各个小功能点而集成的统计接口/** * 某一个仓库的代码贡献量的统计故居 * @param repository 仓库地址 * @param workCopyPath 本地工作路径 * @param filters 文件类型过滤字符串数组 * @return 该仓库的代码贡献量的统计结果 */ public Collection<LineNumber> computeCodeLineForEveryone(String repository, String workCopyPath, String[] filters) throws IOException, SVNException { // 仓库检出 checkOut(repository, workCopyPath); // 获取需要统计的文件 List<File> files = scanFolder(new File(workCopyPath), filters ); // 遍历文件,并计算文件各个更新版本中的代码共享量 Map<String, LineNumber> authors = new HashMap<String, LineNumber>(); for (File file : files) { System.out.println("compute:"+file.getAbsolutePath()); computeDiff(file, authors); } // 返回计算记过 return authors.values(); }
测试
写了个简单的main方法进行测试该功能public static void main(final String[] args) throws SVNException, IOException { // 初始化统计工具 SvnKitUtils test = new SvnKitUtils("test", "123456"); //test.setDefaultPreVersion(); //设置统计版本起点, 默认从第1版开始 // 仓库地址 String repositoryURL ="http://172.28.1.238:8080/svn/ProductCode/Java/Test"; // 本地工作目录 String workingCopyPath = "E:/temp1/"; // 需要统计的文件格式 String[] filters = new String[]{".java", ".html", ".css", ".js", ".jsp", ".properties"}; // 执行统计 Collection<LineNumber> lineNumbers = test.computeCodeLineForEveryone(repositoryURL, workingCopyPath, filters); // 输出所有作者的贡献指标 for (LineNumber lineNumber : lineNumbers) { System.out.println(lineNumber); } }
总结
经过半天的磕磕碰碰终于将这个小功能实现了,才发现自己的基础薄弱,知其然,不知其所以然。用svn很久了,却不清楚SVN各个操作的具体原理,导致自己不断的在试错,才让自己完成这个小功能花了如此多的时间。虽然该功能完成了,我知道这绝不是最有的解决方案。就拿我自己知道的就可以从单线程改为多线程,还有一些我不知道的svn内幕,使用low level接口解决效率应该会更高。相关文章推荐
- Shell脚本实现的基于SVN的代码提交量统计工具
- 使用svnkit对svn新增代码统计、代码查询
- Shell脚本实现的基于SVN的代码提交量统计工具
- 使用statsvn 统计代码行数等项目信息
- 基于svn diff结果的有效代码量统计
- 基于svn diff结果的有效代码量统计
- 用StatSVN统计svn项目中每人代码提交量
- 一个基于SVN 的代码提交量统计工具
- 用StatSVN统计svn项目中每人代码提交量
- 基于svn diff结果的有效代码量统计
- SvnHost开源社区,开源项目支持直接浏览代码了
- 基于.NET项目的代码书写规范要求书
- 今天统计了一下我们项目的代码
- 统计一个项目的有效代码
- svn触发检出项目代码
- 同一个项目, SVN和CVS之间代码同步更新[转载]
- 用动软代码生成器2.41将批量生成的代码部署到已有项目详细教程(基于抽象工厂模式的三层架构)
- SharpStreaming项目开发纪实:构建基于RTSP协议的服务器及客户端应用(三)——客户端的业务代码实现
- 推荐几个免费的SVN空间,免费项目托管,免费代码托管