Git
章節 ▾ 第二版

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

底層指令

JGit 有兩個基本 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 會自動抓取實際master 參考,它位於 refs/heads/master,並傳回一個物件,讓您可以擷取關於該參考的資訊。您可以取得名稱 (.getName()),以及直接參考的目標物件 (.getObjectId()) 或符號參考所指向的參考 (.getTarget())。Ref 物件也用於表示標籤參考和物件,因此您可以詢問標籤是否「已展開」,這表示它指向 (可能很長一串) 標籤物件的最終目標。

第二行取得 master 參考的目標,並以 ObjectId 實例傳回。ObjectId 代表物件的 SHA-1 雜湊值,該物件可能存在於 Git 的物件資料庫中,也可能不存在。第三行類似,但顯示 JGit 如何處理 rev-parse 語法 (如需更多資訊,請參閱分支參考);您可以傳遞 Git 可理解的任何物件指定符,而 JGit 會傳回該物件的有效 ObjectId 或 null

接下來兩行顯示如何載入物件的原始內容。在此範例中,我們呼叫 ObjectLoader.copyTo() 將物件的內容直接串流到 stdout,但 ObjectLoader 也具有讀取物件類型和大小以及將其做為位元組陣列傳回的方法。對於大型物件 (.isLarge() 會傳回 true),您可以呼叫 .openStream() 來取得類似 InputStream 的物件,該物件可以在不一次將所有資料拉入記憶體的情況下讀取原始物件資料。

接下來幾行顯示建立新分支所需的步驟。我們建立 RefUpdate 實例、設定一些參數,並呼叫 .update() 以觸發變更。緊接著是刪除相同分支的程式碼。請注意,必須使用 .setForceUpdate(true) 才能使其運作;否則 .delete() 呼叫會傳回 REJECTED,並且不會發生任何事情。

最後一個範例顯示如何從 Git 設定檔中擷取 user.name 值。此 Config 實例使用我們稍早開啟的存放庫進行本機設定,但也會自動偵測全域和系統設定檔,並從中讀取值。

這只是完整管道 API 的一小部分範例;還有許多方法和類別可用。這裡也沒有顯示 JGit 如何處理錯誤,這是透過使用例外狀況來處理的。JGit API 有時會擲回標準 Java 例外狀況 (例如 IOException),但也提供許多 JGit 特定的例外狀況類型 (例如 NoRemoteRepositoryExceptionCorruptObjectExceptionNoMergeBaseException)。

瓷器 (Porcelain)

管道 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 遠端傳回標籤,但不要求傳回 HEAD。另請注意,使用 CredentialsProvider 物件進行驗證。

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

延伸閱讀

這只是 JGit 完整功能的一小部分範例。如果您有興趣並想瞭解更多資訊,請在此處尋找資訊和靈感

scroll-to-top