svn: Refactored "MergeFromTheirsResolver" - code simplified, @NotNull

This commit is contained in:
Konstantin Kolosovsky
2016-09-20 14:21:38 +03:00
parent 58b6dbee77
commit 88f7eea6d1
2 changed files with 258 additions and 316 deletions
@@ -16,19 +16,19 @@
package org.jetbrains.idea.svn.treeConflict;
import com.intellij.CommonBundle;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diff.impl.patch.*;
import com.intellij.openapi.diff.impl.patch.BinaryFilePatch;
import com.intellij.openapi.diff.impl.patch.FilePatch;
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
@@ -38,23 +38,23 @@ import com.intellij.openapi.vcs.changes.patch.ApplyPatchMode;
import com.intellij.openapi.vcs.changes.patch.TextFilePatchInProgress;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.ObjectUtils;
import com.intellij.util.SmartList;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.CalledInAwt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.SvnConfiguration;
import org.jetbrains.idea.svn.SvnContentRevision;
import org.jetbrains.idea.svn.SvnRevisionNumber;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.conflict.TreeConflictDescription;
import org.jetbrains.idea.svn.history.SvnChangeList;
@@ -62,71 +62,81 @@ import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
import org.tmatesoft.svn.core.wc.SVNRevision;
import java.io.IOException;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder.buildPatch;
import static com.intellij.openapi.util.io.FileUtil.getRelativePath;
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
import static com.intellij.openapi.vcs.changes.ChangesUtil.getAfterPath;
import static com.intellij.openapi.vcs.changes.ChangesUtil.getBeforePath;
import static com.intellij.util.ObjectUtils.notNull;
import static com.intellij.util.containers.ContainerUtil.*;
import static com.intellij.util.containers.ContainerUtilRt.newArrayList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.partitioningBy;
import static java.util.stream.Collectors.toMap;
import static org.jetbrains.idea.svn.SvnBundle.message;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 5/18/12
* Time: 2:44 PM
*/
public class MergeFromTheirsResolver {
private final SvnVcs myVcs;
private final TreeConflictDescription myDescription;
private final Change myChange;
private final FilePath myOldFilePath;
private final FilePath myNewFilePath;
private final String myOldPresentation;
private final String myNewPresentation;
@NotNull private final SvnVcs myVcs;
@NotNull private final TreeConflictDescription myDescription;
@NotNull private final Change myChange;
@NotNull private final FilePath myOldFilePath;
@NotNull private final FilePath myNewFilePath;
@NotNull private final String myOldPresentation;
@NotNull private final String myNewPresentation;
private final SvnRevisionNumber myCommittedRevision;
private Boolean myAdd;
private final List<Change> myTheirsChanges;
private final List<Change> myTheirsBinaryChanges;
private final List<VcsException> myWarnings;
@NotNull private final List<Change> myTheirsChanges;
@NotNull private final List<Change> myTheirsBinaryChanges;
@NotNull private final List<VcsException> myWarnings;
private List<TextFilePatch> myTextPatches;
private VirtualFile myBaseForPatch;
private final VirtualFile myBaseForPatch;
public MergeFromTheirsResolver(SvnVcs vcs, TreeConflictDescription description, Change change, SvnRevisionNumber revision) {
public MergeFromTheirsResolver(@NotNull SvnVcs vcs,
@NotNull TreeConflictDescription description,
@NotNull Change change,
SvnRevisionNumber revision) {
myVcs = vcs;
myDescription = description;
myChange = change;
myCommittedRevision = revision;
myOldFilePath = myChange.getBeforeRevision().getFile();
myNewFilePath = myChange.getAfterRevision().getFile();
myOldFilePath = notNull(myChange.getBeforeRevision()).getFile();
myNewFilePath = notNull(myChange.getAfterRevision()).getFile();
myBaseForPatch = ChangesUtil.findValidParentAccurately(myNewFilePath);
myOldPresentation = TreeConflictRefreshablePanel.filePath(myOldFilePath);
myNewPresentation = TreeConflictRefreshablePanel.filePath(myNewFilePath);
myTheirsChanges = new ArrayList<>();
myTheirsBinaryChanges = new ArrayList<>();
myWarnings = new ArrayList<>();
myTextPatches = Collections.emptyList();
myTheirsChanges = newArrayList();
myTheirsBinaryChanges = newArrayList();
myWarnings = newArrayList();
myTextPatches = emptyList();
}
@CalledInAwt
public void execute() {
int ok = Messages.showOkCancelDialog(myVcs.getProject(), (myChange.isMoved() ?
SvnBundle.message("confirmation.resolve.tree.conflict.merge.moved", myOldPresentation, myNewPresentation) :
SvnBundle.message("confirmation.resolve.tree.conflict.merge.renamed", myOldPresentation, myNewPresentation)),
TreeConflictRefreshablePanel.TITLE, Messages.getQuestionIcon());
String messageKey =
myChange.isMoved() ? "confirmation.resolve.tree.conflict.merge.moved" : "confirmation.resolve.tree.conflict.merge.renamed";
String message = message(messageKey, myOldPresentation, myNewPresentation);
int ok = Messages.showOkCancelDialog(myVcs.getProject(), message, TreeConflictRefreshablePanel.TITLE, Messages.getQuestionIcon());
if (Messages.OK != ok) return;
FileDocumentManager.getInstance().saveAllDocuments();
//final String name = "Merge changes from theirs for: " + myOldPresentation;
final Continuation fragmented = Continuation.createFragmented(myVcs.getProject(), false);
fragmented.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
myWarnings.add(e);
if (e.isWarning()) {
return;
}
Continuation fragmented = Continuation.createFragmented(myVcs.getProject(), false);
fragmented.addExceptionHandler(VcsException.class, e -> {
myWarnings.add(e);
if (!e.isWarning()) {
AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(myWarnings, TreeConflictRefreshablePanel.TITLE);
}
});
final List<TaskDescriptor> tasks = new SmartList<>();
List<TaskDescriptor> tasks = new SmartList<>();
tasks.add(myDescription.isDirectory() ? new PreloadChangesContentsForDir() : new PreloadChangesContentsForFile());
tasks.add(new ConvertTextPaths());
tasks.add(new PatchCreator());
@@ -136,27 +146,6 @@ public class MergeFromTheirsResolver {
fragmented.run(tasks);
}
private void appendResolveConflictToContext(final ContinuationContext context) {
context.next(new ResolveConflictInSvn());
}
private void appendTailToContextLast(final ContinuationContext context) {
context.last(new ApplyBinaryChanges(), new FinalNotification());
}
private List<Change> filterOutBinary(List<Change> paths) {
List<Change> result = null;
for (Iterator<Change> iterator = paths.iterator(); iterator.hasNext(); ) {
final Change change = iterator.next();
if (ChangesUtil.isBinaryChange(change)) {
result = (result == null ? new SmartList<>() : result);
result.add(change);
iterator.remove();
}
}
return result;
}
private class FinalNotification extends TaskDescriptor {
private FinalNotification() {
super("", Where.AWT);
@@ -164,9 +153,8 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
final StringBuilder message = new StringBuilder().append("Theirs changes merged for ").append(myOldPresentation);
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), message.toString(), MessageType.INFO);
if (! myWarnings.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Theirs changes merged for " + myOldPresentation, MessageType.INFO);
if (!myWarnings.isEmpty()) {
AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(myWarnings, TreeConflictRefreshablePanel.TITLE);
}
}
@@ -195,7 +183,6 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
initAddOption();
List<Change> convertedChanges = new SmartList<>();
try {
// revision contents is preloaded, so ok to call in awt
@@ -216,23 +203,23 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
final ChangeListManager clManager = ChangeListManager.getInstance(myVcs.getProject());
final LocalChangeList changeList = clManager.getChangeList(myChange);
final ApplyPatchDifferentiatedDialog dialog = new ApplyPatchDifferentiatedDialog(myVcs.getProject(),
new TreeConflictApplyTheirsPatchExecutor(myVcs, context, myBaseForPatch),
Collections.<ApplyPatchExecutor>singletonList(new ApplyPatchSaveToFileExecutor(myVcs.getProject(), myBaseForPatch)),
LocalChangeList changeList = ChangeListManager.getInstance(myVcs.getProject()).getChangeList(myChange);
ApplyPatchDifferentiatedDialog dialog = new ApplyPatchDifferentiatedDialog(
myVcs.getProject(), new TreeConflictApplyTheirsPatchExecutor(myVcs, context, myBaseForPatch),
singletonList(new ApplyPatchSaveToFileExecutor(myVcs.getProject(), myBaseForPatch)),
ApplyPatchMode.APPLY_PATCH_IN_MEMORY, myTextPatches, changeList);
context.suspend();
dialog.show();
}
}
private class TreeConflictApplyTheirsPatchExecutor implements ApplyPatchExecutor<TextFilePatchInProgress> {
private final SvnVcs myVcs;
private final ContinuationContext myInner;
@NotNull private final SvnVcs myVcs;
@NotNull private final ContinuationContext myInner;
private final VirtualFile myBaseDir;
public TreeConflictApplyTheirsPatchExecutor(SvnVcs vcs, ContinuationContext inner, final VirtualFile baseDir) {
public TreeConflictApplyTheirsPatchExecutor(@NotNull SvnVcs vcs, @NotNull ContinuationContext inner, final VirtualFile baseDir) {
myVcs = vcs;
myInner = inner;
myBaseDir = baseDir;
@@ -244,11 +231,12 @@ public class MergeFromTheirsResolver {
}
@Override
public void apply(@NotNull List<FilePatch> remaining, @NotNull MultiMap<VirtualFile, TextFilePatchInProgress> patchGroupsToApply,
public void apply(@NotNull List<FilePatch> remaining,
@NotNull MultiMap<VirtualFile, TextFilePatchInProgress> patchGroupsToApply,
@Nullable LocalChangeList localList,
@Nullable String fileName,
@Nullable TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final List<FilePatch> patches;
List<FilePatch> patches;
try {
patches = ApplyPatchSaveToFileExecutor.patchGroupsToOneGroup(patchGroupsToApply, myBaseDir);
}
@@ -257,22 +245,16 @@ public class MergeFromTheirsResolver {
return;
}
final PatchApplier<BinaryFilePatch> patchApplier =
new PatchApplier<>(myVcs.getProject(), myBaseDir, patches, localList, null, null);
patchApplier.execute(false, true); // 3
boolean thereAreCreations = false;
for (FilePatch patch : patches) {
if (patch.isNewFile() || ! Comparing.equal(patch.getAfterName(), patch.getBeforeName())) {
thereAreCreations = true;
break;
}
}
new PatchApplier<BinaryFilePatch>(myVcs.getProject(), myBaseDir, patches, localList, null, null).execute(false, true);
boolean thereAreCreations =
patches.stream().anyMatch(patch -> patch.isNewFile() || !Comparing.equal(patch.getAfterName(), patch.getBeforeName()));
if (thereAreCreations) {
// restore deletion of old directory:
myInner.next(new DirectoryAddition()); // 2
myInner.next(new DirectoryAddition());
}
appendResolveConflictToContext(myInner); // 1
appendTailToContextLast(myInner); // 4
myInner.next(new ResolveConflictInSvn());
myInner.last(new ApplyBinaryChanges(), new FinalNotification());
myInner.ping();
}
}
@@ -304,16 +286,9 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
final Project project = myVcs.getProject();
final List<FilePatch> patches;
try {
patches = IdeaTextPatchBuilder.buildPatch(project, myTheirsChanges, ObjectUtils.assertNotNull(myBaseForPatch).getPath(), false);
myTextPatches = ObjectsConvertor.convert(patches, new Convertor<FilePatch, TextFilePatch>() {
@Override
public TextFilePatch convert(FilePatch o) {
return (TextFilePatch)o;
}
});
List<FilePatch> patches = buildPatch(myVcs.getProject(), myTheirsChanges, notNull(myBaseForPatch).getPath(), false);
myTextPatches = map(patches, o -> (TextFilePatch)o);
}
catch (VcsException e) {
context.handleException(e, true);
@@ -328,8 +303,7 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
if (myTheirsBinaryChanges.isEmpty()) return;
final List<Change> converted;
List<Change> converted;
try {
converted = convertPaths(myTheirsBinaryChanges);
}
@@ -338,14 +312,14 @@ public class MergeFromTheirsResolver {
return;
}
if (converted.isEmpty()) return;
final Map<FilePath, Change> map = new HashMap<>();
for (Change change : converted) {
map.put(ChangesUtil.getFilePath(change), change);
}
final Collection<FilePath> selected = chooseBinaryFiles(converted, map.keySet());
Map<FilePath, Change> map = converted.stream().collect(toMap(ChangesUtil::getFilePath, identity()));
Collection<FilePath> selected = chooseBinaryFiles(converted, map.keySet());
myTheirsBinaryChanges.clear();
for (FilePath filePath : selected) {
myTheirsBinaryChanges.add(map.get(filePath));
if (!isEmpty(selected)) {
for (FilePath filePath : selected) {
myTheirsBinaryChanges.add(map.get(filePath));
}
}
}
}
@@ -358,53 +332,44 @@ public class MergeFromTheirsResolver {
@Override
public void run(final ContinuationContext context) {
if (myTheirsBinaryChanges.isEmpty()) return;
final Application application = ApplicationManager.getApplication();
final List<FilePath> dirtyPaths = new ArrayList<>();
for (final Change change : myTheirsBinaryChanges) {
List<FilePath> dirtyPaths = newArrayList();
for (Change change : myTheirsBinaryChanges) {
try {
application.runWriteAction(new ThrowableComputable<Void, VcsException>() {
@Override
public Void compute() throws VcsException {
try {
if (change.getAfterRevision() == null) {
final FilePath path = change.getBeforeRevision().getFile();
dirtyPaths.add(path);
final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(path.getIOFile());
if (file == null) {
context.handleException(new VcsException("Can not delete file: " + file.getPath(), true), false);
return null;
}
file.delete(TreeConflictRefreshablePanel.class);
ApplicationManager.getApplication().runWriteAction((ThrowableComputable<Void, VcsException>)() -> {
try {
if (change.getAfterRevision() == null) {
FilePath path = notNull(change.getBeforeRevision()).getFile();
dirtyPaths.add(path);
VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path.getPath());
if (file == null) {
context.handleException(new VcsException("Can not delete file: " + path.getPath(), true), false);
return null;
}
else {
final FilePath file = change.getAfterRevision().getFile();
dirtyPaths.add(file);
final String parentPath = file.getParentPath().getPath();
final VirtualFile parentFile = VfsUtil.createDirectoryIfMissing(parentPath);
if (parentFile == null) {
context.handleException(new VcsException("Can not create directory: " + parentPath, true), false);
return null;
}
final VirtualFile child = parentFile.createChildData(TreeConflictRefreshablePanel.class, file.getName());
if (child == null) {
context.handleException(new VcsException("Can not create file: " + file.getPath(), true), false);
return null;
}
final BinaryContentRevision revision = (BinaryContentRevision)change.getAfterRevision();
final byte[] content = revision.getBinaryContent();
// actually it was the fix for IDEA-91572 Error saving merged data: Argument 0 for @NotNull parameter of > com/intellij/
if (content == null) {
context.handleException(new VcsException("Can not load Theirs content for file " + file.getPath()), false);
return null;
}
child.setBinaryContent(content);
file.delete(TreeConflictRefreshablePanel.class);
}
else {
FilePath file = change.getAfterRevision().getFile();
dirtyPaths.add(file);
String parentPath = notNull(file.getParentPath()).getPath();
VirtualFile parentFile = VfsUtil.createDirectoryIfMissing(parentPath);
if (parentFile == null) {
context.handleException(new VcsException("Can not create directory: " + parentPath, true), false);
return null;
}
VirtualFile child = parentFile.createChildData(TreeConflictRefreshablePanel.class, file.getName());
byte[] content = ((BinaryContentRevision)change.getAfterRevision()).getBinaryContent();
// actually it was the fix for IDEA-91572 Error saving merged data: Argument 0 for @NotNull parameter of > com/intellij/
if (content == null) {
context.handleException(new VcsException("Can not load Theirs content for file " + file.getPath()), false);
return null;
}
child.setBinaryContent(content);
}
catch (IOException e) {
throw new VcsException(e);
}
return null;
}
catch (IOException e) {
throw new VcsException(e);
}
return null;
});
}
catch (VcsException e) {
@@ -416,87 +381,82 @@ public class MergeFromTheirsResolver {
}
}
private Collection<FilePath> chooseBinaryFiles(List<Change> converted, Set<FilePath> paths) {
@Nullable
private Collection<FilePath> chooseBinaryFiles(@NotNull List<Change> changes, @NotNull Set<FilePath> paths) {
String singleMessage = "";
if (paths.size() == 1) {
final Change change = converted.get(0);
final FileStatus status = change.getFileStatus();
final FilePath path = ChangesUtil.getFilePath(change);
final String stringPath = TreeConflictRefreshablePanel.filePath(path);
if (FileStatus.DELETED.equals(status)) {
singleMessage = "Delete binary file " + stringPath + " (according to theirs changes)?";
} else if (FileStatus.ADDED.equals(status)) {
singleMessage = "Create binary file " + stringPath + " (according to theirs changes)?";
} else {
singleMessage = "Apply changes to binary file " + stringPath + " (according to theirs changes)?";
}
if (changes.size() == 1) {
FileStatus status = changes.get(0).getFileStatus();
FilePath path = ChangesUtil.getFilePath(changes.get(0));
String prefix = FileStatus.DELETED.equals(status)
? "Delete"
: FileStatus.ADDED.equals(status)
? "Create"
: "Apply changes to";
singleMessage = prefix + "binary file " + TreeConflictRefreshablePanel.filePath(path) + " (according to theirs changes)?";
}
return AbstractVcsHelper.getInstance(myVcs.getProject()).selectFilePathsToProcess(new ArrayList<>(paths),
TreeConflictRefreshablePanel.TITLE, "Select binary files to patch", TreeConflictRefreshablePanel.TITLE,
return AbstractVcsHelper.getInstance(myVcs.getProject()).selectFilePathsToProcess(
newArrayList(paths), TreeConflictRefreshablePanel.TITLE, "Select binary files to patch", TreeConflictRefreshablePanel.TITLE,
singleMessage, new VcsShowConfirmationOption() {
@Override
public Value getValue() {
return null;
}
@Override
public void setValue(Value value) {
}
@Override
public boolean isPersistent() {
return false;
}
});
}
private List<Change> convertPaths(List<Change> changesForPatch) throws VcsException {
initAddOption();
final List<Change> changes = new ArrayList<>();
for (Change change : changesForPatch) {
if (! isUnderOldDir(change, myOldFilePath)) continue;
ContentRevision before = null;
ContentRevision after = null;
if (change.getBeforeRevision() != null) {
before = new SimpleContentRevision(change.getBeforeRevision().getContent(),
rebasePath(myOldFilePath, myNewFilePath, change.getBeforeRevision().getFile()),
change.getBeforeRevision().getRevisionNumber().asString());
}
if (change.getAfterRevision() != null) {
// if addition or move - do not move to the new path
if (myAdd && (change.getBeforeRevision() == null || change.isMoved() || change.isRenamed())) {
after = change.getAfterRevision();
} else {
after = new SimpleContentRevision(change.getAfterRevision().getContent(),
rebasePath(myOldFilePath, myNewFilePath, change.getAfterRevision().getFile()),
change.getAfterRevision().getRevisionNumber().asString());
@Override
public Value getValue() {
return null;
}
}
changes.add(new Change(before, after));
}
return changes;
@Override
public void setValue(Value value) {
}
@Override
public boolean isPersistent() {
return false;
}
});
}
private boolean isUnderOldDir(Change change, FilePath path) {
if (change.getBeforeRevision() != null) {
final boolean isUnder = FileUtil.isAncestor(path.getIOFile(), change.getBeforeRevision().getFile().getIOFile(), true);
if (isUnder) {
return true;
@NotNull
private List<Change> convertPaths(@NotNull List<Change> changes) throws VcsException {
initAddOption();
List<Change> result = newArrayList();
for (Change change : changes) {
if (isUnderOldDir(change, myOldFilePath)) {
result.add(new Change(convertBeforeRevision(change.getBeforeRevision()), convertAfterRevision(change, change.getAfterRevision())));
}
}
if (change.getAfterRevision() != null) {
final boolean isUnder = FileUtil.isAncestor(path.getIOFile(), change.getAfterRevision().getFile().getIOFile(), true);
if (isUnder) {
return isUnder;
}
}
return false;
return result;
}
@Nullable
private ContentRevision convertBeforeRevision(@Nullable ContentRevision revision) throws VcsException {
return revision != null ? toSimpleRevision(revision, true) : null;
}
@Nullable
private ContentRevision convertAfterRevision(@NotNull Change change, @Nullable ContentRevision revision) throws VcsException {
if (revision == null) return null;
return myAdd && (change.getBeforeRevision() == null || change.isMoved() || change.isRenamed()) ? revision : toSimpleRevision(revision,
true);
}
@NotNull
private SimpleContentRevision toSimpleRevision(@NotNull ContentRevision revision, boolean rebasePath) throws VcsException {
return new SimpleContentRevision(revision.getContent(),
rebasePath ? rebasePath(myOldFilePath, myNewFilePath, revision.getFile()) : myNewFilePath,
revision.getRevisionNumber().asString());
}
private static boolean isUnderOldDir(@NotNull Change change, @NotNull FilePath path) {
FilePath beforePath = getBeforePath(change);
FilePath afterPath = getAfterPath(change);
return beforePath != null && isAncestor(path.getPath(), beforePath.getPath(), true) ||
afterPath != null && isAncestor(path.getPath(), afterPath.getPath(), true);
}
@NotNull
private static FilePath rebasePath(@NotNull FilePath oldBase, @NotNull FilePath newBase, @NotNull FilePath path) {
String relativePath = ObjectUtils.assertNotNull(FileUtil.getRelativePath(oldBase.getPath(), path.getPath(), '/'));
String relativePath = notNull(getRelativePath(oldBase.getPath(), path.getPath(), '/'));
return VcsUtil.getFilePath(newBase.getPath() + "/" + relativePath, path.isDirectory());
}
@@ -507,13 +467,11 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
final SvnContentRevision base = SvnContentRevision.createBaseRevision(myVcs, myNewFilePath, myCommittedRevision.getRevision());
final SvnContentRevision remote = SvnContentRevision.createRemote(myVcs, myOldFilePath, SVNRevision.create(
myDescription.getSourceRightVersion().getPegRevision()));
SvnContentRevision base = SvnContentRevision.createBaseRevision(myVcs, myNewFilePath, myCommittedRevision.getRevision());
SvnContentRevision remote =
SvnContentRevision.createRemote(myVcs, myOldFilePath, SVNRevision.create(myDescription.getSourceRightVersion().getPegRevision()));
try {
final ContentRevision newBase = new SimpleContentRevision(base.getContent(), myNewFilePath, base.getRevisionNumber().asString());
final ContentRevision newRemote = new SimpleContentRevision(remote.getContent(), myNewFilePath, remote.getRevisionNumber().asString());
myTheirsChanges.add(new Change(newBase, newRemote));
myTheirsChanges.add(new Change(toSimpleRevision(base, false), toSimpleRevision(remote, false)));
}
catch (VcsException e) {
context.handleException(e, true);
@@ -528,61 +486,51 @@ public class MergeFromTheirsResolver {
@Override
public void run(ContinuationContext context) {
final List<Change> changesForPatch;
List<Change> changes;
try {
final List<CommittedChangeList> lst = loadSvnChangeListsForPatch(myDescription);
changesForPatch = CommittedChangesTreeBrowser.collectChanges(lst, true);
for (Change change : changesForPatch) {
if (change.getBeforeRevision() != null) {
preloadRevisionContents(change.getBeforeRevision());
}
if (change.getAfterRevision() != null) {
preloadRevisionContents(change.getAfterRevision());
}
changes = CommittedChangesTreeBrowser.collectChanges(loadSvnChangeListsForPatch(myDescription), true);
for (Change change : changes) {
preloadRevisionContents(change.getBeforeRevision());
preloadRevisionContents(change.getAfterRevision());
}
}
catch (VcsException e) {
context.handleException(e, true);
return;
}
final List<Change> binaryChanges = filterOutBinary(changesForPatch);
if (binaryChanges != null && ! binaryChanges.isEmpty()) {
myTheirsBinaryChanges.addAll(binaryChanges);
Map<Boolean, List<Change>> changesSplit = changes.stream().collect(partitioningBy(ChangesUtil::isBinaryChange));
myTheirsBinaryChanges.addAll(changesSplit.get(Boolean.TRUE));
myTheirsChanges.addAll(changesSplit.get(Boolean.FALSE));
}
}
private static void preloadRevisionContents(@Nullable ContentRevision revision) throws VcsException {
if (revision != null) {
if (revision instanceof BinaryContentRevision) {
((BinaryContentRevision)revision).getBinaryContent();
}
if (! changesForPatch.isEmpty()) {
myTheirsChanges.addAll(changesForPatch);
else {
revision.getContent();
}
}
}
private void preloadRevisionContents(ContentRevision cr) throws VcsException {
if (cr instanceof BinaryContentRevision) {
((BinaryContentRevision) cr).getBinaryContent();
} else {
cr.getContent();
}
}
private List<CommittedChangeList> loadSvnChangeListsForPatch(TreeConflictDescription description) throws VcsException {
@NotNull
private List<SvnChangeList> loadSvnChangeListsForPatch(@NotNull TreeConflictDescription description) throws VcsException {
long max = description.getSourceRightVersion().getPegRevision();
long min = description.getSourceLeftVersion().getPegRevision();
final ChangeBrowserSettings settings = new ChangeBrowserSettings();
SvnRepositoryLocation location = new SvnRepositoryLocation(description.getSourceRightVersion().getRepositoryRoot().toString());
ChangeBrowserSettings settings = new ChangeBrowserSettings();
settings.USE_CHANGE_BEFORE_FILTER = settings.USE_CHANGE_AFTER_FILTER = true;
settings.CHANGE_BEFORE = "" + max;
settings.CHANGE_AFTER = "" + min;
final List<SvnChangeList> committedChanges = myVcs.getCachingCommittedChangesProvider().getCommittedChanges(
settings, new SvnRepositoryLocation(description.getSourceRightVersion().getRepositoryRoot().toString()), 0);
final List<CommittedChangeList> lst = new ArrayList<>(committedChanges.size() - 1);
for (SvnChangeList change : committedChanges) {
if (change.getNumber() == min) {
continue;
}
lst.add(change);
}
return lst;
settings.CHANGE_BEFORE = String.valueOf(max);
settings.CHANGE_AFTER = String.valueOf(min);
//noinspection unchecked
List<SvnChangeList> committedChanges = notNull(myVcs.getCachingCommittedChangesProvider()).getCommittedChanges(settings, location, 0);
return filter(committedChanges, changeList -> changeList.getNumber() != min);
}
@CalledInAwt
private void initAddOption() {
ApplicationManager.getApplication().assertIsDispatchThread();
if (myAdd == null) {
@@ -591,60 +539,50 @@ public class MergeFromTheirsResolver {
}
private boolean getAddedFilesPlaceOption() {
final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
boolean add = Boolean.TRUE.equals(configuration.isKeepNewFilesAsIsForTreeConflictMerge());
if (configuration.isKeepNewFilesAsIsForTreeConflictMerge() != null) {
SvnConfiguration configuration = myVcs.getSvnConfiguration();
Boolean add = configuration.isKeepNewFilesAsIsForTreeConflictMerge();
if (add != null) {
return add;
}
if (!containAdditions(myTheirsChanges) && !containAdditions(myTheirsBinaryChanges)) {
return false;
}
return Messages.YES == MessageDialogBuilder.yesNo(TreeConflictRefreshablePanel.TITLE, "Keep newly created file(s) in their original place?").yesText("Keep").noText("Move").doNotAsk(
new DialogWrapper.DoNotAskOption() {
@Override
public boolean isToBeShown() {
return true;
}
return Messages.YES ==
MessageDialogBuilder.yesNo(TreeConflictRefreshablePanel.TITLE, "Keep newly created file(s) in their original place?")
.yesText("Keep").noText("Move").doNotAsk(
new DialogWrapper.DoNotAskOption() {
@Override
public boolean isToBeShown() {
return true;
}
@Override
public void setToBeShown(boolean value, int exitCode) {
if (!value) {
if (exitCode == 0) {
// yes
configuration.setKeepNewFilesAsIsForTreeConflictMerge(true);
}
else {
configuration.setKeepNewFilesAsIsForTreeConflictMerge(false);
}
}
}
@Override
public void setToBeShown(boolean value, int exitCode) {
if (!value) {
configuration.setKeepNewFilesAsIsForTreeConflictMerge(exitCode == 0);
}
}
@Override
public boolean canBeHidden() {
return true;
}
@Override
public boolean canBeHidden() {
return true;
}
@Override
public boolean shouldSaveOptionsOnCancel() {
return true;
}
@Override
public boolean shouldSaveOptionsOnCancel() {
return true;
}
@NotNull
@Override
public String getDoNotShowMessage() {
return CommonBundle.message("dialog.options.do.not.ask");
}
}).show();
@NotNull
@Override
public String getDoNotShowMessage() {
return CommonBundle.message("dialog.options.do.not.ask");
}
}).show();
}
private boolean containAdditions(final List<Change> changes) {
boolean addFound = false;
for (Change change : changes) {
if (change.getBeforeRevision() == null || change.isMoved() || change.isRenamed()) {
addFound = true;
break;
}
}
return addFound;
private static boolean containAdditions(@NotNull List<Change> changes) {
return changes.stream().anyMatch(change -> change.getBeforeRevision() == null || change.isMoved() || change.isRenamed());
}
}
@@ -42,6 +42,7 @@ import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.VcsBackgroundTask;
import com.intellij.vcsUtil.VcsUtil;
import gnu.trove.TLongArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.ConflictedSvnChange;
import org.jetbrains.idea.svn.SvnRevisionNumber;
import org.jetbrains.idea.svn.SvnVcs;
@@ -61,6 +62,8 @@ import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.List;
import static com.intellij.util.ObjectUtils.notNull;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
@@ -379,8 +382,9 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
};
}
public static String filePath(FilePath newFilePath) {
return newFilePath.getName() + " (" + newFilePath.getParentPath().getPath() + ")";
@NotNull
public static String filePath(@NotNull FilePath newFilePath) {
return newFilePath.getName() + " (" + notNull(newFilePath.getParentPath()).getPath() + ")";
}
private static ActionListener createBoth(TreeConflictDescription description) {