当前位置: 首页 > 工具软件 > JGit > 使用案例 >

jgit使用_有什么不同? 使用JGit创建差异

姜献
2023-12-01

jgit使用

在本文中,我将深入探讨如何通过JGit进行修订和创建补丁的细节。 从高级DiffCommand一直到功能更广泛的API,以查找文件中的特定更改。

DiffCommand,带我

diff命令可用于比较两个修订并报告更改,添加或删除的文件。 因此,在这种情况下,修订既可以是提交,也可以是工作目录或索引。

在JGit中创建差异的简单形式如下:

git.diff().setOutputStream( System.out ).call();

如果在调用时未指定要比较的修订版本,则会确定工作目录和索引之间的差异。

该命令将diff的文本表示形式打印到指定的输出流:

diff --git a/file.txt b/file.txt
index 19def74..d5fcacb 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 existing line
+added line
\ No newline at end of file

另外,call()方法还返回DiffEntries的列表。 这些数据结构描述了添加,删除和更改的文件,还可以用于确定特定文件中的更改。

但是如何比较两个任意修订版本? 通过仔细查看DiffCommand,可以发现它实际上是比较两棵树而不是修订版。 这就解释了为什么也可以比较工作目录和索引(它们本身是树)的原因。

因此,diff命令期望类型为AbstractTreeIterator的参数指定要比较的旧树和新树。 有时,旧的和​​新的也称为源和目的地,或简称为a和b。 要了解有关Git中的树的更多信息,您可能需要阅读JGit API的Explore Git Internals

树迭代器

但是如何获得特定的树迭代器呢? 查看AbstractTreeIterator的类型层次结构,可以发现存在三种感兴趣的实现。

FileTreeIterator可用于访问存储库的工作目录。 这样将存储库传递给其构造函数,就可以使用了。

AbstractTreeIterator treeIterator = new FileTreeIterator( git.getRepository() );

DirCacheIterator揭示目录高速缓存(也称为索引)的内容,并且可以与FileTreeIterator相似的方式创建。 给定一个存储库,我们可以告诉它读取索引并将该实例传递给DirCacheIterator,如下所示:

AbstractTreeIterator treeIterator = new DirCacheIterator( git.getRepository().readDirCache() );

但是,最有趣的可能是CanonicalTreeParser 。 可以将其配置为解析任意Git树对象。 因此,需要使用存储库中树对象的ID 重置它。 设置完成后,即可用于遍历此树的内容。

下面的示例可以最好地说明这一点:

CanonicalTreeParser treeParser = new CanonicalTreeParser();
ObjectId treeId = repository.resolve( "my-branch^{tree}" );
try( ObjectReader reader = repository.newObjectReader() ) {
  treeParser.reset( reader, treeId );
}

树解析器配置为遍历my-branch指向的提交的树。

