-
1. 起步
-
2. Git 基础
-
3. Git 分支
-
4. 服务器上的 Git
- 4.1 协议
- 4.2 在服务器上部署 Git
- 4.3 生成 SSH 公钥
- 4.4 架设服务器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管服务
- 4.10 小结
-
5. 分布式 Git
-
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://projects.eclipse.org/projects/technology.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://projects.eclipse.org/projects/technology.jgit/downloads 获取预构建的 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:底层命令(plumbing)和高级命令(porcelain)。这些术语来自 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() 将对象内容直接流式传输到标准输出,但 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 相当完整,但将它们串联起来以实现常见目标(例如将文件添加到索引或创建新提交)可能会很麻烦。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 远程请求标签,而不是头。另请注意使用 CredentialsProvider 对象进行身份验证。
许多其他命令都可以通过 Git 类获得,包括但不限于 add、blame、commit、clean、push、rebase、revert 和 reset。
延伸阅读
这只是 JGit 全部功能的一小部分。如果您有兴趣并想了解更多信息,请查阅以下资料和灵感来源
-
官方的 JGit API 文档可以在 https://help.eclipse.org/latest/topic/org.eclipse.egit.doc/help/JGit/User_Guide/User-Guide.html 找到。这些是标准的 Javadoc,因此您喜欢的 JVM IDE 也能够将其本地安装。
-
https://github.com/centic9/jgit-cookbook 上的 JGit Cookbook 包含许多关于如何使用 JGit 执行特定任务的示例。