章節 ▾ 第二版

A2.3 附錄 B:在你的應用程式中嵌入 Git - JGit

JGit

如果你想在 Java 程式中使用 Git,有一個功能齊全的 Git 庫叫做 JGit。JGit 是一個用 Java 原生編寫的、功能相對齊全的 Git 實現,並廣泛應用於 Java 社群。JGit 專案隸屬於 Eclipse 旗下,其主頁可以在以下地址找到:https://www.eclipse.org/jgit/

環境搭建

有多種方法可以將你的專案與 JGit 連線並開始編寫程式碼。最簡單的方法可能是使用 Maven——透過將以下程式碼片段新增到你的pom.xml檔案中的<dependencies>標籤中即可完成整合

<dependency>
    <groupId>org.eclipse.jgit</groupId>
    <artifactId>org.eclipse.jgit</artifactId>
    <version>3.5.0.201409260305-r</version>
</dependency>

等你讀到這裡時,version 可能已經更新了;請檢視https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit以獲取最新的倉庫資訊。完成此步驟後,Maven 將自動獲取並使用你所需的 JGit 庫。

如果你更喜歡自己管理二進位制依賴,可以從https://www.eclipse.org/jgit/download獲取預編譯的 JGit 二進位制檔案。你可以透過執行以下命令將其構建到你的專案中

javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java
java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App

底層 API

JGit 有兩個基本級別的 API:底層 API 和高層 API。這些術語來源於 Git 本身,JGit 也大致分為這些領域:高層 API 是常用使用者級操作的友好前端(普通使用者會使用 Git 命令列工具進行的那些操作),而底層 API 則用於直接與低階倉庫物件互動。

大多數 JGit 會話的起點是 Repository 類,你要做的第一件事就是建立它的一個例項。對於基於檔案系統的倉庫(是的,JGit 允許其他儲存模型),這可以透過使用 FileRepositoryBuilder 來完成

// Create a new repository
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
    new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();

// Open an existing repository
Repository existingRepo = new FileRepositoryBuilder()
    .setGitDir(new File("my_repo/.git"))
    .build();

該構建器提供了一個流暢的 API,用於提供查詢 Git 倉庫所需的所有資訊,無論你的程式是否確切知道它的位置。它可以使用環境變數(.readEnvironment()),從工作目錄中的某個位置開始搜尋(.setWorkTree(…).findGitDir()),或者像上面那樣直接開啟一個已知的 .git 目錄。

一旦你擁有了一個 Repository 例項,你就可以用它做各種各樣的事情。這裡有一個快速示例

// Get a reference
Ref master = repo.getRef("master");

// Get the object the reference points to
ObjectId masterTip = master.getObjectId();

// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");

// Load raw object contents
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);

// Create a branch
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();

// Delete a branch
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();

// Config
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");

這裡有很多內容,所以我們一點一點地來看。

第一行獲取指向 master 引用的指標。JGit 會自動獲取位於 refs/heads/master實際 master 引用,並返回一個允許你獲取該引用資訊的物件。你可以獲取名稱(.getName()),以及直接引用的目標物件(.getObjectId())或符號引用指向的引用(.getTarget())。引用物件也用於表示標籤引用和物件,因此你可以詢問標籤是否“已剝離 (peeled)”,這意味著它指向一系列(可能很長)標籤物件的最終目標。

第二行獲取 master 引用的目標,它以 ObjectId 例項的形式返回。ObjectId 表示物件的 SHA-1 雜湊值,該物件可能存在也可能不存在於 Git 的物件資料庫中。第三行類似,但展示了 JGit 如何處理 rev-parse 語法(更多資訊請參閱分支引用);你可以傳遞任何 Git 理解的物件指示符,JGit 將返回該物件的有效 ObjectId 或 null

接下來的兩行展示瞭如何載入物件的原始內容。在此示例中,我們呼叫 ObjectLoader.copyTo() 將物件內容直接流式傳輸到標準輸出,但 ObjectLoader 也有讀取物件型別和大小的方法,以及將其作為位元組陣列返回的方法。對於大型物件(當 .isLarge() 返回 true 時),你可以呼叫 .openStream() 來獲取一個類似 InputStream 的物件,該物件可以在不一次性將所有原始物件資料載入到記憶體的情況下讀取資料。

接下來的幾行展示了建立新分支所需的步驟。我們建立一個 RefUpdate 例項,配置一些引數,然後呼叫 .update() 來觸發更改。緊隨其後的是刪除該分支的程式碼。請注意,要使其生效,需要呼叫 .setForceUpdate(true);否則 .delete() 呼叫將返回 REJECTED,並且什麼都不會發生。

最後一個示例展示瞭如何從 Git 配置檔案中獲取 user.name 值。這個 Config 例項使用我們之前開啟的倉庫進行本地配置,但也會自動檢測全域性和系統配置檔案並從中讀取值。

這只是完整的底層 API 的一小部分示例;還有更多的方法和類可用。這裡也沒有展示 JGit 處理錯誤的方式,即透過使用異常。JGit API 有時會丟擲標準的 Java 異常(例如 IOException),但同時還提供了許多 JGit 特有的異常型別(例如 NoRemoteRepositoryExceptionCorruptObjectExceptionNoMergeBaseException)。

高層 API

底層 API 相當完善,但要將它們組合起來實現常見目標(例如將檔案新增到索引或建立新提交)可能會很繁瑣。JGit 提供了一組更高級別的 API 來幫助解決這個問題,這些 API 的入口點是 Git

Repository repo;
// construct repo...
Git git = new Git(repo);

Git 類有一套很好的高階構建器風格的方法,可用於構建一些相當複雜的行為。讓我們看一個例子——執行類似 git ls-remote 的操作

CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");
Collection<Ref> remoteRefs = git.lsRemote()
    .setCredentialsProvider(cp)
    .setRemote("origin")
    .setTags(true)
    .setHeads(false)
    .call();
for (Ref ref : remoteRefs) {
    System.out.println(ref.getName() + " -> " + ref.getObjectId().name());
}

這是 Git 類的一個常見模式;這些方法返回一個命令物件,允許你鏈式呼叫方法來設定引數,這些引數在呼叫 .call() 時執行。在這個例子中,我們請求 origin 遠端倉庫的標籤,而不是頭(heads)。另外請注意,這裡使用了 CredentialsProvider 物件進行身份驗證。

透過 Git 類還可以使用許多其他命令,包括但不限於 addblamecommitcleanpushrebaserevertreset

延伸閱讀

這只是 JGit 全部功能的一小部分示例。如果你感興趣並想了解更多,可以參考以下資料和靈感來源

scroll-to-top