具體代碼詳見:https://gitee.com/RainyGao/DocSys
JGIT是非常好用的Java庫,通過JGIT的API可以新建、克隆倉庫、CheckOut和Commit文件,但使用JGIT時需要關注一些地方,否則會出現很多異常。
1. 關於倉庫的遍歷
JGIT通過treeWalk來實現對倉庫的遍歷,原理上是首先根據revision來獲取這個revision對應的revTree,換句話說就是這個版本上的文件節點樹(這些文件在該版本一定是存在的),然後獲取該revTree相應的入口文件(treeWalk)來實現遍歷。
默認情況下獲取的treeWalk是遞歸的(換句話說,就是會自動進入子目錄進行遍歷),所以如果只是遍歷所有文件那麼沒有問題,但實際上的應用場景而言,通常只需要獲取該目錄下的文件列表即可(尤其對於需要展示目錄結構的場景),如果這個時候需要將treeWalk設置爲不可遞歸。
treeWalk.setRecursive(true)
(1)獲取GIT根目錄的revTree的入口的方法如下:
treeWalk = new TreeWalk( repository )
treeWalk.reset(revTree)
需要注意的是如果是根目錄的treeWalk,那麼此時treeWalk指向的是根目錄下的第一個文件,而不是根目錄,換句話說根目錄本身是不會有treeWalk的,所以如果在這個revision上沒有文件的話,那麼treeWalk將會是Null值。
由於指向的是根目錄下的第一個文件,那麼設置非遞歸的話,調用treeWalk.next就可以遍歷根目錄下的文件列表。
(2)獲取指定的文件的treeWalk入口:
treeWalk = TreeWalk.forPath(repository, entryPath, revTree);
網上還有利用更底層的PathFilter接口來實現treeWalk的獲取,我也嘗試過,但似乎並不能達到我想要的效果,個人建議直接使用forPath接口即可,forPath返回的treeWalk的遞歸設置默認是不遞歸。
forPath返回的treeWalk可能是文件也可能是目錄,通過treeWalk.isSubTree可以判斷是不是目錄,該接口是通過FileMode的值來實現的,但某些情況下treeWalk的FileMode是個Null值(例如根目錄的treeWalk),所以直接對treeWalk進行判斷會導致異常,因此對於treeWalk的判斷需要區分是不是根目錄的treeWalk。
另外treeWalk指向的是revTree的某個節點,在不遞歸的情況下,要遍歷其子目錄需要調用 treeWalk.enterSubtree()來進入子目錄(treeWalk將指向該目錄下的第一個文件)
2、關於CheckOut
只要treeWalk搞定了,CheckOut是最簡單的,找到指定的文件節點的入口(即treeWalk),用如下代碼即可將文件下載下來:
out = new FileOutputStream(localParentPath + targetName);
ObjectId blobId = treeWalk.getObjectId(0);
ObjectLoader loader = repository.open(blobId);
loader.copyTo(out);
3、關於Commit
Commit目前的實現還是依賴於jgit提供的commit接口,原理根本地的git命令沒什麼區別,但是對本地WorkingCopy是有依賴的(也就是說必須本地CheckOut一個branch才能Commit),這並不是我想要的(我更傾向於直接利用文件數據流直接向Stash中寫入想要Commit的文件),但似乎還有點複製,所以會放在後面來實現。
以下是Commit和Push的實現代碼。
RevCommit ret = null;
try {
ret = git.commit().setCommitter(commitUser, "").setMessage(commitMsg).call();
System.out.println("doAutoCommmit() commitId:" + ret.getName());
} catch (Exception e) {
System.out.println("doAutoCommmit() commit error");
e.printStackTrace();
return null;
}
if(isRemote)
{
try {
git.push().call();
} catch (Exception e) {
System.out.println("doAutoCommmit() Push Error");
e.printStackTrace();
//Do roll back commit
rollBackCommit(git, null);
return null;
}
}
rollBackCommit是用來將本地倉庫還原掉,因爲push失敗的原因,通常是遠程倉庫已經有更新了,那麼需要對本地倉庫先進行rebase以保證本地倉庫與遠程倉庫的同步。
4. 關於刪除
實際上刪除也是一個Commit操作,但有有些不同,因爲刪除實際上並沒有內容需要commit到倉庫中去,只是刪除了節點,那麼這時候的操作就需要一點點技巧了。