注意,如果存在多个匹配项,则resolve()返回的值是不确定的。 例如,如果存在分支和具有该名称的缩写提交ID,则调用resolve( "aabbccdde^{tree}" )可能返回错误的树。 因此,最好使用完全合格的引用(例如refs/heads/my-branch来引用名为my-tag的标签的分支my-branch或refs/tags/my-tag my-tag。

如果提交的ID已经以ObjectId (或AnyObjectId)的形式可用,请使用以下代码段获取其树ID:

try( RevWalk walk = new RevWalk( git.getRepository() ) ) {
  RevCommit commit = walk.parseCommit( commitId );
  ObjectId treeId = commit.getTree().getId();
  try( ObjectReader reader = git.getRepository().newObjectReader() ) {
    return new CanonicalTreeParser( null, reader, tree );
  }
}

该代码假定给定的对象ID引用了提交,并解析了关联的RevCommit,后者又保存了相应树的ID。

重访DiffCommand

现在我们知道了如何获取树迭代器,其余的操作很简单:

git.diff()
  .setOldTree( oldTreeIterator )
  .setNewTree( newTreeIterator )
  .call();

使用setOldTree()和setNewTree()方法,可以指定要比较的树。

除了这些主要属性外,还可以控制命令的其他几个方面:

  • setPathFilter允许将扫描文件限制为存储库中的某些路径
  • setSourcePrefix和setDetinationPrefix更改源(旧)路径和目标(新)路径的前缀。 默认值为a/b/
  • setContextLines更改上下文行的数量,即在修改的行之前和之后打印的行数。 默认值为三。
  • setProgressMonitor允许在计算差异时跟踪进度。 您可以实现自己的进度监视器,也可以使用JGit随附的预定义监视器之一
  • setShowNameAndStatusOnly跳过生成文本输出,仅返回DiffEntries的计算列表。 (顾名思义)

除了到目前为止描述的属性之外,DiffCommand还会从 部分。

  • noPrefix:如果设置为true,则源前缀和目标前缀默认为空,而不是a/b/
  • 重命名:如果设置为true,该命令将尝试基于相似的内容来检测重命名的文件。 稍后将详细介绍重命名的内容。
  • algorithm:应使用的diff算法。 JGit当前支持myershistogram

DiffEntry,带我

如前所述,我们将仔细研究diff命令的主要输出:DiffEntry。 对于添加,删除或修改的每个文件,将返回一个单独的DiffEntry。 getChangeType()指示更改的类型,可以是ADD,DELETE或MODIFY。 如果在扫描更改时涉及到重命名检测器,则更改类型也可以是RENAME或COPY。

此外,DiffEntry包含有关文件的旧状态和新状态的信息,包括路径,模式和ID。 这些方法分别命名为getOldPath / Mode / Id和getNewPath / Mode / Id。 根据条目是表示添加还是删除,getNew或getOld方法可能返回“空”值。 JavaDoc详细说明了返回的值。 请注意,该ID引用了存储库数据库中包含文件内容的Blob对象。

在DiffCommand的掩护下

在某些情况下,DiffCommand可能不足以完成任务。 例如,在比较两个修订时检测重命名和副本,或者创建自定义补丁。 在这种情况下,请不要掩饰。

DiffCommand主要使用DiffFormatter,也可以直接访问DiffFormatter以扫描更改并创建补丁。

它的scan()方法需要新旧树的迭代器,并返回DiffEntries的列表。 还有一些重载版本,这些版本接受要提供的树对象的ID。

一个扫描更改的简单示例如下所示:

OutputStream outputStream = DisabledOutputStream.INSTANCE;
try( DiffFormatter formatter = new DiffFormatter( outputStream ) ) {
  formatter.setRepository( git.getRepository() );
  List<DiffEntry> entries = formatter.scan( oldTreeIterator, newTreeIterator );
}

format()使用的输出流在构造函数中指定。 由于我们现在对输出不感兴趣,因此提供了空输出流。 使用setRepository可以指定应扫描的存储库。 最后,将树解析器传递给scan()方法,该方法返回两者之间的更改列表。

请注意,DiffFormatter需要显式关闭或在try-with-resources语句中使用,如示例代码所示。

为了创建补丁,可以使用format()方法之一。 该补丁表示为修改旧树以使其成为新树的指令。

与scan()方法一样,format()方法接受旧树和新树的指针或迭代器。 任一侧都可以为null,以指示已添加或删除树。 在这种情况下,差异将不计算。

下面的代码段使用format()将补丁写入传递给构造函数的输出流。

OutputStream outputStream = new ByteArrayOutputStream();
try( DiffFormatter formatter = new DiffFormatter( outputStream ) ) {
  formatter.setRepository( git.getRepository() );
  formatter.format( oldTreeIterator, newTreeIterator );
}

还有重载的format()方法可打印单个DiffEntry或DiffEntries列表,这可能是由先前对scan()的调用获得的。

尽管上述示例的结果也可以使用普通的DiffCommand来完成,但让我们看一下DiffFormater还提供了什么。

如前所述,重命名文件可以在计算差异时进行关联。 要启用重命名检测,必须建议DiffFormatter使用setDetectRenames()进行此操作。 此后,可以使用getRenameDetector()获得RenameDetector进行微调。

请记住,Git是内容跟踪器, 不跟踪重命名 。 而是在diff操作期间从相似的内容推导出重命名。

此外,DiffFormatter还有一些其他属性可用于微调其行为,如下所示:

  • setAbbreviationLength:要打印的对象ID的位数。
  • setDiffAlgorithm:用于构建差异输出的算法。
  • setBinaryFileThreshold:大于此大小的文件将被视为二进制文件而非文本文件。 默认值为50 MB。
  • setDiffComparator:用于确定两行文本是否相同的比较器。 可以将比较器配置为忽略各种类型的空白。 但是,我无法让DiffFormatter忽略所有空格

再次进入

如果您对某个文件中发生的更改感兴趣,则可能需要再次查看DiffEntry和DiffFormatter。

使用diffFormatter.toFileHeader(),可以从给定的DiffEntry获得所谓的FileHeader。 通过其toEditList()方法,可以获得编辑列表。

以下代码示例显示了如何获取扫描产生的第一个差异条目的编辑列表:

OutputStream outputStream = DisabledOutputStream.INSTANCE;
try( DiffFormatter formatter = new DiffFormatter( outputStream ) ) {
  formatter.setRepository( git.getRepository() );
  List<DiffEntry> entries = formatter.scan( oldTreeIterator, newTreeIterator );
  FileHeader fileHeader = formatter.toFileHeader( entries.get( 0 ) );
  return fileHeader.toEditList();
}

此列表可以解释为需要进行修改才能将其转换为新内容的旧内容。

每个“编​​辑”都描述了一个被插入,删除或替换的区域以及受影响的行。
这些行从零开始计数,可以使用getBeginA(),getEndA(),getBeginB()和getEndB()查询。

例如,给定一个包含以下两行内容的文件:

line 1
line 3

在两行之间插入line2将导致INSERT编辑类型为A(1-1)和B(1-2)。 换句话说,将第1行替换为第1行和第2行。再次删除第2行会导致插入编辑:DELETE与A(1-2)和B(1-1)相反。 并且更改第2行的文本将产生具有相同的A和B区域1-2的REPLACE类型的Edit。

使用JGit创建差异

尽管DiffCommand使用起来非常简单,但DiffFormatter具有令人恐惧的API。 但是在您的项目中使用JGit之前,您肯定一定会将自己与库隔离开了,对吗? …从而选择更合适的API。

但是除此之外,JGit提供了一种手段来完成大多数(如果不是全部)与Git中的差异和补丁相关的任务。

全文中显示的摘录摘自一系列学习测试。 完整的源代码可以在这里找到:
https://gist.github.com/rherrmann/5341e735ce197f306949fc58e9aed141

如果您想自己尝试此处列出的示例,建议您设置JGit,使其可以访问源和JavaDoc,以便获得有意义的上下文信息,内容帮助,调试源等。

如果您有困难或其他问题,请发表评论或向友好而乐于助人的JGit社区寻求帮助。

翻译自: https://www.javacodegeeks.com/2016/06/whats-difference-creating-diffs-jgit.html

jgit使用

 类似资料: