Java工具类-JGit基本功能实现

一、简介

最近在写一个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);

    }
}

三、 运行结果

查看两个分支的差异情况
在这里插入图片描述
在这里插入图片描述

  • 10
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值