使用JGit API探索Git内部

您是否想过提交及​​其内容如何存储在Git中? 好吧,我有,在上一个下雨的周末,我有一些空闲时间,所以我做了一些研究。

因为我对Java的感觉比对Bash的感觉要多,所以我使用了JGit和一些学习测试来探索提交的Git内部。 这是我的发现:

Git –对象数据库

Git的核心是简单的内容可寻址数据存储。 这意味着您可以在其中插入任何类型的内容,并且它将返回一个密钥,您可以使用该密钥在以后的某个时间点再次检索数据。

对于Git,关键是从内容计算出的20字节SHA-1哈希。 内容也被称为在GIT中术语的对象 ,因此数据存储也被称为对象数据库

让我们看看如何使用JGit来存储和检索内容。

斑点

在JGit中,ObjectInserter用于将内容存储到对象数据库中。 可以将其视为与Git中的git hash-object大致等效。

使用其insert()方法,您可以将对象写入数据存储,而其idFor()方法仅计算给定字节的SHA-1哈希。 因此,用于存储字符串的代码如下所示:

ObjectInserter objectInserter = repository.newObjectInserter();
byte[] bytes = "Hello World!".getBytes( "utf-8" );
ObjectId blobId = objectInserter.insert( Constants.OBJ_BLOB, bytes );
objectInserter.flush();

所有代码示例均假定存储库变量指向在代码段外部创建的空存储库 。

第一参数表示要插入的对象的对象类型 ,在这种情况下,斑点的类型。 还有其他对象类型,我们将在后面学习。 Blob类型用于存储任意内容。

有效载荷必须在第二个参数中给出,在这种情况下,必须作为字节数组。 也可以使用接受InputStream的重载方法。

最后,需要刷新ObjectInserter,以使其他访问存储库的更改可见。

insert()方法返回根据类型,内容长度和内容字节计算得出的SHA-1哈希。 但是,在JGit中,SHA-1哈希通过ObjectId类表示,ObjectId类是一种不变的数据结构,可以在字节,整数和字符串之间来回转换。

现在,您可以使用返回的blobId取回内容,从而确保上面的代码实际写入了内容。

ObjectReader objectReader = repository.newObjectReader();
ObjectLoader objectLoader = objectReader.open( blobId );
int type = objectLoader.getType(); // Constants.OBJ_BLOB
byte[] bytes = objectLoader.getBytes();
String helloWorld = new String( bytes, "utf-8" ) // Hello World!

ObjectReader的open()方法返回一个ObjectLoader,该对象可用于访问由给定对象ID标识的对象。 借助ObjectLoader,您可以将对象的类型,大小以及内容作为字节数组或流获取。

要验证JGit编写的对象与本机Git兼容,可以使用git cat-file检索其内容。

$ git cat-file -p c57eff55ebc0c54973903af5f72bac72762cf4f4
Hello World!
git cat-file -t c57eff55ebc0c54973903af5f72bac72762cf4f4
blob

如果查看存储库的.git/objects目录,则会发现一个名为'c5'的目录,其中包含名为'7eff55ebc0c54973903af5f72bac72762cf4f4'的文件。 最初是这样存储内容的:每个对象作为一个文件,以内容的SHA-1哈希命名。 子目录以SHA-1的前两个字符命名,文件名由其余字符组成。

现在您可以存储文件的内容,下一步就是存储文件的名称。 而且可能不仅仅是一个文件,因为提交通常由一组文件组成。 为了保存此类信息,Git使用了所谓的树对象。

树对象

树对象可以看作是简化的文件系统结构,其中包含有关文件和目录的信息。

它包含任意数量的树条目。 每个条目都有一个路径名,一个文件模式,并指向一个文件(一个blob对象)或另一个(子)树对象(如果它代表一个目录)的内容。 指针当然是Blob对象或树对象的SHA-1哈希。

首先,您可以创建一个树,其中包含一个名为“ hello-world.txt”的文件的单个条目,该文件指向上面存储的“ Hello World!”。 内容。

