Move conflict computation to merge command

Therefore revert changes to diff command and introduce new
MergeConflictResult instead of streaming result.
This commit is contained in:
Rene Pfeuffer
2019-11-11 13:06:07 +01:00
parent c65ac7f61d
commit 913e5289e6
9 changed files with 257 additions and 160 deletions

View File

@@ -8,7 +8,6 @@ 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;
@@ -38,17 +37,6 @@ 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);
@@ -58,32 +46,40 @@ 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 {

View File

@@ -31,21 +31,12 @@
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 org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.api.DiffCommandBuilder;
import java.io.IOException;
import java.util.List;
/**
*
@@ -53,64 +44,22 @@ import java.util.List;
*/
public class GitDiffCommand extends AbstractGitCommand implements DiffCommand {
private final GitWorkdirFactory workdirFactory;
GitDiffCommand(GitContext context, Repository repository, GitWorkdirFactory workdirFactory) {
GitDiffCommand(GitContext context, Repository repository) {
super(context, repository);
this.workdirFactory = workdirFactory;
}
@Override
public DiffCommandBuilder.OutputStreamConsumer getDiffResult(DiffCommandRequest request) throws IOException {
if (Strings.isNullOrEmpty(request.getMergeChangeset())) {
Differ.Diff diff = Differ.diff(open(), request);
return computeDiff(diff.getEntries(), open());
} else {
WorkingCopyCloser closer = new WorkingCopyCloser();
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);
}
@SuppressWarnings("squid:S2095") // repository will be closed with the RepositoryService
org.eclipse.jgit.lib.Repository repository = open();
return outputStream -> {
CanonicalTreeParser treeParser = new CanonicalTreeParser();
ObjectId treeId = git.getRepository().resolve(request.getMergeChangeset() + "^{tree}");
try (ObjectReader reader = git.getRepository().newObjectReader()) {
treeParser.reset(reader, treeId);
git
.diff()
.setOldTree(treeParser)
.setOutputStream(outputStream)
.call();
DiffCommandRequest clone = request.clone();
clone.setRevision(sourceRevision.name());
} catch (GitAPIException e) {
throw new InternalRepositoryException(repository, "could not calculate diff", e);
} finally {
closer.close();
}
};
}
}, workdirFactory, request.getMergeChangeset(), closer);
}
}
private DiffCommandBuilder.OutputStreamConsumer computeDiff(List<DiffEntry> entries, org.eclipse.jgit.lib.Repository repository) throws IOException {
Differ.Diff diff = Differ.diff(repository, request);
return output -> {
try (DiffFormatter formatter = new DiffFormatter(output)) {
formatter.setRepository(repository);
for (DiffEntry e : entries) {
for (DiffEntry e : diff.getEntries()) {
if (!e.getOldId().equals(e.getNewId())) {
formatter.format(e);
}

View File

@@ -4,11 +4,15 @@ import com.google.common.base.Strings;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ResolveMerger;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.GitWorkdirFactory;
@@ -17,6 +21,7 @@ import sonia.scm.repository.Person;
import sonia.scm.repository.api.MergeCommandResult;
import sonia.scm.repository.api.MergeDryRunCommandResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
@@ -55,6 +60,12 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
}
}
@Override
public MergeConflictResult computeConflicts(MergeCommandRequest request) {
WorkingCopyCloser closer = new WorkingCopyCloser();
return inClone(git -> new ConflictWorker(git, request, closer), workdirFactory, request.getTargetBranch());
}
private class MergeWorker extends GitCloneWorker<MergeCommandResult> {
private final String target;
@@ -115,4 +126,82 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
return MergeCommandResult.failure(result.getConflicts().keySet());
}
}
private class ConflictWorker extends GitCloneWorker<MergeConflictResult> {
private final Git git;
private final MergeCommandRequest request;
private final WorkingCopyCloser closer;
private ConflictWorker(Git git, MergeCommandRequest request, WorkingCopyCloser closer) {
super(git);
this.git = git;
this.request = request;
this.closer = closer;
}
@Override
MergeConflictResult run() throws IOException {
ObjectId sourceRevision = resolveRevision(request.getBranchToMerge());
MergeResult mergeResult;
try {
mergeResult = getClone().merge()
.setFastForward(FastForwardMode.NO_FF)
.setCommit(false)
.include(request.getBranchToMerge(), sourceRevision)
.call();
} catch (GitAPIException e) {
throw new InternalRepositoryException(context.getRepository(), "could not merge branch " + request.getBranchToMerge() + " into " + request.getTargetBranch(), e);
}
if (mergeResult.getConflicts() == null) {
return new MergeConflictResult();
}
Status status;
try {
status = getClone().status().call();
} catch (GitAPIException e) {
throw new InternalRepositoryException(context.getRepository(), "could not get status", e);
}
MergeConflictResult result = new MergeConflictResult();
CanonicalTreeParser treeParser = new CanonicalTreeParser();
ObjectId treeId = git.getRepository().resolve(request.getTargetBranch() + "^{tree}");
ByteArrayOutputStream diffBuffer = new ByteArrayOutputStream();
status.getConflictingStageState().entrySet().forEach(conflictEntry -> {
String path = conflictEntry.getKey();
switch (conflictEntry.getValue()) {
case BOTH_MODIFIED:
diffBuffer.reset();
try (ObjectReader reader = git.getRepository().newObjectReader()) {
treeParser.reset(reader, treeId);
git
.diff()
.setOldTree(treeParser)
.setPathFilter(PathFilter.create(path))
.setOutputStream(diffBuffer)
.call();
result.addBothModified(path, diffBuffer.toString());
} catch (GitAPIException | IOException e) {
throw new InternalRepositoryException(repository, "could not calculate diff for path " + path, e);
} finally {
closer.close();
}
break;
case DELETED_BY_THEM:
result.addDeletedByThem(path);
break;
case DELETED_BY_US:
result.addDeletedByUs(path);
break;
default:
throw new InternalRepositoryException(context.getRepository(), "unexpected conflict type: " + conflictEntry.getValue());
}
});
return result;
}
}
}

View File

@@ -169,7 +169,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
@Override
public DiffCommand getDiffCommand()
{
return new GitDiffCommand(context, repository, handler.getWorkdirFactory());
return new GitDiffCommand(context, repository);
}
@Override