怎樣通過JGit獲得一次Commit的所有相關信息(如變更代碼行數)

之前這篇博客中,我介紹瞭如何使用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);

可以看到其Java Doc:https://download.eclipse.org/jgit/site/5.3.0.201903130848-r/apidocs/org/eclipse/jgit/lib/Repository.html#resolve-java.lang.String-

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();
		}
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章