From 88f7eea6d1437206ff7f283e24928c567ac02aa3 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Sun, 28 Aug 2016 02:30:11 +0300 Subject: [PATCH] svn: Refactored "MergeFromTheirsResolver" - code simplified, @NotNull --- .../treeConflict/MergeFromTheirsResolver.java | 566 ++++++++---------- .../TreeConflictRefreshablePanel.java | 8 +- 2 files changed, 258 insertions(+), 316 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java index 14cb8e777176..6df2473202bb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java @@ -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 myTheirsChanges; - private final List myTheirsBinaryChanges; - private final List myWarnings; + @NotNull private final List myTheirsChanges; + @NotNull private final List myTheirsBinaryChanges; + @NotNull private final List myWarnings; private List 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() { - @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 tasks = new SmartList<>(); + List 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 filterOutBinary(List paths) { - List result = null; - for (Iterator 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 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.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 { - 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 remaining, @NotNull MultiMap patchGroupsToApply, + public void apply(@NotNull List remaining, + @NotNull MultiMap patchGroupsToApply, @Nullable LocalChangeList localList, @Nullable String fileName, @Nullable TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo) { - final List patches; + List patches; try { patches = ApplyPatchSaveToFileExecutor.patchGroupsToOneGroup(patchGroupsToApply, myBaseDir); } @@ -257,22 +245,16 @@ public class MergeFromTheirsResolver { return; } - final PatchApplier 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(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 patches; try { - patches = IdeaTextPatchBuilder.buildPatch(project, myTheirsChanges, ObjectUtils.assertNotNull(myBaseForPatch).getPath(), false); - myTextPatches = ObjectsConvertor.convert(patches, new Convertor() { - @Override - public TextFilePatch convert(FilePatch o) { - return (TextFilePatch)o; - } - }); + List 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 converted; + List converted; try { converted = convertPaths(myTheirsBinaryChanges); } @@ -338,14 +312,14 @@ public class MergeFromTheirsResolver { return; } if (converted.isEmpty()) return; - final Map map = new HashMap<>(); - for (Change change : converted) { - map.put(ChangesUtil.getFilePath(change), change); - } - final Collection selected = chooseBinaryFiles(converted, map.keySet()); + Map map = converted.stream().collect(toMap(ChangesUtil::getFilePath, identity())); + Collection 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 dirtyPaths = new ArrayList<>(); - for (final Change change : myTheirsBinaryChanges) { + List dirtyPaths = newArrayList(); + for (Change change : myTheirsBinaryChanges) { try { - application.runWriteAction(new ThrowableComputable() { - @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)() -> { + 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 chooseBinaryFiles(List converted, Set paths) { + @Nullable + private Collection chooseBinaryFiles(@NotNull List changes, @NotNull Set 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 convertPaths(List changesForPatch) throws VcsException { - initAddOption(); - final List 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 convertPaths(@NotNull List changes) throws VcsException { + initAddOption(); + + List 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 changesForPatch; + List changes; try { - final List 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 binaryChanges = filterOutBinary(changesForPatch); - if (binaryChanges != null && ! binaryChanges.isEmpty()) { - myTheirsBinaryChanges.addAll(binaryChanges); + Map> 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 loadSvnChangeListsForPatch(TreeConflictDescription description) throws VcsException { + @NotNull + private List 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 committedChanges = myVcs.getCachingCommittedChangesProvider().getCommittedChanges( - settings, new SvnRepositoryLocation(description.getSourceRightVersion().getRepositoryRoot().toString()), 0); - final List 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 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 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 changes) { + return changes.stream().anyMatch(change -> change.getBeforeRevision() == null || change.isMoved() || change.isRenamed()); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java index d697d9a558da..8b1f5e45c9bd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java @@ -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) {