【问题标题】:How to show changes between commits with JGit如何使用 JGit 显示提交之间的更改
【发布时间】:2015-02-06 07:37:32
【问题描述】:

我试图显示一个文件的两次提交之间的 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<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} 必须在字符串中才能解析树而不是提交ID。有什么想法让它发挥作用吗?

【问题讨论】:

  • 附带说明:repository.getDirectory().getParent() 不一定返回工作目录,请改用repository.getWorkTree()。还有一点注意:你也可以通过git.getRepository()获取repository,这样就可以省掉commitFileContent中的repository参数。

标签: java jgit


【解决方案1】:

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

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

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

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

如果您对更多详细信息感兴趣,请搜索“Git caret and tilde”。

总而言之,这里有一个计算两次提交差异的 sn-p:

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

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

【讨论】:

  • 这很好用,除非新提交是具有两个或更多父级的合并提交。如果我将此代码与多个父母一起使用,那么我将所有差异合并到合并基本提交。知道如何在不包括合并基础的所有差异的情况下获取合并提交的差异(大多数情况下它们都没有,但有时会更改一些文件)?注意:正常的 git diff 也是如此,所以这是正确的行为,而不是我在这种情况下想要的行为。我需要的是像git show &lt;merged_commit&gt; 这样的东西,不幸的是在JGit 中不存在。非常感谢任何帮助。
  • 上面的代码比较了两棵树,独立于它们的原始提交。你如何获得提交?我建议使用您到目前为止所附的代码(最好是测试用例)打开一个新问题。
  • 如果不需要输出,可以使用new DiffFormatter(NullOutputStream.INSTANCE)org.eclipse.jgit.util.io.NullOutputStream
【解决方案2】:

我正在使用以下代码来打印两个提交之间的差异。使用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();
    }
}

【讨论】:

    猜你喜欢
    • 2015-05-01
    • 2018-12-11
    • 2017-06-23
    • 2011-03-09
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-15
    相关资源
    最近更新 更多