Q1-java 程序如何访问 git 的各种信息?比如分支信息,分支差异,提交记录等等

Java程序可以使用Git客户端库(如JGit)来访问Git的各种信息。

这些库提供了Java API,使得Java程序可以直接与Git进行交互和查询。

下面是一些常见的Git信息访问操作示例:

  1. 访问分支信息:
    • 获取所有分支:使用 Git.branchList() 方法可以获取所有分支的列表。
    • 获取当前分支:使用 Git.getRepository().getBranch() 方法可以获取当前所在的分支。
    • 创建新分支:使用 Git.branchCreate().setName("branch_name").call() 方法可以创建一个新分支。
  2. 访问提交记录:
    • 获取提交记录:使用 Git.log().all().call() 方法可以获取所有提交记录的列表。
    • 获取某个分支的提交记录:使用 Git.log().add(git.getRepository().resolve("branch_name")).call() 方法可以获取特定分支的提交记录。
    • 获取某个提交的详细信息:使用 Git.show().setObjectId(commitId).call() 方法可以获取特定提交的详细信息。
  3. 访问分支差异:
    • 获取两个分支之间的差异:使用 Git.diff().setOldTree(oldTree).setNewTree(newTree).call() 方法可以获取两个分支之间的差异。
    • 获取某个提交与其父提交之间的差异:使用 Git.diff().setOldTree(parentCommit).setNewTree(commit).call() 方法可以获取某个提交与其父提交之间的差异。

这些只是一些常见的操作示例,实际上,Git客户端库(如JGit)提供了更多的API和方法来访问Git的各种信息。具体的使用方法可以参考相应的Git客户端库的文档和示例代码。

Q2-可以访问 git 的开源库

Java可以使用以下开源工具库来访问Git信息:

  1. JGit:JGit是一个纯Java实现的Git客户端库,由Eclipse项目提供。它提供了Java API来与Git进行交互和查询,包括访问分支、提交记录、差异等功能。

  2. GitPython:GitPython是一个用于访问Git仓库的Python库,它提供了Python API来操作和查询Git信息。Java程序可以通过使用Jython或通过与Python交互的方式来使用GitPython库。

  3. LibGit2:LibGit2是一个用C语言编写的跨平台Git客户端库,它提供了多种语言的绑定,包括Java。通过使用LibGit2的Java绑定,可以在Java程序中访问Git仓库的信息。

Q3-一些工具插件

不,除了JGit之外,还有其他Java API可以用于访问Git信息。

以下是一些常见的Java API和工具库:

  1. Gitblit: Gitblit是一个基于Java的Git服务器,它提供了RESTful API和Java API来管理和访问Git仓库。它可以用作独立的Git服务器,也可以嵌入到其他Java应用程序中。

  2. Apache GitBox: Apache GitBox是Apache软件基金会提供的Git托管服务,它提供了Java API来访问和管理Git仓库。它使用了JGit作为底层实现。

  3. EGit: EGit是Eclipse项目提供的Git插件,它提供了Java API来访问和操作Git仓库。它是基于JGit的,可以与Eclipse集成使用。

  4. SourceTree API: SourceTree是一款流行的Git客户端,它提供了Java API来访问Git仓库的信息和操作。它可以用于自动化Git流程的集成。

这些工具库提供了不同的功能和集成选项,可以根据项目需求选择适合的工具库。

jgit 入门例子

maven

<dependency>
    <groupId>org.eclipse.jgit</groupId>
    <artifactId>org.eclipse.jgit</artifactId>
    <version>5.11.0.202103091610-r</version>
</dependency>

例子

