Come mostrare le modifiche tra commit con JGit

Sto cercando di mostrare un diff di git tra due commit di un file. Fondamentalmente, l’ho fatto come in https://github.com/centic9/jgit-cookbook/blob/master/src/main/java/org/dstadler/jgit/porcelain/ShowChangedFilesBetweenCommits.java

Puoi vedere il mio codice completo sotto 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; } } 

il codice risulta in questo messaggio:

 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) 

la seguente riga deve essere falsa, ma è come nell’esempio di jgit-cookbook

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

Ho provato HEAD^1{Tree} ma questo non funziona, sembra che {tree} debba essere nella stringa per la risoluzione di un albero invece di un id di commit. Qualche idea per farlo funzionare?

Per ottenere l’albero della testa commetta, chiama

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

e per ottenere l’albero del genitore del commit HEAD, chiama

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

Cerca ‘Git caret and tilde’ se sei interessato a maggiori dettagli.

Per riassumere, ecco uno snippet che calcola il diff di due commit:

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

Per ulteriori considerazioni sulla visualizzazione delle modifiche tra commit, puoi leggere questa discussione approfondita sulle API diff di JGit che possono essere trovate qui: http://www.codeaffine.com/2016/06/16/jgit-diff/

Sto usando il seguente codice per stampare le differenze tra due commit. Usare DiffFormatter.scan sembra più semplice delle altre soluzioni che ho visto su SO.

Potrebbe essere che mi mancano alcuni casi in cui questo non è supportato, ma funziona bene per il nostro caso d’uso.

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