-
A1. 附錄 A: Git 在其他環境
- A1.1 圖形介面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小結
-
A2. 附錄 B: 在應用程式中嵌入 Git
-
A3. 附錄 C: 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
底層 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 特有的異常型別(例如 NoRemoteRepositoryException
、CorruptObjectException
和 NoMergeBaseException
)。
高層 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
類還可以使用許多其他命令,包括但不限於 add
、blame
、commit
、clean
、push
、rebase
、revert
和 reset
。
延伸閱讀
這只是 JGit 全部功能的一小部分示例。如果你感興趣並想了解更多,可以參考以下資料和靈感來源
-
官方 JGit API 文件可以在https://www.eclipse.org/jgit/documentation找到。它們是標準的 Javadoc,所以你最喜歡的 JVM IDE 也能夠將它們安裝到本地。
-
JGit Cookbook 位於https://github.com/centic9/jgit-cookbook,其中包含許多如何使用 JGit 完成特定任務的示例。