package org.example;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.revwalk.RevCommit;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class JGitExample {

    public static void main(String[] args) {
        try {
            // 打开现有的Git仓库
            Repository repository = new RepositoryBuilder()
                    .setGitDir(new File("D:\\code\\github\\jgit-learn/.git"))
                    .build();

            // 创建Git对象
            Git git = new Git(repository);

            // 获取所有分支
            List<Ref> branches = git.branchList().call();
            for (Ref branch : branches) {
                System.out.println("Branch: " + branch.getName());
            }

            // 获取最新的提交记录
            Iterable<RevCommit> commits = git.log().setMaxCount(10).call();
            for (RevCommit commit : commits) {
                System.out.println("Commit: " + commit.getName());
                System.out.println("Author: " + commit.getAuthorIdent().getName());
                System.out.println("Message: " + commit.getFullMessage());
                System.out.println("-----------------------");
            }

            // 创建新分支
            Ref newBranch = git.branchCreate().setName("new-branch").call();
            System.out.println("New branch created: " + newBranch.getName());

            // 关闭Git对象和仓库
            git.close();
            repository.close();
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
    }
}

日志如下:

Branch: refs/heads/master
Commit: c17645337ed4b158a6269e76412a76d1d4679bb6
Author: d
Message: Initial commit

-----------------------
New branch created: refs/heads/new-branch

可以看到分支的信息。

也可以创建分支。

git 分支差异对比

代码

package org.example;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class JGitDifferExample {

    public static void main(String[] args) {
        try {
            // 打开现有的Git仓库
            Repository repository = new RepositoryBuilder()
                    .setGitDir(new File("D:\\code\\github\\jgit-learn/.git"))
                    .build();

            // 创建Git对象
            Git git = new Git(repository);

            // 比较两个分支的差异
            List<DiffEntry> diffs = git.diff()
                    .setOldTree(prepareTreeParser(git, repository, "refs/heads/release_1.0.0"))
                    .setNewTree(prepareTreeParser(git,repository, "refs/heads/master"))
                    .call();

            // 打印差异
            for (DiffEntry diff : diffs) {
                System.out.println("Diff: " + diff.getChangeType() +
                        ", old: " + diff.getOldPath() +
                        ", new: " + diff.getNewPath());
            }

            // 关闭Git对象和仓库
            git.close();
            repository.close();
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
    }

    private static AbstractTreeIterator prepareTreeParser(Git git, Repository repository, String branch) throws IOException {
        Ref head = getRefByName(git, branch);
        RevWalk walk = new RevWalk(repository);
        RevCommit commit = walk.parseCommit(head.getObjectId());
        RevTree tree = walk.parseTree(commit.getTree().getId());

        CanonicalTreeParser treeParser = new CanonicalTreeParser();
        try (ObjectReader reader = repository.newObjectReader()) {
            treeParser.reset(reader, tree.getId());
        }

        walk.dispose();

        return treeParser;
    }

    private static Ref getRefByName(Git git, String branchName) {
        try {
            // 获取所有分支
            List<Ref> branches = git.branchList().call();

            for (Ref branch : branches) {
//                System.out.println("Branch: " + branch.getName());
                if(branch.getName().equals(branchName)) {
                    return branch;
                }
            }

            return null;
        } catch (GitAPIException e) {
            throw new RuntimeException(e);
        }
    }

}

日志

Diff: DELETE, old: src/main/java/org/example/JGitDifferExample.java, new: /dev/null
Diff: MODIFY, old: src/main/java/org/example/Main.java, new: src/main/java/org/example/Main.java

可以看到差异的类别,文件信息。

git 差异精确到行

说明

如果是编辑,可以精确到具体的行信息。

这样我们就可以知道具体变化的行,而不用关心整个文件。

代码

// 打印差异
for (DiffEntry diff : diffs) {
    System.out.println("Diff: " + diff.getChangeType() +
            ", old: " + diff.getOldPath() +
            ", new: " + diff.getNewPath());
    outputModifyByLine(diff, repository);
}

对应的行变化内容:

private static void outputModifyByLine(DiffEntry diff, Repository repository)  {
    try {
        if (diff.getChangeType() == DiffEntry.ChangeType.MODIFY) {
            // 获取变更的具体行号
            DiffFormatter formatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
            formatter.setRepository(repository);
            FileHeader fileHeader = formatter.toFileHeader(diff);
            for (HunkHeader hunkHeader : fileHeader.getHunks()) {
                for (Edit edit : hunkHeader.toEditList()) {
                    if (edit.getType() != Edit.Type.DELETE) {
                        System.out.println("Line range: " + (edit.getBeginA() + 1) + " - " + (edit.getEndA() + 1));
                    }
                }
            }
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

对应的结果:

Diff: DELETE, old: src/main/java/org/example/JGitDifferExample.java, new: /dev/null
Diff: MODIFY, old: src/main/java/org/example/Main.java, new: src/main/java/org/example/Main.java
Line range: 5 - 6

拓展阅读

jgit-cookbook

基于jacoco,JGit 二次开发增量代码覆盖率工具

参考资料

chat