TreeFormatter treeFormatter = new TreeFormatter();
treeFormatter.append( "hello-world.txt", FileMode.REGULAR_FILE, blobId );
ObjectId treeId = objectInserter.insert( treeFormatter );
objectInserter.flush();

TreeFormatter在这里用于构造内存中的树对象。 通过调用append(),将添加具有给定路径名,模式和ID(用于存储其内容)的条目。

从根本上讲,您可以自由选择任何路径名。 但是,Git希望路径名是相对于工作目录的,且不带前导“ /”。

此处使用的文件模式表示正常文件。 其他模式是EXECUTABLE_FILE(它是一个可执行文件)和SYMLINK(它指定一个符号链接)。 对于目录条目,文件模式始终为TREE。

同样,您将需要一个ObjectInserter。 其重载的insert()方法之一接受TreeFormatter并将其写入对象数据库。

现在,您可以使用TreeWalk检索和检查树对象:

TreeWalk treeWalk = new TreeWalk( repository );
treeWalk.addTree( treeId );
treeWalk.next();
String filename = treeWalk.getPathString(); // hello-world.txt

实际上,TreeWalk旨在对添加的树及其子树进行迭代。 但是由于我们知道只有一个条目,因此只需调用next()就足够了。

如果使用本地Git查看刚刚编写的树对象,则会看到以下内容:

$ git cat-file -p 44d52a975c793e5a4115e315b8d89369e2919e51
100644 blob c57eff55ebc0c54973903af5f72bac72762cf4f4    hello-world.txt

现在您已经拥有提交的必要条件,让我们创建提交对象本身。

提交对象

提交对象 (通过树对象)引用构成提交的文件以及一些元数据。 详细而言,提交包括:

  • 指向树对象的指针
  • 指向零个或多个父提交的指针(稍后会详细介绍)
  • 提交消息
  • 以及作者和提交者

由于提交对象只是对象数据库中的另一个对象,因此它也被在其内容上计算出的SHA-1哈希密封。

为了形成提交对象,JGit提供了CommitBuilder实用程序类。

CommitBuilder commitBuilder = new CommitBuilder();
commitBuilder.setTreeId( treeId );
commitBuilder.setMessage( "My first commit!" );
PersonIdent person = new PersonIdent( "me", "me@example.com" );
commitBuilder.setAuthor( person );
commitBuilder.setCommitter( person );
ObjectInserter objectInserter = repository.newObjectInserter();
ObjectId commitId = objectInserter.insert( commitBuilder );
objectInserter.flush();

使用它很简单,它具有针对提交的所有属性的setter方法。

作者和提交者通过PersonIdent类表示,该类包含名称,电子邮件,时间戳和时区。 此处使用的构造函数应用给定的名称和电子邮件,并采用当前时间和时区。

剩下的应该已经很熟悉了:ObjectInserter用于实际写入提交对象并返回提交ID。

要从存储库中检索提交对象,可以再次使用ObjectReader:

ObjectReader objectReader = repository.newObjectReader();
ObjectLoader objectLoader = objectReader.open( commitId );
RevCommit commit = RevCommit.parse( objectLoader.getBytes() );

生成的RevCommit表示具有与CommitBuilder中指定的相同属性的提交。

再一次-仔细检查git cat-file的输出:

$ git cat-file -p 783341299c95ddda51e6b2393c16deaf0c92d5a0
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
author me <me@example.com> 1412872859 +0200
committer me <me@example.com> 1412872859 +0200My first commit!

父母

父母链形成了Git仓库的历史,并建模了有向无环图 。 这意味着提交“遵循”一个方向

一个提交可以有零个或多个父母。 存储库中的第一个提交没有父提交(aka根提交)。 第二个提交又将第一个提交作为其父级,依此类推。

创建多个根提交完全合法。 如果使用git checkout --orphan new_branch将创建一个新的孤立分支并将其切换到。 在此分支上进行的第一次提交将没有父项,并且将成为与所有其他提交断开连接的新历史记录的根。

