這是 git 各個階段版本控管的概念圖,非常重要! 雖然會想了解 JGit 的人,一定對這些都很熟了,但是還是貼出來大家看一下,因為這和接下來要談的差異比對有關。
首先看一下我的環境,如下圖所示,分了幾個 branch,故意弄的有點亂是為了測試。
要走訪 git repository,要先確定是要走訪那個 branch? 及要走訪的節點,一般來說,都是走訪 commit 或 tag 所在的節點。當然更詳細一點點的如下圖: (這些圖都是 TortoiseGit 產生的)。
這上面的每個節點的 name 和 object id,是非常重要的,要找到節點,就靠這些了! 下面是這篇要說明的程式,它會走訪被版控的檔案、目錄、並進行差異比對。 前兩篇說明過的。這篇不會重複,如果有疑問請翻閱前兩篇。
1 public class DiffTest { 2 3 @Before 4 public void setUp() throws Exception { 5 } 6 7 @After 8 public void tearDown() throws Exception { 9 } 10 11 //@Test 12 public void testDiff() throws Exception { 13 Repository repo = new FileRepositoryBuilder() 14 .setGitDir(new File("D:/TestJGit/local/.git")) 15 .build(); 16 17 RevTree headTree = getRevTree(repo, "refs/heads/master"); 18 RevTree branchTree = getRevTree(repo, "refs/heads/branch_001"); 19 20 ObjectReader reader = repo.newObjectReader(); 21 List<DiffEntry> listDiffs = compareTree(repo, reader, headTree, branchTree); 22 23 for (DiffEntry diff : listDiffs) { 24 System.out.println(diff); 25 FileMode mode = diff.getNewMode(); 26 System.out.println(mode.toString()); 27 } 28 29 repo.close(); 30 } 31 32 @Test 33 public void testWalk() throws Exception { 34 Repository repo = new FileRepositoryBuilder() 35 .setGitDir(new File("D:/TestJGit/local/.git")) 36 .build(); 37 38 RevTree headTree = getRevTree(repo, "refs/heads/branch_003"); 39 40 try (TreeWalk treeWalk = new TreeWalk(repo)) { 41 treeWalk.addTree(headTree); 42 treeWalk.setRecursive(false); 43 44 while (treeWalk.next()) { 45 String rawPath = new String(treeWalk.getPathString()); 46 FileMode mode = treeWalk.getFileMode(0); 47 System.out.print("rawPath = " + rawPath); 48 if ((mode.getBits() & FileMode.TYPE_FILE) != 0) { 49 System.out.println(" -- file"); 50 } 51 if ((mode.getBits() & FileMode.TYPE_TREE) != 0) { 52 System.out.println(" -- folder"); 53 } 54 System.out.println(mode.toString()); 55 System.out.println(mode.getBits()); 56 System.out.println(treeWalk.getNameString()); 57 } 58 } 59 } 60 61 private List<DiffEntry> compareTree(Repository repo, ObjectReader reader, 62 RevTree newTree, RevTree oldTree) 63 throws IncorrectObjectTypeException, IOException, GitAPIException { 64 65 CanonicalTreeParser newTreeIter = new CanonicalTreeParser(); 66 newTreeIter.resetRoot(reader, newTree.getId()); 67 68 CanonicalTreeParser oldTreeIter = new CanonicalTreeParser(); 69 oldTreeIter.resetRoot(reader, oldTree.getId()); 70 71 Git git = new Git(repo); 72 List<DiffEntry> listDiffs = git.diff().setOldTree(oldTreeIter).setNewTree(newTreeIter).call(); 73 return listDiffs; 74 } 75 76 private RevTree getRevTree(Repository repo, String objectName) 77 throws AmbiguousObjectException, IncorrectObjectTypeException, 78 IOException, MissingObjectException { 79 80 RevTree tree = null; 81 try (RevWalk revWalk = new RevWalk(repo)) { 82 ObjectId objId = repo.resolve(objectName); 83 RevCommit commit = revWalk.parseCommit(objId); 84 tree = commit.getTree(); 85 } 86 87 return tree; 88 } 89 }
- 先找到樹頭: getRevTree() method 就是用 object name (revision string) 去找到樹頭! 像是程式 17、18 行,我們要比對的是 master 和 branch_003 間的差異,就是先把這兩個節點的 object name 傳入 getRevTree() 以取得樹頭。JGit 的觀念多半是,要做什麼事,就會有個相對應的類別,這裡提供的就是 RevWalk 這個類別。簡單說明一下這個 method 的運作。
- 程式第 82 行,resolve() method 可以由 object name 取得 object id,因為很多 method 要的參數是 object id。
- 程式第 83 行,RevWalk 的 parseCommit() 可以根據傳入的 object id 得到參考到 commit 那個節點的物件。
- 由 RevCommit 的 getTree() 即可取得那個 commit 時的所有目錄、檔案,當然這些檔案的內容也是那個當下的內容。
- 差異比對: 在 17、18 行取得檔案的樹狀結構後,在 21 行透過 compareTree() 比對,結果會以 List<DiffEntry> 傳回,這個 method 的寫法我還不太懂,先不解釋。
- TreeWalk (第 40 行) 是 JGit 提供用來走訪檔案樹狀結構的類別,只要將檔案樹狀結構傳入 (第 41 行) 就可以開始走訪各個節點,setRecursive 傳入 false 就只會走訪最上層,要完整的走訪整棵樹就傳入 true。
- 每個節點都可以取得它的 FileMode,這是記錄該節點基本屬性的物件,像是第 48、51 行所顯示的,呼叫 getBits() 後,傳回的整數其實是每個 bit 有它的意義,可以透過遮罩的方式取得各個 bit 的值,上面的程式就示範了判斷該節點是檔案或路徑的方法。
- FileMode 的 toString() 在 API Document 裡雖然有說明,僅適合用在 debug 時,不過,這個 method 可傳回如 100644 這樣的字串,它是 getBits() 取得的數字以 8 進位表示成字串,後面三個數字是像 Unix 的權限表示法,644 就是 rw-r--r--。
.setGitDir(new File("D:/TestJGit/local/.git")) .build(); Git git = new Git(repo); LogCommand logCmd = git.log(); ObjectId objId = repo.resolve("refs/remotes/origin/master"); Iterable<RevCommit> revCommit = logCmd.add(objId).call(); Iterator<RevCommit> commitIter = revCommit.iterator(); while (commitIter.hasNext()) { RevCommit commit = commitIter.next(); System.out.println("commiter name = " + commit.getAuthorIdent().getName()); System.out.println("commiter email = " + commit.getAuthorIdent().getEmailAddress()); Date date = new Date(((long) commit.getCommitTime()) * 1000); System.out.println("commit time = " + date.toString()); }
沒有留言:
張貼留言