如何使用JGit获取文件中的更改列表?

2022-09-04 01:56:39

使用JGit,我想获得提交文件中的更改列表,就像使用.git log --full-history -p -1 <hash-id>

这可能吗?如果是这样,你是怎么做到的?

我知道如何获取每个提交,并查看提交消息:

//Load repo
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repo = builder.setGitDir(new File("/path/to/repo/.git")).setMustExist(true).build();
Git git = new Git(repo);
//get commits
Iterable<RevCommit> log = git.log().call();
for (RevCommit commit : log) {
    //Shows the hashid
    System.out.println("LogCommit: " + commit);
    //Shows only commit message
    String logMessage = commit.getFullMessage();
    System.out.println("LogMessage: " + logMessage);
}
git.close();

什么允许我获得文件中的更改?

例如,我写道:

git log --full-history -p -1 8309c1262e1b7ffce8fc86efc1ae5777a4a96777

响应是

commit 8309c1262e1b7ffce8fc86efc1ae5777a4a96777
Author: <redacted>
Date:   Thu Aug 4 12:15:23 2016 -0400

    Fixed typo in commit

diff --git a/Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java b/Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java
index fa55e7e..4f3c155 100644
--- a/Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java
+++ b/Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java
@@ -56,6 +57,7 @@ public abstract class BaseService {

     protected AssertionType getAssertion(WebServiceContext context, AssertionType assertionIn) {
         AssertionType assertion;
-        WSAHeaderHelper wsaHlpr = new WSAHeaderHelper();
+        WSAHeaderHelper wsaHelper = new WSAHeaderHelper();
         if (assertionIn == null) {
             assertion = SAML2AssertionExtractor.getInstance().extractSamlAssertion(context);

我想要像下面这样的东西。更改是一个虚构的类:

//Load repo
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repo = builder.setGitDir(new File("/path/to/repo/.git")).setMustExist(true).build();
Git git = new Git(repo);
//get commits
Iterable<RevCommit> log = git.log().call();
for (RevCommit commit : log) {
    //Shows the hashid
    System.out.println("LogCommit: " + commit);
    //Shows only commit message
    String logMessage = commit.getFullMessage();
    System.out.println("LogMessage: " + logMessage);
    List<Change> changes = commit.getChanges();
    for(Change change: changes):
        System.out.println("File: " + change.getFile());
        System.out.println("Change: " + change.getChange());
        System.out.println("ChangeType: " + change.getChangeType());
}
git.close();

输出将如下所示:

LogCommit: 8309c1262e1b7ffce8fc86efc1ae5777a4a96777
LogMessage: Fixed typo in commit
File: Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java
Change: WSAHeaderHelper wsaHlpr = new WSAHeaderHelper();
ChangeType: D
File: Product/Production/Common/CONNECTCoreLib/src/main/java/gov/hhs/fha/nhinc/messaging/server/BaseService.java
Change: WSAHeaderHelper wsaHelper = new WSAHeaderHelper();
ChangeType: A

答案 1

JGit有一个非常简单的命令,它将两个提交之间的更改的文本差异写入输出流。diff

例如:

OutputStream outputStream = ...
List<DiffEntry> diffEntries = git.diff().setOutputStream(outputStream).call();

可能更有趣的是调用命令后返回的列表。每个文件都表示一个已更改的文件,并告知其路径名(是添加、更改还是删除)、指向新旧内容的指针(blob-ID)等。DiffEntryDiffEntry

从每个 ,您可以获得一个 哪个保存有关哪些行已更改的信息。DiffEntryEditList

例如:

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

此代码还演示如何在不使用 的情况下获取具有更详细控件的差异条目。DiffCommand

就在最近,我写了一篇关于JGit的diff API的博客文章。有关更多详细信息,请参阅此处:http://www.codeaffine.com/2016/06/16/jgit-diff/


答案 2

感谢吕迪格·赫尔曼(Rüdiger Herrmann)的反馈和在他的要点上找到的部分代码。

我创建了一个方法,其中包含3个帮助程序函数,其工作方式与diffCommit(String hashID)git log --full-history -p -1 <hash-id>.

private Git git;
private Repository repo;

private void diffCommit(String hashID) throws IOException {
    //Initialize repositories.
    FileRepositoryBuilder builder = new FileRepositoryBuilder();
    repo = builder.setGitDir(new File("/path/to/repo" + "/.git")).setMustExist(true)
            .build();
    git = new Git(repo);

    //Get the commit you are looking for.
    RevCommit newCommit;
    try (RevWalk walk = new RevWalk(repo)) {
        newCommit = walk.parseCommit(repo.resolve(hashID));
    }

    System.out.println("LogCommit: " + newCommit);
    String logMessage = newCommit.getFullMessage();
    System.out.println("LogMessage: " + logMessage);
    //Print diff of the commit with the previous one.
    System.out.println(getDiffOfCommit(newCommit));

}
//Helper gets the diff as a string.
private String getDiffOfCommit(RevCommit newCommit) throws IOException {

    //Get commit that is previous to the current one.
    RevCommit oldCommit = getPrevHash(newCommit);
    if(oldCommit == null){
        return "Start of repo";
    }
    //Use treeIterator to diff.
    AbstractTreeIterator oldTreeIterator = getCanonicalTreeParser(oldCommit);
    AbstractTreeIterator newTreeIterator = getCanonicalTreeParser(newCommit);
    OutputStream outputStream = new ByteArrayOutputStream();
    try (DiffFormatter formatter = new DiffFormatter(outputStream)) {
        formatter.setRepository(git.getRepository());
        formatter.format(oldTreeIterator, newTreeIterator);
    }
    String diff = outputStream.toString();
    return diff;
}
//Helper function to get the previous commit.
public RevCommit getPrevHash(RevCommit commit)  throws  IOException {

    try (RevWalk walk = new RevWalk(repo)) {
        // Starting point
        walk.markStart(commit);
        int count = 0;
        for (RevCommit rev : walk) {
            // got the previous commit.
            if (count == 1) {
                return rev;
            }
            count++;
        }
        walk.dispose();
    }
    //Reached end and no previous commits.
    return null;
}
//Helper function to get the tree of the changes in a commit. Written by Rüdiger Herrmann
private AbstractTreeIterator getCanonicalTreeParser(ObjectId commitId) throws IOException {
    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, treeId);
        }
    }
}

