当前位置: 首页 > 工具软件 > JGit > 使用案例 >

利用java操作JGit教程

刘安志
2023-12-01

背景

由于公司会统计每个人每个月提交的代码行数,提交代码次数等信息,本人基于此情况,开发了一个自动化统计小组内代码提交信息的code;自己之前也在网上找了一些相关信息,绝大多数都是采用linux命令统计的方式,我自己试了一下,发现不是特别准,并且信息也不完整;所以自己采用java的JGit框架开发了完整的代码,以下是具体的开发流程;

开发流程

1.建表

CREATE TABLE `git_commit_info2` (
  `data_dt` date DEFAULT NULL COMMENT '跑数日期',
  `project_name` varchar(255) DEFAULT '' COMMENT '项目名称',
  `branch_name` varchar(50) DEFAULT NULL COMMENT '分支名称',
  `commit_id` varchar(100) NOT NULL COMMENT '提交的id',
  `commit_name` varchar(50) DEFAULT NULL COMMENT '提交人',
  `commit_email` varchar(100) DEFAULT NULL COMMENT '提交人的email',
  `commit_comment` varchar(255) DEFAULT NULL COMMENT '提交备注',
  `add_lines` int(11) DEFAULT '0' COMMENT '相对于上次提交新增行数',
  `sub_lines` int(11) DEFAULT '0' COMMENT '相对于上次提交减少行数',
  `commit_time` timestamp NULL DEFAULT NULL COMMENT '提交时间',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '写入时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`commit_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='git提交参数信息';

2.代码开发
在代码开发之前,需要提供一些公用的class;
CommitLogInfo类用于记录单次commit的详细信息
CommitLines用于记录单次提交代码新增的行数和删除的行数;

public class CommitLogInfo {

    // 项目名称
    public String projectName;

    // 分支
    public String branchName;

    // 提交的commit id
    public String commitID;

    // 提交的次数
    public int commitNum;

    //提交人
    public String commitUserName;

    //提交的邮箱
    public String commitEmail;

    //提交的备注
    public String commitComment;

    //提交时间
    public Timestamp commitTime;

    // 新增行数
    public int addLines;

    // 删除行数
    public int subLines;

    public CommitLogInfo() {
    }

    public CommitLogInfo(String commitUserName, String commitEmail, String commitComment, Timestamp commitTime) {
        this.commitUserName = commitUserName;
        this.commitEmail = commitEmail;
        this.commitComment = commitComment;
        this.commitTime = commitTime;
    }

    public int getAddLines() {
        return addLines;
    }

    public void setAddLines(int addLines) {
        this.addLines = addLines;
    }

    public int getSubLines() {
        return subLines;
    }

    public void setSubLines(int subLines) {
        this.subLines = subLines;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    public int getCommitNum() {
        return commitNum;
    }

    public void setCommitNum(int commitNum) {
        this.commitNum = commitNum;
    }

    public String getCommitID() {
        return commitID;
    }

    public void setCommitID(String commitID) {
        this.commitID = commitID;
    }


    @Override
    public String toString() {
        return "CommitLogInfo{" +
                "commitUserName='" + commitUserName + '\'' +
                ", commitEmail='" + commitEmail + '\'' +
                ", commitComment='" + commitComment + '\'' +
                ", commitTime=" + commitTime +
                '}';
    }

    public String getCommitUserName() {
        return commitUserName;
    }

    public void setCommitUserName(String commitUserName) {
        this.commitUserName = commitUserName;
    }

    public String getCommitEmail() {
        return commitEmail;
    }

    public void setCommitEmail(String commitEmail) {
        this.commitEmail = commitEmail;
    }

    public String getCommitComment() {
        return commitComment;
    }

    public void setCommitComment(String commitComment) {
        this.commitComment = commitComment;
    }

    public Timestamp getCommitTime() {
        return commitTime;
    }

    public void setCommitTime(Timestamp commitTime) {
        this.commitTime = commitTime;
    }
}

public	class CommitLines{

    public int addLines;

    public int subLines;

    public CommitLines() {
    }

    public CommitLines(int addLines, int subLines) {
        this.addLines = addLines;
        this.subLines = subLines;
    }

    public int getAddLines() {
        return addLines;
    }

    public void setAddLines(int addLines) {
        this.addLines = addLines;
    }

    public int getSubLines() {
        return subLines;
    }

    public void setSubLines(int subLines) {
        this.subLines = subLines;
    }
}

具体代码:

public class GitDemo2 {

    public static void main(String[] args) throws GitAPIException, IOException, SQLException {
        // 1.获取git操作对象
        String dir = "xxx";
        String projectName = dir.substring(dir.lastIndexOf('\\') + 1);
        Git git  = null;
        Repository repository = null;
        try{
            repository = new FileRepositoryBuilder().setGitDir(Paths.get(dir,".git").toFile()).build();
            git = new Git(repository);
        }catch(IOException e){
            e.printStackTrace();
        }

        // 2.执行git pull操作,获取其他人的操作信息
        git.pull();
        ArrayList<CommitLogInfo> results = new ArrayList<>();

        // 3.执行git log查看日志信息,并进行过滤,只查询最近15天的所有的数据
        LogCommand logCommand = git.log();
        Iterable<RevCommit> call = logCommand.call();
        java.util.Date nowDate = new java.util.Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(nowDate);
        calendar.add(Calendar.DAY_OF_MONTH, -15);
        java.util.Date yesterday = calendar.getTime();
        Timestamp yesterdayTimestamp = new Timestamp(yesterday.getTime());
        String branchName = git.getRepository().getBranch();

        calendar.setTime(nowDate);
        calendar.add(Calendar.DAY_OF_MONTH, -366);
        java.util.Date yearTime = calendar.getTime();
        Timestamp yearTimestamp = new Timestamp(yearTime.getTime());

        ArrayList<RevCommit> arrayList = new ArrayList<>();
        for(RevCommit revCommit : call){
            long commitTime = revCommit.getCommitTime() * 1000L;
            Timestamp runningTimestamp = new Timestamp(commitTime);
            //需要添加初始节点,解决第一次处理的问题
            if(runningTimestamp.compareTo(yearTimestamp) < 0){
                arrayList.add(revCommit);
                break;
            }else{
                arrayList.add(revCommit);
            }
        }
        int length = arrayList.size();
        for(int i = 0; i <= length - 2; i++){
            RevCommit revCommit = arrayList.get(i);
            RevCommit prevCommit = arrayList.get(i + 1);
            String commitID = revCommit.getId().toString().split(" ")[1];
            String commitComment = revCommit.getFullMessage().replaceAll("'","").replaceAll("\n","\t");
            PersonIdent personIdent = revCommit.getCommitterIdent();
            String commitName = personIdent.getName();
            String commitEmail = personIdent.getEmailAddress();
            long commitTime = revCommit.getCommitTime() * 1000L;
            Timestamp runningTimestamp = new Timestamp(commitTime);
            // 只获取15天的数据
            if(runningTimestamp.compareTo(yesterdayTimestamp) >= 0){
                // 获取变化的行数
                CommitLines commitLines = getCommitLines2(git,prevCommit,revCommit,repository);
                CommitLogInfo commitLogInfo = new CommitLogInfo();
                commitLogInfo.setProjectName(projectName);
                commitLogInfo.setBranchName(branchName);
                commitLogInfo.setCommitID(commitID);
                commitLogInfo.setCommitUserName(commitName);
                commitLogInfo.setCommitEmail(commitEmail);
                commitLogInfo.setCommitComment(commitComment);
                commitLogInfo.setCommitTime(runningTimestamp);
                commitLogInfo.setAddLines(commitLines.getAddLines());
                commitLogInfo.setSubLines(commitLines.getSubLines());
                results.add(commitLogInfo);
            }
        }
        // 4.mysql写入数据库中
        InputStream inputStream = new BufferedInputStream(new FileInputStream("src/resources/config.properties"));

        Properties properties = new Properties();
        properties.load(inputStream);
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        String driverClass = properties.getProperty("driverClass");
        String url = properties.getProperty("url");
        Connection connection = JDBCUtils.createConnection(driverClass, url, username, password);
        String stmtStr = "insert into git_commit_info2(data_dt,project_name,branch_name,commit_id,commit_name,commit_email,commit_comment,add_lines,sub_lines,commit_time) values";
        long num = 0;
        int bulkNum = 20;
        Statement statement = connection.createStatement();
        for(CommitLogInfo commitLogInfo : results){
            String sql_val = String.format("(%s,\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\',\'%s\'),","current_date",commitLogInfo.getProjectName(),commitLogInfo.getBranchName(),commitLogInfo.getCommitID(), commitLogInfo.getCommitUserName(),commitLogInfo.getCommitEmail(),commitLogInfo.getCommitComment(),commitLogInfo.getAddLines(),commitLogInfo.getSubLines(),commitLogInfo.getCommitTime());
            stmtStr = stmtStr + sql_val;
            num = num + 1;
            if(num % bulkNum == 0 || num == results.size()){
                stmtStr = stmtStr.substring(0,stmtStr.lastIndexOf(","));
                stmtStr = stmtStr + " ON DUPLICATE KEY UPDATE update_time=current_timestamp";
                statement.addBatch(stmtStr);
                if(num % bulkNum == 0 || num == results.size()){
                    System.out.println(stmtStr);
                    statement.executeBatch();
                    statement.clearBatch();
                }
                stmtStr ="insert into git_commit_info2(data_dt,project_name,branch_name,commit_id,commit_name,commit_email,commit_comment,add_lines,sub_lines,commit_time) values";
            }
        }
    }

    /**
     *
     * @param git
     * @param headCommit
     * @param currentCommit
     * @param repository
     * @return
     * @throws GitAPIException
     * @throws IOException
     */
    public static CommitLines getCommitLines2(Git git,RevCommit headCommit,RevCommit currentCommit, Repository repository) throws GitAPIException, IOException {
        List<DiffEntry> diffs = null;

        AbstractTreeIterator oldTreeIter = prepareTreeParser(repository, headCommit);
        AbstractTreeIterator newTreeIter = prepareTreeParser(repository, currentCommit);
        diffs = git.diff().setNewTree(newTreeIter).setOldTree(oldTreeIter).call();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DiffFormatter df = new DiffFormatter(out);
        df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
        df.setRepository(repository);
//        int sum = 0;
        int addLines = 0;
        int subLines = 0;
        for(DiffEntry diffEntry: diffs){
            df.format(diffEntry);
            String diffText = out.toString("UTF-8");
            FileHeader fileHeader = df.toFileHeader(diffEntry);
            List<HunkHeader> hunks = (List<HunkHeader>)fileHeader.getHunks();
            int addSize = 0;
            int subSize = 0;
            for(HunkHeader hunkHeader : hunks){
                EditList editList = hunkHeader.toEditList();
                for(Edit edit : editList){
                    subSize = subSize + edit.getEndA() - edit.getBeginA();
                    addSize = addSize + edit.getEndB() - edit.getBeginB();
                }
            }
            out.reset();
            addLines += addSize;
            subLines += subSize;
        }
        return new CommitLines(addLines, subLines);

    }
    public static AbstractTreeIterator prepareTreeParser(Repository repository,RevCommit commit){
        System.out.println(commit.getId());
        try (RevWalk walk = new RevWalk(repository)) {
            System.out.println(commit.getTree().getId());
            RevTree tree = walk.parseTree(commit.getTree().getId());

            CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
            try (ObjectReader oldReader = repository.newObjectReader()) {
                oldTreeParser.reset(oldReader, tree.getId());
            }

            walk.dispose();

            return oldTreeParser;
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

注:
(1) 我们在开发过程中,发现获取git log时是按照时间从近到远来的,并且是严格按照时间从近到远来的;因此这边的程序是直接遍历实现的;
(2) 其实代码的整体逻辑不是很复杂,最复杂的是获取commit的行数,在这上面我们花了不少的时间,这边说一下大致的逻辑;在数据库中,我们add_lines和sub_lines列记录的是本次commit,我们添加了多少的行数和删除了多少行;我们的处理逻辑是将本次commit和上次commit进行比较,确定本次提交了多少行数;
(3)我们每个迭代都会建一个分支,该分支是用我们历史创建的null分支来生成的,因为我们采用时间方式找出本迭代第一次commit的信息;当committime和当前时间比超过一年,即认为是初始化的commit;
(4)我们在数据库中记录的是单次提交行数,因此可以采用sql来查询本迭代提交的行数,具体sql如下:

--统计本迭代一共添加了多少行和删除了多少行
select commit_email,sum(add_lines),sum(sub_lines) from git_commit_info2 
where project_name='xxx'
and branch_name='xxx'
group by commit_email;

--统计本迭代一共有多少天提交了代码
select t1.commit_email,count(1) from (
	select distinct commit_email,date(commit_time)  from git_commit_info2
	where project_name='xxx'
	and branch_name='xxx'
) t1 
group by t1.commit_email
 类似资料: