mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
svn: Refactored "MergeFromTheirsResolver" - code simplified, @NotNull
This commit is contained in:
+252
-314
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user