mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
Add POC for diff against temporary merge result
This fails for files with merge conflicts, because results for these files are not added to git and therefore there is no blob the diff formatter can use to render. This has somehow to be fixed by making jgit use the version from the file system. Taking a look at the implementation of Git.clone() might help here.
This commit is contained in:
@@ -90,8 +90,10 @@ public final class DiffCommandRequest extends FileBaseCommandRequest
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return !Strings.isNullOrEmpty(getPath())
|
||||
||!Strings.isNullOrEmpty(getRevision());
|
||||
return (!Strings.isNullOrEmpty(getPath())
|
||||
||!Strings.isNullOrEmpty(getRevision()))
|
||||
&& (Strings.isNullOrEmpty(getAncestorChangeset())
|
||||
|| Strings.isNullOrEmpty(getMergeChangeset()));
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
@@ -101,7 +103,7 @@ public final class DiffCommandRequest extends FileBaseCommandRequest
|
||||
*
|
||||
*
|
||||
* @param format format of the diff output
|
||||
*
|
||||
*
|
||||
* @since 1.34
|
||||
*/
|
||||
public void setFormat(DiffFormat format)
|
||||
@@ -112,14 +114,19 @@ public final class DiffCommandRequest extends FileBaseCommandRequest
|
||||
public void setAncestorChangeset(String ancestorChangeset) {
|
||||
this.ancestorChangeset = ancestorChangeset;
|
||||
}
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
public void setMergeChangeset(String mergeChangeset) {
|
||||
this.mergeChangeset = mergeChangeset;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the output format of the diff command.
|
||||
*
|
||||
*
|
||||
* @return output format
|
||||
*
|
||||
*
|
||||
* @since 1.34
|
||||
*/
|
||||
public DiffFormat getFormat()
|
||||
@@ -130,10 +137,17 @@ public final class DiffCommandRequest extends FileBaseCommandRequest
|
||||
public String getAncestorChangeset() {
|
||||
return ancestorChangeset;
|
||||
}
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
public String getMergeChangeset() {
|
||||
return mergeChangeset;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** diff format */
|
||||
private DiffFormat format = DiffFormat.NATIVE;
|
||||
|
||||
private String ancestorChangeset;
|
||||
|
||||
private String mergeChangeset;
|
||||
}
|
||||
|
||||
@@ -151,6 +151,32 @@ class AbstractGitCommand
|
||||
}
|
||||
}
|
||||
|
||||
<R, W extends GitCloneWorker<R>> R inCloneWithPostponedClose(Function<Git, W> workerSupplier, GitWorkdirFactory workdirFactory, String initialBranch, WorkingCopyCloser closer) {
|
||||
try {
|
||||
WorkingCopy<Repository, Repository> workingCopy = workdirFactory.createWorkingCopy(context, initialBranch);
|
||||
closer.setWorkingCopy(workingCopy);
|
||||
Repository repository = workingCopy.getWorkingRepository();
|
||||
logger.debug("cloned repository to folder {}", repository.getWorkTree());
|
||||
return workerSupplier.apply(new Git(repository)).run();
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(context.getRepository(), "could not clone repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
static class WorkingCopyCloser {
|
||||
private WorkingCopy<?, ?> workingCopy;
|
||||
|
||||
private void setWorkingCopy(WorkingCopy<?, ?> workingCopy) {
|
||||
this.workingCopy = workingCopy;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (workingCopy != null) {
|
||||
workingCopy.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectId resolveRevisionOrThrowNotFound(Repository repository, String revision) throws IOException {
|
||||
ObjectId resolved = repository.resolve(revision);
|
||||
if (resolved == null) {
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.FileTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
@@ -37,6 +38,17 @@ final class Differ implements AutoCloseable {
|
||||
private static Differ create(Repository repository, DiffCommandRequest request) throws IOException {
|
||||
RevWalk walk = new RevWalk(repository);
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getMergeChangeset()))
|
||||
{
|
||||
ObjectId otherRevision = repository.resolve(request.getMergeChangeset());
|
||||
RevTree tree = walk.parseCommit(otherRevision).getTree();
|
||||
TreeWalk treeWalk = new TreeWalk(repository);
|
||||
treeWalk.addTree(tree);
|
||||
treeWalk.addTree(new FileTreeIterator( repository ));
|
||||
return new Differ(null, walk, treeWalk);
|
||||
} else {
|
||||
|
||||
|
||||
ObjectId revision = repository.resolve(request.getRevision());
|
||||
RevCommit commit = walk.parseCommit(revision);
|
||||
|
||||
@@ -46,40 +58,32 @@ final class Differ implements AutoCloseable {
|
||||
treeWalk.reset();
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
if (Util.isNotEmpty(request.getPath()))
|
||||
{
|
||||
if (Util.isNotEmpty(request.getPath())) {
|
||||
treeWalk.setFilter(PathFilter.create(request.getPath()));
|
||||
}
|
||||
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset()))
|
||||
{
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
||||
ObjectId otherRevision = repository.resolve(request.getAncestorChangeset());
|
||||
ObjectId ancestorId = GitUtil.computeCommonAncestor(repository, revision, otherRevision);
|
||||
RevTree tree = walk.parseCommit(ancestorId).getTree();
|
||||
treeWalk.addTree(tree);
|
||||
}
|
||||
else if (commit.getParentCount() > 0)
|
||||
{
|
||||
} else if (commit.getParentCount() > 0) {
|
||||
RevTree tree = commit.getParent(0).getTree();
|
||||
|
||||
if (tree != null)
|
||||
{
|
||||
if (tree != null) {
|
||||
treeWalk.addTree(tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
|
||||
treeWalk.addTree(commit.getTree());
|
||||
|
||||
return new Differ(commit, walk, treeWalk);
|
||||
return new Differ(commit, walk, treeWalk);
|
||||
}
|
||||
}
|
||||
|
||||
private Diff diff() throws IOException {
|
||||
|
||||
@@ -31,8 +31,14 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.eclipse.jgit.api.MergeCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import sonia.scm.repository.GitWorkdirFactory;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.DiffCommandBuilder;
|
||||
|
||||
@@ -44,15 +50,42 @@ import java.io.IOException;
|
||||
*/
|
||||
public class GitDiffCommand extends AbstractGitCommand implements DiffCommand {
|
||||
|
||||
GitDiffCommand(GitContext context, Repository repository) {
|
||||
private final GitWorkdirFactory workdirFactory;
|
||||
|
||||
GitDiffCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) {
|
||||
super(context, repository);
|
||||
this.workdirFactory = workdirFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiffCommandBuilder.OutputStreamConsumer getDiffResult(DiffCommandRequest request) throws IOException {
|
||||
@SuppressWarnings("squid:S2095") // repository will be closed with the RepositoryService
|
||||
org.eclipse.jgit.lib.Repository repository = open();
|
||||
WorkingCopyCloser closer = new WorkingCopyCloser();
|
||||
if (Strings.isNullOrEmpty(request.getMergeChangeset())) {
|
||||
return computeDiff(request, open(), closer);
|
||||
} else {
|
||||
return inCloneWithPostponedClose(git -> new GitCloneWorker<DiffCommandBuilder.OutputStreamConsumer>(git) {
|
||||
@Override
|
||||
DiffCommandBuilder.OutputStreamConsumer run() throws IOException {
|
||||
ObjectId sourceRevision = resolveRevision(request.getRevision());
|
||||
try {
|
||||
getClone().merge()
|
||||
.setFastForward(MergeCommand.FastForwardMode.NO_FF)
|
||||
.setCommit(false) // we want to set the author manually
|
||||
.include(request.getRevision(), sourceRevision)
|
||||
.call();
|
||||
} catch (GitAPIException e) {
|
||||
throw new InternalRepositoryException(context.getRepository(), "could not merge branch " + request.getRevision() + " into " + request.getMergeChangeset(), e);
|
||||
}
|
||||
|
||||
DiffCommandRequest clone = request.clone();
|
||||
clone.setRevision(sourceRevision.name());
|
||||
return computeDiff(request, getClone().getRepository(), closer);
|
||||
}
|
||||
}, workdirFactory, request.getMergeChangeset(), closer);
|
||||
}
|
||||
}
|
||||
|
||||
private DiffCommandBuilder.OutputStreamConsumer computeDiff(DiffCommandRequest request, org.eclipse.jgit.lib.Repository repository, WorkingCopyCloser closer) throws IOException {
|
||||
Differ.Diff diff = Differ.diff(repository, request);
|
||||
|
||||
return output -> {
|
||||
@@ -66,8 +99,9 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand {
|
||||
}
|
||||
|
||||
formatter.flush();
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
@Override
|
||||
public DiffCommand getDiffCommand()
|
||||
{
|
||||
return new GitDiffCommand(context, repository);
|
||||
return new GitDiffCommand(context, repository, handler.getWorkdirFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,7 +40,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
public void diffForOneRevisionShouldCreateDiff() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository);
|
||||
GitDiffCommand gitDiffCommand = createDiffCommand();
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
@@ -50,7 +50,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
public void diffForOneBranchShouldCreateDiff() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository);
|
||||
GitDiffCommand gitDiffCommand = createDiffCommand();
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("test-branch");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
@@ -60,7 +60,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
public void diffForPathShouldCreateLimitedDiff() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository);
|
||||
GitDiffCommand gitDiffCommand = createDiffCommand();
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("test-branch");
|
||||
diffCommandRequest.setPath("a.txt");
|
||||
@@ -71,7 +71,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
public void diffBetweenTwoBranchesShouldCreateDiff() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository);
|
||||
GitDiffCommand gitDiffCommand = createDiffCommand();
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("master");
|
||||
diffCommandRequest.setAncestorChangeset("test-branch");
|
||||
@@ -82,7 +82,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
public void diffBetweenTwoBranchesForPathShouldCreateLimitedDiff() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository);
|
||||
GitDiffCommand gitDiffCommand = createDiffCommand();
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("master");
|
||||
diffCommandRequest.setAncestorChangeset("test-branch");
|
||||
@@ -91,4 +91,8 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A_MULTIPLE_REVISIONS, output.toString());
|
||||
}
|
||||
|
||||
private GitDiffCommand createDiffCommand() {
|
||||
return new GitDiffCommand(createContext(), repository, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.repository.util.WorkdirProvider;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class GitDiffCommand_Merge_Test extends AbstractGitCommandTestBase {
|
||||
|
||||
static final String DIFF_HEADER = "diff --git a/Main.java b/Main.java";
|
||||
static final String DIFF_FILE_A_MULTIPLE_REVISIONS = "--- a/Main.java\n" +
|
||||
"+++ b/Main.java\n" +
|
||||
"@@ -1,5 +1,5 @@\n" +
|
||||
" class Main {\n" +
|
||||
"- public static void main(String[] args) {\n" +
|
||||
"+ public static void main(String[] arguments) {\n" +
|
||||
" System.out.println(\"Expect nothing more to happen.\");\n" +
|
||||
" System.out.println(\"This is for demonstration, only.\");\n" +
|
||||
" }\n";
|
||||
|
||||
@Rule
|
||||
public BindTransportProtocolRule transportProtocolRule = new BindTransportProtocolRule();
|
||||
|
||||
@Test
|
||||
public void diffBetweenTwoBranchesWithoutConflict() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider()));
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("feature/rename_variable");
|
||||
diffCommandRequest.setMergeChangeset("integration");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertThat(output.toString())
|
||||
.contains(DIFF_HEADER)
|
||||
.contains(DIFF_FILE_A_MULTIPLE_REVISIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diffBetweenTwoBranchesWithSimpleConflict() throws IOException {
|
||||
GitDiffCommand gitDiffCommand = new GitDiffCommand(createContext(), repository, new SimpleGitWorkdirFactory(new WorkdirProvider()));
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("feature/print_args");
|
||||
diffCommandRequest.setMergeChangeset("integration");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertThat(output.toString())
|
||||
.contains(DIFF_HEADER)
|
||||
.contains(DIFF_FILE_A_MULTIPLE_REVISIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getZippedRepositoryResource() {
|
||||
return "sonia/scm/repository/spi/scm-git-spi-merge-diff-test.zip";
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user