From 1b5e164c8952eea679e7697056519c1759eed929 Mon Sep 17 00:00:00 2001 From: irengrig Date: Fri, 29 Jan 2010 11:27:21 +0300 Subject: [PATCH] IDEA-26360 (Performance and inconsistency issues with svn:externals and "Detect nested working copies") --- .../src/com/intellij/openapi/util/io/FileUtil.java | 4 + .../vcs/update/SequentialUpdatesContext.java | 1 + .../com/intellij/openapi/vcs/changes/VcsGuess.java | 2 +- .../vcs/update/AbstractCommonUpdateAction.java | 4 +- .../intellij/cvsSupport2/CvsUpdateEnvironment.java | 4 + .../src/org/jetbrains/idea/svn/NestedCopyType.java | 16 +- .../src/org/jetbrains/idea/svn/RootUrlInfo.java | 10 ++ .../idea/svn/SvnAuthenticationNotifier.java | 7 + .../jetbrains/idea/svn/SvnFileUrlMappingImpl.java | 27 ++- .../jetbrains/idea/svn/SvnNestedTypeRechecker.java | 183 +++++++++++++++++++++ .../src/org/jetbrains/idea/svn/SvnVcs.java | 16 +- .../jetbrains/idea/svn/dialogs/SvnMapDialog.java | 24 ++- .../src/org/jetbrains/idea/svn/dialogs/WCInfo.java | 12 +- .../idea/svn/dialogs/WCInfoWithBranches.java | 6 +- .../jetbrains/idea/svn/history/WcInfoLoader.java | 11 +- .../idea/svn/integrate/IntegrateEventHandler.java | 2 +- .../AbstractSvnUpdateIntegrateEnvironment.java | 17 +- .../idea/svn/update/SvnUpdateContext.java | 49 ++++++ .../idea/svn/update/SvnUpdateRootOptionsPanel.form | 12 +- .../idea/svn/update/SvnUpdateRootOptionsPanel.java | 20 +++ .../idea/svn/update/UpdateEventHandler.java | 13 +- 21 files changed, 394 insertions(+), 46 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/SvnNestedTypeRechecker.java create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateContext.java diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index e3c5e147c4..850b4d56fe 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -641,6 +641,10 @@ public class FileUtil { return aFileName.replace('\\', '/'); } + public static String nameToCompare(@NonNls @NotNull String name) { + return (SystemInfo.isFileSystemCaseSensitive ? name : name.toLowerCase()).replace('\\', '/'); + } + public static String unquote(String urlString) { urlString = urlString.replace('/', File.separatorChar); return URLUtil.unescapePercentSequences(urlString); diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/update/SequentialUpdatesContext.java b/platform/vcs-api/src/com/intellij/openapi/vcs/update/SequentialUpdatesContext.java index 70b15cdb13..6fbf4e7ebc 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/update/SequentialUpdatesContext.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/update/SequentialUpdatesContext.java @@ -20,4 +20,5 @@ import org.jetbrains.annotations.NotNull; public interface SequentialUpdatesContext { @NotNull String getMessageWhenInterruptedBeforeStart(); + boolean shouldFail(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java index 365fa2eacb..6799585d48 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java @@ -45,7 +45,7 @@ public class VcsGuess { return null; } if (myExcludedFileIndex.isInContent(file) || isFileInBaseDir(file) || - myVcsManager.hasExplicitMapping(file)) { + myVcsManager.hasExplicitMapping(file) || file.equals(myProject.getBaseDir())) { if (myExcludedFileIndex.isExcludedFile(file)) { return null; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java index 6ef340a281..f227b67789 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java @@ -472,7 +472,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } boolean continueChain = false; for (SequentialUpdatesContext context : myContextInfo.values()) { - continueChain |= context != null; + continueChain |= (context != null) && (context.shouldFail()); } final boolean continueChainFinal = continueChain; @@ -563,7 +563,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { private void gatherContextInterruptedMessages() { for (Map.Entry entry : myContextInfo.entrySet()) { final SequentialUpdatesContext context = entry.getValue(); - if (context == null) continue; + if ((context == null) || (! context.shouldFail())) continue; final VcsException exception = new VcsException(context.getMessageWhenInterruptedBeforeStart()); gatherExceptions(entry.getKey(), Collections.singletonList(exception)); } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/CvsUpdateEnvironment.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/CvsUpdateEnvironment.java index da81dd3da2..dc937e3078 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/CvsUpdateEnvironment.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/CvsUpdateEnvironment.java @@ -81,6 +81,10 @@ public class CvsUpdateEnvironment implements UpdateEnvironment { return "Merge (" + mergeString + ") wasn't started, only update (-r " + myUpdateTagName + ") was performed"; } + public boolean shouldFail() { + return true; + } + public UpdateSettingsOnCvsConfiguration getConfiguration() { return myConfiguration; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopyType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopyType.java index d57e62e388..37be04f56a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopyType.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopyType.java @@ -16,7 +16,17 @@ package org.jetbrains.idea.svn; public enum NestedCopyType { - external, - switched, - inner + external("External"), + switched("Switched"), + inner("Nested"); + + private final String myName; + + NestedCopyType(String name) { + myName = name; + } + + public String getName() { + return myName; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootUrlInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootUrlInfo.java index 4a79ea15ed..55309962ae 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootUrlInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootUrlInfo.java @@ -30,6 +30,7 @@ public class RootUrlInfo implements RootUrlPair { private final VirtualFile myVfile; // vcs root private final VirtualFile myRoot; + private NestedCopyType myType; public RootUrlInfo(final SVNURL repositoryUrl, final SVNURL absoluteUrlAsUrl, final WorkingCopyFormat format, final VirtualFile vfile, final VirtualFile root) { @@ -41,6 +42,7 @@ public class RootUrlInfo implements RootUrlPair { final String asString = repositoryUrl.toString(); myRepositoryUrl = asString.endsWith("/") ? asString.substring(0, asString.length() - 1) : asString; myAbsoluteUrlAsUrl = absoluteUrlAsUrl; + //myType = NestedCopyType.inner; // default } public String getRepositoryUrl() { @@ -79,4 +81,12 @@ public class RootUrlInfo implements RootUrlPair { public String getUrl() { return myAbsoluteUrlAsUrl.toString(); } + + public NestedCopyType getType() { + return myType; + } + + public void setType(NestedCopyType type) { + myType = type; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java index 9e10f22623..e28d5dc510 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java @@ -67,6 +67,13 @@ public class SvnAuthenticationNotifier extends GenericNotifierImpl outdatedRequests = new LinkedList(); final Collection keys = getAllCurrentKeys(); for (SVNURL key : keys) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java index 41f8857d2c..17e730c6f1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java @@ -326,11 +326,24 @@ class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentStateCompone clManager.invokeAfterUpdate(new Runnable() { public void run() { final List nestedRoots = new ArrayList(); - - for (NestedCopiesBuilder.MyPointInfo info : myGate.get().getSet()) { - if (NestedCopyType.external.equals(info.getType())) { + + final NestedCopiesData data = myGate.get(); + for (NestedCopiesBuilder.MyPointInfo info : data.getSet()) { + if (NestedCopyType.external.equals(info.getType()) || NestedCopyType.switched.equals(info.getType())) { + final File infoFile = new File(info.getFile().getPath()); + boolean copyFound = false; + for (RootUrlInfo topRoot : myTopRoots) { + if (topRoot.getIoFile().equals(infoFile)) { + topRoot.setType(info.getType()); + copyFound = true; + break; + } + } + if (copyFound) { + continue; + } try { - final SVNStatus svnStatus = SvnUtil.getStatus(myVcs, new File(info.getFile().getPath())); + final SVNStatus svnStatus = SvnUtil.getStatus(myVcs, infoFile); if (svnStatus.getURL() == null) continue; info.setUrl(svnStatus.getURL()); info.setFormat(WorkingCopyFormat.getInstance(svnStatus.getWorkingCopyFormat())); @@ -343,12 +356,16 @@ class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentStateCompone if (VfsUtil.isAncestor(topRoot.getVirtualFile(), info.getFile(), true)) { final SVNURL repoRoot = myRepositoryRoots.ask(info.getUrl()); if (repoRoot != null) { - nestedRoots.add(new RootUrlInfo(repoRoot, info.getUrl(), info.getFormat(), info.getFile(), topRoot.getRoot())); + final RootUrlInfo rootInfo = new RootUrlInfo(repoRoot, info.getUrl(), info.getFormat(), info.getFile(), topRoot.getRoot()); + rootInfo.setType(info.getType()); + nestedRoots.add(rootInfo); } break; } } } + // check those top roots which ARE externals, but that was not detected due to they itself were the status request target + //new SvnNestedTypeRechecker(myVcs.getProject(), myTopRoots).run(); myTopRoots.addAll(nestedRoots); myApplier.apply(myVcs, myAtomicSectionsAware, myTopRoots, myLonelyRoots); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnNestedTypeRechecker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnNestedTypeRechecker.java new file mode 100644 index 0000000000..83a9b726c0 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnNestedTypeRechecker.java @@ -0,0 +1,183 @@ +/* + * Copyright 2000-2010 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; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.AbstractFilterChildren; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.MultiMap; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.*; + +import java.io.File; +import java.util.*; + +// referencies of RootUrlInfo will be kept; so type flag will be raised right in original object +public class SvnNestedTypeRechecker { + private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnNestedTypeRechecker"); + + private final MultiMap myRoots; + private Map mySourceItems; + private Map> myTreeTravellingMap; + private final Project myProject; + + public SvnNestedTypeRechecker(final Project project, final List roots) { + myProject = project; + final NestedGatherer nestedGatherer = new NestedGatherer(); + nestedGatherer.doFilter(new ArrayList(roots)); + myRoots = nestedGatherer.getNested(); + mySourceItems = new HashMap(); + for (VirtualFile root : myRoots.keySet()) { + final Collection items = myRoots.get(root); + for (RootUrlInfo item : items) { + mySourceItems.put(FileUtil.nameToCompare(item.getVirtualFile().getPath()), item); + } + } + } + + private void initTravellingMap() { + myTreeTravellingMap = new HashMap>(); + + final MultiMap tmpMap = new MultiMap(); + for (VirtualFile parent : myRoots.keySet()) { + final Collection children = myRoots.get(parent); + for (RootUrlInfo child : children) { + + VirtualFile current = child.getVirtualFile(); + while (! current.equals(parent)) { + tmpMap.putValue(current.getParent(), new File(current.getPath())); + current = current.getParent(); + if (current.getParent() == null) break; //+- + } + } + } + + for (VirtualFile dir : tmpMap.keySet()) { + final Map currMap = new HashMap(); + final Collection coll = tmpMap.get(dir); + for (File file : coll) { + currMap.put(file.getAbsolutePath(), file); + } + myTreeTravellingMap.put(FileUtil.nameToCompare(dir.getPath()), currMap); + } + } + + public void run() { + if (! myRoots.isEmpty()) { + initTravellingMap(); + + final ISVNStatusFileProvider fileProvider = new ISVNStatusFileProvider() { + public Map getChildrenFiles(File parent) { + final Map children = myTreeTravellingMap.get(FileUtil.nameToCompare(parent.getAbsolutePath())); + return children == null ? Collections.emptyMap() : children; + } + }; + final SvnVcs vcs = SvnVcs.getInstance(myProject); + final SVNStatusClient client = vcs.createStatusClient(); + client.setFilesProvider(fileProvider); + + for (VirtualFile parent : myRoots.keySet()) { + try { + client.doStatus(new File(parent.getPath()), SVNRevision.WORKING, SVNDepth.INFINITY, false, true, true, false, new ISVNStatusHandler() { + public void handleStatus(final SVNStatus status) throws SVNException { + final boolean switched = status.isSwitched(); + final boolean externals = SVNStatusType.STATUS_EXTERNAL.equals(status.getContentsStatus()); + + if (switched || externals) { + final RootUrlInfo urlInfo = mySourceItems.get(FileUtil.nameToCompare(status.getFile().getAbsolutePath())); + if (urlInfo != null) { + if (switched) { + urlInfo.setType(NestedCopyType.switched); + } else { + urlInfo.setType(NestedCopyType.external); + } + } + } + } + }, null); + } + catch (SVNException e) { + LOG.debug(e); + } + } + } + } + + /*private static SVNStatus getExternalItemStatus(final SvnVcs vcs, final File file) { + final SVNStatusClient statusClient = vcs.createStatusClient(); + try { + if (file.isDirectory()) { + return statusClient.doStatus(file, false); + } else { + final File parent = file.getParentFile(); + if (parent != null) { + statusClient.setFilesProvider(new ISVNStatusFileProvider() { + public Map getChildrenFiles(File parent) { + return Collections.singletonMap(file.getAbsolutePath(), file); + } + }); + final Ref refStatus = new Ref(); + statusClient.doStatus(parent, SVNRevision.WORKING, SVNDepth.FILES, false, true, false, false, new ISVNStatusHandler() { + public void handleStatus(final SVNStatus status) throws SVNException { + if (file.equals(status.getFile())) { + refStatus.set(status); + } + } + }, null); + return refStatus.get(); + } + } + } + catch (SVNException e) { + // + } + return null; + }*/ + + private static class NestedGatherer extends AbstractFilterChildren { + private final MultiMap myNested; + + private NestedGatherer() { + myNested = new MultiMap(); + } + + @Override + protected void sortAscending(List rootUrlInfos) { + Collections.sort(rootUrlInfos, new Comparator() { + public int compare(final RootUrlInfo r1, final RootUrlInfo r2) { + return new Integer(r1.getVirtualFile().getPath().length()).compareTo(r2.getVirtualFile().getPath().length()); + } + }); + } + + @Override + protected boolean isAncestor(RootUrlInfo parent, RootUrlInfo child) { + final boolean result = VfsUtil.isAncestor(parent.getVirtualFile(), child.getVirtualFile(), true); + if (result) { + myNested.putValue(parent.getVirtualFile(), child); + } + return result; + } + + public MultiMap getNested() { + return myNested; + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 8c39bee647..586a55341f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -222,11 +222,15 @@ public class SvnVcs extends AbstractVcs { if (asynchronous) { myCopiesRefreshManager.getCopiesRefresh().asynchRequest(); } else { - ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - public void run() { - myCopiesRefreshManager.getCopiesRefresh().synchRequest(); - } - }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject); + if (ApplicationManager.getApplication().isDispatchThread()) { + ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + myCopiesRefreshManager.getCopiesRefresh().synchRequest(); + } + }, SvnBundle.message("refreshing.working.copies.roots.progress.text"), true, myProject); + } else { + myCopiesRefreshManager.getCopiesRefresh().synchRequest(); + } } } } @@ -816,7 +820,7 @@ public class SvnVcs extends AbstractVcs { for (RootUrlInfo info : infoList) { final File file = info.getIoFile(); infos.add(new WCInfo(file.getAbsolutePath(), info.getAbsoluteUrlAsUrl(), - SvnFormatSelector.getWorkingCopyFormat(file), info.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file))); + SvnFormatSelector.getWorkingCopyFormat(file), info.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file), info.getType())); } return infos; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnMapDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnMapDialog.java index 996fcc9ea0..bd12669107 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnMapDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnMapDialog.java @@ -15,6 +15,8 @@ */ package org.jetbrains.idea.svn.dialogs; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; @@ -23,17 +25,12 @@ import com.intellij.openapi.ui.MultiLineLabelUI; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsDirectoryMapping; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.ui.table.TableView; -import com.intellij.util.messages.Topic; import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.messages.Topic; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ListTableModel; -import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.WorkingCopyFormat; +import org.jetbrains.idea.svn.*; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -187,7 +184,7 @@ public class SvnMapDialog extends DialogWrapper { } private void createUIComponents() { - myTableModel = new ListTableModel(new ColumnInfo[]{WC_ROOT_PATH, WC_URL, WC_COPY_ROOT, WC_FORMAT}, Collections.emptyList(), 0); + myTableModel = new ListTableModel(new ColumnInfo[]{WC_ROOT_PATH, WC_URL, WC_COPY_ROOT, WC_FORMAT, WC_TYPE}, Collections.emptyList(), 0); myTableView = new TableView(myTableModel); } @@ -207,15 +204,16 @@ public class SvnMapDialog extends DialogWrapper { } }; private static final ColumnInfo WC_FORMAT = new ColumnInfo(SvnBundle.message("dialog.show.svn.map.table.header.column.format.title")) { - @Override - public String getName() { - return super.getName(); - } - public String valueOf(final WCInfo info) { final WorkingCopyFormat format = info.getFormat(); return SvnUtil.formatRepresentation(format); } }; + private static final ColumnInfo WC_TYPE = new ColumnInfo("Type") { + public String valueOf(final WCInfo info) { + final NestedCopyType type = info.getType(); + return type == null ? "" : type.getName(); + } + }; } \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfo.java index 047effb5e9..1c0913953d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfo.java @@ -15,9 +15,10 @@ */ package org.jetbrains.idea.svn.dialogs; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.idea.svn.NestedCopyType; import org.jetbrains.idea.svn.WorkingCopyFormat; import org.tmatesoft.svn.core.SVNURL; -import com.intellij.openapi.vfs.VirtualFile; public class WCInfo implements WCPaths { private final String myPath; @@ -25,13 +26,16 @@ public class WCInfo implements WCPaths { private final WorkingCopyFormat myFormat; private final String myRepositoryRoot; private final boolean myIsWcRoot; + private final NestedCopyType myType; - public WCInfo(final String path, final SVNURL url, final WorkingCopyFormat format, final String repositoryRoot, final boolean isWcRoot) { + public WCInfo(final String path, final SVNURL url, final WorkingCopyFormat format, final String repositoryRoot, final boolean isWcRoot, + final NestedCopyType type) { myPath = path; myUrl = url; myFormat = format; myRepositoryRoot = repositoryRoot; myIsWcRoot = isWcRoot; + myType = type; } public String getPath() { @@ -82,4 +86,8 @@ public class WCInfo implements WCPaths { public int hashCode() { return (myPath != null ? myPath.hashCode() : 0); } + + public NestedCopyType getType() { + return myType; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfoWithBranches.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfoWithBranches.java index cdf186803a..3c04f247a5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfoWithBranches.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/WCInfoWithBranches.java @@ -16,6 +16,7 @@ package org.jetbrains.idea.svn.dialogs; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.idea.svn.NestedCopyType; import org.jetbrains.idea.svn.WorkingCopyFormat; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; @@ -30,8 +31,9 @@ public class WCInfoWithBranches extends WCInfo { public WCInfoWithBranches(final String path, final SVNURL url, final WorkingCopyFormat format, final String repositoryRoot, final boolean isWcRoot, final List branches, final VirtualFile root, - final String trunkToot) { - super(path, url, format, repositoryRoot, isWcRoot); + final String trunkToot, + final NestedCopyType type) { + super(path, url, format, repositoryRoot, isWcRoot, type); myBranches = branches; myRoot = root; myTrunkRoot = trunkToot; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/WcInfoLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/WcInfoLoader.java index c2a27122e2..bdf9d83da1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/WcInfoLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/WcInfoLoader.java @@ -23,7 +23,6 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; -import org.jetbrains.idea.svn.branchConfig.InfoStorage; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; import org.jetbrains.idea.svn.dialogs.WCInfo; import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches; @@ -31,7 +30,10 @@ import org.jetbrains.idea.svn.integrate.SvnBranchItem; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; public class WcInfoLoader { private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.WcInfoLoader"); @@ -77,7 +79,8 @@ public class WcInfoLoader { return null; } final WCInfo wcInfo = new WCInfo(rootInfo.getIoFile().getAbsolutePath(), rootInfo.getAbsoluteUrlAsUrl(), - SvnFormatSelector.getWorkingCopyFormat(file), rootInfo.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file)); + SvnFormatSelector.getWorkingCopyFormat(file), rootInfo.getRepositoryUrl(), SvnUtil.isWorkingCopyRoot(file), + rootInfo.getType()); return createInfo(wcInfo, vcs, urlMapping); } @@ -122,7 +125,7 @@ public class WcInfoLoader { final String branchRoot = createBranchesList(url, configuration, items); return new WCInfoWithBranches(info.getPath(), info.getUrl(), info.getFormat(), info.getRepositoryRoot(), info.isIsWcRoot(), items, root, - branchRoot); + branchRoot, info.getType()); } private static String createBranchesList(final String url, final SvnBranchConfigurationNew configuration, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegrateEventHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegrateEventHandler.java index 5799dfd3e7..75533d8801 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegrateEventHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegrateEventHandler.java @@ -26,7 +26,7 @@ import org.tmatesoft.svn.core.wc.SVNStatusType; public class IntegrateEventHandler extends UpdateEventHandler { public IntegrateEventHandler(final SvnVcs vcs, final ProgressIndicator progressIndicator) { - super(vcs, progressIndicator); + super(vcs, progressIndicator, null); } protected boolean handleInDescendants(final SVNEvent event) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdateIntegrateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdateIntegrateEnvironment.java index c4bcda5020..8e98177565 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdateIntegrateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdateIntegrateEnvironment.java @@ -21,6 +21,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -69,19 +70,31 @@ public abstract class AbstractSvnUpdateIntegrateEnvironment implements UpdateEnv final ProgressIndicator progressIndicator, @NotNull final Ref context) throws ProcessCanceledException { + if (context.isNull()) { + context.set(new SvnUpdateContext()); + } + final ArrayList exceptions = new ArrayList(); - UpdateEventHandler eventHandler = new UpdateEventHandler(myVcs, progressIndicator); + UpdateEventHandler eventHandler = new UpdateEventHandler(myVcs, progressIndicator, (SvnUpdateContext) context.get()); eventHandler.setUpdatedFiles(updatedFiles); boolean totalUpdate = true; AbstractUpdateIntegrateCrawler crawler = createCrawler(eventHandler, totalUpdate, exceptions, updatedFiles); Collection updatedRoots = new HashSet(); + Arrays.sort(contentRoots, new Comparator() { + public int compare(FilePath o1, FilePath o2) { + return SystemInfo.isFileSystemCaseSensitive ? o1.getPath().replace("/", "\\").compareTo(o2.getPath().replace("/", "\\")) : + o1.getPath().replace("/", "\\").compareToIgnoreCase(o2.getPath().replace("/", "\\")); + } + }); for (FilePath contentRoot : contentRoots) { if (progressIndicator != null && progressIndicator.isCanceled()) { throw new ProcessCanceledException(); } - Collection roots = SvnUtil.crawlWCRoots(contentRoot.getIOFile(), crawler, progressIndicator); + final File ioRoot = contentRoot.getIOFile(); + if (! ((SvnUpdateContext)context.get()).shouldRunFor(ioRoot)) continue; + Collection roots = SvnUtil.crawlWCRoots(ioRoot, crawler, progressIndicator); updatedRoots.addAll(roots); } if (updatedRoots.isEmpty()) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateContext.java new file mode 100644 index 0000000000..2fb2a9d8db --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateContext.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2010 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.update; + +import com.intellij.openapi.vcs.update.SequentialUpdatesContext; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +public class SvnUpdateContext implements SequentialUpdatesContext { + private final Set myUpdatedExternals; + + public SvnUpdateContext() { + myUpdatedExternals = new HashSet(); + } + + @NotNull + public String getMessageWhenInterruptedBeforeStart() { + // never + return null; + } + + public boolean shouldFail() { + return false; + } + + public void registerExternalRootBeingUpdated(final File root) { + myUpdatedExternals.add(root); + } + + public boolean shouldRunFor(final File ioRoot) { + return ! myUpdatedExternals.contains(ioRoot); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.form index a1b5114f30..59fcc82cb7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.form +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.form @@ -3,12 +3,12 @@ - + - + @@ -74,6 +74,14 @@ + + + + + + + + diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.java index bffa3e3570..7be228a22e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateRootOptionsPanel.java @@ -33,6 +33,7 @@ import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.wc.SVNRevision; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -49,6 +50,7 @@ public class SvnUpdateRootOptionsPanel implements SvnPanel{ private TextFieldWithBrowseButton myBranchField; private JLabel myBranchLabel; private JLabel myUrlLabel; + private JLabel myCopyType; private String mySourceUrl; private SVNURL myBranchUrl; @@ -118,6 +120,24 @@ public class SvnUpdateRootOptionsPanel implements SvnPanel{ myRevisionText.setEnabled(myRevisionBox.isSelected()); myURLText.setEnabled(myUpdateToSpecificUrl.isSelected()); myBranchField.setEnabled(myUpdateToSpecificUrl.isSelected() && (myBranchUrl != null) && (mySourceUrl != null)); + + final boolean revisionCanBeSpecifiedForRoot = isRevisionCanBeSpecifiedForRoot(); + myRevisionBox.setEnabled(revisionCanBeSpecifiedForRoot); + myRevisionText.setEnabled(revisionCanBeSpecifiedForRoot); + myCopyType.setVisible(! revisionCanBeSpecifiedForRoot); + myCopyType.setFont(myCopyType.getFont().deriveFont(Font.ITALIC)); + } + + private boolean isRevisionCanBeSpecifiedForRoot() { + final RootUrlInfo info = myVcs.getSvnFileUrlMapping().getWcRootForFilePath(myRoot.getIOFile()); + if (info != null) { + final boolean result = (!NestedCopyType.external.equals(info.getType())) && (!NestedCopyType.switched.equals(info.getType())); + if (! result) { + myCopyType.setText(info.getType().getName() + " copy"); + } + return result; + } + return true; } private void chooseBranch() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateEventHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateEventHandler.java index 862ab4cc3a..cb3a2252c7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateEventHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateEventHandler.java @@ -16,11 +16,12 @@ package org.jetbrains.idea.svn.update; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.update.FileGroup; import com.intellij.openapi.vcs.update.UpdatedFiles; -import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.WindowManager; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnFileUrlMapping; import org.jetbrains.idea.svn.SvnVcs; @@ -44,13 +45,16 @@ public class UpdateEventHandler implements ISVNEventHandler { private UpdatedFiles myUpdatedFiles; private int myExternalsCount; private final SvnVcs myVCS; + @Nullable private final SvnUpdateContext mySequentialUpdatesContext; protected String myText; protected String myText2; - public UpdateEventHandler(SvnVcs vcs, ProgressIndicator progressIndicator) { + public UpdateEventHandler(SvnVcs vcs, ProgressIndicator progressIndicator, + @Nullable final SvnUpdateContext sequentialUpdatesContext) { myProgressIndicator = progressIndicator; myVCS = vcs; + mySequentialUpdatesContext = sequentialUpdatesContext; myExternalsCount = 1; } @@ -134,6 +138,9 @@ public class UpdateEventHandler implements ISVNEventHandler { } } else if (event.getAction() == SVNEventAction.UPDATE_EXTERNAL) { + if (mySequentialUpdatesContext != null) { + mySequentialUpdatesContext.registerExternalRootBeingUpdated(event.getFile()); + } myExternalsCount++; if (myUpdatedFiles.getGroupById(AbstractSvnUpdateIntegrateEnvironment.EXTERNAL_ID) == null) { myUpdatedFiles.registerGroup(new FileGroup(SvnBundle.message("status.group.name.externals"), -- 2.11.4.GIT