How to show changes between commits with JGit
To obtain the tree of the head commit, call
git.getRepository().resolve( "HEAD^{tree}" )
and to obtain the tree of the parent of the HEAD commit, call
git.getRepository().resolve( "HEAD~1^{tree}" )
Search for 'Git caret and tilde' if you are interested in more details.
To summarize, here goes a snippet that computes the diff of two commits:
File file = new File( git.getRepository().getWorkTree(), "file.txt" );
writeFile( file, "first version" );
RevCommit newCommit = commitChanges();
writeFile( file, "second version" );
RevCommit oldCommit = commitChanges();
ObjectReader reader = git.getRepository().newObjectReader();
CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
ObjectId oldTree = git.getRepository().resolve( "HEAD^{tree}" ); // equals newCommit.getTree()
oldTreeIter.reset( reader, oldTree );
CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
ObjectId newTree = git.getRepository().resolve( "HEAD~1^{tree}" ); // equals oldCommit.getTree()
newTreeIter.reset( reader, newTree );
DiffFormatter df = new DiffFormatter( new ByteArrayOutputStream() ); // use NullOutputStream.INSTANCE if you don't need the diff output
df.setRepository( git.getRepository() );
List<DiffEntry> entries = df.scan( oldTreeIter, newTreeIter );
for( DiffEntry entry : entries ) {
System.out.println( entry );
}
private RevCommit commitChanges() throws GitAPIException {
git.add().addFilepattern( "." ).call();
return git.commit().setMessage( "commit message" ).call();
}
private static void writeFile( File file, String content ) throws IOException {
FileOutputStream outputStream = new FileOutputStream( file );
outputStream.write( content.getBytes( "UTF-8" ) );
outputStream.close();
}
For further considerations about showing changes between commits, you may want to read this in-depth discussion of JGit's diff APIs that can be found here: http://www.codeaffine.com/2016/06/16/jgit-diff/
I am using the following code to print diffs between two commits. Using DiffFormatter.scan
seems simpler than the other solutions I have seen on SO.
It might be that I am missing some case(s) where this is not supported, but it works fine for our use case.
public static void main(String[] args) throws Exception {
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File("c:/temp/jgit-test/.git")).build();
// Here we get the head commit and it's first parent.
// Adjust to your needs to locate the proper commits.
RevCommit headCommit = getHeadCommit(repository);
RevCommit diffWith = headCommit.getParent(0);
FileOutputStream stdout = new FileOutputStream(FileDescriptor.out);
try (DiffFormatter diffFormatter = new DiffFormatter(stdout)) {
diffFormatter.setRepository(repository);
for (DiffEntry entry : diffFormatter.scan(diffWith, headCommit)) {
diffFormatter.format(diffFormatter.toFileHeader(entry));
}
}
}
private static RevCommit getHeadCommit(Repository repository) throws Exception {
try (Git git = new Git(repository)) {
Iterable<RevCommit> history = git.log().setMaxCount(1).call();
return history.iterator().next();
}
}