2014-12-08 3 views
6

Я пытаюсь показать git diff между двумя коммитами для файла. В принципе, я сделал это, как и в https://github.com/centic9/jgit-cookbook/blob/master/src/main/java/org/dstadler/jgit/porcelain/ShowChangedFilesBetweenCommits.javaКак показать изменения между коммитами с JGit

Вы можете увидеть мой полный код под 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<DiffEntry> 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) 

следующая строка должна быть ложной, но это как в примере из jgit-cookbook

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

Я тестировал HEAD^1{Tree}, но это не работает, похоже, что {tree} должен быть в строке для разрешения дерева вместо идентификатора фиксации. Любые идеи, чтобы заставить его работать?

+1

В качестве примечания:. 'Repository.getDirectory() GetParent()' не обязательно возвращать рабочий каталог, то используйте 'repository.getWorkTree()' вместо этого. И еще одно замечание: вы также можете получить репозиторий через 'git.getRepository()' и, таким образом, сохранить параметр репозитория в 'commitFileContent'. –

ответ

6

Чтобы получить дерево головы совершал, называют

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

и получить дерево родителя РУКОВОДИТЕЛЯ совершал, звоните

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

Искать «Git каретки и тильду» если вас интересует более подробная информация.

Итак, здесь идет фрагмент, который вычисляет диф двух фиксаций:

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

Для дальнейших рассуждений о показе изменений между зафиксируется, вы можете прочитать это углубленное обсуждение дифф API, JGit о том, что можно найти здесь: http://www.codeaffine.com/2016/06/16/jgit-diff/

+0

Это хорошо работает, за исключением случаев, когда новая фиксация является объединенной фиксацией с двумя или более родителями. Если я использую этот код с несколькими родителями, я получаю все различие, чтобы слить базовую фиксацию. Любая идея, как получить diffs слияния commit (в основном они никого, но иногда есть некоторые изменения файла) без включения всех различий, чтобы объединить базу? Примечание: нормальный git diff делает то же самое, поэтому это правильное поведение, а не поведение, которое я желаю в этом случае. Мне нужно что-то вроде 'git show ', которого, к сожалению, не существует в JGit. Буду признателен за любую оказанную помощь. –

+0

Приведенный выше код сравнивает два дерева, независимо от их исходных коммитов. Как вы получаете коммиты? Я предлагаю открыть новый вопрос с кодом (в идеале тестовым примером), который у вас есть. –

+0

Вы можете использовать 'new DiffFormatter (NullOutputStream.INSTANCE)', если вам не нужен вывод. 'org.eclipse.jgit.util.io.NullOutputStream' –

0

Я использую следующий код для распечатки разностей между двумя коммитами. Использование 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<RevCommit> history = git.log().setMaxCount(1).call(); 
     return history.iterator().next(); 
    } 
}