3

I'd like to use jgit to get a list of files that were changed in a merge commit, similar to what git show --name-only does in git.

There are plenty of examples on how to get changed files for normal commits with one parent, but they do not work for merge commits with multiple parents.

Peter Knego
  • 79,991
  • 11
  • 123
  • 154

1 Answers1

1

From my understanding you would simply need to combine the diffs of the parents of the merge commit with their respective parents.

The snippet below creates two commits with each one file added (main.txt and side.txt), the merge commit is then diffed as described above

public void diffMerge() throws Exception {
  RevCommit baseCommit = commitChanges();
  Ref sideBranch = git.branchCreate().setName( "side" ).call();
  File mainFile = new File( git.getRepository().getWorkTree(), "main.txt" );
  mainFile.createNewFile();
  RevCommit mainCommit = commitChanges();
  git.checkout().setName( sideBranch.getName() ).call();
  File sideFile = new File( git.getRepository().getWorkTree(), "side.txt" );
  sideFile.createNewFile();
  RevCommit sideCommit = commitChanges();
  git.checkout().setName( "refs/heads/master" ).call();
  ObjectId mergeCommitId = git.merge().include( sideCommit ).call().getNewHead();

  DiffFormatter diffFormatter = new DiffFormatter( NullOutputStream.INSTANCE );
  diffFormatter.setRepository( git.getRepository() );
  RevCommit mergeCommit = parseCommit( mergeCommitId );
  List<DiffEntry> mainEntries = diffFormatter.scan( parseCommit( mergeCommit.getParent( 0 ) ).getParent( 0 ), mergeCommit.getParent( 0 ) );
  List<DiffEntry> sideEntries = diffFormatter.scan( parseCommit( mergeCommit.getParent( 1 ) ).getParent( 0 ), mergeCommit.getParent( 1 ) );
  diffFormatter.close();

  mainEntries.forEach( entry -> System.out.println( entry.getNewPath() ) );
  sideEntries.forEach( entry -> System.out.println( entry.getNewPath() ) );
}

private RevCommit parseCommit( ObjectId commitId ) throws IOException {
  try( RevWalk revWalk = new RevWalk( git.getRepository() ) ) {
    return revWalk.parseCommit( commitId );
  }
}

private RevCommit commitChanges() throws GitAPIException {
  git.add().addFilepattern( "." ).call();
  return git.commit().setMessage( "commit message" ).call();
}

Note that the code blindly assumes the the parents of the merge commits aren't merges themselves.

Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
  • Sorry, but above code just finds changes between two commits (parent and it's parent) - it does so for each branch separately. I'm looking for a way to only get files that were changed in the merge commit, but not commits before it. Changed files either needed to be merged by hand due to a merge conflict, or user actually had some changes in the staging. The `git show --name-only` finds those files, but there is no porcelain command for it in jgit. In your above case she `git show --name-only` would show nothing. – Peter Knego Dec 02 '15 at 11:13
  • Wouldn't that be the combined diff of the merge commit with both its parents? I.e. `diffFormatter.scan( merge, merge.getParent( 0 )` )` combined with `diffFormatter.scan( merge, merge.getParent( 1 )` )` . – Rüdiger Herrmann Dec 02 '15 at 13:38
  • It's actually the difference of diffs merged^1 - base and merged^2 - base, plus additional staged changes that user might have comited in a merge commit. – Peter Knego Dec 02 '15 at 14:07
  • The diff of merge - parent1 basically gives you all changes that were made in the other branch (parent2). – Peter Knego Dec 02 '15 at 14:09