下面是将生成类似于git log --full-history

public void commit_logs() throws IOException, NoHeadException, GitAPIException {
    List<String> logMessages = new ArrayList<String>();
    FileRepositoryBuilder builder = new FileRepositoryBuilder();
    Repository repo = builder.setGitDir(new File("/path/to/repo" + "/.git"))
            .setMustExist(true).build();
    git = new Git(repo);
    Iterable<RevCommit> log = git.log().call();
    RevCommit previousCommit = null;
    for (RevCommit commit : log) {
        if (previousCommit != null) {
            AbstractTreeIterator oldTreeIterator = getCanonicalTreeParser( previousCommit );
            AbstractTreeIterator newTreeIterator = getCanonicalTreeParser( commit );
            OutputStream outputStream = new ByteArrayOutputStream();
            try( DiffFormatter formatter = new DiffFormatter( outputStream ) ) {
              formatter.setRepository( git.getRepository() );
              formatter.format( oldTreeIterator, newTreeIterator );
            }
            String diff = outputStream.toString();
            System.out.println(diff);
        }
        System.out.println("LogCommit: " + commit);
        String logMessage = commit.getFullMessage();
        System.out.println("LogMessage: " + logMessage);
        logMessages.add(logMessage.trim());
        previousCommit = commit;
    }
    git.close();
}


  private AbstractTreeIterator getCanonicalTreeParser( ObjectId commitId ) throws IOException {
    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, treeId );
      }
    }
  }

推荐