jgit-commit-merge
如果开始分支并最终合并变化的分歧线,通常会导致合并提交 。 并且这样的提交具有分支的父分支作为其父分支。

为了构造父提交,需要在CommitBuilder中指定父提交的ID。

commitBuilder.setParents( parentId );

还可以查询RevCommit类,该类表示存储库中的提交,并且可以查询其父级。 它的getParents()和getParent(int)方法返回全部或第n个父RevCommit。

请注意,尽管这些方法返回RevCommits,但这些方法尚未完全解决。 设置其ID属性后,所有其他属性(fullMessage,author,committer等)均未设置。 因此,例如,尝试调用parent.getFullMessage()会引发NullPointerException。 为了实际使用父提交,您需要通过上面概述的ObjectReader检索完整的RevCommit,或者使用RevWalk加载和解析提交标头:

RevWalk revWalk = new RevWalk( repository );
revWalk.parseHeaders( parentCommit );

总而言之,请记住将返回的父提交视为ObjectId而不是RevCommits。

有关树对象的更多信息

如果要将文件存储在子目录中,则需要自己构造子树。 假设您要将文件“ file.txt”的内容存储在文件夹“文件夹”中。

首先,为子树创建并存储TreeFormatter,该子树具有文件的条目:

TreeFormatter subtreeFormatter = new TreeFormatter();
subtreeFormatter.append( "file.txt", FileMode.REGULAR_FILE, blobId );
ObjectId subtreeId = objectInserter.insert( subtreeFormatter );

然后,创建并存储TreeFormatter以及一个表示文件夹的条目,并指向刚创建的子树。

TreeFormatter treeFormatter = new TreeFormatter();
treeFormatter.append( "folder", FileMode.TREE, subtreeId );
ObjectId treeId = objectInserter.insert( treeFormatter );

jgit-internals树
条目的文件模式为TREE,表示目录,其ID指向保存文件条目的子树。 返回的treeId是将传递给CommitBuilder的那个。

Git对树对象中的条目要求一定的排序顺序。 我在这里找到的“ Git数据格式”文档指出:

树条目按包含条目名称的字节序列排序。 但是,出于排序比较的目的,比较树对象的条目,就好像条目名称字节序列具有尾随的ASCII'/'(0x2f)。

要读取树对象的内容,可以再次使用TreeWalk。 但是这一次,如果要访问所有条目,则需要告诉它递归到子树中。 而且,如果您希望看到指向树的条目,请不要忘记将postOrderTraversal设置为true。 否则将被跳过。

最终,整个TreeWalk循环将如下所示:

TreeWalk treeWalk = new TreeWalk( repository );
treeWalk.addTree( treeId );
treeWalk.setRecursive( true );
treeWalk.setPostOrderTraversal( true );
while( treeWalk.next() ) {int fileMode = Integer.parseInt( treeWalk.getFileMode( 0 ).toString() );String objectId = treeWalk.getObjectId( 0 ).name();String path = treeWalk.getPathString();System.out.println( String.format( "%06d %s %s", fileMode, objectId, path ) );
}

…并将导致以下输出:

100644 6b584e8ece562ebffc15d38808cd6b98fc3d97ea folder/file.txt 
040000 541550ddcf8a29bcd80b0800a142a7d47890cfd6 folder

尽管我发现API不是很直观,但它可以完成工作并显示树对象的所有详细信息。

总结Git内部

毫无疑问,对于常见用例,建议将高层Add-和CommitCommands提交到存储库中。 不过,我发现值得深入研究JGit和Git,希望您也这样做。 而且,在您需要将文件提交到没有工作目录和/或索引的存储库的情况下(这种情况不太常见),这里提供的信息可能会有所帮助。

