一、简介
最近在写一个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(blobId);
byte[] bytes = loader.getBytes();
walk.dispose();
return new String(bytes);
}
/**
* 分析分支树结构信息
* @param localRef 本地分支
* @return
* @throws IOException
*/
public AbstractTreeIterator prepareTreeParser(Ref localRef) throws IOException {
return prepareTreeParser(localRef.getObjectId().getName());
}
/**
* 通过CommitId获取分支树结构信息
* 此方法是为了兼容
* 被比较的分支(一般为master分支)的commitId已知的情况下,无需在获取Ref直接通过commitId获取分支树结构
* @param commitId
* @return
* @throws IOException
*/
public AbstractTreeIterator prepareTreeParser(String commitId) throws IOException {
RevWalk walk = new RevWalk(repository);
RevCommit revCommit = walk.parseCommit(repository.resolve(commitId));
RevTree tree = walk.parseTree(revCommit.getTree().getId());
CanonicalTreeParser treeParser = new CanonicalTreeParser();
ObjectReader reader = repository.newObjectReader();
treeParser.reset(reader, tree.getId());
walk.dispose();
return treeParser;
}
/**
* 切换分支
* @param branchName 分支名称
* @throws GitAPIException GitAPIException
*/
public void checkOut(String branchName) throws GitAPIException {
// 切换分支
git.checkout().setCreateBranch(false).setName(branchName).call();
}
/**
* 更新分支代码
* @param branchName 分支名称
* @throws GitAPIException GitAPIException
*/
public Ref checkOutAndPull(String branchName) throws GitAPIException {
// 1. 获取此分支的Ref信息
Ref branchRef = getBranchRef(branchName);
boolean isCreateBranch = branchRef == null;
// 2. 如果Ref不为null,则校验Ref是否为最新,最新直接返回,不为最新则重新拉取分支信息
if (!isCreateBranch && checkBranchNewVersion(branchRef)) {
return branchRef;
}
// 3. 切换分支
git.checkout().setCreateBranch(isCreateBranch).setName(branchName)
.setStartPoint( "origin/"+branchName)
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM)
.call();
// 4. 拉取最新代码
git.pull().setCredentialsProvider(usernamePasswordCredentialsProvider).call();
branchRef = getBranchRef(branchName);
return branchRef;
}
/**
* 切换到目标分支的版本中(暂时不用)
* @param versionCommit
* @throws IOException
* @throws GitAPIException
*/
public void checkoutFromVersionCommit(String versionCommit) throws IOException, GitAPIException {
RevWalk walk = new RevWalk(repository);
ObjectId versionId = repository.resolve(versionCommit);
RevCommit verCommit = walk.parseCommit(versionId);
git.checkout().setCreateBranch(false).setName(versionCommit).setStartPoint(verCommit).call();
git.branchDelete().setBranchNames(versionCommit);
Collection<Ref> remoteRefs = git.lsRemote().setHeads(true).setCredentialsProvider(usernamePasswordCredentialsProvider).call();
String ref = git.getRepository().getRefDatabase().getRefs().toString();
System.out.println(ref);
}
/**
* 判断本地分支是否是最新版本。目前不考虑分支在远程仓库不存在,本地存在
* 此方法有点耗时,可以考虑直接拉取最新版本
* @param localRef 本地分支
* @return boolean
* @throws GitAPIException GitAPIException
*/
private boolean checkBranchNewVersion(Ref localRef) throws GitAPIException {
String localRefName = localRef.getName(); // refs/heads/master 分支名
String localRefObjectId = localRef.getObjectId().getName(); //commit_id
// 获取远程所有分支
Collection<Ref> remoteRefs = git.lsRemote().setCredentialsProvider(usernamePasswordCredentialsProvider).setHeads(true).call();
for (Ref remoteRef : remoteRefs) {
String remoteRefName = remoteRef.getName();
String remoteRefObjectId = remoteRef.getObjectId().getName();
if (remoteRefName.equals(localRefName)) {
if (remoteRefObjectId.equals(localRefObjectId)) {
return true;
}
return false;
}
}
return false;
}
/**
* 获取当前分支的所有提交记录
* @return
* @throws IOException
* @throws GitAPIException
*/
public List<CommitMessage> getCommitMessages() throws IOException, GitAPIException {
List<CommitMessage> commitMessages = new ArrayList<>();
CommitMessage commitMessage = null;
Iterable<RevCommit> commits = git.log().all().call();
RevWalk walk = new RevWalk(repository);
int flag =0;
for(RevCommit commit:commits) {
commitMessage = new CommitMessage();
boolean foundInThisBranch = false;
RevCommit targetCommit = walk.parseCommit(commit.getId());
for(Map.Entry< String,Ref> e : repository.getAllRefs().entrySet()){
if(e.getKey().startsWith("refs/remotes/origin")) {
if(walk.isMergedInto(targetCommit,walk.parseCommit(e.getValue().getObjectId()))) {
String foundInBranch = e.getValue().getTarget().getName();
// foundInBranch = foundInBranch.replace("refs/heads","");
if(foundInBranch.contains(branchName)) {
// 等于2 说明是来自两个合并的分支 算的是merge的记录
if(targetCommit.getParents().length==2) {
flag++;
foundInThisBranch = true;
break;
}
}
}
}
}
if(foundInThisBranch) {
commitMessage.setCommitId(commit.getName());
commitMessage.setCommitIdent(commit.getAuthorIdent().getName());
commitMessage.setCommitMessage(commit.getFullMessage());
commitMessage.setCommitDate(new Date(commit.getCommitTime()*1000L).toString());
commitMessage.setLastCommitId(commit.getParent(0).getName());
commitMessage.setMergeBranchCommitId(commit.getParent(1).getName());
commitMessages.add(commitMessage);
}
if(flag==5) {
// 只取merge合并记录的前五条
break;
}
}
return commitMessages;
}
}
5. CodeDiff.java
真正对比git工程中差异文件的类
/**
* Demo class
*
* @author bigman
* @date 2021/3/1
*/
public class CodeDiff {
/**
* 分支和分支之间的比较
* @param newBranchName 当前分支取最新的commit版本
* @param oldBranchName 比较分支取最新的commit版本
* @return
*/
public static List<ClassInfo> diffBranchToBranch(GitAdapter gitAdapter, String newBranchName, String oldBranchName) {
return diffBranchToBranch(gitAdapter, newBranchName, oldBranchName,null);
}
/**
* 当前分支与某个分支的某个版本进行比较
* @param gitAdapter
* @param newBranchName 当前分支当前分支取最新的commit版本
* @param oldBranchName 比较分支
* @param commitId 比较分支的commit版本Id
* @return
*/
public static List<ClassInfo> diffBranchToBranch(GitAdapter gitAdapter, String newBranchName, String oldBranchName, String commitId) {
List<ClassInfo> classInfos = diffMethods(gitAdapter, newBranchName, oldBranchName,commitId);
return classInfos;
}
private static List<ClassInfo> diffMethods(GitAdapter gitAdapter,String newBranchName, String oldBranchName,String commitId) {
try {
// 获取Git
Git git = gitAdapter.initGit();
// 获取两个分支的最新代码信息
Ref newBranchRef = gitAdapter.checkOutAndPull(newBranchName);
Ref oldBranchRef = gitAdapter.checkOutAndPull(oldBranchName);
// 获取当前分支信息
AbstractTreeIterator newTreeParser = gitAdapter.prepareTreeParser(newBranchRef.getObjectId().getName());
// 获取比较分支的版本信息 如果commit为null,则默认取比较分支的最新版本信息
if(null == commitId && oldBranchRef!=null) {
commitId = oldBranchRef.getObjectId().getName();
}
AbstractTreeIterator oldTreeParser = gitAdapter.prepareTreeParser(commitId);
// 对比差异
List<DiffEntry> diffs = git.diff().setOldTree(oldTreeParser).setNewTree(newTreeParser).setShowNameAndStatusOnly(true).call();
ByteArrayOutputStream out = new ByteArrayOutputStream();
DiffFormatter df = new DiffFormatter(out);
// 设置比较器为忽略空白字符对比(Ignores all whitespace)
df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
df.setRepository(git.getRepository());
List<ClassInfo> allClassInfos = batchPrepareDiffMethod(gitAdapter, newBranchName, oldBranchName, df, diffs);
return allClassInfos;
} catch (Exception e) {
e.printStackTrace();
}
return new ArrayList<ClassInfo>();
}
/**
* 多线程执行对比
*
* @return
*/
private static List<ClassInfo> batchPrepareDiffMethod(final GitAdapter gitAdapter, final String branchName, final String oldBranchName, final DiffFormatter df, List<DiffEntry> diffs) {
int threadSize = 100;
int dataSize = diffs.size();
int threadNum = dataSize / threadSize + 1;
boolean special = dataSize % threadSize == 0;
ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
List<Callable<List<ClassInfo>>> tasks = new ArrayList<Callable<List<ClassInfo>>>();
Callable<List<ClassInfo>> task = null;
List<DiffEntry> cutList = null;
// 分解每条线程的数据
for (int i = 0; i < threadNum; i++) {
if (i == threadNum - 1) {
if (special) {
break;
}
cutList = diffs.subList(threadSize * i, dataSize);
} else {
cutList = diffs.subList(threadSize * i, threadSize * (i + 1));
}
final List<DiffEntry> diffEntryList = cutList;
task = new Callable<List<ClassInfo>>() {
@Override
public List<ClassInfo> call() throws Exception {
List<ClassInfo> allList = new ArrayList<ClassInfo>();
for (DiffEntry diffEntry : diffEntryList) {
ClassInfo classInfo = prepareDiffMethod(gitAdapter, branchName, oldBranchName, df, diffEntry);
if (classInfo != null) {
allList.add(classInfo);
}
}
return allList;
}
};
// 这里提交的任务容器列表和返回的Future列表存在顺序对应的关系
tasks.add(task);
}
List<ClassInfo> allClassInfoList = new ArrayList<ClassInfo>();
try {
List<Future<List<ClassInfo>>> results = executorService.invokeAll(tasks);
//结果汇总
for (Future<List<ClassInfo>> future : results) {
allClassInfoList.addAll(future.get());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
executorService.shutdown();
}
return allClassInfoList;
}
/**
* 单个差异文件对比
*
* @param gitAdapter
* @param branchName
* @param oldBranchName
* @param df
* @param diffEntry
* @return
*/
private synchronized static ClassInfo prepareDiffMethod(GitAdapter gitAdapter, String branchName, String oldBranchName, DiffFormatter df, DiffEntry diffEntry) {
List<MethodInfo> methodInfoList = new ArrayList<MethodInfo>();
try {
String newJavaPath = diffEntry.getNewPath();
// 排除测试类
if (newJavaPath.contains("/src/test/java/")) {
return null;
}
// 非java文件 和 删除类型不记录
if (!newJavaPath.endsWith(".java") || diffEntry.getChangeType() == DiffEntry.ChangeType.DELETE) {
return null;
}
String newClassContent = gitAdapter.getBranchSpecificFileContent(branchName, newJavaPath);
ASTGenerator newAstGenerator = new ASTGenerator(newClassContent);
/* 新增类型 */
if (diffEntry.getChangeType() == DiffEntry.ChangeType.ADD) {
return newAstGenerator.getClassInfo();
}
/* 修改类型 */
// 获取文件差异位置,从而统计差异的行数,如增加行数,减少行数
FileHeader fileHeader = df.toFileHeader(diffEntry);
List<int[]> addLines = new ArrayList<int[]>();
List<int[]> delLines = new ArrayList<int[]>();
EditList editList = fileHeader.toEditList();
for (Edit edit : editList) {
if (edit.getLengthA() > 0) {
delLines.add(new int[]{edit.getBeginA(), edit.getEndA()});
}
if (edit.getLengthB() > 0) {
addLines.add(new int[]{edit.getBeginB(), edit.getEndB()});
}
}
String oldJavaPath = diffEntry.getOldPath();
String oldClassContent = gitAdapter.getBranchSpecificFileContent(oldBranchName, oldJavaPath);
ASTGenerator oldAstGenerator = new ASTGenerator(oldClassContent);
MethodDeclaration[] newMethods = newAstGenerator.getMethods();
MethodDeclaration[] oldMethods = oldAstGenerator.getMethods();
Map<String, MethodDeclaration> methodsMap = new HashMap<String, MethodDeclaration>();
for (int i = 0; i < oldMethods.length; i++) {
methodsMap.put(oldMethods[i].getName().toString() + oldMethods[i].parameters().toString(), oldMethods[i]);
}
for (final MethodDeclaration method : newMethods) {
// 如果方法名是新增的,则直接将方法加入List
if (!ASTGenerator.isMethodExist(method, methodsMap)) {
MethodInfo methodInfo = newAstGenerator.getMethodInfo(method);
methodInfoList.add(methodInfo);
continue;
}
// 如果两个版本都有这个方法,则根据MD5判断方法是否一致
if (!ASTGenerator.isMethodTheSame(method, methodsMap.get(method.getName().toString() + method.parameters().toString()))) {
MethodInfo methodInfo = newAstGenerator.getMethodInfo(method);
methodInfoList.add(methodInfo);
}
}
return newAstGenerator.getClassInfo(methodInfoList, translate(addLines), translate(delLines));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将修改文件的行数信息 int[] 转化为list类型
* diff返回的int[] 表示修改的区间,修改区间是左开右闭( 】
* @param listInt
* @return
*/
public static List<Integer> translate(List<int[]> listInt) {
List<Integer> list = new ArrayList<>();
for(int[] ints:listInt) {
for(int i=ints[0];i<ints[1];i++) {
list.add(i+1);
}
}
return list;
}
}
6. ASTGenerator.java
此文件是将java转化为AST树,如果是react工程此类不可用,需要定制化修改
/**
* Demo class
*
* @author bigman
* @date 2021/3/1
*/
public class ASTGenerator {
private String javaText;
private CompilationUnit compilationUnit;
public ASTGenerator(String javaText) {
this.javaText = javaText;
this.initCompilationUnit();
}
/**
* 获取AST编译单元,首次加载很慢
*/
private void initCompilationUnit() {
// AST编译
final ASTParser astParser = ASTParser.newParser(8);
final Map<String, String> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
astParser.setCompilerOptions(options);
astParser.setKind(ASTParser.K_COMPILATION_UNIT);
astParser.setResolveBindings(true);
astParser.setBindingsRecovery(true);
astParser.setStatementsRecovery(true);
astParser.setSource(javaText.toCharArray());
compilationUnit = (CompilationUnit) astParser.createAST(null);
}
/**
* 获取java类包名
* @return
*/
public String getPackageName() {
if (compilationUnit == null) {
return "";
}
PackageDeclaration packageDeclaration = compilationUnit.getPackage();
if (packageDeclaration == null){
return "";
}
String packageName = packageDeclaration.getName().toString();
return packageName;
}
/**
* 获取普通类单元
* @return
*/
public TypeDeclaration getJavaClass() {
if (compilationUnit == null) {
return null;
}
TypeDeclaration typeDeclaration = null;
final List<?> types = compilationUnit.types();
for (final Object type : types) {
if (type instanceof TypeDeclaration) {
typeDeclaration = (TypeDeclaration) type;
break;
}
}
return typeDeclaration;
}
/**
* 获取java类中所有方法
* @return 类中所有方法
*/
public MethodDeclaration[] getMethods() {
TypeDeclaration typeDec = getJavaClass();
if (typeDec == null) {
return new MethodDeclaration[]{};
}
MethodDeclaration[] methodDec = typeDec.getMethods();
return methodDec;
}
/**
* 获取新增类中的所有方法信息
* @return
*/
public List<MethodInfo> getMethodInfoList() {
MethodDeclaration[] methodDeclarations = getMethods();
List<MethodInfo> methodInfoList = new ArrayList<MethodInfo>();
for (MethodDeclaration method: methodDeclarations) {
MethodInfo methodInfo = new MethodInfo();
setMethodInfo(methodInfo, method);
methodInfoList.add(methodInfo);
}
return methodInfoList;
}
/**
* 获取修改类型的类的信息以及其中的所有方法,排除接口类
* @param methodInfos
* @param addLines
* @param delLines
* @return
*/
public ClassInfo getClassInfo(List<MethodInfo> methodInfos, List<Integer> addLines, List<Integer> delLines) {
TypeDeclaration typeDec = getJavaClass();
if (typeDec == null || typeDec.isInterface()) {
return null;
}
ClassInfo classInfo = new ClassInfo();
classInfo.setClassFile(getJavaClass().getName().toString());
classInfo.setClassName(getJavaClass().getName().toString());
classInfo.setPackages(getPackageName());
classInfo.setMethodInfos(methodInfos);
classInfo.setAddLines(addLines);
classInfo.setDelLines(delLines);
classInfo.setType("REPLACE");
return classInfo;
}
/**
* 获取新增类型的类的信息以及其中的所有方法,排除接口类
* @return
*/
public ClassInfo getClassInfo() {
TypeDeclaration typeDec = getJavaClass();
if (typeDec == null || typeDec.isInterface()) {
return null;
}
MethodDeclaration[] methodDeclarations = getMethods();
ClassInfo classInfo = new ClassInfo();
classInfo.setClassName(getJavaClass().getName().toString());
classInfo.setPackages(getPackageName());
classInfo.setType("ADD");
List<MethodInfo> methodInfoList = new ArrayList<>();
for (MethodDeclaration method: methodDeclarations) {
MethodInfo methodInfo = new MethodInfo();
setMethodInfo(methodInfo, method);
methodInfoList.add(methodInfo);
}
classInfo.setMethodInfos(methodInfoList);
return classInfo;
}
/**
* 获取修改中的方法
* @param methodDeclaration
* @return
*/
public MethodInfo getMethodInfo(MethodDeclaration methodDeclaration) {
MethodInfo methodInfo = new MethodInfo();
setMethodInfo(methodInfo, methodDeclaration);
return methodInfo;
}
private void setMethodInfo(MethodInfo methodInfo,MethodDeclaration methodDeclaration) {
methodInfo.setMd5(MD5Encode(methodDeclaration.toString()));
methodInfo.setMethodName(methodDeclaration.getName().toString());
methodInfo.setParameters(methodDeclaration.parameters().toString());
}
/**
* 计算方法的MD5的值
* @param s
* @return
*/
public static String MD5Encode(String s) {
String MD5String = "";
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
MD5String = Base64.encodeBase64String(md5.digest(s.getBytes("utf-8")));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return MD5String;
}
/**
* 判断方法是否存在
* @param method 新分支的方法
* @param methodsMap master分支的方法
* @return
*/
public static boolean isMethodExist(final MethodDeclaration method, final Map<String, MethodDeclaration> methodsMap) {
// 方法名+参数一致才一致
if (!methodsMap.containsKey(method.getName().toString() + method.parameters().toString())) {
return false;
}
return true;
}
/**
* 判断方法是否一致
* @param method1
* @param method2
* @return
*/
public static boolean isMethodTheSame(final MethodDeclaration method1,final MethodDeclaration method2) {
if (MD5Encode(method1.toString()).equals(MD5Encode(method2.toString()))) {
return true;
}
return false;
}
}
7. TestDemo.java
这个是主函数,测试的时候用
/**
* Demo class
*
* @author bigman
* @date 2021/3/1
*/
public class TestDemo {
//远程库路径
public static String remotePath = "https://github.com/****/***.git";
//下载已有仓库到本地路径
public static String localPath = "/Users/bigman/software/local";
public static String branchName = "master";
public static String projectName = "***";
public static void main(String[] args) throws GitAPIException, IOException {
String filePath = TestDemo.localPath+"/"+TestDemo.projectName;
// 初始化adapter
GitAdapter gitAdapter = new GitAdapter(TestDemo.remotePath,filePath,TestDemo.branchName);
// 获取当前分支的所有commit提交记录
gitAdapter.initGit();
List<CommitMessage> commitMessages = gitAdapter.getCommitMessages();
int i=1;
for(CommitMessage commitMessage : commitMessages) {
System.out.println("===================================================================================================================================");
System.out.println(i+" "+commitMessage.toString());
i++;
}
// 对比master分支的commitId版本的代码差异
// String commitId = "ce52c68b8bed4b6f76d469e951bbb509b29e9662";
String commitId = "f15035ba055864429e99b2a763f1b2de3f612b9f";
String commitId = "7b29e6d30a9c16af5f67d2ed2e72de988504166d";
// List<ClassInfo> classInfos = CodeDiff.diffBranchToBranch(gitUtil,"f20210126","master",commitId);
// for(ClassInfo classInfo:classInfos)
// System.out.println(classInfo.toString());
// Ref branchRef = gitAdapter.checkOutAndPull("master","1cf7615d811a0e92d53cb672fcec245eaec4506e");
// GitAdapter gitAdapterA = new GitAdapter(TestMain.remotePath,filePath,"f20210126");
// Ref ref = gitAdapter.getBranchRef();
// gitAdapterA.getMergeMessage(ref);
}
}
三、 运行结果
查看两个分支的差异情况