由于公司会统计每个人每个月提交的代码行数,提交代码次数等信息,本人基于此情况,开发了一个自动化统计小组内代码提交信息的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