From 80ee021430c03deef9b4378d124e1b603e207955 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 8 Jun 2011 18:17:03 +0400 Subject: [PATCH] SVN: detect tree conflicts on not changed files; allow to mark deleted and locally deleted files: "tree conflict resolved" --- .../vcs/changes/ui/ChangesBrowserNode.java | 10 +- .../vcs/changes/ui/ChangesListView.java | 24 ++- plugins/svn4idea/src/META-INF/plugin.xml | 6 + .../idea/svn/SvnChangeProviderContext.java | 3 + .../idea/svn/SvnLocallyDeletedChange.java | 6 +- ...allyDeletedTreeConflictResolvedAction.java | 163 ++++++++++++++++++ 6 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkLocallyDeletedTreeConflictResolvedAction.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java index 4ed46939777f..8406bd08b0e9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java @@ -144,13 +144,17 @@ public class ChangesBrowserNode extends DefaultMutableTreeNode { } public List getAllChangesUnder() { - List changes = new ArrayList(); + return getAllObjectsUnder(Change.class); + } + + public List getAllObjectsUnder(final Class clazz) { + List changes = new ArrayList(); final Enumeration enumeration = depthFirstEnumeration(); while (enumeration.hasMoreElements()) { ChangesBrowserNode child = (ChangesBrowserNode)enumeration.nextElement(); final Object value = child.getUserObject(); - if (value instanceof Change) { - changes.add((Change)value); + if (clazz.isAssignableFrom(value.getClass())) { + changes.add((T) value); } } return changes; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java index b970975ee2ce..c7012dc45b12 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java @@ -77,6 +77,7 @@ public class ChangesListView extends Tree implements TypeSafeDataProvider, Advan @NonNls public static final String ourHelpId = "ideaInterface.changes"; @NonNls public static final DataKey> UNVERSIONED_FILES_DATA_KEY = DataKey.create("ChangeListView.UnversionedFiles"); @NonNls public static final DataKey> MISSING_FILES_DATA_KEY = DataKey.create("ChangeListView.MissingFiles"); + @NonNls public static final DataKey> LOCALLY_DELETED_CHANGES = DataKey.create("ChangeListView.LocallyDeletedChanges"); @NonNls public static final DataKey HELP_ID_DATA_KEY = DataKey.create(HELP_ID_KEY); private ActionGroup myMenuGroup; @@ -195,8 +196,9 @@ public class ChangesListView extends Tree implements TypeSafeDataProvider, Advan } else if (key == VcsDataKeys.MODIFIED_WITHOUT_EDITING_DATA_KEY) { sink.put(VcsDataKeys.MODIFIED_WITHOUT_EDITING_DATA_KEY, getSelectedModifiedWithoutEditing()); - } - else if (key == MISSING_FILES_DATA_KEY) { + } else if (key == LOCALLY_DELETED_CHANGES) { + sink.put(LOCALLY_DELETED_CHANGES, getSelectedLocallyDeletedChanges()); + } else if (key == MISSING_FILES_DATA_KEY) { sink.put(MISSING_FILES_DATA_KEY, getSelectedMissingFiles()); } else if (VcsDataKeys.HAVE_LOCALLY_DELETED == key) { sink.put(VcsDataKeys.HAVE_LOCALLY_DELETED, haveLocallyDeleted()); @@ -265,6 +267,24 @@ public class ChangesListView extends Tree implements TypeSafeDataProvider, Advan return new ArrayList(files); } + private List getSelectedLocallyDeletedChanges() { + Set files = new HashSet(); + final TreePath[] paths = getSelectionPaths(); + if (paths != null) { + for (TreePath path : paths) { + if (path.getPathCount() > 1) { + ChangesBrowserNode firstNode = (ChangesBrowserNode)path.getPathComponent(1); + if (firstNode.getUserObject() == TreeModelBuilder.LOCALLY_DELETED_NODE) { + ChangesBrowserNode node = (ChangesBrowserNode)path.getLastPathComponent(); + final List objectsUnder = node.getAllObjectsUnder(LocallyDeletedChange.class); + files.addAll(objectsUnder); + } + } + } + } + return new ArrayList(files); + } + private List getSelectedMissingFiles() { return getSelectedFilePaths(TreeModelBuilder.LOCALLY_DELETED_NODE); } diff --git a/plugins/svn4idea/src/META-INF/plugin.xml b/plugins/svn4idea/src/META-INF/plugin.xml index 83678d867667..4dfe7269b05b 100644 --- a/plugins/svn4idea/src/META-INF/plugin.xml +++ b/plugins/svn4idea/src/META-INF/plugin.xml @@ -86,6 +86,12 @@ + + + + + + diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 7e1f78e3a04a..1b137549d159 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -239,6 +239,9 @@ class SvnChangeProviderContext implements StatusReceiver { myChangelistBuilder.processChangeInList(createChange(SvnContentRevision.create(myVcs, filePath, status.getCommittedRevision()), CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), changeListNameFromStatus(status), SvnVcs.getKey()); + } else if (status.getTreeConflict() != null) { + myChangelistBuilder.processChange(createChange(SvnContentRevision.create(myVcs, filePath, status.getCommittedRevision()), + CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), SvnVcs.getKey()); } checkSwitched(filePath, myChangelistBuilder, status, fStatus); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnLocallyDeletedChange.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnLocallyDeletedChange.java index 33278d97abf1..c290aa7b5ad5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnLocallyDeletedChange.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnLocallyDeletedChange.java @@ -15,8 +15,8 @@ */ package org.jetbrains.idea.svn; -import com.intellij.openapi.vcs.changes.LocallyDeletedChange; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.changes.LocallyDeletedChange; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -42,4 +42,8 @@ public class SvnLocallyDeletedChange extends LocallyDeletedChange { } return description; } + + public ConflictState getConflictState() { + return myConflictState; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkLocallyDeletedTreeConflictResolvedAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkLocallyDeletedTreeConflictResolvedAction.java new file mode 100644 index 000000000000..a363423aba5a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkLocallyDeletedTreeConflictResolvedAction.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.AbstractVcsHelper; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsDataKeys; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.LocallyDeletedChange; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vcs.changes.ui.ChangesListView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.ConflictedSvnChange; +import org.jetbrains.idea.svn.SvnBundle; +import org.jetbrains.idea.svn.SvnLocallyDeletedChange; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNConflictChoice; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.util.Collections; +import java.util.List; + +/** + * @author irengrig + * Date: 6/8/11 + * Time: 4:58 PM + */ +public class MarkLocallyDeletedTreeConflictResolvedAction extends AnAction { + public MarkLocallyDeletedTreeConflictResolvedAction() { + super(SvnBundle.message("action.mark.tree.conflict.resolved.text")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final MyLocallyDeletedChecker locallyDeletedChecker = new MyLocallyDeletedChecker(e); + if (! locallyDeletedChecker.isEnabled()) return; + + final String markText = SvnBundle.message("action.mark.tree.conflict.resolved.confirmation.title"); + final Project project = locallyDeletedChecker.getProject(); + final int result = Messages.showYesNoDialog(project, + SvnBundle.message("action.mark.tree.conflict.resolved.confirmation.text"), markText, + Messages.getQuestionIcon()); + if (result == DialogWrapper.OK_EXIT_CODE) { + final Ref exception = new Ref(); + ProgressManager + .getInstance().run(new Task.Backgroundable(project, markText, true, BackgroundFromStartOption.getInstance()) { + public void run(@NotNull ProgressIndicator indicator) { + resolveLocallyDeletedTextConflict(locallyDeletedChecker, exception); + } + }); + if (! exception.isNull()) { + AbstractVcsHelper.getInstance(project).showError(exception.get(), markText); + } + } + } + + @Override + public void update(AnActionEvent e) { + final MyLocallyDeletedChecker locallyDeletedChecker = new MyLocallyDeletedChecker(e); + e.getPresentation().setVisible(locallyDeletedChecker.isEnabled()); + e.getPresentation().setEnabled(locallyDeletedChecker.isEnabled()); + //e.getPresentation().setText(SvnBundle.message("action.mark.tree.conflict.resolved.text")); + } + + private void resolveLocallyDeletedTextConflict(MyLocallyDeletedChecker checker, Ref exception) { + final FilePath path = checker.getPath(); + resolve(checker.getProject(), exception, path); + VcsDirtyScopeManager.getInstance(checker.getProject()).filePathsDirty(Collections.singletonList(path), null); + } + + private void resolve(Project project, Ref exception, FilePath path) { + final SVNWCClient client = SvnVcs.getInstance(project).createWCClient(); + try { + client.doResolve(path.getIOFile(), SVNDepth.EMPTY, false, false, true, SVNConflictChoice.MERGED); + } + catch (SVNException e1) { + exception.set(new VcsException(e1)); + } + } + + private static class MyLocallyDeletedChecker { + private final boolean myEnabled; + private final FilePath myPath; + private final Project myProject; + + public MyLocallyDeletedChecker(final AnActionEvent e) { + final DataContext dc = e.getDataContext(); + myProject = PlatformDataKeys.PROJECT.getData(dc); + if (myProject == null) { + myPath = null; + myEnabled = false; + return; + } + + final List missingFiles = e.getData(ChangesListView.LOCALLY_DELETED_CHANGES); + + if (missingFiles == null || missingFiles.size() != 1) { + final Change[] changes = e.getData(VcsDataKeys.CHANGES); + if (changes == null || changes.length != 1 || changes[0].getAfterRevision() != null) { + myPath = null; + myEnabled = false; + return; + } + myEnabled = changes[0] instanceof ConflictedSvnChange && ((ConflictedSvnChange) changes[0]).getConflictState().isTree(); + if (myEnabled) { + myPath = changes[0].getBeforeRevision().getFile(); + } else { + myPath = null; + } + return; + } + + final LocallyDeletedChange change = missingFiles.get(0); + myEnabled = change instanceof SvnLocallyDeletedChange && ((SvnLocallyDeletedChange) change).getConflictState().isTree(); + if (myEnabled) { + myPath = ((SvnLocallyDeletedChange) change).getPath(); + } + else { + myPath = null; + } + } + + public boolean isEnabled() { + return myEnabled; + } + + public FilePath getPath() { + return myPath; + } + + public Project getProject() { + return myProject; + } + } +}