diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommandRequest.java index 5cfd57f71b..e26b2eb5aa 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommandRequest.java @@ -109,7 +109,10 @@ public final class DiffCommandRequest extends FileBaseCommandRequest this.format = format; } - //~--- get methods ---------------------------------------------------------- + public void setAncestorChangeset(String ancestorChangeset) { + this.ancestorChangeset = ancestorChangeset; + } +//~--- get methods ---------------------------------------------------------- /** * Return the output format of the diff command. @@ -124,8 +127,13 @@ public final class DiffCommandRequest extends FileBaseCommandRequest return format; } - //~--- fields --------------------------------------------------------------- + public String getAncestorChangeset() { + return ancestorChangeset; + } +//~--- fields --------------------------------------------------------------- /** diff format */ private DiffFormat format = DiffFormat.NATIVE; + + private String ancestorChangeset; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 13340a20e7..6d7be2e93c 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.RefSpec; @@ -716,6 +717,18 @@ public final class GitUtil return (id != null) &&!id.equals(ObjectId.zeroId()); } + /** + * Computes the first common ancestor of two revisions, aka merge base. + */ + public static ObjectId computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException { + try (RevWalk mergeBaseWalk = new RevWalk(repository)) { + mergeBaseWalk.setRevFilter(RevFilter.MERGE_BASE); + mergeBaseWalk.markStart(mergeBaseWalk.lookupCommit(revision1)); + mergeBaseWalk.markStart(mergeBaseWalk.parseCommit(revision2)); + return mergeBaseWalk.next().getId(); + } + } + //~--- methods -------------------------------------------------------------- /** diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java index 83977ef290..2d56c8e786 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java @@ -34,27 +34,25 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.EmptyTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sonia.scm.repository.GitUtil; import sonia.scm.repository.Repository; import sonia.scm.util.Util; -//~--- JDK imports ------------------------------------------------------------ - import java.io.BufferedOutputStream; +import java.io.IOException; import java.io.OutputStream; - import java.util.List; /** @@ -107,7 +105,8 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand walk = new RevWalk(gr); - RevCommit commit = walk.parseCommit(gr.resolve(request.getRevision())); + ObjectId revision = gr.resolve(request.getRevision()); + RevCommit commit = walk.parseCommit(revision); walk.markStart(commit); commit = walk.next(); @@ -120,7 +119,15 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand treeWalk.setFilter(PathFilter.create(request.getPath())); } - if (commit.getParentCount() > 0) + + if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) + { + ObjectId otherRevision = gr.resolve(request.getAncestorChangeset()); + ObjectId ancestorId = computeCommonAncestor(gr, revision, otherRevision); + RevTree tree = walk.parseCommit(ancestorId).getTree(); + treeWalk.addTree(tree); + } + else if (commit.getParentCount() > 0) { RevTree tree = commit.getParent(0).getTree(); @@ -156,7 +163,6 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand } catch (Exception ex) { - // TODO throw exception logger.error("could not create diff", ex); } @@ -167,4 +173,9 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand GitUtil.release(formatter); } } + + private ObjectId computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException { + return GitUtil.computeCommonAncestor(repository, revision1, revision2); + } + } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitDiffCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitDiffCommandTest.java index 3b12fb0c3f..d07801ceab 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitDiffCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitDiffCommandTest.java @@ -42,4 +42,28 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase { gitDiffCommand.getDiffResult(diffCommandRequest, output); assertEquals(DIFF_LATEST_COMMIT_TEST_BRANCH, output.toString()); } + + @Test + public void diffBetweenTwoBranchesShouldCreateDiff() { + GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository); + DiffCommandRequest diffCommandRequest = new DiffCommandRequest(); + diffCommandRequest.setRevision("master"); + diffCommandRequest.setAncestorChangeset("test-branch"); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + gitDiffCommand.getDiffResult(diffCommandRequest, output); + assertEquals("diff --git a/a.txt b/a.txt\n" + + "index 7898192..2f8bc28 100644\n" + + "--- a/a.txt\n" + + "+++ b/a.txt\n" + + "@@ -1 +1,2 @@\n" + + " a\n" + + "+line for blame\n" + + "diff --git a/f.txt b/f.txt\n" + + "new file mode 100644\n" + + "index 0000000..6a69f92\n" + + "--- /dev/null\n" + + "+++ b/f.txt\n" + + "@@ -0,0 +1 @@\n" + + "+f\n", output.toString()); + } }