How to use JGit to get list of changes in files?

Whitecat picture Whitecat · Oct 8, 2016 · Viewed 7.2k times · Source

Using JGit, I want to get a list of changes in files of a commits as is possible with git log --full-history -p -1 <hash-id>.

Is this possible? If so, how do you do it?

I know how to get each commit, and look at the commit message:

//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();

What allows me to get the changes in the files?

For example I write:

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

The response is

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);

I want to have something like the following. Change is a made up class:

//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();

The output would look something like:

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

Answer

R&#252;diger Herrmann picture Rüdiger Herrmann · Oct 8, 2016

JGit has a very simple diff command that writes a textual diff of the changes between two commits to an output stream.

For example:

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

Probably more interesting is the list of DiffEntry returned after calling the command. Each DiffEntry represents a changed file and tells its path name, whether it was added, changed, or deleted, pointers (blob-ID's) to the old and new content and more.

And from each DiffEntry, you can obtain an EditList which holds information about which lines were changed.

For Example:

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();
}

This code also shows how to obtain diff entries with more detailed control without using the DiffCommand.

Just recently I wrote an entire blog post about JGit's diff APIs. For more details please see here: http://www.codeaffine.com/2016/06/16/jgit-diff/