如何使用JGit显示提交之间的更改

我试图在文件的两个提交之间显示一个git diff。 基本上,我是在https://github.com/centic9/jgit-cookbook/blob/master/src/main/java/org/dstadler/jgit/porcelain/ShowChangedFilesBetweenCommits.java中做到的。

您可以在https://github.com/svenhornberg/JGitDiff下查看我的完整代码

public class RepoDiff { public void compare(byte[] fileContentOld, byte[] fileContentNew) { try { Repository repository = createNewRepository(); Git git = new Git(repository); // create the file commitFileContent(fileContentOld, repository, git, true); commitFileContent(fileContentNew, repository, git, false); // The {tree} will return the underlying tree-id instead of the commit-id itself! ObjectId oldHead = repository.resolve("HEAD^^^^{tree}"); //here is my nullpointer ObjectId head = repository.resolve("HEAD^{tree}"); System.out.println("Printing diff between tree: " + oldHead + " and " + head); // prepare the two iterators to compute the diff between ObjectReader reader = repository.newObjectReader(); CanonicalTreeParser oldTreeIter = new CanonicalTreeParser(); oldTreeIter.reset(reader, oldHead); CanonicalTreeParser newTreeIter = new CanonicalTreeParser(); newTreeIter.reset(reader, head); // finally get the list of changed files List diffs= new Git(repository).diff() .setNewTree(newTreeIter) .setOldTree(oldTreeIter) .call(); for (DiffEntry entry : diffs) { System.out.println("Entry: " + entry); } System.out.println("Done"); } catch (Exception exception) { exception.printStackTrace(); } } /** * Adds and optionally commits fileContent to a repository * @param commit True if file should be committed, False if not * @throws Exception catch all for testing */ private void commitFileContent(byte[] fileContent, Repository repository, Git git, boolean commit) throws Exception { File myfile = new File(repository.getDirectory().getParent(), "testfile"); myfile.createNewFile(); FileOutputStream fos = new FileOutputStream (myfile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(fileContent); baos.writeTo(fos); // run the add git.add().addFilepattern("testfile").call(); if(commit) { // and then commit the changes git.commit().setMessage("Added fileContent").call(); } fos.close(); } /** * Helperfunction from * https://github.com/centic9/jgit-cookbook */ public static Repository createNewRepository() throws IOException { // prepare a new folder File localPath = File.createTempFile("TestGitRepository", ""); localPath.delete(); // create the directory Repository repository = FileRepositoryBuilder.create(new File( localPath, ".git")); repository.create(); return repository; } } 

代码导致此消息:

 Printing diff between tree: null and AnyObjectId[c11a3a58c23b0dd6e541c4bcd553197772626bc6] java.lang.NullPointerException at org.eclipse.jgit.internal.storage.file.UnpackedObjectCache$Table.index(UnpackedObjectCache.java:146) at org.eclipse.jgit.internal.storage.file.UnpackedObjectCache$Table.contains(UnpackedObjectCache.java:109) at org.eclipse.jgit.internal.storage.file.UnpackedObjectCache.isUnpacked(UnpackedObjectCache.java:64) at org.eclipse.jgit.internal.storage.file.ObjectDirectory.openObject(ObjectDirectory.java:367) at org.eclipse.jgit.internal.storage.file.WindowCursor.open(WindowCursor.java:145) at org.eclipse.jgit.treewalk.CanonicalTreeParser.reset(CanonicalTreeParser.java:202) at javadiff.RepoDiff.compare(RepoDiff.java:40) at javadiff.GitDiff.main(GitDiff.java:30) 

以下行必须为false,但它类似于jgit-cookbook的示例

  ObjectId oldHead = repository.resolve("HEAD^^^^{tree}"); //here is my nullpointer 

我测试了HEAD^1{Tree}但这不起作用,看起来{tree}必须在字符串中才能解析树而不是提交ID。 任何让它运作的想法?

要获取头部提交的树,请调用

 git.getRepository().resolve( "HEAD^{tree}" ) 

并获取HEAD提交的父级树,调用

 git.getRepository().resolve( "HEAD~1^{tree}" ) 

如果您对更多细节感兴趣,请搜索’Git caret and tilde’。

总而言之,这里有一个片段来计算两个提交的差异:

 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() ); df.setRepository( git.getRepository() ); List 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(); } 

有关在提交之间显示更改的进一步考虑,您可能希望阅读有关JGit的diff API的深入讨论,可在此处找到: http : //www.codeaffine.com/2016/06/16/jgit-diff/

我使用以下代码在两个提交之间打印差异。 使用DiffFormatter.scan似乎比我在SO上看到的其他解决方案更简单。

可能是我错过了一些不支持的案例,但它适用于我们的用例。

 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 history = git.log().setMaxCount(1).call(); return history.iterator().next(); } }