在之前這篇博客中,我介紹瞭如何使用JGit獲取一次Commit修改的文件列表。那如果我們想獲得更多的統計信息呢?如每一個文件被修改的代碼行數。
一般情況下,可以使用諸如:
git log --date-order --stat > change-details.txt
和:
git log --date-order --pretty=fuller --name-status > version-log-order.txt
的Git Log命令來獲取多個Git Log的文本文件,再通過分析文本文件即可以得到。但是問題在於,例如這樣的記錄:
fs/readdir.c | 128 +++++++++++++++++++++++++++++++++++++++++++----------------
如果文件路徑特別長(在C、C++項目中一般不會出現,但是在較複雜的Java項目中是可能出現的),導致文件路徑中可能出現省略號,從而使文件關聯不準確。這時候我們就需要一些更準確的獲取方法。
在給出代碼之前,簡單提一句,對於下面代碼段中這條語句中的方法:
ObjectId versionId=Repo.resolve(versionTag);
JGit完整API文檔鏈接:https://download.eclipse.org/jgit/site/5.3.0.201903130848-r/apidocs/index.html (可能會由版本變化而變化)
中介紹,其字符串參數可以是:SHA-1,也可以是Tag:id^{tag}: ensure id is a tag
我當時看文檔的時候是在不瞭解id^{}是個什麼鬼。嘗試了一下,發現直接給出tag對應的字符串即可。
下面直接給出代碼,主要參考了這位網友的介紹:https://blog.csdn.net/u012621115/article/details/52171679
(CSDN這個排版不能正常縮進了,也是呵呵呵呵了)
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
public class JGitMetricsBack {
static Repository Repo;
static void printTime(int commitTime) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestampString=String.valueOf(commitTime);
Long timestamp = Long.parseLong(timestampString) * 1000;
String date = formatter.format(new Date(timestamp));
System.out.println("It's commit time: "+date);
}
static List<DiffEntry> getChangedFileList(RevCommit revCommit, Repository repo) {
List<DiffEntry> returnDiffs = null;
try {
RevCommit previsouCommit=getPrevHash(revCommit,repo);
if(previsouCommit==null)
return null;
ObjectId head=revCommit.getTree().getId();
ObjectId oldHead=previsouCommit.getTree().getId();
System.out.println("Printing diff between the Revisions: " + revCommit.getName() + " and " + previsouCommit.getName());
// prepare the two iterators to compute the diff between
try (ObjectReader reader = repo.newObjectReader()) {
CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
oldTreeIter.reset(reader, oldHead);
CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
newTreeIter.reset(reader, head);
// finally get the list of changed files
try (Git git = new Git(repo)) {
List<DiffEntry> diffs= git.diff()
.setNewTree(newTreeIter)
.setOldTree(oldTreeIter)
.call();
for (DiffEntry entry : diffs) {
// System.out.println("Entry: " + entry);
}
returnDiffs=diffs;
} catch (GitAPIException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return returnDiffs;
}
public static RevCommit getPrevHash(RevCommit commit, Repository repo) throws IOException {
try (RevWalk walk = new RevWalk(repo)) {
// Starting point
walk.markStart(commit);
int count = 0;
for (RevCommit rev : walk) {
// got the previous commit.
if (count == 1) {
return rev;
}
count++;
}
walk.dispose();
}
//Reached end and no previous commits.
return null;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// String versionTag="v2.6.19";//定位到某一次Commi,既可以使用Tag,也可以使用其hash
String versionTag="9f79b78ef74436c7507bac6bfb7b8b989263bccb";
String path="D:\\Projects\\Linux-Kernel\\linux-stable";
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.setMustExist(true);
builder.addCeilingDirectory(new File(path));
builder.findGitDir(new File(path));
try {
Repo=builder.build();
RevWalk walk = new RevWalk(Repo);
ObjectId versionId=Repo.resolve(versionTag);
RevCommit verCommit=walk.parseCommit(versionId);
String commitName=verCommit.getName();
System.out.println("This version is: "+versionTag+", It hash: "+commitName);//如果通過Tag定位,可以獲得其SHA-1 Hash Value
int verCommitTime=verCommit.getCommitTime();
printTime(verCommitTime);//打印出本次Commit的時間
System.out.println("The author is: "+verCommit.getAuthorIdent().getEmailAddress());//獲得本次Commit Author的郵箱
List<DiffEntry> diffFix=getChangedFileList(verCommit,Repo);//獲取變更的文件列表
ByteArrayOutputStream out = new ByteArrayOutputStream();
DiffFormatter df = new DiffFormatter(out);
// df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);//如果加上這句,就是在比較的時候不計算空格,WS的意思是White Space
df.setRepository(Repo);
for (DiffEntry entry : diffFix) {
df.format(entry);
String diffText = out.toString("UTF-8");
// System.out.println(diffText);
System.out.println(entry.getNewPath());//變更文件的路徑
FileHeader fileHeader = df.toFileHeader(entry);
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 += edit.getEndA()-edit.getBeginA();
addSize += edit.getEndB()-edit.getBeginB();
}
}
System.out.println("addSize="+addSize);//增加和減少的代碼行數統計,我和Git Log覈對了一下,還挺準確的。
System.out.println("subSize="+subSize);
out.reset();
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}