一、简介
最近在写一个web前端代码覆盖率的工程(react),主要是负责后台的管理功能。目前的一个需求就是获取增量代码的覆盖率;需要去获取代码提交前后的代码差异,将代码差异信息提供给前端同事进行处理计算此覆盖率; 通过使用GitLab的API和Java提供的JGit后发现,java提供的JGit效果更好,差异的信息也准确;
此工具类基于Jacoc二开代码,修改部分代码,实现自己需要的功能的一个工具类。也仅仅只获取了java工程的代码差异,react工程的代码差异与此原理差不多,删除掉几个不重要的文件即可。
主要实现以下功能
- 远程拉取分支信息
- 切换分支
- 获取当前分支的所有commit信息(merge的commit信息)
- 对比当前分支与master分支的最新代码差异
- 对比当前分支与master分支某个commit时刻的代码差异
- 校验本地分支是否是最新版本
注: 查阅此文档前可能需要先了解下Jgit的基本操作
二、工程代码
1. pom.xml pom依赖
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
<version>3.19.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>5.9.0.202009080501-r</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2. ClassInfo.java
保存差异类信息的实体类
@Data
@ToString
public class ClassInfo {
/**
* java文件
*/
private String classFile;
/**
* 类名
*/
private String className;
/**
* 包名
*/
private String packages;
/**
* 类中的方法
*/
private List<MethodInfo> methodInfos;
/**
* 新增的行数
*/
private List<Integer> addLines;
/**
* 删除的行数
*/
private List<Integer> delLines;
/**
* 修改类型
*/
private String type;
}
3. MethodInfo.java
保存差异文件中的差异方法信息
@Data
@ToString
public class MethodInfo {
/**
* 方法的md5
*/
public String md5;
/**
* 方法名
*/
public String methodName;
/**
* 方法参数
*/
public String parameters;
}
4. GitAdapter.java
Git工具操作类
public class GitAdapter {
private static final Logger logger = LoggerFactory.getLogger(GitAdapter.class);
private final static String REF_HEADS = "refs/heads/";
private final static String MASTER_BRANCH = "master";
// 远程仓库路径 用的就是.git
private String remotePath;
// 本地仓库路径 包含了项目工程名projectName
private String localPath;
private String localGitPath;
private Git git;
private Repository repository;
private Ref branchRef;
public String branchName;
// Git授权
private static UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider;
/**
* 构造函数:没有传分支信息则默认拉取master代码
* @param remotePath
* @param localPath
*/
public GitAdapter(String remotePath, String localPath) {
this(remotePath,localPath,MASTER_BRANCH);
}
public GitAdapter(String remotePath, String localPath, String branchName) {
this.remotePath = remotePath;
this.localPath = localPath;
this.branchName = branchName;
localGitPath = this.localPath+"/.git";
// 鉴权账户密码可用自己gitHub的账户密码,或者是设置token
this.usernamePasswordCredentialsProvider = new UsernamePasswordCredentialsProvider("account","password");
// 初始化git
// this.initGit();
// repository = new FileRepository(localGitPath);
}
/**
* 使用Git时需要先初始化git
* 默认初始化的时候会自动拉取 @branchName 的最新代码
* @return
*/
public Git initGit() {
File file = new File(localPath);
System.out.println("文件路径"+localPath);
// 如果文件存在 说明已经拉取过代码,则拉取最新代码
if(file.exists()) {
try {
git = Git.open(new File(localPath));
// 判断是否是最新代码 判断是否是最新代码好像耗时更久??!
boolean isLatest = checkBranchNewVersion(git.getRepository().exactRef(REF_HEADS+branchName));
if (isLatest==true) {
logger.info("the local version is latest, need not pull it");
} else {
// 拉取最新的提交
git.pull().setCredentialsProvider(usernamePasswordCredentialsProvider).call();
logger.info("pull success");
}
} catch (GitAPIException e) {
logger.info("pull failure");
e.printStackTrace();
} catch (IOException e) {
logger.info("pull failure");
e.printStackTrace();
}
}
// 文件不存在,说明未拉取过代码 则拉取最新代码
else {
try {
git = Git.cloneRepository()
.setCredentialsProvider(usernamePasswordCredentialsProvider)
.setURI(remotePath)
.setBranch(branchName)
.setDirectory(new File(localPath))
.call();
// 拉取最新的提交
git.pull().setCredentialsProvider(usernamePasswordCredentialsProvider).call();
logger.info("down success");
} catch (GitAPIException e) {
logger.error("远程仓库下载异常");
e.printStackTrace();
}
}
repository = git.getRepository();
return git;
}
/**
* 获取ref信息
* @return
* @throws IOException
*/
public Ref getBranchRef() throws IOException {
return getBranchRef(this.branchName);
}
/**
* 根据branch 获取ref信息
* @param branchName
* @return
*/
public Ref getBranchRef(String branchName) {
try {
return this.branchRef = git.getRepository().exactRef(REF_HEADS+branchName);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取指定分支的指定文件内容
* @param branchName 分支名称
* @param javaPath 文件路径
* @return java类
* @throws IOException
*/
public String getBranchSpecificFileContent(String branchName, String javaPath) throws IOException {
Ref branch = repository.exactRef( REF_HEADS + branchName);
ObjectId objId = branch.getObjectId();
RevWalk walk = new RevWalk(repository);
RevTree tree = walk.parseTree(objId);
return getFileContent(javaPath,tree,walk);
}
/**
* 获取指定分支指定的指定文件内容
* @param javaPath 件路径
* @param tree git RevTree
* @param walk git RevWalk
* @return java类
* @throws IOException
*/
private String getFileContent(String javaPath,RevTree tree,RevWalk walk) throws IOException {
TreeWalk treeWalk = TreeWalk.forPath(repository, javaPath, tree);
ObjectId blobId = treeWalk.getObjectId(0);
ObjectLoader loader = repository.open