如果您想亲自尝试此处列出的示例,建议您设置JGit并对其源代码和JavaDoc进行访问,以便您拥有有意义的上下文信息,内容帮助,调试源等。

  • 完整的源代码托管在这里: https : //gist.github.com/rherrmann/02d8d4fe81bb60d9049e

为简便起见,此处显示的示例省略了用于释放分配的资源的代码。 请参考完整的源代码以获取所有详细信息。

翻译自: https://www.javacodegeeks.com/2014/10/explore-git-internals-with-the-jgit-api.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Html5 布局方式

在Html5之前&#xff0c;统一采用的是Div css的方式进行布局&#xff0c;但是却和开发人员的命名方式&#xff0c;喜好有关。在新的Html5中&#xff0c;布局却显得更加人性化&#xff0c;更易理解了。如增加了Header&#xff0c;Footer&#xff0c;Section&#xff0c;Aside标签…

PrimeFaces Mobile入门

介绍 如果您已经开发了利用PrimeFaces的应用程序&#xff0c;或者打算开发可在台式机和移动设备上使用的Web应用程序&#xff0c;请考虑将PrimeFaces Mobile用于您的移动实施。 这篇博客文章将介绍一些基础知识&#xff0c;以帮助您开始为现有的PrimeFaces应用程序开发移动界面…

attachEvent和addEventListener

attachEvent和addEventListener在前端开发过程中经常性的使用&#xff0c;他们都可以用来绑定脚本事件&#xff0c;取代在html中写 obj.οnclickmethod。相同点&#xff1a; 它们都是DOM对象的方法&#xff0c;可以实现一种事件绑定多个事件处理函数。 obj document.getElemen…

如果删除github上项目的文件

1. 你要有前面一章的开发平台和github插件&#xff0c;下面就是基于前面来做的。 如何删掉你github上的文件呢&#xff1f;想必你的电脑有一个下载的git工具了&#xff0c;如果还是没有的话&#xff0c;请用npm下载一个git。这是我已经下载好的。 2. 然后打开这个git&#xff…

在WildFly和OpenShift上的WebSocket聊天

聊天是解释WebSocket的最典型示例之一。 它是一个相当常用的界面&#xff0c;可以很容易地解释WebSocket的基本概念。 当然&#xff0c;Java EE 7 WebSocket也有一个&#xff0c; 在这里可用 &#xff01; 您可以使用以下步骤在WildFly上轻松运行它&#xff1a; curl -O http:…

Ubuntu20.04纯命令配置PCL(点云库)

Ubuntu20.04纯命令配置PCL&#xff08;点云库&#xff09; 最近在学习点云库&#xff08;PCL&#xff09;的使用&#xff0c;第一步就是在自己的电脑安装配置PCL。 首先&#xff0c;对于ubuntu 16.04以上版本&#xff0c;可以直接使用命令进行安装&#xff0c;新建好一个文件夹…

css html应用实例1:滑动门技术的简单实现

关于滑动门&#xff0c;现在的页面中好多地方都会用到滑动门&#xff0c;一般用作于导航背景&#xff0c;它的官方解释如下&#xff1a; 滑动门&#xff1a;根据文本自适应大小&#xff0c;根据背景的层叠性制作&#xff0c;并允许他们在彼此之上进行滑动&#xff0c;以创造出…

魔戒1

转载于:https://www.cnblogs.com/moonlightpeng/p/11240880.html

jQuery应用实例2:简单动画

效果&#xff1a; 代码&#xff1a; 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2 <html xmlns"http://www.w3.org/1999/xhtml">3 <head>…

Spring Data JPA教程:简介

创建使用Java Persistence API的存储库是一个繁琐的过程&#xff0c;需要大量时间&#xff0c;并且需要大量样板代码。 通过执行以下步骤&#xff0c;我们可以消除一些样板代码&#xff1a; 创建一个抽象的基础存储库类&#xff0c;该类为实体提供CRUD操作。 创建扩展抽象基础…

对怀孕的人有害的食物。。。朋友们记住咯!(欢迎转载)

