章节 ▾ 第二版

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