如果您想知道如何在JGit中执行诸如git init
, git checkout
等基本的Git命令,请继续阅读。
本教程概述了JGit中最常用的git命令及其对应的命令。 它逐步执行以下步骤:创建存储库,从远程获取内容,向历史记录添加文件或从历史记录中删除文件,检查历史记录,最后将更改推回原始存储库。
JGit提供了一个类似于Git高级命令的API。 代替
git commit -m "My first commit"
在命令行上,您将编写
git.commit().setMessage( "My first commit" ).call();
在JGit中。
所有JGit命令都有一个call()方法,在设置该命令后,该方法将用于实际执行它。 这些类以各自的Git命令后缀Command命名。 尽管某些命令提供了公共构造函数,但建议使用Git工厂类来创建命令实例,如上例所示。
获取图书馆
但是在进一步研究JGit API之前,让我们先掌握该库。 获取JGit的最常见方法可能是从Maven存储库中。 但是,如果您更喜欢OSGi捆绑软件,那么还可以使用p2存储库。 下载页面列出了集成库所需的信息。
对于本文的范围,在项目/捆绑包org.eclipse.jgit中集成所谓的核心库就足够了。 如果您对JGit源代码存储库中的内容还有兴趣,我建议您阅读JGit源代码简介。
创建一个仓库
首先,我们需要一个存储库。 为了掌握这种情况,我们可以初始化一个新的存储库或克隆一个现有的存储库。
InitCommand使我们可以创建一个空的存储库。 下一行
Git git = Git.init().setDirectory( "/path/to/repo" ).call();
将在给setDirectory()的位置创建一个带有工作目录的存储库。 .git目录将直接位于/path/to/repo/.git中。 有关InitCommand的详细说明,请阅读使用JGit初始化Git存储库 。
可以使用CloneCommand克隆现有的存储库
Git git = Git.cloneRepository()
.setURI( "https://github.com/eclipse/jgit.git" )
.setDirectory( "/path/to/repo" )
.call();
上面的代码会将JGit存储库克隆到本地目录“ path / to / repo”中。 在如何使用JGit克隆Git存储库中 ,对CloneCommand的所有选项进行了详细说明。
如果您碰巧已经要使用现有的本地存储库,则可以按照如何使用JGit访问Git存储库中所述进行操作 。
完成后关闭Git
请注意,如果不再需要关闭未显式关闭(git.close())的命令,则返回InitCommand或CloneCommand之类的Git实例的命令可能会泄漏文件句柄。
幸运的是,Git实现了AutoCloseable,因此您可以使用try-with-resources语句 。
填充存储库
现在我们有了一个存储库,我们可以开始填充其历史记录。 但是,为了提交文件,我们首先需要将其添加到所谓的索引(也称为暂存区)中。 commit命令将仅考虑在索引中添加(或从索引中删除)的文件。
因此,JGit命令是–您猜到了– AddCommand。
DirCache index = git.add().addFilePattern( "readme.txt" ).call();
因此,以上行将文件readme.txt添加到索引中。 值得注意的是,文件的实际内容被复制到索引中。 这意味着以后对文件的修改将不包含在索引中,除非再次添加它们。
给addFilePattern()提供的路径必须相对于工作目录根目录。 如果路径未指向现有文件,则将其忽略。
尽管方法名称暗示也可以接受模式,但是以前的JGit支持是有限的。 传递“。” 将以递归方式添加工作目录中的所有文件。 但是尚不支持本地Git中可用的fileglob(例如* .java)。
可以检查JGit中名为DirCache的call()返回的索引,以验证它是否确实包含我们期望的内容。 其getEntryCount()方法返回文件总数,而getEntry()返回指定位置的条目。
现在,一切准备就绪,可以使用CommitCommand来将更改存储在存储库中。
RevCommit commit = git.commit().setMessage( "Create readme file" ).call();
至少必须指定消息,否则call()会发出NoMessageException投诉。 但是,允许输入空消息。 如果未使用相应标记的方法表示作者和提交者,则将其从配置中删除。
返回的RevCommit用消息,作者,提交者,时间戳记来描述提交,当然还指向构成该提交的文件和目录树的指针。
与需要添加新文件或更改文件的方式相同,需要明确删除已删除的文件。 RmCommand与AddCommand是对等的,并且可以以相同的方式使用(当然会产生相反的结果)。
DirCache index = git.rm().addFilepattern( "readme.txt" ).call();
上面的行将再次删除给定的文件。 由于它是存储库中的唯一文件,因此当询问其中的条目数时,返回的索引将返回零。
除非指定了setCached(true),否则该文件还将从工作目录中删除。 由于Git不跟踪目录,因此RmCommand还会删除给定文件的空父目录。
删除不存在文件的尝试将被忽略。 但是与AddCommand不同,RmCommand在其addFilepattern()方法中不接受通配符。 所有要删除的文件都需要单独指定。
在下一次提交时,这些更改将存储在存储库中。 请注意,创建一个空的提交是完全合法的,即,在执行之前没有添加或删除文件的提交。 虽然我不知道一个合适的用例。
储存库状态
status命令列出了索引与当前HEAD提交或工作目录之间具有差异的文件,以及索引或Git未跟踪的文件。
StatusCommand以最简单的形式收集属于该存储库的所有文件的状态:
Status status = git.status().call();
Status对象的获取器应该是不言自明的。 它们返回处于方法名描述状态的文件名集。 例如,将readme.txt文件添加到如上所示的索引中之后,status.getAdded()将返回一个集合,其中包含刚添加的文件的路径。
如果根本没有差异,也没有未跟踪的文件,则Status.isClean()将返回true。 顾名思义,如果存在未提交的更改,则返回Status.hasUncommittedChanges()true。
使用addPath(),可以将StatusCommand配置为仅显示某些文件的状态。 给定的路径必须命名文件或目录。 不存在的路径将被忽略,并且不支持正则表达式或通配符。
Status status = git.status().addPath( "documentation" ).call();
在上面的示例中,将递归计算“文档”目录下所有文件的状态。
浏览存储库
现在,该存储库具有(小的)历史记录,我们将研究该命令以列出现有提交。
JGit的git log
副本的最简单形式允许列出从当前HEAD可以访问的所有提交。
Iterable<RevCommit> iterable = git.log().call();
返回的迭代器可用于循环遍历LogCommand找到的所有提交。
对于更高级的用例,我建议直接使用RevWalk API,该类与LogCommand也使用相同的类。 除了提供更大的灵活性外,它还避免了可能发生的资源泄漏,因为LogCommand内部使用的RevWalk永远不会关闭。
例如,它的markStart()方法还可用于列出其他分支(或更一般地说,其他引用 )可以到达的提交。
不幸的是,仅接受ObjectId,因此需要首先解析所需的引用。 JGit中的ObjectId封装了一个SHA-1哈希,该哈希指向Gits对象数据库中的一个对象。 在这里,指向提交的ObjectId是必需的,在这种情况下解析意味着获取特定引用所指向的ObjectId。
放在一起,看起来就像下面的代码片段:
Repository repository = git.getRepository()
try( RevWalk revWalk = new RevWalk( repository ) ) {
ObjectId commitId = repository.resolve( "refs/heads/side-branch" );
revWalk.markStart( revWalk.parseCommit( commitId ) );
for( RevCommit commit : revWalk ) {
System.out.println( commit.getFullMessage );
}
}
获取分支“侧分支”所指向的提交ID,然后指示RevWalk从那里开始遍历历史记录。 因为markStart()需要RevCommit,所以RevWalk的parseCommit()用于将提交ID解析为实际的提交。
一旦RevWalk设置好,该代码段就会在提交上循环,以打印每次提交的消息。
try-with-resource语句确保在完成后将关闭RevWalk。 请注意,多次调用markStart()以在遍历中包含多个引用是合法的。
RevWalk还可以配置为通过匹配提交对象本身的属性或匹配其表示的目录树的路径来过滤提交。 如果事先知道,则可以将不感兴趣的提交及其祖先链从输出中排除。 当然可以对输出进行排序,例如按日期或拓扑排序(所有孩子都在父母之前)。 但是这些功能不在本文讨论范围之内,但可能会在以后的文章中进行介绍。
与远程存储库交换
通常,本地存储库是从远程存储库克隆的。 并且本地所做的更改最终应发布到原始存储库。 为此,需要使用PushCommand,它是git push
的对应版本。
最简单的形式将当前分支推送到其相应的远程分支。
Iterable<PushResult> iterable = local.push().call();
PushResult pushResult = iterable.iterator().next();
Status status
= pushResult.getRemoteUpdate( "refs/heads/master" ).getStatus();
该命令返回一个可迭代的PushResults。 在上述情况下,迭代器仅包含一个元素。 为了验证推送是否成功,可以要求pushResult返回给定分支的RemoteRefUpdate。
RemoteRefUpdate详细描述了更新内容以及更新方式。 但是它也有一个状态属性,用于总结结果。 并且如果状态返回OK,我们可以放心操作成功。
即使该命令在没有给出任何建议的情况下也可以运行,但是它具有许多选项,在下面仅列出了更常用的选项。 默认情况下,该命令将推送到名为“ origin”的默认遥控器。 使用setRemote()可以指定其他远程存储库的URL或名称。 如果其他分支比当前应该推refspecs可以与setRefSpec()来指定。 可以使用setPushTags()来控制是否也应传送标签。 最后,如果不确定是否需要结果,则可以使用空运行选项来模拟推送操作。
现在,我们已经了解了如何将本地对象传输到远程存储库,我们将看看相反的方向是如何工作的。 FetchCommand的用法与推入命令非常相似,并且默认设置也可以成功使用。
FetchResult fetchResult = local.fetch().call();
TrackingRefUpdate refUpdate
= fetchResult.getTrackingRefUpdate( "refs/remotes/origin/master" );
Result result = refUpdate.getResult();
如果没有进一步的配置,该命令将从与默认远程服务器上当前分支相对应的分支中获取更改。
FetchResult提供有关操作结果的详细信息。 对于每个受影响的分支,都可以获取TrackingRefUpdate实例。 最有趣的可能是getResult()的返回值,该值总结了更新的结果。 此外,它还包含有关以下信息的信息:更新了哪个本地引用(getLocalBame()),使用哪个远程引用(getRemoteName())以及更新之前和之后本地引用指向的对象id(getOldObjectId()和getNewObjectid())。
如果远程存储库需要身份验证,则可以使用与所有与远程存储库通信的命令相同的方式来准备PushCommand和FetchCommand。
- 可以在“ JGit身份验证说明”文章中找到详细的讨论。
结束JGit入门
现在轮到您来tkae JGit了。 不难理解高级JGit API。 如果您知道要使用什么git命令,则可以轻松猜测JGit中要使用的类和方法。
尽管并非所有Git命令行的精妙之处都可用,但它为最常用的功能提供了坚实的支持。 而且,如果有一些关键的缺失,您通常可以诉诸JGit的低级API来解决该限制。
整篇文章中显示的摘录是一系列学习测试的摘录。
- 完整版本可以在这里找到: https : //gist.github.com/rherrmann/433adb44b3d15ed0f0c7
如果您仍然有困难或问题,请发表评论或向友好而乐于助人的JGit社区寻求帮助。
翻译自: https://www.javacodegeeks.com/2015/12/getting-started-jgit.html