有几个同事和朋友要生BB啦&#xff0c;好东东&#xff0c;转给你们提前学习一下~ 容易流产食物&#xff1a; 1、螃蟹&#xff1a;它味道鲜美&#xff0c;但其性寒凉&#xff0c;有活血祛瘀之功&#xff0c;故对孕妇不利&#xff0c;尤其是蟹爪&#xff0c;有明显的堕胎作用。 2…

Vss服务端用户存在,但客户端登陆不进去

打开客户端Vss提示“Cannot find SS.INI file for user userName”,这个错误是找不到用户userName的SS.INI文件。 解决办法 在服务器上找到Vss共享的文件夹&#xff0c;打开此文件夹下的users文件夹&#xff0c;然后找到userName文件夹打开后&#xff1a; 如果没有SS.INI文件&a…

Red Hat Enterprise 5 server 上安装 memcached 的问题记录

国内私募机构九鼎控股打造APP&#xff0c;来就送 20元现金领取地址&#xff1a;http://jdb.jiudingcapital.com/phone.html内部邀请码&#xff1a;C8E245J &#xff08;不写邀请码&#xff0c;没有现金送&#xff09;国内私募机构九鼎控股打造&#xff0c;九鼎投资是在全国股份…

Java扩展机制可加载所有JAR

Java扩展机制在Java教程中被描述为“一种标准的&#xff0c;可伸缩的方式&#xff0c;以使自定义API可供Java平台上运行的所有应用程序使用。” 如了解扩展类加载中所述 &#xff0c;“扩展框架利用类加载委托机制”&#xff0c;其中扩展类在rt.jar &#xff08;和相关的JAR&am…

C++的文艺复兴: Why C++? 王者归来

因为又有人叫我去Quora的C2C站去回答问题了&#xff0c;这回是 关于 《2012 不宜进入的三个技术点ActionScript&#xff0c;Thread 和 C&#xff0c; C争议的争议最大。(要我说&#xff0c;.NET比C更需要慎重进入&#xff0c;呵)。我就在这里回复一下这个问题吧。 正好我前段时…

Drools和jBPM KIE A​​pps平台

随着Drools和jBPM&#xff08;KIE&#xff09;6系列出现了一个新的工作台&#xff0c;并有望最终实现用户的可扩展性。 我终于有了一些预告片&#xff0c;以显示此工作原理以及所存储的内容。 确保选择1080p并全屏显示&#xff0c;以达到最佳效果。 &#xff08;点击放大&…

js 严格模式

一、概述 除了正常运行模式&#xff0c;ECMAscript 5添加了第二种运行模式&#xff1a;"严格模式"&#xff08;strict mode&#xff09;。顾名思义&#xff0c;这种模式使得Javascript在更严格的条件下运行。 设立"严格模式"的目的&#xff0c;主要有以下…

模态对话框和全选反选

一、目标 制作一个表格&#xff0c;第一行分别为选择、主机名和端口增加一个按钮&#xff0c;名称为添加点击添加按钮&#xff0c;出现一个半透明的遮罩层&#xff0c;遮罩层中间有个弹出框弹出框中有两个输入框&#xff0c;分别为主机名和端口&#xff0c;还有两个按钮&#…

(转)iReaper for wp7正式发布

原文地址&#xff1a;http://www.cnblogs.com/AlexCheng/archive/2012/02/06/2339968.htmliReaper for windows phone 7今天正式发布了。有windows phone 7手机的朋友可以通过手机在线收听收看webcast中文课程。只要你有网络任何时间任何地点都可以学习微软技术&#xff0c;为您…

Neo4j:Cypher –避免热切

当心渴望的管道 尽管我喜欢Cypher的LOAD CSV命令使它容易地将数据获取到Neo4j中的方法&#xff0c;但它目前打破了最不惊奇的规则&#xff0c;因为它急切地在所有行中加载某些查询&#xff0c;即使是那些使用定期提交的查询。 这是我的同事Michael在第二篇博客文章中指出的&a…