diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/IdeaSVNConfigFile.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/IdeaSVNConfigFile.java index 0c0e36f56b1b..03b804496d43 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/IdeaSVNConfigFile.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/IdeaSVNConfigFile.java @@ -22,9 +22,6 @@ import org.jetbrains.idea.svn.config.ProxyGroup; import org.tmatesoft.svn.core.internal.wc.SVNConfigFile; import java.io.File; -import java.net.InetSocketAddress; -import java.net.PasswordAuthentication; -import java.net.Proxy; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -49,59 +46,16 @@ public class IdeaSVNConfigFile { myPatternsMap = new HashMap(); } - public static void putProxyIntoServersFile(final File configDir, final String host, final Proxy proxyInfo) { - final IdeaSVNConfigFile configFile = new IdeaSVNConfigFile(new File(configDir, SERVERS_FILE_NAME)); - configFile.updateGroups(); - - String groupName = ensureHostGroup(host, configFile); - - final HashMap map = new HashMap(); - final InetSocketAddress address = ((InetSocketAddress) proxyInfo.address()); - map.put(SvnAuthenticationManager.HTTP_PROXY_HOST, address.getHostName()); - map.put(SvnAuthenticationManager.HTTP_PROXY_PORT, String.valueOf(address.getPort())); - configFile.addGroup(groupName, host + "*", map); - configFile.save(); - } - - @NotNull - public static String ensureHostGroup(@NotNull String host, @NotNull IdeaSVNConfigFile configFile) { - String groupName = SvnAuthenticationManager.getGroupForHost(host, configFile); - - if (StringUtil.isEmptyOrSpaces(groupName)) { - groupName = getNewGroupName(host, configFile); - } - - return groupName; - } - @NotNull public static String getNewGroupName(@NotNull String host, @NotNull IdeaSVNConfigFile configFile) { String groupName = host; - final Map groups = configFile.getAllGroups(); + final Map groups = configFile.getAllGroups(); while (StringUtil.isEmptyOrSpaces(groupName) || groups.containsKey(groupName)) { groupName += "1"; } return groupName; } - public static boolean putProxyCredentialsIntoServerFile(@NotNull final File configDir, @NotNull final String host, - @NotNull final PasswordAuthentication authentication) { - final IdeaSVNConfigFile configFile = new IdeaSVNConfigFile(new File(configDir, SERVERS_FILE_NAME)); - configFile.updateGroups(); - - String groupName = SvnAuthenticationManager.getGroupForHost(host, configFile); - // no proxy defined in group -> no sense in password - if (StringUtil.isEmptyOrSpaces(groupName)) return false; - final Map properties = configFile.getAllGroups().get(groupName).getProperties(); - if (StringUtil.isEmptyOrSpaces(properties.get(SvnAuthenticationManager.HTTP_PROXY_HOST))) return false; - if (StringUtil.isEmptyOrSpaces(properties.get(SvnAuthenticationManager.HTTP_PROXY_PORT))) return false; - - configFile.setValue(groupName, SvnAuthenticationManager.HTTP_PROXY_USERNAME, authentication.getUserName()); - configFile.setValue(groupName, SvnAuthenticationManager.HTTP_PROXY_PASSWORD, String.valueOf(authentication.getPassword())); - configFile.save(); - return true; - } - public void updateGroups() { if (myLatestUpdate != myFile.lastModified()) { myPatternsMap.clear(); @@ -155,10 +109,10 @@ public class IdeaSVNConfigFile { public void modifyGroup(final String name, final String patterns, final Collection delete, final Map addOrModify, final boolean isDefault) { - if (! isDefault) { + if (!isDefault) { mySVNConfigFile.setPropertyValue(GROUPS_GROUP_NAME, name, patterns, false); } - final Map deletedPrepared = new HashMap(delete.size()); + final Map deletedPrepared = new HashMap(delete.size()); for (String property : delete) { deletedPrepared.put(property, null); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/IgnoredFileInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/IgnoredFileInfo.java deleted file mode 100644 index 32432959e7d2..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/IgnoredFileInfo.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2000-2009 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 org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; -import org.tmatesoft.svn.core.wc.ISVNStatusHandler; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNStatus; -import org.tmatesoft.svn.core.wc.SVNStatusType; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class IgnoredFileInfo { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.IgnoredFileInfo"); - - // directory for which properties are collected - private final File myFile; - private final List myPatterns; - private final Set myFileNames; - private final Set myOldPatterns; - - public IgnoredFileInfo(final File file, final Set oldPatterns) { - myFile = file; - myPatterns = new ArrayList(); - myFileNames = new HashSet(); - myOldPatterns = oldPatterns; - } - - public void addFileName(final String name) { - myFileNames.add(name); - } - - public void addPattern(final String value) { - myPatterns.add(value); - } - - public void calculatePatterns(final SvnVcs vcs) { - final List names = new ArrayList(); - try { - vcs.createStatusClient().doStatus(myFile, SVNRevision.WORKING, SVNDepth.IMMEDIATES, false, true, true, false, new ISVNStatusHandler() { - public void handleStatus(SVNStatus status) throws SVNException { - if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) { - final String name = status.getFile().getName(); - if (! myFileNames.contains(name)) { - names.add(name); - } - } - } - }, null); - } - catch (SVNException e) { - LOG.info(e); - } - - for (String pattern : myOldPatterns) { - boolean usedSomewhereElse = false; - for (String name : names) { - if (DefaultSVNOptions.matches(pattern, name)) { - usedSomewhereElse = true; - break; - } - } - - if (! usedSomewhereElse) { - for (String name : myFileNames) { - if (DefaultSVNOptions.matches(pattern, name)) { - myPatterns.add(pattern); - break; - } - } - } - } - } - - public File getFile() { - return myFile; - } - - public List getPatterns() { - return myPatterns; - } - - public Set getFileNames() { - return myFileNames; - } - - public Set getOldPatterns() { - return myOldPatterns; - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootMixedInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootMixedInfo.java deleted file mode 100644 index 3d0784687abf..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootMixedInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2009 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.vfs.VirtualFile; -import org.tmatesoft.svn.core.SVNURL; - -public class RootMixedInfo { - /** - * working copy root path - */ - private final String myFilePath; - private final VirtualFile myFile; - /** - * url corresponding to working copy root - */ - private final SVNURL myUrl; - private final VirtualFile myParentVcsRoot; - - public RootMixedInfo(final String filePath, final VirtualFile file, final SVNURL url, final VirtualFile parentVcsRoot) { - myFilePath = filePath; - myFile = file; - myUrl = url; - myParentVcsRoot = parentVcsRoot; - } - - public String getFilePath() { - return myFilePath; - } - - public VirtualFile getFile() { - return myFile; - } - - public SVNURL getUrl() { - return myUrl; - } - - public VirtualFile getParentVcsRoot() { - return myParentVcsRoot; - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartner.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartner.java index adba27a55143..09d7d87ec1dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartner.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartner.java @@ -15,12 +15,77 @@ */ package org.jetbrains.idea.svn; +import com.intellij.lifecycle.PeriodicalTasksCloser; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.roots.FileIndexFacade; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vfs.VirtualFile; -import org.tmatesoft.svn.core.wc.SVNStatusClient; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNCancelException; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider; +import org.tmatesoft.svn.core.wc.SVNEvent; -public interface StatusWalkerPartner { - SVNStatusClient createStatusClient(); - void checkCanceled(); - boolean isExcluded(final VirtualFile vFile); - boolean isIgnoredIdeaLevel(final VirtualFile vFile); +public class StatusWalkerPartner { + private final SvnVcs myVcs; + private final ChangeListManager myClManager; + private final FileIndexFacade myExcludedFileIndex; + private final ProgressIndicator myIndicator; + private ISVNStatusFileProvider myFileProvider; + + public StatusWalkerPartner(final SvnVcs vcs, final ProgressIndicator pi) { + myVcs = vcs; + myClManager = ChangeListManager.getInstance(myVcs.getProject()); + myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myVcs.getProject(), FileIndexFacade.class); + myIndicator = pi; + } + + public void setFileProvider(final ISVNStatusFileProvider fileProvider) { + myFileProvider = fileProvider; + } + + @NotNull + public ISVNEventHandler getEventHandler() { + return new ISVNEventHandler() { + public void handleEvent(SVNEvent event, double progress) throws SVNException { + // + } + + public void checkCancelled() throws SVNCancelException { + if (myIndicator != null) { + myIndicator.checkCanceled(); + } + } + }; + } + + @Nullable + public ISVNStatusFileProvider getFileProvider() { + return myFileProvider; + } + + public void checkCanceled() { + if (myIndicator != null) { + myIndicator.checkCanceled(); + } + } + + public boolean isExcluded(final VirtualFile vFile) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + if (myVcs.getProject().isDisposed()) throw new ProcessCanceledException(); + return myExcludedFileIndex.isExcludedFile(vFile); + } + }); + } + + public boolean isIgnoredIdeaLevel(VirtualFile vFile) { + return myClManager.isIgnoredFile(vFile); + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartnerImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartnerImpl.java deleted file mode 100644 index 15bc2b866c3e..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusWalkerPartnerImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2000-2009 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.lifecycle.PeriodicalTasksCloser; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.roots.FileIndexFacade; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.vcs.changes.ChangeListManager; -import com.intellij.openapi.vfs.VirtualFile; -import org.tmatesoft.svn.core.SVNCancelException; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.wc.ISVNEventHandler; -import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider; -import org.tmatesoft.svn.core.wc.SVNEvent; -import org.tmatesoft.svn.core.wc.SVNStatusClient; - -public class StatusWalkerPartnerImpl implements StatusWalkerPartner { - private final SvnVcs myVcs; - private final ChangeListManager myClManager; - private final FileIndexFacade myExcludedFileIndex; - private final ProgressIndicator myIndicator; - private ISVNStatusFileProvider myFileProvider; - - public StatusWalkerPartnerImpl(final SvnVcs vcs, final ProgressIndicator pi) { - myVcs = vcs; - myClManager = ChangeListManager.getInstance(myVcs.getProject()); - myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myVcs.getProject(), FileIndexFacade.class); - myIndicator = pi; - } - - public void setFileProvider(final ISVNStatusFileProvider fileProvider) { - myFileProvider = fileProvider; - } - - public SVNStatusClient createStatusClient() { - final SVNStatusClient result = myVcs.createStatusClient(); - result.setFilesProvider(myFileProvider); - result.setEventHandler(new ISVNEventHandler() { - public void handleEvent(SVNEvent event, double progress) throws SVNException { - // - } - - public void checkCancelled() throws SVNCancelException { - if (myIndicator != null) { - myIndicator.checkCanceled(); - } - } - }); - return result; - } - - public void checkCanceled() { - if (myIndicator != null) { - myIndicator.checkCanceled(); - } - } - - public boolean isExcluded(final VirtualFile vFile) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - if (myVcs.getProject().isDisposed()) throw new ProcessCanceledException(); - return myExcludedFileIndex.isExcludedFile(vFile); - } - }); - } - - public boolean isIgnoredIdeaLevel(VirtualFile vFile) { - return myClManager.isIgnoredFile(vFile); - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBranchConfigurationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBranchConfigurationManager.java index 65e9028c8185..6a25f57cf906 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBranchConfigurationManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBranchConfigurationManager.java @@ -71,6 +71,22 @@ public class SvnBranchConfigurationManager implements PersistentStateComponent statusReceiver = EventDispatcher.create(StatusReceiver.class); @@ -94,12 +94,6 @@ public class SvnChangeProvider implements ChangeProvider { walker.go(item.getDir(), SVNDepth.IMMEDIATES); } - // they are taken under non recursive: ENTRIES file is read anyway, so we get to know parent status also for free - /*for (FilePath path : zipper.getSingleFiles()) { - FileStatus status = getParentStatus(context, path); - processFile(path, context, status, false, context.getClient()); - }*/ - processCopiedAndDeleted(context, dirtyScope); processUnsaved(dirtyScope, addGate, context); @@ -177,7 +171,7 @@ public class SvnChangeProvider implements ChangeProvider { public void getChanges(final FilePath path, final boolean recursive, final ChangelistBuilder builder) throws SVNException { final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, null); - final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, ProgressManager.getInstance().getProgressIndicator()); + final StatusWalkerPartner partner = new StatusWalkerPartner(myVcs, ProgressManager.getInstance().getProgressIndicator()); final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, context, partner); walker.go(path, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES); processCopiedAndDeleted(context, null); @@ -251,9 +245,10 @@ public class SvnChangeProvider implements ChangeProvider { File wcPath = guessWorkingCopyPath(copiedStatus.getFile(), copiedStatus.getURL(), copyFromURL); SVNStatus status; try { - status = context.getClient().doStatus(wcPath, false); + status = myVcs.getFactory(wcPath).createStatusClient().doStatus(wcPath, false); } catch(SVNException ex) { + LOG.info(ex); status = null; } if (status != null && SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index d99331dc4eeb..f7e5857569ff 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -26,9 +26,13 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNLock; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; import java.io.File; import java.util.*; @@ -37,7 +41,6 @@ class SvnChangeProviderContext implements StatusReceiver { private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnChangeProviderContext"); private final ChangelistBuilder myChangelistBuilder; - private final SVNStatusClient myStatusClient; private List myCopiedFiles = null; private final List myDeletedFiles = new ArrayList(); // for files moved in a subtree, which were the targets of merge (for instance). @@ -50,7 +53,6 @@ class SvnChangeProviderContext implements StatusReceiver { public SvnChangeProviderContext(SvnVcs vcs, final ChangelistBuilder changelistBuilder, final ProgressIndicator progress) { myVcs = vcs; - myStatusClient = vcs.createStatusClient(); myChangelistBuilder = changelistBuilder; myProgress = progress; myTreeConflicted = new HashMap(); @@ -81,10 +83,6 @@ class SvnChangeProviderContext implements StatusReceiver { return myChangelistBuilder; } - public SVNStatusClient getClient() { - return myStatusClient; - } - public void reportTreeConflict(final SVNStatus status) { myTreeConflicted.put(status.getFile().getAbsolutePath(), status); } @@ -131,7 +129,7 @@ class SvnChangeProviderContext implements StatusReceiver { return null; } StringBuilder relPathBuilder = new StringBuilder(); - while(filePath != null) { + while (filePath != null) { String copyFromURL = myCopyFromURLs.get(filePath); if (copyFromURL != null) { return copyFromURL + relPathBuilder.toString(); @@ -160,7 +158,7 @@ class SvnChangeProviderContext implements StatusReceiver { myCopyFromURLs.put(filePath, url); } -// + // void processStatusFirstPass(final FilePath filePath, final SVNStatus status) throws SVNException { if (status == null) { @@ -170,12 +168,14 @@ class SvnChangeProviderContext implements StatusReceiver { if (status.getRemoteLock() != null) { final SVNLock lock = status.getRemoteLock(); myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(), - new LogicalLock(false, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate())); + new LogicalLock(false, lock.getOwner(), lock.getComment(), lock.getCreationDate(), + lock.getExpirationDate())); } if (status.getLocalLock() != null) { final SVNLock lock = status.getLocalLock(); myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(), - new LogicalLock(true, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate())); + new LogicalLock(true, lock.getOwner(), lock.getComment(), lock.getCreationDate(), + lock.getExpirationDate())); } if (filePath.isDirectory() && status.isLocked()) { myChangelistBuilder.processLockedFolder(filePath.getVirtualFile()); @@ -191,7 +191,8 @@ class SvnChangeProviderContext implements StatusReceiver { String parentCopyFromURL = getParentCopyFromURL(filePath); if (parentCopyFromURL != null) { addCopiedFile(filePath, status, parentCopyFromURL); - } else { + } + else { processStatus(filePath, status); } } @@ -225,7 +226,8 @@ class SvnChangeProviderContext implements StatusReceiver { propStatus == SVNStatusType.STATUS_CONFLICTED) { myChangelistBuilder.processChangeInList( createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status), CurrentContentRevision.create(filePath), fStatus, - status), SvnUtil.getChangelistName(status), SvnVcs.getKey()); + status), SvnUtil.getChangelistName(status), SvnVcs.getKey() + ); checkSwitched(filePath, myChangelistBuilder, status, fStatus); } else if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) { @@ -249,16 +251,19 @@ class SvnChangeProviderContext implements StatusReceiver { if (file != null && FileDocumentManager.getInstance().isFileModified(file)) { myChangelistBuilder.processChangeInList( createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status), CurrentContentRevision.create(filePath), - FileStatus.MODIFIED, status), SvnUtil.getChangelistName(status), SvnVcs.getKey()); - } else if (status.getTreeConflict() != null) { + FileStatus.MODIFIED, status), SvnUtil.getChangelistName(status), SvnVcs.getKey() + ); + } + else if (status.getTreeConflict() != null) { myChangelistBuilder.processChange(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status), - CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), SvnVcs.getKey()); + CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), + SvnVcs.getKey()); } checkSwitched(filePath, myChangelistBuilder, status, fStatus); } } } - + public void addModifiedNotSavedChange(final VirtualFile file) throws SVNException { final FilePath filePath = new FilePathImpl(file); final SVNInfo svnInfo = myVcs.getInfo(file); @@ -268,7 +273,8 @@ class SvnChangeProviderContext implements StatusReceiver { svnStatus.setRevision(svnInfo.getRevision()); myChangelistBuilder.processChangeInList( createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, svnInfo.getRevision()), CurrentContentRevision.create(filePath), - FileStatus.MODIFIED, svnStatus), (String)null, SvnVcs.getKey()); + FileStatus.MODIFIED, svnStatus), (String)null, SvnVcs.getKey() + ); } } @@ -317,7 +323,7 @@ class SvnChangeProviderContext implements StatusReceiver { // seems here we can only have a tree conflict; which can be marked on either path (?) // .. ok try to merge states Change createMovedChange(final ContentRevision before, final ContentRevision after, final SVNStatus copiedStatus, - final SVNStatus deletedStatus) throws SVNException { + final SVNStatus deletedStatus) throws SVNException { // todo no convertion needed for the contents status? final ConflictedSvnChange conflictedSvnChange = new ConflictedSvnChange(before, after, ConflictState.mergeState(getState(copiedStatus), getState(deletedStatus)), @@ -331,12 +337,16 @@ class SvnChangeProviderContext implements StatusReceiver { return patchWithPropertyChange(conflictedSvnChange, copiedStatus, deletedStatus); } - private Change createChange(final ContentRevision before, final ContentRevision after, final FileStatus fStatus, final SVNStatus svnStatus) + private Change createChange(final ContentRevision before, + final ContentRevision after, + final FileStatus fStatus, + final SVNStatus svnStatus) throws SVNException { final ConflictedSvnChange conflictedSvnChange = new ConflictedSvnChange(before, after, correctContentsStatus(fStatus, svnStatus), - getState(svnStatus), after == null ? before.getFile() : after.getFile()); + getState(svnStatus), + after == null ? before.getFile() : after.getFile()); if (svnStatus != null) { - if (SVNStatusType.STATUS_DELETED.equals(svnStatus.getNodeStatus()) && ! svnStatus.getRevision().isValid()) { + if (SVNStatusType.STATUS_DELETED.equals(svnStatus.getNodeStatus()) && !svnStatus.getRevision().isValid()) { conflictedSvnChange.setIsPhantom(true); } conflictedSvnChange.setBeforeDescription(svnStatus.getTreeConflict()); @@ -354,7 +364,8 @@ class SvnChangeProviderContext implements StatusReceiver { return new SvnLocallyDeletedChange(filePath, getState(status)); } - private Change patchWithPropertyChange(final Change change, final SVNStatus svnStatus, final SVNStatus deletedStatus) throws SVNException { + private Change patchWithPropertyChange(final Change change, final SVNStatus svnStatus, final SVNStatus deletedStatus) + throws SVNException { if (svnStatus == null) return change; final SVNStatusType propertiesStatus = svnStatus.getPropertiesStatus(); if (SVNStatusType.STATUS_CONFLICTED.equals(propertiesStatus) || SVNStatusType.CHANGED.equals(propertiesStatus) || @@ -378,8 +389,10 @@ class SvnChangeProviderContext implements StatusReceiver { final String afterRevisionNu = change.getAfterRevision() == null ? null : change.getAfterRevision().getRevisionNumber().asString(); final Change propertyChange = new Change(beforeList == null ? null : new SimpleContentRevision(beforeList, path, beforeRevisionNu), - afterList == null ? null : new SimpleContentRevision(afterList, path, afterRevisionNu), - deletedStatus != null ? FileStatus.MODIFIED : SvnStatusConvertor.convertPropertyStatus(propertiesStatus)); + afterList == null ? null : new SimpleContentRevision(afterList, path, afterRevisionNu), + deletedStatus != null + ? FileStatus.MODIFIED + : SvnStatusConvertor.convertPropertyStatus(propertiesStatus)); change.addAdditionalLayerElement(SvnChangeProvider.PROPERTY_LAYER, propertyChange); } return change; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.java index 287cb1c5eb1b..e659280bff3a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.java @@ -38,6 +38,7 @@ import com.intellij.util.Consumer; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.config.SvnConfigureProxiesDialog; import javax.swing.*; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java index 6c0be5f0a3dc..b782f5a657d3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java @@ -22,9 +22,10 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; import org.jetbrains.idea.svn.config.SvnServerFileKeys; -import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider; -import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider; +import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider; +import org.jetbrains.idea.svn.auth.SvnInteractiveAuthenticationProvider; import org.jetbrains.idea.svn.update.MergeRootInfo; import org.jetbrains.idea.svn.update.UpdateRootInfo; import org.tmatesoft.svn.core.SVNDepth; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxyAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxyAuthenticationNotifier.java deleted file mode 100644 index e949b4aae8f7..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxyAuthenticationNotifier.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2009 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.notification.NotificationType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.impl.GenericNotifierImpl; -import org.jetbrains.annotations.NotNull; - -// todo be done -// todo : two kinds. positive: either into common proxy, or into system properties -public class SvnProxyAuthenticationNotifier extends GenericNotifierImpl { - public SvnProxyAuthenticationNotifier(Project project, @NotNull String groupId, @NotNull String title, @NotNull NotificationType type) { - super(project, groupId, title, type); - } - - @Override - protected boolean ask(String obj, String description) { - return false; //To change body of implemented methods use File | Settings | File Templates. - } - - @NotNull - @Override - protected String getKey(String obj) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @NotNull - @Override - protected String getNotificationContent(String obj) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java index 741de4f6c2d7..45b28e34b3d0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java @@ -29,16 +29,18 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; import com.intellij.vcsUtil.VcsUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; -import org.jetbrains.idea.svn.portable.SvnStatusClientI; -import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient; +import org.jetbrains.idea.svn.status.StatusClient; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.ISVNStatusHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; import java.io.File; import java.util.LinkedList; @@ -62,7 +64,7 @@ public class SvnRecursiveStatusWalker { } public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException { - final MyItem root = new MyItem(myVcs, rootPath, depth, myPartner.createStatusClient(), false); + final MyItem root = createItem(rootPath, depth, false); myQueue.add(root); while (! myQueue.isEmpty()) { @@ -75,7 +77,7 @@ public class SvnRecursiveStatusWalker { if (path.isDirectory()) { myHandler.setCurrentItem(item); try { - final SvnStatusClientI client = item.getClient(ioFile); + final StatusClient client = item.getClient(); client.doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, true, myHandler, null); myHandler.checkIfCopyRootWasReported(null, ioFile); } @@ -84,7 +86,7 @@ public class SvnRecursiveStatusWalker { } } else { try { - final SVNStatus status = item.getClient(ioFile).doStatus(ioFile, false, false); + final SVNStatus status = item.getClient().doStatus(ioFile, false, false); myReceiver.process(path, status); } catch (SVNException e) { handleStatusException(item, path, e); @@ -113,48 +115,31 @@ public class SvnRecursiveStatusWalker { } private static class MyItem { - private final Project myProject; - private final FilePath myPath; - private final SVNDepth myDepth; - private final SvnStatusClientI mySvnClient; - private final SvnStatusClientI myCommandLineClient; + @NotNull private final FilePath myPath; + @NotNull private final SVNDepth myDepth; + @NotNull private final StatusClient myStatusClient; private final boolean myIsInnerCopyRoot; - private final SvnConfiguration myConfiguration17; - private final SvnVcs myVcs; - private MyItem(SvnVcs vcs, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) { - myVcs = vcs; - myProject = vcs.getProject(); - myConfiguration17 = SvnConfiguration.getInstance(myProject); + private MyItem(@NotNull FilePath path, @NotNull SVNDepth depth, boolean isInnerCopyRoot, @NotNull StatusClient statusClient) { myPath = path; myDepth = depth; - mySvnClient = new SvnkitSvnStatusClient(myVcs, client); - myCommandLineClient = new SvnCommandLineStatusClient(myVcs); + myStatusClient = statusClient; myIsInnerCopyRoot = isInnerCopyRoot; } + @NotNull public FilePath getPath() { return myPath; } + @NotNull public SVNDepth getDepth() { return myDepth; } - public SvnStatusClientI getClient(final File file) { - // TODO: refactor to ClientFactory usage but carefully save all parameters passed in myClient - fileProvider and - // TODO: event handler (for cancel support) - WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); - - if (format == WorkingCopyFormat.ONE_DOT_EIGHT) { - return myCommandLineClient; - } - - if (format == WorkingCopyFormat.ONE_DOT_SIX) { - return mySvnClient; - } - - return myConfiguration17.isCommandLine() ? myCommandLineClient : mySvnClient; + @NotNull + public StatusClient getClient() { + return myStatusClient; } public boolean isIsInnerCopyRoot() { @@ -187,7 +172,7 @@ public class SvnRecursiveStatusWalker { return true; } if (file.isDirectory() && new File(file, SVNFileUtil.getAdminDirectoryName()).exists()) { - final MyItem childItem = new MyItem(myVcs, path, newDepth, myPartner.createStatusClient(), true); + final MyItem childItem = createItem(path, newDepth, true); myQueue.add(childItem); } else if (vf != null) { myReceiver.processUnversioned(vf); @@ -219,6 +204,14 @@ public class SvnRecursiveStatusWalker { FileUtil.processFilesRecursively(ioFile, processor, directoryFilter); } + @NotNull + private MyItem createItem(@NotNull FilePath path, @NotNull SVNDepth depth, boolean isInnerCopyRoot) { + StatusClient statusClient = + myVcs.getFactory(path.getIOFile()).createStatusClient(myPartner.getFileProvider(), myPartner.getEventHandler()); + + return new MyItem(path, depth, isInnerCopyRoot, statusClient); + } + private class MyHandler implements ISVNStatusHandler { private MyItem myCurrentItem; private boolean myMetCurrentItem; @@ -235,7 +228,7 @@ public class SvnRecursiveStatusWalker { SVNStatus statusInner; try { statusInner = ioFileStatus != null ? ioFileStatus : - myCurrentItem.getClient(itemFile).doStatus(itemFile, false); + myCurrentItem.getClient().doStatus(itemFile, false); } catch (SVNException e) { LOG.info(e); @@ -295,8 +288,7 @@ public class SvnRecursiveStatusWalker { //myReceiver.processUnversioned(vFile); //processRecursively(vFile, myCurrentItem.getDepth()); } else { - final MyItem childItem = new MyItem(myVcs, new FilePathImpl(vFile), SVNDepth.INFINITY, - myPartner.createStatusClient(), true); + final MyItem childItem = createItem(new FilePathImpl(vFile), SVNDepth.INFINITY, true); myQueue.add(childItem); } } else { @@ -309,7 +301,7 @@ public class SvnRecursiveStatusWalker { } } - private VirtualFile getVirtualFile(File ioFile) { + private static VirtualFile getVirtualFile(File ioFile) { final LocalFileSystem lfs = LocalFileSystem.getInstance(); VirtualFile vFile = lfs.findFileByIoFile(ioFile); if (vFile == null) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnStatusConvertor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnStatusConvertor.java index 3c0d396657c7..84c316691c1e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnStatusConvertor.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnStatusConvertor.java @@ -18,7 +18,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.vcs.FileStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.portable.PortableStatus; +import org.jetbrains.idea.svn.status.PortableStatus; import org.tmatesoft.svn.core.wc.SVNStatus; import org.tmatesoft.svn.core.wc.SVNStatusType; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 89a1eef5f47b..54616f46c1a6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -73,6 +73,7 @@ import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider; import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.api.CmdClientFactory; import org.jetbrains.idea.svn.api.SvnKitClientFactory; +import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; import org.jetbrains.idea.svn.commandLine.SvnExecutableChecker; @@ -84,7 +85,6 @@ import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.lowLevel.PrimitivePool; import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser; -import org.jetbrains.idea.svn.portable.SvnWcClientI; import org.jetbrains.idea.svn.properties.PropertyClient; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment; @@ -1386,6 +1386,11 @@ public class SvnVcs extends AbstractVcs { return getFactory(getProjectRootFormat(), false); } + @NotNull + public ClientFactory getFactory(@NotNull WorkingCopyFormat format) { + return getFactory(format, false); + } + public ClientFactory getSvnKitFactory() { return svnKitClientFactory; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeListsMergerFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeListsMergerFactory.java index 38c77bf0dbf0..eac55c666ed2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeListsMergerFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeListsMergerFactory.java @@ -41,12 +41,4 @@ public class ChangeListsMergerFactory implements MergerFactory { String branchName) { return new Merger(vcs, myChangeListsList, target, handler, currentBranchUrl, branchName); } - - public List getListsToMerge() { - return myChangeListsList; - } - - public boolean isMergeAll() { - return false; - } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeSetMergerFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeSetMergerFactory.java index 974035e4ce73..623816513537 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeSetMergerFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ChangeSetMergerFactory.java @@ -26,7 +26,6 @@ import org.tmatesoft.svn.core.SVNURL; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class ChangeSetMergerFactory implements MergerFactory { @@ -45,12 +44,4 @@ public class ChangeSetMergerFactory implements MergerFactory { String branchName) { return new PointMerger(vcs, mySelectedList, target, handler, currentBranchUrl, mySelectedChanges, branchName); } - - public List getListsToMerge() { - return Collections.singletonList(mySelectedList); - } - - public boolean isMergeAll() { - return false; - } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateBranchOrTagAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateBranchOrTagAction.java index 43b0a479d089..7655751ded40 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateBranchOrTagAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateBranchOrTagAction.java @@ -35,20 +35,18 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.checkin.CommitEventHandler; import org.jetbrains.idea.svn.checkin.IdeaCommitHandler; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; import org.jetbrains.idea.svn.dialogs.CreateBranchOrTagDialog; import org.jetbrains.idea.svn.update.AutoSvnUpdater; import org.jetbrains.idea.svn.update.SingleRootSwitcher; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; -import java.util.ArrayList; -import java.util.List; public class CreateBranchOrTagAction extends BasicAction { protected String getActionName(AbstractVcs vcs) { @@ -89,14 +87,13 @@ public class CreateBranchOrTagAction extends BasicAction { dstSvnUrl = SVNURL.parseURIEncoded(dstURL); parentUrl = dstSvnUrl.removePathTail(); - if (!dirExists(activeVcs, project, parentUrl)) { + if (!dirExists(activeVcs, parentUrl)) { int rc = Messages.showYesNoDialog(project, "The repository path '" + parentUrl + "' does not exist. Would you like to create it?", "Branch or Tag", Messages.getQuestionIcon()); if (rc == Messages.NO) { return; } } - } catch (SVNException e) { throw new VcsException(e); @@ -111,11 +108,10 @@ public class CreateBranchOrTagAction extends BasicAction { progress.setText(SvnBundle.message("progress.text.copy.to", dstURL)); handler = new IdeaCommitHandler(progress); } - checkCreateDir(parentUrl, activeVcs, comment); SvnTarget source = isSrcFile ? SvnTarget.fromFile(srcFile, revision) : SvnTarget.fromURL(srcUrl, revision); long newRevision = activeVcs.getFactory(source).createCopyMoveClient() - .copy(source, SvnTarget.fromURL(dstSvnUrl), revision, true, comment, handler); + .copy(source, SvnTarget.fromURL(dstSvnUrl), revision, true, false, comment, handler); updateStatusBar(newRevision, project); } @@ -124,7 +120,8 @@ public class CreateBranchOrTagAction extends BasicAction { } } }; - ProgressManager.getInstance().runProcessWithProgressSynchronously(copyCommand, SvnBundle.message("progress.title.copy"), false, project); + ProgressManager.getInstance() + .runProcessWithProgressSynchronously(copyCommand, SvnBundle.message("progress.title.copy"), false, project); if (!exception.isNull()) { throw new VcsException(exception.get()); } @@ -148,25 +145,7 @@ public class CreateBranchOrTagAction extends BasicAction { } } - private static void checkCreateDir(@NotNull SVNURL url, @NotNull final SvnVcs activeVcs, @NotNull final String comment) - throws SVNException, VcsException { - final SVNURL baseUrl = url; - List dirsToCreate = new ArrayList(); - while(!dirExists(activeVcs, activeVcs.getProject(), url)) { - dirsToCreate.add(0, url); - url = url.removePathTail(); - if (url.getPath().length() == 0) { - throw new VcsException("Invalid repository root path for " + baseUrl); - } - } - if (! dirsToCreate.isEmpty()) { - // TODO: Works for 1.8 - implement this for command line - SVNCommitClient commitClient = activeVcs.createCommitClient(); - commitClient.doMkDir(dirsToCreate.toArray(new SVNURL[dirsToCreate.size()]), comment); - } - } - - private static boolean dirExists(@NotNull final SvnVcs vcs, @NotNull Project project, @NotNull final SVNURL url) throws SVNException { + private static boolean dirExists(@NotNull final SvnVcs vcs, @NotNull final SVNURL url) throws SVNException { final Ref excRef = new Ref(); final Ref resultRef = new Ref(Boolean.TRUE); @@ -176,7 +155,6 @@ public class CreateBranchOrTagAction extends BasicAction { vcs.getInfo(url, SVNRevision.HEAD); } catch (SVNException e) { - // TODO: Check what error code returned by cmd client and implement corresponding support if (e.getErrorMessage().getErrorCode().equals(SVNErrorCode.RA_ILLEGAL_URL)) { resultRef.set(Boolean.FALSE); } @@ -189,11 +167,12 @@ public class CreateBranchOrTagAction extends BasicAction { final Application application = ApplicationManager.getApplication(); if (application.isDispatchThread()) { - ProgressManager.getInstance().runProcessWithProgressSynchronously(taskImpl, "Checking target folder", true, project); - } else { + ProgressManager.getInstance().runProcessWithProgressSynchronously(taskImpl, "Checking target folder", true, vcs.getProject()); + } + else { taskImpl.run(); } - if (! excRef.isNull()) throw excRef.get(); + if (!excRef.isNull()) throw excRef.get(); return resultRef.get(); } @@ -204,5 +183,4 @@ public class CreateBranchOrTagAction extends BasicAction { protected boolean isBatchAction() { return false; } - } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java index 21baf2d925ba..d20b5f14d6b5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java @@ -35,7 +35,7 @@ import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.conflict.ConflictClient; import org.jetbrains.idea.svn.dialogs.SelectFilesDialog; -import org.jetbrains.idea.svn.portable.SvnStatusClientI; +import org.jetbrains.idea.svn.status.StatusClient; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.*; @@ -122,7 +122,7 @@ public class MarkResolvedAction extends BasicAction { for (VirtualFile file : files) { try { File path = new File(file.getPath()); - SvnStatusClientI client = vcs.getFactory(path).createStatusClient(); + StatusClient client = vcs.getFactory(path).createStatusClient(); client.doStatus(path, true, false, false, false, new ISVNStatusHandler() { public void handleStatus(SVNStatus status) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java index 0a81b08d6858..7c715a3971f7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java @@ -37,7 +37,6 @@ import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; import org.jetbrains.idea.svn.dialogs.ShareDialog; -import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; @@ -220,10 +219,10 @@ public class ShareProjectAction extends BasicAction { return folderEmpty[0]; } - private static Pair createRemoteFolder(final SvnVcs vcs, - final SVNURL parent, + private static Pair createRemoteFolder(@NotNull final SvnVcs vcs, + @NotNull final SVNURL parent, final String folderName, - String commitText) throws SVNException { + String commitText) throws VcsException, SVNException { SVNURL url = parent.appendPath(folderName, false); final String urlText = url.toString(); final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); @@ -231,11 +230,13 @@ public class ShareProjectAction extends BasicAction { indicator.checkCanceled(); indicator.setText(SvnBundle.message("share.directory.create.dir.progress.text", urlText)); } - // TODO: Implement with command line client - final SVNCommitInfo info = - vcs.createCommitClient().doMkDir(new SVNURL[]{url}, SvnBundle.message("share.directory.commit.message", folderName, - ApplicationNamesInfo.getInstance().getFullProductName(), commitText)); - return new Pair(SVNRevision.create(info.getNewRevision()), url); + + String message = + SvnBundle.message("share.directory.commit.message", folderName, ApplicationNamesInfo.getInstance().getFullProductName(), commitText); + SvnTarget target = SvnTarget.fromURL(url); + long revision = vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, message, false); + + return Pair.create(SVNRevision.create(revision), url); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java index adbc3f6437b0..4811e3286d70 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java @@ -43,7 +43,7 @@ public class CmdAddClient extends BaseSvnClient implements AddClient { // TODO: handler should be called in parallel with command execution, but this will be in other thread // TODO: check if that is ok for current handler implementation // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed - CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.add, parameters, null); + CommandExecutor command = execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.add, parameters, null); FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new AddStatusConvertor()); parser.parse(command.getOutput()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java index d6250c71f073..2c397c73fa67 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java @@ -16,6 +16,10 @@ import java.io.File; */ public class SvnKitAddClient extends BaseSvnClient implements AddClient { + /** + * TODO: Implement correct support for includeIgnored parameter. Also check that correct depth will be used for all cases (when another + * TODO: overload of doAdd() is used) as, for instance, SVNDepth.recurseFromDepth(EMPTY) = false, SVNDepth.fromRecursive(false) = FILES. + */ @Override public void add(@NotNull File file, @Nullable SVNDepth depth, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java index 23c75a73dd86..5cb18a35fc5c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java @@ -42,7 +42,7 @@ public class CmdAnnotateClient extends BaseSvnClient implements AnnotateClient { CommandUtil.put(parameters, diffOptions); parameters.add("--xml"); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.blame, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.blame, parameters, null); parseOutput(command.getOutput(), handler); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java index e140a62db91f..bb8c613c8135 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java @@ -6,7 +6,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.WorkingCopyFormat; -import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.jetbrains.idea.svn.auth.IdeaSvnkitBasedAuthenticationCallback; +import org.jetbrains.idea.svn.commandLine.*; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNEvent; @@ -15,6 +16,7 @@ import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.util.Collection; +import java.util.List; /** * @author Konstantin Kolosovsky. @@ -71,6 +73,42 @@ public abstract class BaseSvnClient implements SvnClient { } } + /** + * Utility method for running commands. + * // TODO: Should be replaced with non-static analogue. + * + * @param vcs + * @param target + * @param name + * @param parameters + * @param listener + * @throws com.intellij.openapi.vcs.VcsException + */ + public static CommandExecutor execute(@NotNull SvnVcs vcs, + @NotNull SvnTarget target, + @NotNull SvnCommandName name, + @NotNull List parameters, + @Nullable LineCommandListener listener) throws SvnBindException { + return execute(vcs, target, null, name, parameters, listener); + } + + public static CommandExecutor execute(@NotNull SvnVcs vcs, + @NotNull SvnTarget target, + @Nullable File workingDirectory, + @NotNull SvnCommandName name, + @NotNull List parameters, + @Nullable LineCommandListener listener) throws SvnBindException { + Command command = new Command(name); + + command.setTarget(target); + command.setWorkingDirectory(workingDirectory); + command.setResultBuilder(listener); + command.put(parameters); + + CommandRuntime runtime = new CommandRuntime(vcs, new IdeaSvnkitBasedAuthenticationCallback(vcs)); + return runtime.runWithAuthenticationAttempt(command); + } + protected static void callHandler(@Nullable ISVNEventHandler handler, @NotNull SVNEvent event) throws VcsException { if (handler != null) { try { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java index 9c01217da9f1..1021ad4d796c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java @@ -1,11 +1,13 @@ package org.jetbrains.idea.svn.api; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.add.AddClient; import org.jetbrains.idea.svn.annotate.AnnotateClient; import org.jetbrains.idea.svn.browse.BrowseClient; import org.jetbrains.idea.svn.change.ChangeListClient; +import org.jetbrains.idea.svn.checkin.CheckinClient; import org.jetbrains.idea.svn.checkin.ImportClient; import org.jetbrains.idea.svn.checkout.CheckoutClient; import org.jetbrains.idea.svn.checkout.ExportClient; @@ -18,13 +20,15 @@ import org.jetbrains.idea.svn.diff.DiffClient; import org.jetbrains.idea.svn.history.HistoryClient; import org.jetbrains.idea.svn.integrate.MergeClient; import org.jetbrains.idea.svn.lock.LockClient; -import org.jetbrains.idea.svn.portable.SvnStatusClientI; -import org.jetbrains.idea.svn.update.UpdateClient; -import org.jetbrains.idea.svn.portable.SvnWcClientI; +import org.jetbrains.idea.svn.info.InfoClient; +import org.jetbrains.idea.svn.status.StatusClient; import org.jetbrains.idea.svn.properties.PropertyClient; import org.jetbrains.idea.svn.revert.RevertClient; import org.jetbrains.idea.svn.update.RelocateClient; +import org.jetbrains.idea.svn.update.UpdateClient; import org.jetbrains.idea.svn.upgrade.UpgradeClient; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider; /** * @author Konstantin Kolosovsky. @@ -40,8 +44,8 @@ public abstract class ClientFactory { protected HistoryClient historyClient; protected RevertClient revertClient; protected DeleteClient deleteClient; - protected SvnStatusClientI statusClient; - protected SvnWcClientI infoClient; + protected StatusClient statusClient; + protected InfoClient infoClient; protected CopyMoveClient copyMoveClient; protected ConflictClient conflictClient; protected PropertyClient propertyClient; @@ -57,6 +61,7 @@ public abstract class ClientFactory { protected UpgradeClient myUpgradeClient; protected BrowseClient myBrowseClient; protected DiffClient myDiffClient; + protected CheckinClient myCheckinClient; protected ClientFactory(@NotNull SvnVcs vcs) { myVcs = vcs; @@ -91,15 +96,18 @@ public abstract class ClientFactory { } @NotNull - public SvnStatusClientI createStatusClient() { - // TODO: Update this in same like other clients - move to corresponding package, rename clients - return statusClient; + public StatusClient createStatusClient() { + return prepare(statusClient); } @NotNull - public SvnWcClientI createInfoClient() { - // TODO: Update this in same like other clients - move to corresponding package, rename clients - return infoClient; + public StatusClient createStatusClient(@Nullable ISVNStatusFileProvider provider, @NotNull ISVNEventHandler handler) { + return createStatusClient(); + } + + @NotNull + public InfoClient createInfoClient() { + return prepare(infoClient); } // TODO: Update this in same like other clients - move to corresponding package, rename clients @@ -187,6 +195,11 @@ public abstract class ClientFactory { return prepare(myDiffClient); } + @NotNull + public CheckinClient createCheckinClient() { + return prepare(myCheckinClient); + } + @NotNull protected T prepare(@NotNull T client) { client.setVcs(myVcs); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java index 653852ca3f49..13e231345b43 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java @@ -6,25 +6,26 @@ import org.jetbrains.idea.svn.add.CmdAddClient; import org.jetbrains.idea.svn.annotate.CmdAnnotateClient; import org.jetbrains.idea.svn.browse.CmdBrowseClient; import org.jetbrains.idea.svn.change.CmdChangeListClient; +import org.jetbrains.idea.svn.checkin.CmdCheckinClient; import org.jetbrains.idea.svn.checkin.CmdImportClient; import org.jetbrains.idea.svn.checkout.CmdCheckoutClient; import org.jetbrains.idea.svn.checkout.CmdExportClient; import org.jetbrains.idea.svn.cleanup.CmdCleanupClient; -import org.jetbrains.idea.svn.diff.CmdDiffClient; -import org.jetbrains.idea.svn.update.CmdUpdateClient; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; +import org.jetbrains.idea.svn.status.CmdStatusClient; +import org.jetbrains.idea.svn.info.CmdInfoClient; import org.jetbrains.idea.svn.conflict.CmdConflictClient; import org.jetbrains.idea.svn.content.CmdContentClient; import org.jetbrains.idea.svn.copy.CmdCopyMoveClient; import org.jetbrains.idea.svn.delete.CmdDeleteClient; +import org.jetbrains.idea.svn.diff.CmdDiffClient; import org.jetbrains.idea.svn.history.CmdHistoryClient; import org.jetbrains.idea.svn.integrate.CmdMergeClient; import org.jetbrains.idea.svn.lock.CmdLockClient; -import org.jetbrains.idea.svn.update.UpdateClient; import org.jetbrains.idea.svn.properties.CmdPropertyClient; import org.jetbrains.idea.svn.revert.CmdRevertClient; import org.jetbrains.idea.svn.update.CmdRelocateClient; +import org.jetbrains.idea.svn.update.CmdUpdateClient; +import org.jetbrains.idea.svn.update.UpdateClient; import org.jetbrains.idea.svn.upgrade.CmdUpgradeClient; /** @@ -59,8 +60,9 @@ public class CmdClientFactory extends ClientFactory { myUpgradeClient = new CmdUpgradeClient(); myBrowseClient = new CmdBrowseClient(); myDiffClient = new CmdDiffClient(); - statusClient = new SvnCommandLineStatusClient(myVcs); - infoClient = new SvnCommandLineInfoClient(myVcs); + myCheckinClient = new CmdCheckinClient(); + statusClient = new CmdStatusClient(); + infoClient = new CmdInfoClient(); } @NotNull diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java index 3fb1d83c159d..89039348530b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java @@ -1,11 +1,13 @@ package org.jetbrains.idea.svn.api; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.add.SvnKitAddClient; import org.jetbrains.idea.svn.annotate.SvnKitAnnotateClient; import org.jetbrains.idea.svn.browse.SvnKitBrowseClient; import org.jetbrains.idea.svn.change.SvnKitChangeListClient; +import org.jetbrains.idea.svn.checkin.SvnKitCheckinClient; import org.jetbrains.idea.svn.checkin.SvnKitImportClient; import org.jetbrains.idea.svn.checkout.SvnKitCheckoutClient; import org.jetbrains.idea.svn.checkout.SvnKitExportClient; @@ -18,14 +20,17 @@ import org.jetbrains.idea.svn.diff.SvnKitDiffClient; import org.jetbrains.idea.svn.history.SvnKitHistoryClient; import org.jetbrains.idea.svn.integrate.SvnKitMergeClient; import org.jetbrains.idea.svn.lock.SvnKitLockClient; -import org.jetbrains.idea.svn.update.SvnKitUpdateClient; -import org.jetbrains.idea.svn.update.UpdateClient; -import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient; -import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient; +import org.jetbrains.idea.svn.status.StatusClient; +import org.jetbrains.idea.svn.status.SvnKitStatusClient; +import org.jetbrains.idea.svn.info.SvnKitInfoClient; import org.jetbrains.idea.svn.properties.SvnKitPropertyClient; import org.jetbrains.idea.svn.revert.SvnKitRevertClient; import org.jetbrains.idea.svn.update.SvnKitRelocateClient; +import org.jetbrains.idea.svn.update.SvnKitUpdateClient; +import org.jetbrains.idea.svn.update.UpdateClient; import org.jetbrains.idea.svn.upgrade.SvnKitUpgradeClient; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider; /** * @author Konstantin Kolosovsky. @@ -59,8 +64,15 @@ public class SvnKitClientFactory extends ClientFactory { myUpgradeClient = new SvnKitUpgradeClient(); myBrowseClient = new SvnKitBrowseClient(); myDiffClient = new SvnKitDiffClient(); - statusClient = new SvnkitSvnStatusClient(myVcs, null); - infoClient = new SvnkitSvnWcClient(myVcs); + myCheckinClient = new SvnKitCheckinClient(); + statusClient = new SvnKitStatusClient(); + infoClient = new SvnKitInfoClient(); + } + + @NotNull + @Override + public StatusClient createStatusClient(@Nullable ISVNStatusFileProvider provider, @NotNull ISVNEventHandler handler) { + return prepare(new SvnKitStatusClient(provider, handler)); } @NotNull diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AbstractAuthenticator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AbstractAuthenticator.java new file mode 100644 index 000000000000..2183c0d3e948 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AbstractAuthenticator.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2014 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.auth; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import com.intellij.util.messages.MessageBusConnection; +import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnConfiguration; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.*; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.IOException; + +/** + * @author Konstantin Kolosovsky. + */ +abstract class AbstractAuthenticator { + + private static final Logger LOG = Logger.getInstance(AbstractAuthenticator.class); + + @NotNull protected final IdeaSvnkitBasedAuthenticationCallback myAuthService; + @NotNull protected final SvnVcs myVcs; + @NotNull protected final SVNURL myUrl; + protected final String myRealm; + protected boolean myStoreInUsual; + protected SvnAuthenticationManager myTmpDirManager; + + AbstractAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService, @NotNull SVNURL url, String realm) { + myAuthService = authService; + myVcs = myAuthService.getVcs(); + myUrl = url; + myRealm = realm; + } + + protected boolean tryAuthenticate() { + final SvnAuthenticationManager passive = myVcs.getSvnConfiguration().getPassiveAuthenticationManager(myVcs.getProject()); + final SvnAuthenticationManager active = myVcs.getSvnConfiguration().getAuthenticationManager(myVcs); + + try { + boolean authenticated = getWithPassive(passive) || getWithActive(active); + if (!authenticated) return false; + + SvnAuthenticationManager manager = myStoreInUsual ? active : createTmpManager(); + manager.setArtificialSaving(true); + return acknowledge(manager); + } + catch (IOException e) { + LOG.info(e); + VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR); + return false; + } + catch (SVNException e) { + LOG.info(e); + VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR); + return false; + } + } + + @NotNull + protected SvnAuthenticationManager createTmpManager() throws IOException { + if (myTmpDirManager == null) { + myAuthService.initTmpDir(myVcs.getSvnConfiguration()); + myTmpDirManager = new SvnAuthenticationManager(myVcs.getProject(), myAuthService.getTempDirectory()); + myTmpDirManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); + myTmpDirManager.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(myVcs, myTmpDirManager)); + } + + return myTmpDirManager; + } + + protected boolean getWithActive(SvnAuthenticationManager active) throws SVNException { + MessageBusConnection connection = null; + try { + final Project project = myVcs.getProject(); + connection = project.getMessageBus().connect(project); + connection.subscribe(SvnAuthenticationManager.AUTHENTICATION_PROVIDER_LISTENER, new MyAuthenticationProviderListener()); + + makeAuthCall(active); + } + finally { + if (connection != null) { + connection.disconnect(); + } + } + + return afterAuthCall(); + } + + protected void makeAuthCall(@NotNull SvnAuthenticationManager manager) throws SVNException { + myVcs.createWCClient(manager).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + } + + protected void acceptServerAuthentication(SVNURL url, String realm, Object certificate, @MagicConstant int acceptResult) { + } + + public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) { + } + + protected abstract boolean afterAuthCall(); + + protected abstract boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException; + + protected abstract boolean acknowledge(SvnAuthenticationManager manager) throws SVNException; + + private class MyAuthenticationProviderListener implements SvnAuthenticationManager.ISVNAuthenticationProviderListener { + @Override + public void requestClientAuthentication(String kind, + SVNURL url, + String realm, + SVNErrorMessage errorMessage, + SVNAuthentication previousAuth, + boolean authMayBeStored, + SVNAuthentication authentication) { + AbstractAuthenticator.this.requestClientAuthentication(url, realm, authentication); + } + + @Override + public void acceptServerAuthentication(SVNURL url, + String realm, + Object certificate, + boolean resultMayBeStored, + @MagicConstant int acceptResult) { + AbstractAuthenticator.this.acceptServerAuthentication(url, realm, certificate, acceptResult); + } + } + + protected static boolean storeCredentials(@NotNull SvnAuthenticationManager manager, final SVNAuthentication authentication, String realm) + throws SVNException { + try { + if (authentication instanceof SVNSSLAuthentication && (((SVNSSLAuthentication)authentication).getCertificateFile() != null)) { + manager.acknowledgeForSSL(true, authentication); + realm = ((SVNSSLAuthentication)authentication).getCertificateFile().getPath(); + } + manager.acknowledgeAuthentication(true, getFromType(authentication), realm, null, authentication, authentication.getURL()); + } + catch (SvnAuthenticationManager.CredentialsSavedException e) { + return e.isSuccess(); + } + return true; + } + + @NotNull + private static String getFromType(SVNAuthentication authentication) { + if (authentication instanceof SVNPasswordAuthentication) { + return ISVNAuthenticationManager.PASSWORD; + } + if (authentication instanceof SVNSSHAuthentication) { + return ISVNAuthenticationManager.SSH; + } + if (authentication instanceof SVNSSLAuthentication) { + return ISVNAuthenticationManager.SSL; + } + if (authentication instanceof SVNUserNameAuthentication) { + return ISVNAuthenticationManager.USERNAME; + } + throw new IllegalArgumentException(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CredentialsAuthenticator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CredentialsAuthenticator.java new file mode 100644 index 000000000000..410cb0997201 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CredentialsAuthenticator.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2014 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.auth; + +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNCancelException; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; +import org.tmatesoft.svn.core.auth.SVNAuthentication; +import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication; +import org.tmatesoft.svn.core.auth.SVNSSLAuthentication; + +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +class CredentialsAuthenticator extends AbstractAuthenticator { + private String myKind; + // sometimes realm string is different (with <>), so store credentials for both strings.. + private String myRealm2; + private SVNAuthentication myAuthentication; + + CredentialsAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService, + @NotNull SVNURL url, + @Nullable String realm) { + super(authService, url, realm == null ? url.getHost() : realm); + } + + public boolean tryAuthenticate(boolean passwordRequest) { + final List kinds = IdeaSvnkitBasedAuthenticationCallback.getKinds(myUrl, passwordRequest); + for (String kind : kinds) { + myKind = kind; + if (!tryAuthenticate()) { + return false; + } + } + return true; + } + + @Override + protected boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException { + myAuthentication = getWithPassiveImpl(passive); + if (myAuthentication != null && !checkAuthOk(myAuthentication)) { + myAuthService.clearPassiveCredentials(myRealm, myUrl, + myAuthentication instanceof SVNPasswordAuthentication); //clear passive also take into acconut ssl filepath + myAuthentication = null; + } + return myAuthentication != null; + } + + private SVNAuthentication getWithPassiveImpl(SvnAuthenticationManager passive) throws SVNException { + try { + return passive.getFirstAuthentication(myKind, myRealm, myUrl); + } + catch (SVNCancelException e) { + return null; + } + } + + private boolean checkAuthOk(SVNAuthentication authentication) { + if (authentication instanceof SVNPasswordAuthentication && StringUtil.isEmptyOrSpaces(authentication.getUserName())) return false; + if (authentication instanceof SVNSSLAuthentication) { + if (StringUtil.isEmptyOrSpaces(((SVNSSLAuthentication)authentication).getPassword())) return false; + } + return true; + } + + @Override + protected boolean getWithActive(final SvnAuthenticationManager active) throws SVNException { + if (ISVNAuthenticationManager.SSL.equals(myKind)) { + if (super.getWithActive(active)) return true; + } + myAuthentication = active.getProvider().requestClientAuthentication(myKind, myUrl, myRealm, null, null, true); + myStoreInUsual = myAuthService.getTempDirectory() == null && myAuthentication != null && myAuthentication.isStorageAllowed(); + + return myAuthentication != null; + } + + public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) { + if (!myUrl.equals(url)) return; + myAuthentication = authentication; + myRealm2 = realm; + myStoreInUsual = myAuthentication != null && myAuthentication.isStorageAllowed(); + } + + @Override + protected boolean afterAuthCall() { + return myAuthentication != null; + } + + @Override + protected boolean acknowledge(SvnAuthenticationManager manager) throws SVNException { + if (!StringUtil.isEmptyOrSpaces(myRealm2) && !myRealm2.equals(myRealm)) { + storeCredentials(manager, myAuthentication, myRealm2); + } + return storeCredentials(manager, myAuthentication, myRealm); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/IdeaSvnkitBasedAuthenticationCallback.java new file mode 100644 index 000000000000..d3551209ed0c --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/IdeaSvnkitBasedAuthenticationCallback.java @@ -0,0 +1,309 @@ +/* + * Copyright 2000-2013 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.auth; + +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.util.PopupUtil; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.WaitForProgressToShow; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.proxy.CommonProxy; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnBundle; +import org.jetbrains.idea.svn.SvnConfiguration; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.commandLine.AuthenticationCallback; +import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.auth.*; + +import java.io.File; +import java.io.IOException; +import java.net.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/26/13 + * Time: 1:27 PM + */ +public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCallback { + @NotNull private final SvnVcs myVcs; + private static final Logger LOG = Logger.getInstance(IdeaSvnkitBasedAuthenticationCallback.class); + private File myTempDirectory; + private boolean myProxyCredentialsWereReturned; + private SvnConfiguration myConfiguration; + private final Set myRequestedCredentials; + + public IdeaSvnkitBasedAuthenticationCallback(@NotNull SvnVcs vcs) { + myVcs = vcs; + myConfiguration = SvnConfiguration.getInstance(myVcs.getProject()); + myRequestedCredentials = ContainerUtil.newHashSet(); + } + + @NotNull + public SvnVcs getVcs() { + return myVcs; + } + + @Nullable + public File getTempDirectory() { + return myTempDirectory; + } + + @Override + public boolean authenticateFor(String realm, SVNURL repositoryUrl, boolean previousFailed, boolean passwordRequest) { + if (repositoryUrl == null) { + return false; + } + return new CredentialsAuthenticator(this, repositoryUrl, realm).tryAuthenticate(passwordRequest); + } + + @Nullable + @Override + public SVNAuthentication requestCredentials(final SVNURL repositoryUrl, final String type) { + SVNAuthentication authentication = null; + + if (repositoryUrl != null) { + final String realm = repositoryUrl.toDecodedString(); + + authentication = requestCredentials(realm, type, new Getter>() { + @Override + public Pair get() { + SVNAuthentication result = myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getInnerProvider() + .requestClientAuthentication(type, repositoryUrl, realm, null, null, true); + + return new Pair(result, true); + } + }); + } + + if (authentication == null) { + LOG.warn("Could not get authentication. Type - " + type + ", Url - " + repositoryUrl); + } + + return authentication; + } + + @Nullable + private T requestCredentials(@NotNull String realm, @NotNull String type, @NotNull Getter> fromUserProvider) { + T result; + Object data = SvnConfiguration.RUNTIME_AUTH_CACHE.getData(type, realm); + + // we return credentials from cache if they are asked for the first time during command execution, otherwise - user is asked + if (data != null && !myRequestedCredentials.contains(getKey(realm, type))) { + // we already have credentials in memory cache + result = (T)data; + myRequestedCredentials.add(getKey(realm, type)); + } + else { + // ask user for credentials + Pair userData = fromUserProvider.get(); + result = userData.first; + + if (result != null && userData.second) { + // save user credentials to memory cache + myVcs.getSvnConfiguration().acknowledge(type, realm, result); + myRequestedCredentials.add(getKey(realm, type)); + } + } + + return result; + } + + @NotNull + private static String getKey(@NotNull String realm, @NotNull String type) { + return type + "$" + realm; + } + + @Override + @Nullable + public String requestSshCredentials(@NotNull final String realm, + @NotNull final SimpleCredentialsDialog.Mode mode, + @NotNull final String key) { + return requestCredentials(realm, ISVNAuthenticationManager.SSH, new Getter>() { + @Override + public Pair get() { + final Ref answer = new Ref(); + + Runnable command = new Runnable() { + public void run() { + SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myVcs.getProject()); + + dialog.setup(mode, realm, key, true); + dialog.setTitle(SvnBundle.message("dialog.title.authentication.required")); + dialog.setSaveEnabled(false); + dialog.show(); + if (dialog.isOK()) { + answer.set(dialog.getPassword()); + } + } + }; + + // Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in + // the thread that started progress + WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any()); + + return new Pair(answer.get(), true); + } + }); + } + + @Override + public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) { + if (repositoryUrl == null) { + return false; + } + + return new SSLServerCertificateAuthenticator(this, repositoryUrl, realm).tryAuthenticate(); + } + + @Override + public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) { + if (repositoryUrl == null) { + return; + } + + final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); + final List kinds = getKinds(repositoryUrl, password); + + for (String kind : kinds) { + configuration.clearCredentials(kind, realm); + } + } + + @Override + public boolean haveDataForTmpConfig() { + final HttpConfigurable instance = HttpConfigurable.getInstance(); + return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() && + (instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC); + } + + @Nullable + public static Proxy getIdeaDefinedProxy(@NotNull final SVNURL url) { + // SVNKit authentication implementation sets repositories as noProxy() to provide custom proxy authentication logic - see for instance, + // SvnAuthenticationManager.getProxyManager(). But noProxy() setting is not cleared correctly in all cases - so if svn command + // (for command line) is executed on thread where repository url was added as noProxy() => proxies are not retrieved for such commands + // and execution logic is incorrect. + + // To prevent such behavior repositoryUrl is manually removed from noProxy() list (for current thread). + // NOTE, that current method is only called from code flows for executing commands through command line client and should not be called + // from SVNKit code flows. + CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort()); + + final List proxies = CommonProxy.getInstance().select(URI.create(url.toString())); + if (proxies != null && !proxies.isEmpty()) { + for (Proxy proxy : proxies) { + if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) { + return proxy; + } + } + } + return null; + } + + @Override + @Nullable + public PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl) { + Proxy proxy = getIdeaDefinedProxy(repositoryUrl); + PasswordAuthentication result = null; + + if (proxy != null) { + if (myProxyCredentialsWereReturned) { + showFailedAuthenticateProxy(); + } + else { + result = getProxyAuthentication(proxy, repositoryUrl); + myProxyCredentialsWereReturned = result != null; + } + } + + return result; + } + + private static void showFailedAuthenticateProxy() { + HttpConfigurable instance = HttpConfigurable.getInstance(); + String message = instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC + ? "Failed to authenticate to proxy. You can change proxy credentials in HTTP proxy settings." + : "Failed to authenticate to proxy."; + + PopupUtil.showBalloonForActiveComponent(message, MessageType.ERROR); + } + + @Nullable + private static PasswordAuthentication getProxyAuthentication(@NotNull Proxy proxy, @NotNull SVNURL repositoryUrl) { + PasswordAuthentication result = null; + + try { + result = Authenticator.requestPasswordAuthentication(repositoryUrl.getHost(), ((InetSocketAddress)proxy.address()).getAddress(), + repositoryUrl.getPort(), repositoryUrl.getProtocol(), repositoryUrl.getHost(), + repositoryUrl.getProtocol(), new URL(repositoryUrl.toString()), + Authenticator.RequestorType.PROXY); + } + catch (MalformedURLException e) { + LOG.info(e); + } + + return result; + } + + public void reset() { + if (myTempDirectory != null) { + FileUtil.delete(myTempDirectory); + } + } + + @NotNull + public static List getKinds(final SVNURL url, boolean passwordRequest) { + if (passwordRequest || "http".equals(url.getProtocol())) { + return Collections.singletonList(ISVNAuthenticationManager.PASSWORD); + } + else if ("https".equals(url.getProtocol())) { + return Collections.singletonList(ISVNAuthenticationManager.SSL); + } + else if ("svn".equals(url.getProtocol())) { + return Collections.singletonList(ISVNAuthenticationManager.PASSWORD); + } + else if (url.getProtocol().contains("svn+")) { // todo +- + return Arrays.asList(ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.USERNAME); + } + return Collections.singletonList(ISVNAuthenticationManager.USERNAME); + } + + @Nullable + @Override + public File getSpecialConfigDir() { + return myTempDirectory != null ? myTempDirectory : new File(myConfiguration.getConfigurationDirectory()); + } + + public void initTmpDir(SvnConfiguration configuration) throws IOException { + if (myTempDirectory == null) { + myTempDirectory = FileUtil.createTempDirectory("tmp", "Subversion"); + FileUtil.copyDir(new File(configuration.getConfigurationDirectory()), myTempDirectory); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ProxySvnAuthentication.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/ProxySvnAuthentication.java similarity index 98% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/ProxySvnAuthentication.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/auth/ProxySvnAuthentication.java index 5e0c618738d4..c34f085c90ae 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ProxySvnAuthentication.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/ProxySvnAuthentication.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.auth; import org.tmatesoft.svn.core.auth.*; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SSLServerCertificateAuthenticator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SSLServerCertificateAuthenticator.java new file mode 100644 index 000000000000..3b78aa55d867 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SSLServerCertificateAuthenticator.java @@ -0,0 +1,168 @@ +/* + * Copyright 2000-2014 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.auth; + +import com.intellij.openapi.util.text.StringUtil; +import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NotNull; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; +import org.tmatesoft.svn.core.auth.SVNAuthentication; +import org.tmatesoft.svn.core.internal.util.SVNBase64; +import org.tmatesoft.svn.core.internal.util.SVNHashMap; +import org.tmatesoft.svn.core.internal.util.SVNSSLUtil; +import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; +import org.tmatesoft.svn.core.internal.wc.SVNWCProperties; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.security.cert.*; + +/** +* @author Konstantin Kolosovsky. +*/ // plus seems that we also should ask for credentials; but we didn't receive realm name yet +class SSLServerCertificateAuthenticator extends AbstractAuthenticator { + private String myCertificateRealm; + private String myCredentialsRealm; + private Object myCertificate; + private int myResult; + private SVNAuthentication myAuthentication; + + SSLServerCertificateAuthenticator(@NotNull IdeaSvnkitBasedAuthenticationCallback authService, + @NotNull SVNURL url, + String realm) { + super(authService, url, realm); + } + + @Override + public boolean tryAuthenticate() { + myResult = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY; + myStoreInUsual = false; + return super.tryAuthenticate(); + } + + @Override + protected boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException { + String stored = (String)passive.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); + if (stored == null) return false; + + myCertificate = createCertificate(stored); + myCertificateRealm = myRealm; + return true; + } + + @Override + public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) { + if (!myUrl.equals(url)) return; + myCredentialsRealm = realm; + myAuthentication = authentication; + if (myAuthentication != null) { + myStoreInUsual &= myAuthentication.isStorageAllowed(); + } + } + + @Override + public void acceptServerAuthentication(SVNURL url, String realm, Object certificate, @MagicConstant int acceptResult) { + if (!myUrl.equals(url)) return; + myCertificateRealm = realm; + myCertificate = certificate; + myResult = acceptResult; + } + + @Override + protected boolean afterAuthCall() { + myStoreInUsual &= myCertificate != null && ISVNAuthenticationProvider.ACCEPTED == myResult; + // TODO: Previous code always returned not null value, so Boolean == null check was always false in tryAuthenticate(). + // TODO: This was most likely error in code - check once again. + return ISVNAuthenticationProvider.REJECTED != myResult && myCertificate != null; + } + + @Override + protected boolean acknowledge(SvnAuthenticationManager manager) throws SVNException { + // we should store certificate, if it wasn't accepted (if temporally tmp) + if (myCertificate == null) { // this is if certificate was stored only in passive area + String stored = (String)manager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); + if (StringUtil.isEmptyOrSpaces(stored)) { + throw new SVNException( + SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "No stored server certificate was found in runtime")); + } + myCertificate = createCertificate(stored); + myCertificateRealm = myRealm; + } + if (myAuthService.getTempDirectory() != null && myCertificate != null) { + storeServerCertificate(); + + if (myAuthentication != null) { + final String realm = myCredentialsRealm == null ? myCertificateRealm : myCredentialsRealm; + return storeCredentials(manager, myAuthentication, realm); + } + } + return true; + } + + @NotNull + private Certificate createCertificate(@NotNull String stored) throws SVNException { + CertificateFactory factory; + try { + factory = CertificateFactory.getInstance("X509"); + final byte[] buffer = new byte[stored.length()]; + SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer); + + return factory.generateCertificate(new ByteArrayInputStream(buffer)); + } + catch (CertificateException e) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e)); + } + } + + private void storeServerCertificate() throws SVNException { + if (!(myCertificate instanceof X509Certificate)) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not store server certificate: " + myCertificate)); + } + X509Certificate x509Certificate = (X509Certificate)myCertificate; + String stored; + try { + stored = SVNBase64.byteArrayToBase64(x509Certificate.getEncoded()); + } + catch (CertificateEncodingException e) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); + } + + int failures = SVNSSLUtil.getServerCertificateFailures(x509Certificate, myUrl.getHost()); + storeServerCertificate(myAuthService.getTempDirectory(), myCertificateRealm, stored, failures); + } + + private void storeServerCertificate(final File configDir, String realm, String data, int failures) throws SVNException { + //noinspection ResultOfMethodCallIgnored + configDir.mkdirs(); + + File file = new File(configDir, "auth/svn.ssl.server/" + SVNFileUtil.computeChecksum(realm)); + SVNHashMap map = new SVNHashMap(); + map.put("ascii_cert", data); + map.put("svn:realmstring", realm); + map.put("failures", Integer.toString(failures)); + + SVNFileUtil.deleteFile(file); + + File tmpFile = SVNFileUtil.createUniqueFile(configDir, "auth", ".tmp", true); + try { + SVNWCProperties.setProperties(SVNProperties.wrap(map), file, tmpFile, SVNWCProperties.SVN_HASH_TERMINATOR); + } + finally { + SVNFileUtil.deleteFile(tmpFile); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java similarity index 95% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java index 06005731a860..e844374cf341 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.auth; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; @@ -25,10 +25,12 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.CalledInAwt; import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.util.EventDispatcher; @@ -40,9 +42,7 @@ import com.intellij.util.ui.UIUtil; import com.trilead.ssh2.auth.AgentProxy; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.auth.ProviderType; -import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction; -import org.jetbrains.idea.svn.auth.SvnAuthenticationListener; +import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.config.ProxyGroup; import org.jetbrains.idea.svn.config.SvnServerFileKeys; import org.tmatesoft.svn.core.SVNErrorMessage; @@ -62,7 +62,7 @@ import java.util.*; * @author alex */ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager implements SvnAuthenticationListener, ISVNAuthenticationManagerExt { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationManager"); + private static final Logger LOG = Logger.getInstance(SvnAuthenticationManager.class); // while Mac storage not working for IDEA, we use this key to check whether to prompt abt plaintext or just store public static final String SVN_SSH = "svn+ssh"; public static final String HTTP = "http"; @@ -563,7 +563,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im } } - public void acknowledgeForSSL(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication proxy) { + public void acknowledgeForSSL(boolean accepted, SVNAuthentication proxy) { if (accepted && proxy instanceof SVNSSLAuthentication && (((SVNSSLAuthentication) proxy).getCertificateFile() != null)) { final SVNSSLAuthentication svnsslAuthentication = (SVNSSLAuthentication)proxy; final SVNURL url = svnsslAuthentication.getURL(); @@ -572,13 +572,9 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im final SVNCompositeConfigFile serversFile = provider.getServersFile(); String groupName = getGroupName(serversFile.getProperties("groups"), url.getHost()); - if (StringUtil.isEmptyOrSpaces(groupName)) { - serversFile.setPropertyValue("global", SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true); - //serversFile.setPropertyValue("global", SvnServerFileKeys.SSL_CLIENT_CERT_PASSWORD, null, true); - } else { - serversFile.setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true); - //serversFile.setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_PASSWORD, null, true); - } + groupName = StringUtil.isEmptyOrSpaces(groupName) ? "global" : groupName; + serversFile + .setPropertyValue(groupName, SvnServerFileKeys.SSL_CLIENT_CERT_FILE, svnsslAuthentication.getCertificateFile().getPath(), true); serversFile.save(); } } @@ -597,7 +593,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im String host = url.getHost(); String proxyHost = getServersPropertyIdea(host, HTTP_PROXY_HOST); - if ((proxyHost == null) || "".equals(proxyHost.trim())) { + if (StringUtil.isEmptyOrSpaces(proxyHost)) { if (getConfig().isIsUseDefaultProxy()) { // ! use common proxy if it is set try { @@ -607,8 +603,8 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) { final SocketAddress address = proxy.address(); if (address instanceof InetSocketAddress) { - return new MyPromptingProxyManager(((InetSocketAddress)address).getHostName(), "" + ((InetSocketAddress)address).getPort(), - url.getProtocol()); + return new MyPromptingProxyManager(((InetSocketAddress)address).getHostName(), + String.valueOf(((InetSocketAddress)address).getPort()), url.getProtocol()); } } } @@ -642,7 +638,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im private static class MyPromptingProxyManager extends MySimpleProxyManager { - private static final String ourPrompt = "Proxy authentication"; + private final String myProtocol; private MyPromptingProxyManager(final String host, final String port, String protocol) { @@ -766,7 +762,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im return getPropertyIdea(host, serversFile, name); } - private String getPropertyIdea(String host, SVNCompositeConfigFile serversFile, final String name) { + private static String getPropertyIdea(String host, SVNCompositeConfigFile serversFile, final String name) { String groupName = getGroupName(serversFile.getProperties("groups"), host); if (groupName != null) { Map hostProps = serversFile.getProperties(groupName); @@ -837,7 +833,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im return value == null || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value); } - private ModalityState getCurrent() { + private static ModalityState getCurrent() { if (ApplicationManager.getApplication().isDispatchThread()) { return ModalityState.current(); } @@ -848,18 +844,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im return pi.getModalityState(); } - /** - * Shows a yes/no question whether user wants to store his password in plain text and returns his answer. - * @param title title of the questioning dialog. - * @param message questioning message to be displayed. - * @return true if user agrees to store his password in plaintext, false if he doesn't. - */ - @CalledInAwt - private boolean askToStoreUnencrypted(String title, String message) { - final int answer = Messages.showYesNoDialog(myProject, message, title, Messages.getQuestionIcon()); - return answer == Messages.YES; - } - public void setInteraction(SvnAuthenticationInteraction interaction) { myInteraction = interaction; } @@ -914,10 +898,6 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im } } - private static boolean isLion() { - return SystemInfo.isMac && SystemInfo.isMacOSSnowLeopard && ! SystemInfo.OS_VERSION.startsWith("10.6"); - } - public class IdeaSVNHostOptionsProvider extends DefaultSVNHostOptionsProvider { public IdeaSVNHostOptionsProvider() { super(myConfigDirectory); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationNotifier.java similarity index 98% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationNotifier.java index edd5d144019e..e202cc775e85 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationNotifier.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.auth; import com.intellij.notification.NotificationType; import com.intellij.openapi.application.Application; @@ -44,7 +44,7 @@ import com.intellij.util.net.HttpConfigurable; import com.intellij.util.proxy.CommonProxy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider; +import org.jetbrains.idea.svn.*; import org.tmatesoft.svn.core.SVNAuthenticationException; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNException; @@ -64,7 +64,7 @@ import java.util.List; import java.util.Timer; public class SvnAuthenticationNotifier extends GenericNotifierImpl { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationNotifier"); + private static final Logger LOG = Logger.getInstance(SvnAuthenticationNotifier.class); private static final List ourAuthKinds = Arrays.asList(ISVNAuthenticationManager.PASSWORD, ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.SSL, ISVNAuthenticationManager.USERNAME, "svn.ssl.server", "svn.ssh.server"); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationProvider.java similarity index 96% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationProvider.java index 04868ae990b8..3ad9de7badaf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnAuthenticationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationProvider.java @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.dialogs; +package org.jetbrains.idea.svn.auth; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.util.SystemProperties; -import org.jetbrains.idea.svn.SvnAuthenticationManager; -import org.jetbrains.idea.svn.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnInteractiveAuthenticationProvider.java similarity index 97% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnInteractiveAuthenticationProvider.java index b39dddedc632..de2c007b7879 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnInteractiveAuthenticationProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.dialogs; +package org.jetbrains.idea.svn.auth; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -31,11 +31,10 @@ import com.jcraft.jsch.agentproxy.ConnectorFactory; import com.jcraft.jsch.agentproxy.TrileadAgentProxy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnAuthenticationManager; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.auth.ProviderType; +import org.jetbrains.idea.svn.dialogs.*; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.*; @@ -47,7 +46,7 @@ import java.lang.reflect.InvocationTargetException; import java.security.cert.X509Certificate; public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationProvider { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider"); + private static final Logger LOG = Logger.getInstance(SvnInteractiveAuthenticationProvider.class); private final Project myProject; private static final ThreadLocal myCallState = new ThreadLocal(); private final SvnAuthenticationManager myManager; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java index 914a8ebd6746..315954e7b520 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java @@ -17,23 +17,28 @@ package org.jetbrains.idea.svn.branchConfig; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.integrate.SvnBranchItem; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.wc.SVNInfo; -import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.util.ArrayList; import java.util.List; public class DefaultConfigLoader { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.branchConfig.DefaultConfigLoader"); + @NonNls private static final String DEFAULT_TRUNK_NAME = "trunk"; @NonNls private static final String DEFAULT_BRANCHES_NAME = "branches"; @NonNls private static final String DEFAULT_TAGS_NAME = "tags"; @@ -59,25 +64,10 @@ public class DefaultConfigLoader { while(true) { final String s = SVNPathUtil.tail(baseUrl.getPath()); if (s.equalsIgnoreCase(DEFAULT_TRUNK_NAME) || s.equalsIgnoreCase(DEFAULT_BRANCHES_NAME) || s.equalsIgnoreCase(DEFAULT_TAGS_NAME)) { - final SVNURL rootPath = baseUrl.removePathTail(); - SVNLogClient client = vcs.createLogClient(); - client.doList(rootPath, SVNRevision.UNDEFINED, SVNRevision.HEAD, false, false, new ISVNDirEntryHandler() { - public void handleDirEntry(final SVNDirEntry dirEntry) throws SVNException { - if (("".equals(dirEntry.getRelativePath())) || (! SVNNodeKind.DIR.equals(dirEntry.getKind()))) { - // do not use itself or files - return; - } - - if (dirEntry.getName().toLowerCase().endsWith(DEFAULT_TRUNK_NAME)) { - result.setTrunkUrl(rootPath.appendPath(dirEntry.getName(), false).toString()); - } - else { - result.addBranches(rootPath.appendPath(dirEntry.getName(), false).toString(), - new InfoStorage>(new ArrayList(0), InfoReliability.defaultValues)); - } - } - }); + SVNURL rootPath = baseUrl.removePathTail(); + SvnTarget target = SvnTarget.fromURL(rootPath); + vcs.getFactory(target).createBrowseClient().list(target, SVNRevision.HEAD, SVNDepth.IMMEDIATES, createHandler(result, rootPath)); break; } if (SVNPathUtil.removeTail(baseUrl.getPath()).length() == 0) { @@ -91,5 +81,28 @@ public class DefaultConfigLoader { LOG.info(e); return null; } + catch (VcsException e) { + LOG.info(e); + return null; + } + } + + @NotNull + private static ISVNDirEntryHandler createHandler(final SvnBranchConfigurationNew result, final SVNURL rootPath) { + return new ISVNDirEntryHandler() { + public void handleDirEntry(final SVNDirEntry dirEntry) throws SVNException { + if (SVNNodeKind.DIR.equals(dirEntry.getKind())) { + SVNURL childUrl = rootPath.appendPath(dirEntry.getName(), false); + + if (StringUtil.endsWithIgnoreCase(dirEntry.getName(), DEFAULT_TRUNK_NAME)) { + result.setTrunkUrl(childUrl.toString()); + } + else { + result.addBranches(childUrl.toString(), + new InfoStorage>(new ArrayList(0), InfoReliability.defaultValues)); + } + } + } + }; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/BrowseClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/BrowseClient.java index ea59a9a80dbf..92e64bb5230e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/BrowseClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/BrowseClient.java @@ -31,4 +31,6 @@ public interface BrowseClient extends SvnClient { void list(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNDepth depth, @Nullable ISVNDirEntryHandler handler) throws VcsException; + + long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/CmdBrowseClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/CmdBrowseClient.java index 782d521dc5c0..20d60a9e50bf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/CmdBrowseClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/CmdBrowseClient.java @@ -17,9 +17,11 @@ package org.jetbrains.idea.svn.browse; import com.intellij.openapi.vcs.VcsException; import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.checkin.CmdCheckinClient; import org.jetbrains.idea.svn.commandLine.CommandExecutor; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnBindException; @@ -55,7 +57,7 @@ public class CmdBrowseClient extends BaseSvnClient implements BrowseClient { CommandUtil.put(parameters, depth); parameters.add("--xml"); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.list, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.list, parameters, null); try { parseOutput(target.getURL(), command, handler); @@ -65,6 +67,24 @@ public class CmdBrowseClient extends BaseSvnClient implements BrowseClient { } } + @Override + public long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException { + assertUrl(target); + + List parameters = ContainerUtil.newArrayList(); + + CommandUtil.put(parameters, target); + CommandUtil.put(parameters, makeParents, "--parents"); + parameters.add("--message"); + parameters.add(message); + + CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(null); + + execute(myVcs, target, SvnCommandName.mkdir, parameters, listener); + + return listener.getCommittedRevision(); + } + private static void parseOutput(@NotNull SVNURL url, @NotNull CommandExecutor command, @Nullable ISVNDirEntryHandler handler) throws VcsException, SVNException { try { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/SvnKitBrowseClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/SvnKitBrowseClient.java index 95388a12a8ec..aed0c1de14ab 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/SvnKitBrowseClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/browse/SvnKitBrowseClient.java @@ -53,6 +53,20 @@ public class SvnKitBrowseClient extends BaseSvnClient implements BrowseClient { } } + @Override + public long createDirectory(@NotNull SvnTarget target, @NotNull String message, boolean makeParents) throws VcsException { + assertUrl(target); + + try { + SVNCommitInfo commitInfo = myVcs.createCommitClient().doMkDir(new SVNURL[]{target.getURL()}, message, null, makeParents); + + return commitInfo.getNewRevision(); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } + @Nullable private static ISVNDirEntryHandler wrapHandler(@Nullable ISVNDirEntryHandler handler) { return handler == null ? null : new SkipEmptyNameDirectoriesHandler(handler); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/change/CmdChangeListClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/change/CmdChangeListClient.java index c88d5d00054a..2791ce87af12 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/change/CmdChangeListClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/change/CmdChangeListClient.java @@ -32,7 +32,7 @@ public class CmdChangeListClient extends BaseSvnClient implements ChangeListClie // for now parsing of the output is not required as command is executed only for one file // and will be either successful or exception will be thrown - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null); + execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null); } @Override @@ -45,6 +45,6 @@ public class CmdChangeListClient extends BaseSvnClient implements ChangeListClie // for now parsing of the output is not required as command is executed only for one file // and will be either successful or exception will be thrown - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null); + execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.changelist, parameters, null); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateGroups.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CheckinClient.java similarity index 52% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateGroups.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CheckinClient.java index 92b92fdb7b25..628c22219e11 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateGroups.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CheckinClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -13,9 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.update; +package org.jetbrains.idea.svn.checkin; -public interface SvnUpdateGroups { -// String MERGED_WITH_PROPERTY_CONFLICT = "MERGED_WITH_PROPERTY_CONFLICT"; - String MERGED_WITH_TREE_CONFLICT = "MERGED_WITH_TREE_CONFLICT"; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNCommitInfo; + +import java.io.File; +import java.util.Collection; + +/** + * @author Konstantin Kolosovsky. + */ +public interface CheckinClient extends SvnClient { + + @NotNull + SVNCommitInfo[] commit(@NotNull Collection paths, @NotNull String comment) throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdCheckinClient.java similarity index 61% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdCheckinClient.java index d9b760d8b24a..b7c92a3ed126 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdCheckinClient.java @@ -13,17 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.checkin; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.AbstractFilterChildren; import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.*; +import org.jetbrains.idea.svn.status.StatusClient; +import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -37,58 +48,56 @@ import java.util.regex.Pattern; * Date: 2/25/13 * Time: 4:56 PM */ -public class SvnCommitRunner { +public class CmdCheckinClient extends BaseSvnClient implements CheckinClient { - private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.commandLine.SvnCommitRunner"); + private static final Logger LOG = Logger.getInstance(CmdCheckinClient.class); public static final long INVALID_REVISION_NUMBER = -1L; - private SvnCommitRunner.CommandListener myCommandListener; - private SvnVcs myVcs; + @NotNull + @Override + public SVNCommitInfo[] commit(@NotNull Collection paths, @NotNull String message) throws VcsException { + // if directory renames were used, IDEA reports all files under them as moved, but for svn we can not pass some of them + // to commit command - since not all paths are registered as changes -> so we need to filter these cases, but only if + // there at least some child-parent relationships in passed paths + try { + paths = filterCommittables(paths); + } + catch (SVNException e) { + throw new SvnBindException(e); + } - public SvnCommitRunner(@NotNull SvnVcs vcs, @Nullable CommitEventHandler handler) { - myVcs = vcs; - myCommandListener = new CommandListener(handler); + return commit(ArrayUtil.toObjectArray(paths, File.class), message); } - public long commit(File[] paths, - String message, - @Nullable SVNDepth depth, - boolean noUnlock, - boolean keepChangelist, - Collection changelists, - Map revpropTable) throws VcsException { - if (paths.length == 0) return INVALID_REVISION_NUMBER; + @NotNull + public SVNCommitInfo[] commit(@NotNull File[] paths, @NotNull String message) throws VcsException { + if (paths.length == 0) return new SVNCommitInfo[]{SVNCommitInfo.NULL}; final List parameters = new ArrayList(); - CommandUtil.put(parameters, depth); - CommandUtil.put(parameters, noUnlock, "--no-unlock"); - CommandUtil.put(parameters, keepChangelist, "--keep-changelists"); - CommandUtil.putChangeLists(parameters, changelists); + CommandUtil.put(parameters, SVNDepth.EMPTY); + CommandUtil.put(parameters, false, "--no-unlock"); + CommandUtil.put(parameters, false, "--keep-changelists"); + CommandUtil.putChangeLists(parameters, null); - if (revpropTable != null && ! revpropTable.isEmpty()) { - final Set> set = revpropTable.entrySet(); - for (Map.Entry entry : set) { - parameters.add("--with-revprop"); - parameters.add(entry.getKey() + "=" + entry.getValue()); - } - } parameters.add("-m"); parameters.add(message); // TODO: seems that sort is not necessary here Arrays.sort(paths); CommandUtil.put(parameters, paths); - myCommandListener.setBaseDirectory(CommandUtil.correctUpToExistingParent(paths[0])); - CommandUtil.execute(myVcs, SvnTarget.fromFile(paths[0]), SvnCommandName.ci, parameters, myCommandListener); - myCommandListener.throwExceptionIfOccurred(); + IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator()); + CmdCheckinClient.CommandListener listener = new CommandListener(handler); + listener.setBaseDirectory(CommandUtil.correctUpToExistingParent(paths[0])); + execute(myVcs, SvnTarget.fromFile(paths[0]), SvnCommandName.ci, parameters, listener); + listener.throwExceptionIfOccurred(); - return validateRevisionNumber(); + long revision = validateRevisionNumber(listener.getCommittedRevision()); + + return new SVNCommitInfo[]{new SVNCommitInfo(revision, null, null, null)}; } - private long validateRevisionNumber() throws VcsException { - long revision = myCommandListener.getCommittedRevision(); - + private static long validateRevisionNumber(long revision) throws VcsException { if (revision < 0) { throw new VcsException("Wrong committed revision number: " + revision); } @@ -96,6 +105,53 @@ public class SvnCommitRunner { return revision; } + private Collection filterCommittables(@NotNull Collection committables) throws SVNException { + final Set childrenOfSomebody = ContainerUtil.newHashSet(); + new AbstractFilterChildren() { + @Override + protected void sortAscending(List list) { + Collections.sort(list); + } + + @Override + protected boolean isAncestor(File parent, File child) { + // strict here will ensure that for case insensitive file systems paths different only by case will not be treated as ancestors + // of each other which is what we need to perform renames from one case to another on Windows + final boolean isAncestor = FileUtil.isAncestor(parent, child, true); + if (isAncestor) { + childrenOfSomebody.add(child.getPath()); + } + return isAncestor; + } + }.doFilter(ContainerUtil.newArrayList(committables)); + if (!childrenOfSomebody.isEmpty()) { + List result = ContainerUtil.newArrayList(); + StatusClient statusClient = myFactory.createStatusClient(); + + for (File file : committables) { + if (!childrenOfSomebody.contains(file.getPath())) { + result.add(file); + } + else { + try { + final SVNStatus status = statusClient.doStatus(file, false); + if (status != null && !SVNStatusType.STATUS_NONE.equals(status.getContentsStatus()) && + !SVNStatusType.STATUS_UNVERSIONED.equals(status.getContentsStatus())) { + result.add(file); + } + } + catch (SVNException e) { + // not versioned + LOG.info(e); + throw e; + } + } + } + return result; + } + return committables; + } + public static class CommandListener extends LineCommandAdapter { // Status could contain spaces, like "Adding copy of ". But at the end we are not interested in "copy of" part and want to have diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdImportClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdImportClient.java index d6cb8945932e..74eeadbb2fa1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdImportClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CmdImportClient.java @@ -5,9 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.CommandUtil; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.jetbrains.idea.svn.commandLine.SvnCommitRunner; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNCommitHandler; @@ -41,10 +39,10 @@ public class CmdImportClient extends BaseSvnClient implements ImportClient { parameters.add("--message"); parameters.add(message); - SvnCommitRunner.CommandListener listener = new SvnCommitRunner.CommandListener(handler); + CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(handler); listener.setBaseDirectory(path); - CommandUtil.execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.importFolder, parameters, listener); + execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.importFolder, parameters, listener); return listener.getCommittedRevision(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventHandler.java similarity index 95% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventHandler.java index 7effc83352ae..87be3fae7dd5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.checkin; import org.tmatesoft.svn.core.wc.ISVNEventHandler; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventType.java similarity index 96% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventType.java index f64bf745fa08..6fdd9a0b8229 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/CommitEventType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.checkin; /** * Created with IntelliJ IDEA. diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java index b53492013ef1..3b78ac667efa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java @@ -28,8 +28,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; -import org.jetbrains.idea.svn.commandLine.CommitEventType; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNEvent; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java deleted file mode 100644 index 796e1795b360..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java +++ /dev/null @@ -1,704 +0,0 @@ -/* - * Copyright 2000-2013 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.checkin; - -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.popup.util.PopupUtil; -import com.intellij.openapi.util.Getter; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; -import com.intellij.util.ThrowableRunnable; -import com.intellij.util.WaitForProgressToShow; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.messages.MessageBusConnection; -import com.intellij.util.net.HttpConfigurable; -import com.intellij.util.proxy.CommonProxy; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.*; -import org.jetbrains.idea.svn.commandLine.AuthenticationCallback; -import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog; -import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.auth.*; -import org.tmatesoft.svn.core.internal.util.SVNBase64; -import org.tmatesoft.svn.core.internal.util.SVNHashMap; -import org.tmatesoft.svn.core.internal.util.SVNSSLUtil; -import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; -import org.tmatesoft.svn.core.internal.wc.SVNWCProperties; -import org.tmatesoft.svn.core.wc.SVNRevision; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.net.*; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/26/13 - * Time: 1:27 PM - */ -public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCallback { - private final SvnVcs myVcs; - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback"); - private File myTempDirectory; - private boolean myProxyCredentialsWereReturned; - private SvnConfiguration myConfiguration; - private final Set myRequestedCredentials; - - public IdeaSvnkitBasedAuthenticationCallback(SvnVcs vcs) { - myVcs = vcs; - myConfiguration = SvnConfiguration.getInstance(myVcs.getProject()); - myRequestedCredentials = ContainerUtil.newHashSet(); - } - - @Override - public boolean authenticateFor(String realm, SVNURL repositoryUrl, boolean previousFailed, boolean passwordRequest) { - if (repositoryUrl == null) { - return false; - } - return new CredentialsAuthenticator(myVcs).tryAuthenticate(realm, repositoryUrl, previousFailed, passwordRequest); - } - - @Nullable - @Override - public SVNAuthentication requestCredentials(final SVNURL repositoryUrl, final String type) { - SVNAuthentication authentication = null; - - if (repositoryUrl != null) { - final String realm = repositoryUrl.toDecodedString(); - - authentication = requestCredentials(realm, type, new Getter>() { - @Override - public Pair get() { - SVNAuthentication result = myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getInnerProvider() - .requestClientAuthentication(type, repositoryUrl, realm, null, null, true); - - return new Pair(result, true); - } - }); - } - - if (authentication == null) { - LOG.warn("Could not get authentication. Type - " + type + ", Url - " + repositoryUrl); - } - - return authentication; - } - - @Nullable - private T requestCredentials(@NotNull String realm, @NotNull String type, @NotNull Getter> fromUserProvider) { - T result; - Object data = SvnConfiguration.RUNTIME_AUTH_CACHE.getData(type, realm); - - // we return credentials from cache if they are asked for the first time during command execution, otherwise - user is asked - if (data != null && !myRequestedCredentials.contains(getKey(realm, type))) { - // we already have credentials in memory cache - result = (T) data; - myRequestedCredentials.add(getKey(realm, type)); - } - else { - // ask user for credentials - Pair userData = fromUserProvider.get(); - result = userData.first; - - if (result != null && userData.second) { - // save user credentials to memory cache - myVcs.getSvnConfiguration().acknowledge(type, realm, result); - myRequestedCredentials.add(getKey(realm, type)); - } - } - - return result; - } - - @NotNull - private static String getKey(@NotNull String realm, @NotNull String type) { - return type + "$" + realm; - } - - @Override - @Nullable - public String requestSshCredentials(@NotNull final String realm, - @NotNull final SimpleCredentialsDialog.Mode mode, - @NotNull final String key) { - return requestCredentials(realm, ISVNAuthenticationManager.SSH, new Getter>() { - @Override - public Pair get() { - final Ref answer = new Ref(); - - Runnable command = new Runnable() { - public void run() { - SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myVcs.getProject()); - - dialog.setup(mode, realm, key, true); - dialog.setTitle(SvnBundle.message("dialog.title.authentication.required")); - dialog.setSaveEnabled(false); - dialog.show(); - if (dialog.isOK()) { - answer.set(dialog.getPassword()); - } - } - }; - - // Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in - // the thread that started progress - WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any()); - - return new Pair(answer.get(), true); - } - }); - } - - @Override - public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) { - if (repositoryUrl == null) { - return false; - } - - return new SSLServerCertificateAuthenticator(myVcs).tryAuthenticate(repositoryUrl, realm); - } - - @Override - public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) { - if (repositoryUrl == null) { - return; - } - - final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); - final List kinds = getKinds(repositoryUrl, password); - - for (String kind : kinds) { - configuration.clearCredentials(kind, realm); - } - } - - @Override - public boolean haveDataForTmpConfig() { - final HttpConfigurable instance = HttpConfigurable.getInstance(); - return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() && - (instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC); - } - - // TODO: not used - should be removed. - @Override - public boolean persistDataToTmpConfig(final SVNURL repositoryUrl) throws IOException { - // TODO: Make repositoryUrl @NotNull after SvnLineCommand.runWithAuthenticationAttempt refactored - if (repositoryUrl == null) { - return false; - } - - final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); - initTmpDir(configuration); - - final Proxy proxy = getIdeaDefinedProxy(repositoryUrl); - if (proxy != null){ - IdeaSVNConfigFile.putProxyIntoServersFile(myTempDirectory, repositoryUrl.getHost(), proxy); - } - return true; - } - - @Nullable - public static Proxy getIdeaDefinedProxy(@NotNull final SVNURL url) { - // SVNKit authentication implementation sets repositories as noProxy() to provide custom proxy authentication logic - see for instance, - // SvnAuthenticationManager.getProxyManager(). But noProxy() setting is not cleared correctly in all cases - so if svn command - // (for command line) is executed on thread where repository url was added as noProxy() => proxies are not retrieved for such commands - // and execution logic is incorrect. - - // To prevent such behavior repositoryUrl is manually removed from noProxy() list (for current thread). - // NOTE, that current method is only called from code flows for executing commands through command line client and should not be called - // from SVNKit code flows. - CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort()); - - final List proxies = CommonProxy.getInstance().select(URI.create(url.toString())); - if (proxies != null && ! proxies.isEmpty()) { - for (Proxy proxy : proxies) { - if (HttpConfigurable.isRealProxy(proxy) && Proxy.Type.HTTP.equals(proxy.type())) { - return proxy; - } - } - } - return null; - } - - @Override - @Nullable - public PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl) { - final Proxy proxy = getIdeaDefinedProxy(repositoryUrl); - if (proxy == null) return null; - if (myProxyCredentialsWereReturned){ - // ask loud - final HttpConfigurable instance = HttpConfigurable.getInstance(); - if (instance.USE_HTTP_PROXY || instance.USE_PROXY_PAC) { - PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy. You can change proxy credentials in HTTP proxy settings.", MessageType.ERROR); - } else { - PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy.", MessageType.ERROR); - } - return null; - } - final InetSocketAddress address = (InetSocketAddress)proxy.address(); - final PasswordAuthentication authentication; - try { - authentication = Authenticator - .requestPasswordAuthentication(repositoryUrl.getHost(), address.getAddress(), repositoryUrl.getPort(), repositoryUrl.getProtocol(), - repositoryUrl.getHost(), repositoryUrl.getProtocol(), new URL(repositoryUrl.toString()), - Authenticator.RequestorType.PROXY); - } catch (MalformedURLException e) { - LOG.info(e); - return null; - } - - if (authentication != null) { - myProxyCredentialsWereReturned = true; - } - - return authentication; - } - - // TODO: not used - should be removed. - @Override - public boolean askProxyCredentials(SVNURL repositoryUrl) { - if (repositoryUrl == null) { - return false; - } - - PasswordAuthentication authentication = getProxyAuthentication(repositoryUrl); - - if (authentication != null) { - // for 'generic' proxy variant (suppose user defined proxy in Subversion config but no password) - try { - initTmpDir(SvnConfiguration.getInstance(myVcs.getProject())); - } - catch (IOException e) { - PopupUtil.showBalloonForActiveComponent("Failed to authenticate to proxy: " + e.getMessage(), MessageType.ERROR); - return false; - } - return IdeaSVNConfigFile.putProxyCredentialsIntoServerFile(myTempDirectory, repositoryUrl.getHost(), authentication); - } - return false; - } - - public void reset() { - if (myTempDirectory != null) { - FileUtil.delete(myTempDirectory); - } - } - - private abstract class AbstractAuthenticator { - protected final SvnVcs myVcs; - protected boolean myStoreInUsual; - protected SvnAuthenticationManager myTmpDirManager; - - protected AbstractAuthenticator(SvnVcs vcs) { - myVcs = vcs; - } - - protected boolean tryAuthenticate() { - final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); - final SvnAuthenticationManager passive = configuration.getPassiveAuthenticationManager(myVcs.getProject()); - final SvnAuthenticationManager manager = configuration.getAuthenticationManager(myVcs); - - try { - T svnAuthentication = getWithPassive(passive); - if (svnAuthentication == null) { - svnAuthentication = getWithActive(manager); - } - if (svnAuthentication == null) return false; - - if (myStoreInUsual) { - manager.setArtificialSaving(true); - return acknowledge(manager, svnAuthentication); - } else { - if (myTmpDirManager == null) { - initTmpDir(configuration); - myTmpDirManager = createTmpManager(); - } - myTmpDirManager.setArtificialSaving(true); - return acknowledge(myTmpDirManager, svnAuthentication); - } - } - catch (IOException e) { - LOG.info(e); - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR); - return false; - } - catch (SVNException e) { - LOG.info(e); - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), e.getMessage(), MessageType.ERROR); - return false; - } - } - - protected SvnAuthenticationManager createTmpManager() { - final SvnAuthenticationManager interactive = new SvnAuthenticationManager(myVcs.getProject(), myTempDirectory); - - interactive.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); - interactive.setAuthenticationProvider(new SvnInteractiveAuthenticationProvider(myVcs, interactive)); - - return interactive; - } - - protected abstract T getWithPassive(SvnAuthenticationManager passive) throws SVNException; - protected abstract T getWithActive(SvnAuthenticationManager active) throws SVNException; - protected abstract boolean acknowledge(SvnAuthenticationManager manager, T svnAuthentication) throws SVNException; - } - - private void initTmpDir(SvnConfiguration configuration) throws IOException { - if (myTempDirectory == null) { - myTempDirectory = FileUtil.createTempDirectory("tmp", "Subversion"); - FileUtil.copyDir(new File(configuration.getConfigurationDirectory()), myTempDirectory); - } - } - - private void doWithSubscribeToAuthProvider(SvnAuthenticationManager.ISVNAuthenticationProviderListener listener, - final ThrowableRunnable runnable) throws SVNException { - MessageBusConnection connection = null; - try { - final Project project = myVcs.getProject(); - connection = project.getMessageBus().connect(project); - connection.subscribe(SvnAuthenticationManager.AUTHENTICATION_PROVIDER_LISTENER, listener); - runnable.run(); - } finally { - if (connection != null) { - connection.disconnect(); - } - } - } - - // plus seems that we also should ask for credentials; but we didn't receive realm name yet - private class SSLServerCertificateAuthenticator extends AbstractAuthenticator { - private SVNURL myUrl; - private String myRealm; - private String myCertificateRealm; - private String myCredentialsRealm; - private Object myCertificate; - private int myResult; - private SVNAuthentication myAuthentication; - - protected SSLServerCertificateAuthenticator(SvnVcs vcs) { - super(vcs); - } - - public boolean tryAuthenticate(final SVNURL url, final String realm) { - myUrl = url; - myRealm = realm; - myResult = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY; - myStoreInUsual = false; - return tryAuthenticate(); - } - - @Override - protected Boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException { - String stored = (String) passive.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); - if (stored == null) return null; - CertificateFactory cf; - try { - cf = CertificateFactory.getInstance("X509"); - final byte[] buffer = new byte[stored.length()]; - SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer); - myCertificate = cf.generateCertificate(new ByteArrayInputStream(buffer)); - } - catch (CertificateException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e)); - } - myCertificateRealm = myRealm; - return myCertificate != null ? true : null; - } - - @Override - protected Boolean getWithActive(final SvnAuthenticationManager active) throws SVNException { - doWithSubscribeToAuthProvider(new SvnAuthenticationManager.ISVNAuthenticationProviderListener() { - @Override - public void requestClientAuthentication(String kind, - SVNURL url, - String realm, - SVNErrorMessage errorMessage, - SVNAuthentication previousAuth, - boolean authMayBeStored, - SVNAuthentication authentication) { - if (!myUrl.equals(url)) return; - myCredentialsRealm = realm; - myAuthentication = authentication; - if (myAuthentication != null) { - myStoreInUsual &= myAuthentication.isStorageAllowed(); - } - } - - @Override - public void acceptServerAuthentication(SVNURL url, - String realm, - Object certificate, - boolean resultMayBeStored, - int accepted) { - if (!myUrl.equals(url)) return; - myCertificateRealm = realm; - myCertificate = certificate; - myResult = accepted; - } - }, new ThrowableRunnable() { - @Override - public void run() throws SVNException { - // NOTE: DO NOT replace this call - SSL authentication highly tied to SVNKit - myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - //myVcs.getInfo(myUrl, SVNRevision.HEAD, active); - } - } - ); - - myStoreInUsual &= myCertificate != null && ISVNAuthenticationProvider.ACCEPTED == myResult; - return ISVNAuthenticationProvider.REJECTED != myResult && myCertificate != null; - } - - @Override - protected boolean acknowledge(SvnAuthenticationManager manager, Boolean svnAuthentication) throws SVNException { - // we should store certificate, if it wasn't accepted (if temporally tmp) - if (myCertificate == null) { // this is if certificate was stored only in passive area - String stored = (String) manager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); - if (StringUtil.isEmptyOrSpaces(stored)) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "No stored server certificate was found in runtime")); - } - CertificateFactory cf; - try { - cf = CertificateFactory.getInstance("X509"); - final byte[] buffer = new byte[stored.length()]; - SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer); - myCertificate = cf.generateCertificate(new ByteArrayInputStream(buffer)); - } - catch (CertificateException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e)); - } - myCertificateRealm = myRealm; - } - if (myTempDirectory != null && myCertificate != null) { - if (! (myCertificate instanceof X509Certificate)) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not store server certificate: " + myCertificate)); - } - X509Certificate x509Certificate = (X509Certificate) myCertificate; - String stored; - try { - stored = SVNBase64.byteArrayToBase64(x509Certificate.getEncoded()); - } - catch (CertificateEncodingException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); - } - int failures = SVNSSLUtil.getServerCertificateFailures(x509Certificate, myUrl.getHost()); - storeServerCertificate(myTempDirectory, myCertificateRealm, stored, failures); - if (myAuthentication != null) { - final String realm = myCredentialsRealm == null ? myCertificateRealm : myCredentialsRealm; - return storeCredentials(manager, myAuthentication, realm); - } - } - return true; - } - - private void storeServerCertificate(final File configDir, String realm, String data, int failures) throws SVNException { - //noinspection ResultOfMethodCallIgnored - configDir.mkdirs(); - - File file = new File(configDir, "auth/svn.ssl.server/" + SVNFileUtil.computeChecksum(realm)); - SVNHashMap map = new SVNHashMap(); - map.put("ascii_cert", data); - map.put("svn:realmstring", realm); - map.put("failures", Integer.toString(failures)); - - SVNFileUtil.deleteFile(file); - - File tmpFile = SVNFileUtil.createUniqueFile(configDir, "auth", ".tmp", true); - try { - SVNWCProperties.setProperties(SVNProperties.wrap(map), file, tmpFile, SVNWCProperties.SVN_HASH_TERMINATOR); - } finally { - SVNFileUtil.deleteFile(tmpFile); - } - } - } - - private boolean storeCredentials(SvnAuthenticationManager manager, final SVNAuthentication authentication, final String realm) throws SVNException { - try { - final String kind = getFromType(authentication); - if (! acknowledgeSSL(manager, authentication)) { - manager.acknowledgeAuthentication(true, kind, realm, null, authentication, authentication.getURL()); - } - } catch (SvnAuthenticationManager.CredentialsSavedException e) { - return e.isSuccess(); - } - return true; - } - - private class CredentialsAuthenticator extends AbstractAuthenticator { - private String myKind; - private String myRealm; - // sometimes realm string is different (with <>), so store credentials for both strings.. - private String myRealm2; - private SVNURL myUrl; - private SVNAuthentication myAuthentication; - - protected CredentialsAuthenticator(SvnVcs vcs) { - super(vcs); - } - - public boolean tryAuthenticate(String realm, SVNURL url, boolean previousFailed, boolean passwordRequest) { - realm = realm == null ? url.getHost() : realm; - myRealm = realm; - myUrl = url; - final List kinds = getKinds(url, passwordRequest); - for (String kind : kinds) { - myKind = kind; - if (! tryAuthenticate()) { - return false; - } - } - return true; - } - - @Override - protected SVNAuthentication getWithPassive(SvnAuthenticationManager passive) throws SVNException { - final SVNAuthentication impl = getWithPassiveImpl(passive); - if (impl != null && ! checkAuthOk(impl)) { - clearPassiveCredentials(myRealm, myUrl, impl instanceof SVNPasswordAuthentication); //clear passive also take into acconut ssl filepath - return null; - } - return impl; - } - - private SVNAuthentication getWithPassiveImpl(SvnAuthenticationManager passive) throws SVNException { - try { - return passive.getFirstAuthentication(myKind, myRealm, myUrl); - } catch (SVNCancelException e) { - return null; - } - } - - private boolean checkAuthOk(SVNAuthentication authentication) { - if (authentication instanceof SVNPasswordAuthentication && StringUtil.isEmptyOrSpaces(authentication.getUserName())) return false; - if (authentication instanceof SVNSSLAuthentication) { - if (StringUtil.isEmptyOrSpaces(((SVNSSLAuthentication)authentication).getPassword())) return false; - } - return true; - } - - @Override - protected SVNAuthentication getWithActive(final SvnAuthenticationManager active) throws SVNException { - if (ISVNAuthenticationManager.SSL.equals(myKind)) { - doWithSubscribeToAuthProvider(new SvnAuthenticationManager.ISVNAuthenticationProviderListener() { - @Override - public void requestClientAuthentication(String kind, - SVNURL url, - String realm, - SVNErrorMessage errorMessage, - SVNAuthentication previousAuth, - boolean authMayBeStored, - SVNAuthentication authentication) { - if (!myUrl.equals(url)) return; - myAuthentication = authentication; - myRealm2 = realm; - myStoreInUsual = myAuthentication != null && myAuthentication.isStorageAllowed(); - } - - @Override - public void acceptServerAuthentication(SVNURL url, - String realm, - Object certificate, - boolean resultMayBeStored, - int accepted) { - } - }, new ThrowableRunnable() { - @Override - public void run() throws SVNException { - myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - } - } - ); - if (myAuthentication != null) return myAuthentication; - } - myAuthentication = active.getProvider().requestClientAuthentication(myKind, myUrl, myRealm, null, null, true); - myStoreInUsual = myTempDirectory == null && myAuthentication != null && myAuthentication.isStorageAllowed(); - return myAuthentication; - } - - @Override - protected boolean acknowledge(SvnAuthenticationManager manager, SVNAuthentication svnAuthentication) throws SVNException { - if (! StringUtil.isEmptyOrSpaces(myRealm2) && ! myRealm2.equals(myRealm)) { - storeCredentials(manager, svnAuthentication, myRealm2); - } - return storeCredentials(manager, svnAuthentication, myRealm); - } - } - - private boolean acknowledgeSSL(SvnAuthenticationManager manager, SVNAuthentication svnAuthentication) throws SVNException { - if (svnAuthentication instanceof SVNSSLAuthentication && (((SVNSSLAuthentication) svnAuthentication).getCertificateFile() != null)) { - manager.acknowledgeForSSL(true, getFromType(svnAuthentication), - ((SVNSSLAuthentication) svnAuthentication).getCertificateFile().getPath(), - null, svnAuthentication); - manager.acknowledgeAuthentication(true, getFromType(svnAuthentication), - ((SVNSSLAuthentication) svnAuthentication).getCertificateFile().getPath(), - null, svnAuthentication, svnAuthentication.getURL()); - return true; - } - return false; - } - - private static List getKinds(final SVNURL url, boolean passwordRequest) { - if (passwordRequest || "http".equals(url.getProtocol())) { - return Collections.singletonList(ISVNAuthenticationManager.PASSWORD); - } else if ("https".equals(url.getProtocol())) { - return Collections.singletonList(ISVNAuthenticationManager.SSL); - } else if ("svn".equals(url.getProtocol())) { - return Collections.singletonList(ISVNAuthenticationManager.PASSWORD); - } else if (url.getProtocol().contains("svn+")) { // todo +- - return Arrays.asList(ISVNAuthenticationManager.SSH, ISVNAuthenticationManager.USERNAME); - } else if ("file".equals(url.getProtocol())) { - return Collections.singletonList(ISVNAuthenticationManager.USERNAME); - } - return Collections.singletonList(ISVNAuthenticationManager.USERNAME); - } - - @Nullable - @Override - public File getSpecialConfigDir() { - return myTempDirectory != null ? myTempDirectory : new File(myConfiguration.getConfigurationDirectory()); - } - - private String getFromType(SVNAuthentication authentication) { - if (authentication instanceof SVNPasswordAuthentication) { - return ISVNAuthenticationManager.PASSWORD; - } - if (authentication instanceof SVNSSHAuthentication) { - return ISVNAuthenticationManager.SSH; - } - if (authentication instanceof SVNSSLAuthentication) { - return ISVNAuthenticationManager.SSL; - } - if (authentication instanceof SVNUserNameAuthentication) { - return ISVNAuthenticationManager.USERNAME; - } - throw new IllegalArgumentException(); - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/ImportClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/ImportClient.java index 2a8a549ab283..d3c273ee1449 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/ImportClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/ImportClient.java @@ -4,7 +4,6 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.SvnClient; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNCommitHandler; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java index ccfb81efc98c..6fb1b95b7afe 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java @@ -24,8 +24,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.AbstractFilterChildren; import com.intellij.openapi.vcs.CheckinProjectPanel; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; @@ -38,20 +36,23 @@ import com.intellij.openapi.vcs.ui.RefreshableOnComponent; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.*; +import com.intellij.util.FunctionUtil; +import com.intellij.util.NullableFunction; +import com.intellij.util.PairConsumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; -import org.jetbrains.idea.svn.commandLine.SvnCommitRunner; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; import javax.swing.*; import java.awt.*; @@ -83,103 +84,54 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { return null; } - - private List commitInt(List paths, final String comment, final boolean force, final Set feedback) { + private List commitInt(List paths, final String comment, final Set feedback) { final List exception = new ArrayList(); final List committables = getCommitables(paths); - - final SVNCommitClient committer = mySvnVcs.createCommitClient(); - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - IdeaCommitHandler handler = new IdeaCommitHandler(progress, true, true); - if (progress != null) { - committer.setEventHandler(handler); - } if (progress != null) { - doCommit(committables, committer, comment, force, exception, feedback); + doCommit(committables, comment, exception, feedback); } else if (ApplicationManager.getApplication().isDispatchThread()) { ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { public void run() { - doCommit(committables, committer, comment, force, exception, feedback); + doCommit(committables, comment, exception, feedback); } }, SvnBundle.message("progress.title.commit"), false, mySvnVcs.getProject()); } else { - doCommit(committables, committer, comment, force, exception, feedback); + doCommit(committables, comment, exception, feedback); } - // TODO: Check if such processing of deleted files also necessary for command line. And if yes - use one handler instance for both - // TODO: SVNKit and command line code flows. - for(VirtualFile f : handler.getDeletedFiles()) { - f.putUserData(VirtualFile.REQUESTOR_MARKER, this); - } return exception; } - private void doCommit(List committables, - SVNCommitClient committer, - String comment, - boolean force, - List exception, final Set feedback) { + private void doCommit(List committables, String comment, List exception, final Set feedback) { //noinspection unchecked final MultiMap,File> map = SvnUtil.splitIntoRepositoriesMap(mySvnVcs, committables, Convertor.SELF); for (Map.Entry, Collection> entry : map.entrySet()) { - doCommitOneRepo(entry.getValue(), committer, comment, force, exception, feedback, entry.getKey().getSecond()); + try { + doCommitOneRepo(entry.getValue(), comment, exception, feedback, entry.getKey().getSecond()); + } + catch (VcsException e) { + LOG.info(e); + exception.add(e); + } } } private void doCommitOneRepo(Collection committables, - SVNCommitClient committer, String comment, - boolean force, - List exception, final Set feedback, final WorkingCopyFormat format) { + List exception, + final Set feedback, + final WorkingCopyFormat format) + throws VcsException { if (committables.isEmpty()) { return; } - // TODO: Create CommitClient and refactor to use common ClientFactory model. - if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || - !WorkingCopyFormat.ONE_DOT_SIX.equals(format) && mySvnVcs.getSvnConfiguration().isCommandLine()) { - doWithCommandLine(committables, comment, exception, feedback); - } - else { - doWithSvnkit(committables, committer, comment, force, exception, feedback); - } - } - private void doWithSvnkit(Collection committables, - SVNCommitClient committer, - String comment, - boolean force, - List exception, Set feedback) { - File[] pathsToCommit = committables.toArray(new File[committables.size()]); - boolean keepLocks = SvnConfiguration.getInstance(mySvnVcs.getProject()).isKeepLocks(); - SVNCommitPacket[] commitPackets = null; - SVNCommitInfo[] results; - try { - commitPackets = committer.doCollectCommitItems(pathsToCommit, keepLocks, force, SVNDepth.EMPTY, true, null); - results = committer.doCommit(commitPackets, keepLocks, comment); - commitPackets = null; - } - catch (SVNException e) { - // exception on collecting commitables. - exception.add(new VcsException(e)); - LOG.info(e); - return; - } - finally { - if (commitPackets != null) { - for (SVNCommitPacket commitPacket : commitPackets) { - try { - commitPacket.dispose(); - } - catch (SVNException e) { - LOG.info(e); - } - } - } - } + SVNCommitInfo[] results = mySvnVcs.getFactory(format).createCheckinClient().commit(committables, comment); + final StringBuilder committedRevisions = new StringBuilder(); for (SVNCommitInfo result : results) { if (result.getErrorMessage() != null) { @@ -197,77 +149,6 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { } } - private void doWithCommandLine(Collection committables, String comment, List exception, Set feedback) { - // if directory renames were used, IDEA reports all files under them as moved, but for svn we can not pass some of them - // to commit command - since not all paths are registered as changes -> so we need to filter these cases, but only if - // there at least some child-parent relationships in passed paths - try { - committables = filterCommittables(committables); - } catch (SVNException e) { - exception.add(new VcsException(e)); - return; - } - - IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator()); - - try { - SvnCommitRunner runner = new SvnCommitRunner(mySvnVcs, handler); - final long revision = - runner.commit(ArrayUtil.toObjectArray(committables, File.class), comment, SVNDepth.EMPTY, false, false, null, null); - - reportCommittedRevisions(feedback, String.valueOf(revision)); - } - catch (VcsException e) { - exception.add(e); - } - } - - private Collection filterCommittables(Collection committables) throws SVNException { - final Set childrenOfSomebody = ContainerUtil.newHashSet(); - new AbstractFilterChildren() { - @Override - protected void sortAscending(List list) { - Collections.sort(list); - } - - @Override - protected boolean isAncestor(File parent, File child) { - // strict here will ensure that for case insensitive file systems paths different only by case will not be treated as ancestors - // of each other which is what we need to perform renames from one case to another on Windows - final boolean isAncestor = FileUtil.isAncestor(parent, child, true); - if (isAncestor) { - childrenOfSomebody.add(child.getPath()); - } - return isAncestor; - } - }.doFilter(new ArrayList(committables)); - if (! childrenOfSomebody.isEmpty()) { - List result = ContainerUtil.newArrayList(); - SvnCommandLineStatusClient statusClient = new SvnCommandLineStatusClient(mySvnVcs); - - for (File file : committables) { - if (!childrenOfSomebody.contains(file.getPath())) { - result.add(file); - } else { - try { - final SVNStatus status = statusClient.doStatus(file, false); - if (status != null && ! SVNStatusType.STATUS_NONE.equals(status.getContentsStatus()) && - ! SVNStatusType.STATUS_UNVERSIONED.equals(status.getContentsStatus())) { - result.add(file); - } - } - catch (SVNException e) { - // not versioned - LOG.info(e); - throw e; - } - } - } - return result; - } - return committables; - } - private void reportCommittedRevisions(Set feedback, String committedRevisions) { final Project project = mySvnVcs.getProject(); final String message = SvnBundle.message("status.text.comitted.revision", committedRevisions); @@ -372,7 +253,7 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { String preparedComment, @NotNull NullableFunction parametersHolder, Set feedback) { - return commitInt(collectPaths(changes), preparedComment, true, feedback); + return commitInt(collectPaths(changes), preparedComment, feedback); } public List commit(List changes, String preparedComment) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitCheckinClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitCheckinClient.java new file mode 100644 index 000000000000..9cd423cd530b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitCheckinClient.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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.checkin; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.SVNCommitInfo; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNCommitClient; +import org.tmatesoft.svn.core.wc.SVNCommitPacket; + +import java.io.File; +import java.util.Collection; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitCheckinClient extends BaseSvnClient implements CheckinClient { + + private static final Logger LOG = Logger.getInstance(SvnKitCheckinClient.class); + + @NotNull + @Override + public SVNCommitInfo[] commit(@NotNull Collection paths, @NotNull String comment) throws VcsException { + File[] pathsToCommit = ArrayUtil.toObjectArray(paths, File.class); + boolean keepLocks = myVcs.getSvnConfiguration().isKeepLocks(); + SVNCommitPacket[] commitPackets = null; + SVNCommitInfo[] results; + SVNCommitClient committer = myVcs.createCommitClient(); + IdeaCommitHandler handler = new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator(), true, true); + + committer.setEventHandler(handler); + try { + commitPackets = committer.doCollectCommitItems(pathsToCommit, keepLocks, true, SVNDepth.EMPTY, true, null); + results = committer.doCommit(commitPackets, keepLocks, comment); + commitPackets = null; + } + catch (SVNException e) { + throw new SvnBindException(e); + } + finally { + if (commitPackets != null) { + for (SVNCommitPacket commitPacket : commitPackets) { + try { + commitPacket.dispose(); + } + catch (SVNException e) { + LOG.info(e); + } + } + } + } + + // This seems to be necessary only for SVNKit as changes after command line operations should be detected during VFS refresh. + for (VirtualFile f : handler.getDeletedFiles()) { + f.putUserData(VirtualFile.REQUESTOR_MARKER, this); + } + + return results; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitImportClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitImportClient.java index 838e7646a310..e2ee8d988bd4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitImportClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnKitImportClient.java @@ -4,7 +4,6 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java index 6e151e4a6ae8..b292e43b9c1b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java @@ -61,7 +61,7 @@ public class CmdCheckoutClient extends BaseSvnClient implements CheckoutClient { @NotNull List parameters) throws VcsException { BaseUpdateCommandListener listener = new BaseUpdateCommandListener(destination, handler); - CommandUtil.execute(myVcs, source, SvnCommandName.checkout, parameters, listener); + execute(myVcs, source, SvnCommandName.checkout, parameters, listener); listener.throwWrappedIfException(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdExportClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdExportClient.java index 6bff70f5236d..1e4011378fb7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdExportClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdExportClient.java @@ -46,7 +46,7 @@ public class CmdExportClient extends BaseSvnClient implements ExportClient { BaseUpdateCommandListener listener = new BaseUpdateCommandListener(to, handler); - CommandUtil.execute(myVcs, from, to, SvnCommandName.export, parameters, listener); + execute(myVcs, from, to, SvnCommandName.export, parameters, listener); listener.throwWrappedIfException(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java index 92d964e3a208..634d528c17f1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java @@ -45,7 +45,7 @@ import org.jetbrains.idea.svn.actions.ExclusiveBackgroundVcsAction; import org.jetbrains.idea.svn.actions.SvnExcludingIgnoredOperation; import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.checkin.IdeaCommitHandler; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; +import org.jetbrains.idea.svn.checkin.CommitEventHandler; import org.jetbrains.idea.svn.dialogs.CheckoutDialog; import org.jetbrains.idea.svn.dialogs.UpgradeFormatDialog; import org.tmatesoft.svn.core.SVNCancelException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/cleanup/CmdCleanupClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/cleanup/CmdCleanupClient.java index 41eed4ccacc5..6ee1763d4120 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/cleanup/CmdCleanupClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/cleanup/CmdCleanupClient.java @@ -24,6 +24,6 @@ public class CmdCleanupClient extends BaseSvnClient implements CleanupClient { List parameters = new ArrayList(); CommandUtil.put(parameters, path); - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.cleanup, parameters, null); + execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.cleanup, parameters, null); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java index ba6099aae955..11fb831803bb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java @@ -22,7 +22,6 @@ import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.SVNAuthentication; import java.io.File; -import java.io.IOException; import java.net.PasswordAuthentication; /** @@ -46,9 +45,9 @@ public interface AuthenticationCallback { /** * Authenticate for realm and base file belonging to corresponding working copy * - * @param realm - realm that should be used for credential retrieval/storage. + * @param realm - realm that should be used for credential retrieval/storage. * @param repositoryUrl - * @param previousFailed - whether previous credentials were correct + * @param previousFailed - whether previous credentials were correct * @param passwordRequest - if true, password should be asked. Otherwise that may be a certificate (determined by the protocol) * @return false if authentication canceled or was unsuccessful */ @@ -59,7 +58,7 @@ public interface AuthenticationCallback { * For instance, username/password for http/svn protocols. SSL client certificate for two way SSL protocol. * * @param repositoryUrl - * @param type authentication protocol type with svn specific values, like "svn.simple" for http. + * @param type authentication protocol type with svn specific values, like "svn.simple" for http. * @return */ @Nullable @@ -80,7 +79,7 @@ public interface AuthenticationCallback { * Ask user or read from memory storage whether server certificate should be accepted * * @param repositoryUrl - * @param realm - realm that should be used for credential retrieval/storage. + * @param realm - realm that should be used for credential retrieval/storage. * @return true is certificate was accepted */ boolean acceptSSLServerCertificate(SVNURL repositoryUrl, final String realm); @@ -88,9 +87,9 @@ public interface AuthenticationCallback { /** * Clear credentials stored anywhere - in case they were not full, wrong or anything else * - * @param realm - required that credential + * @param realm - required that credential * @param repositoryUrl - * @param password - whether password credential should be deleted or certificate, if protocol might demand certificate + * @param password - whether password credential should be deleted or certificate, if protocol might demand certificate */ void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password); @@ -101,27 +100,8 @@ public interface AuthenticationCallback { */ boolean haveDataForTmpConfig(); - /** - * writes IDEA config settings (that should be written) into tmp config directory - * (now it's IDEA proxy settings) - * - * @param repositoryUrl - * @return true if have written data, false if wasn't able to determine parameters etc - * @throws IOException - */ - boolean persistDataToTmpConfig(SVNURL repositoryUrl) throws IOException; - @Nullable PasswordAuthentication getProxyAuthentication(@NotNull SVNURL repositoryUrl); - /** - * Ask for IDEA-defined proxy credentials, using standard authenticator - * Store data into tmp config - * - * @param repositoryUrl - * @return false if authentication was canceled or related calculations were unsuccessful - */ - boolean askProxyCredentials(SVNURL repositoryUrl); - void reset(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java index a25c9caecbff..5cc66e0a3983 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandRuntime.java @@ -24,6 +24,7 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; import org.tmatesoft.svn.core.SVNURL; import java.io.File; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java index 0b529a954415..0ec9589b5129 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java @@ -2,11 +2,8 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.wc.SVNDiffOptions; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -170,42 +167,6 @@ public class CommandUtil { return (T) unmarshaller.unmarshal(new StringReader(data.trim())); } - /** - * Utility method for running commands. - * // TODO: Should be replaced with non-static analogue. - * - * @param vcs - * @param target - * @param name - * @param parameters - * @param listener - * @throws VcsException - */ - public static CommandExecutor execute(@NotNull SvnVcs vcs, - @NotNull SvnTarget target, - @NotNull SvnCommandName name, - @NotNull List parameters, - @Nullable LineCommandListener listener) throws VcsException { - return execute(vcs, target, null, name, parameters, listener); - } - - public static CommandExecutor execute(@NotNull SvnVcs vcs, - @NotNull SvnTarget target, - @Nullable File workingDirectory, - @NotNull SvnCommandName name, - @NotNull List parameters, - @Nullable LineCommandListener listener) throws VcsException { - Command command = new Command(name); - - command.setTarget(target); - command.setWorkingDirectory(workingDirectory); - command.setResultBuilder(listener); - command.put(parameters); - - CommandRuntime runtime = new CommandRuntime(vcs, new IdeaSvnkitBasedAuthenticationCallback(vcs)); - return runtime.runWithAuthenticationAttempt(command); - } - @NotNull public static File getHomeDirectory() { return new File(PathManager.getHomePath()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/ProxyModule.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/ProxyModule.java index 421a7346b6de..11bb9b654523 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/ProxyModule.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/ProxyModule.java @@ -19,8 +19,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.IdeaSVNConfigFile; -import org.jetbrains.idea.svn.SvnAuthenticationManager; -import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; +import org.jetbrains.idea.svn.auth.IdeaSvnkitBasedAuthenticationCallback; import org.tmatesoft.svn.core.SVNURL; import java.net.InetSocketAddress; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java index 1976a31e311d..80c1f4078f85 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java @@ -18,17 +18,16 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; -import com.intellij.util.LineSeparator; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.hash.HashMap; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnUtil; import org.tmatesoft.svn.core.SVNErrorCode; +import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; -import java.util.Map; import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. @@ -43,13 +42,15 @@ public class SvnBindException extends VcsException { public static final int ERROR_BASE = 120000; public static final int CATEGORY_SIZE = 5000; - private Map errors = new HashMap(); + @NotNull private final MultiMap errors = MultiMap.create(); + @NotNull private final MultiMap warnings = MultiMap.create(); public SvnBindException(String message) { super(message); if (!StringUtil.isEmpty(message)) { - parseErrors(message); + parse(message, SvnUtil.ERROR_PATTERN, errors); + parse(message, SvnUtil.WARNING_PATTERN, warnings); } } @@ -59,50 +60,42 @@ public class SvnBindException extends VcsException { if (throwable instanceof SVNException) { SVNException e = (SVNException)throwable; int code = e.getErrorMessage().getErrorCode().getCode(); + int type = e.getErrorMessage().getType(); - put(code, e.getMessage()); + (type == SVNErrorMessage.TYPE_ERROR ? errors : warnings).putValue(code, e.getMessage()); } } - public boolean contains(int error) { - return errors.containsKey(error); + public boolean contains(int code) { + return errors.containsKey(code) || warnings.containsKey(code); } - public boolean contains(@NotNull SVNErrorCode error) { - return errors.containsKey(error.getCode()); + public boolean contains(@NotNull SVNErrorCode code) { + return contains(code.getCode()); } public boolean containsCategory(int category) { final int categoryCode = getCategoryCode(category); - - return ContainerUtil.exists(errors.keySet(), new Condition() { + Condition belongsToCategoryCondition = new Condition() { @Override public boolean value(Integer code) { return getCategoryCode(code) == categoryCode; } - }); + }; + + return ContainerUtil.exists(errors.keySet(), belongsToCategoryCondition) || + ContainerUtil.exists(warnings.keySet(), belongsToCategoryCondition); } private static int getCategoryCode(int category) { return (category - ERROR_BASE) / CATEGORY_SIZE; } - private void parseErrors(@NotNull String message) { - Matcher matcher = SvnUtil.ERROR_PATTERN.matcher(message); + private static void parse(@NotNull String message, @NotNull Pattern pattern, @NotNull MultiMap map) { + Matcher matcher = pattern.matcher(message); while (matcher.find()) { - put(Integer.valueOf(matcher.group(2)), matcher.group()); - } - } - - private void put(int code, @Nullable String message) { - if (errors.containsKey(code)) { - if (!StringUtil.isEmpty(message)) { - errors.put(code, errors.get(code) + LineSeparator.LF.getSeparatorString() + message); - } - } - else { - errors.put(code, message); + map.putValue(Integer.valueOf(matcher.group(2)), matcher.group()); } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java index c31ce2355276..5bf58891c34d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java @@ -52,7 +52,9 @@ public enum SvnCommandName { export("export", false), upgrade("upgrade", true), list("list", false), - diff("diff", false); + diff("diff", false), + // currently used to create only folders directly in repository - so writable = false + mkdir("mkdir", false); private final String myName; private final boolean myWriteable; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnExceptionWrapper.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnExceptionWrapper.java similarity index 97% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnExceptionWrapper.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnExceptionWrapper.java index d9c6202d23a6..cd451e149d09 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnExceptionWrapper.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnExceptionWrapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.commandLine; import org.tmatesoft.svn.core.SVNException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/RepositoryUrlFilter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/RepositoryUrlFilter.java index b9bb3b717012..0dd0677e470c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/RepositoryUrlFilter.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/RepositoryUrlFilter.java @@ -16,7 +16,7 @@ package org.jetbrains.idea.svn.config; import org.jetbrains.idea.svn.SvnApplicationSettings; -import org.jetbrains.idea.svn.SvnAuthenticationManager; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; import java.util.ArrayList; import java.util.Collection; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java index 64ae7ffcf6b1..39a222dbeaf3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java @@ -35,6 +35,6 @@ public class CmdConflictClient extends BaseSvnClient implements ConflictClient { // for now parsing of the output is not required as command is executed only for one file // and will be either successful or exception will be thrown - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.resolve, parameters, null); + execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.resolve, parameters, null); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java index 17b195eb927e..2995cb40fcbb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java @@ -30,7 +30,7 @@ public class CmdContentClient extends BaseSvnClient implements ContentClient { CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision); CommandUtil.put(parameters, revision); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.cat, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.cat, parameters, null); byte[] bytes = command.getBinaryOutput().toByteArray(); ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java index 832141d254f8..e73e9fb4013f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java @@ -6,7 +6,11 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; -import org.jetbrains.idea.svn.commandLine.*; +import org.jetbrains.idea.svn.checkin.CmdCheckinClient; +import org.jetbrains.idea.svn.checkin.CommitEventHandler; +import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -34,22 +38,21 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient { // directory being renamed will be blocked by svn process File workingDirectory = isMove && !SystemInfo.isFileSystemCaseSensitive && FileUtil.filesEqual(src, dst) ? CommandUtil.getHomeDirectory() : null; - CommandUtil - .execute(myVcs, SvnTarget.fromFile(dst), workingDirectory, isMove ? SvnCommandName.move : SvnCommandName.copy, parameters, null); + execute(myVcs, SvnTarget.fromFile(dst), workingDirectory, getCommandName(isMove), parameters, null); } @Override public long copy(@NotNull SvnTarget source, - @NotNull SvnTarget destination, - @Nullable SVNRevision revision, - boolean makeParents, - @NotNull String message, - @Nullable CommitEventHandler handler) throws VcsException { + @NotNull SvnTarget destination, + @Nullable SVNRevision revision, + boolean makeParents, + boolean isMove, + @NotNull String message, + @Nullable CommitEventHandler handler) throws VcsException { if (!destination.isURL()) { throw new IllegalArgumentException("Only urls are supported as destination " + destination); } - // TODO: Check that command fails when destination exists List parameters = new ArrayList(); CommandUtil.put(parameters, source); @@ -61,12 +64,11 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient { // copy to url output is the same as commit output - just statuses have "copy of" suffix // so "Adding" will be "Adding copy of" - SvnCommitRunner.CommandListener listener = new SvnCommitRunner.CommandListener(handler); - // TODO: Check correctness when source is url + CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(handler); if (source.isFile()) { listener.setBaseDirectory(source.getFile()); } - CommandUtil.execute(myVcs, source, SvnCommandName.copy, parameters, listener); + execute(myVcs, source, getCommandName(isMove), parameters, listener); return listener.getCommittedRevision(); } @@ -87,8 +89,13 @@ public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient { File workingDirectory = CommandUtil.getHomeDirectory(); BaseUpdateCommandListener listener = new BaseUpdateCommandListener(workingDirectory, handler); - CommandUtil.execute(myVcs, source, workingDirectory, SvnCommandName.copy, parameters, listener); + execute(myVcs, source, workingDirectory, SvnCommandName.copy, parameters, listener); listener.throwWrappedIfException(); } + + @NotNull + private static SvnCommandName getCommandName(boolean isMove) { + return isMove ? SvnCommandName.move : SvnCommandName.copy; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java index e8f49d869ba4..dd4395f612b6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java @@ -4,7 +4,7 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.SvnClient; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; +import org.jetbrains.idea.svn.checkin.CommitEventHandler; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -23,6 +23,7 @@ public interface CopyMoveClient extends SvnClient { * @param destination * @param revision * @param makeParents + * @param isMove * @param message * @param handler * @return new revision number @@ -32,6 +33,7 @@ public interface CopyMoveClient extends SvnClient { @NotNull SvnTarget destination, @Nullable SVNRevision revision, boolean makeParents, + boolean isMove, @NotNull String message, @Nullable CommitEventHandler handler) throws VcsException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java index 1f9f7d9b8fe1..0888a546e892 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java @@ -4,7 +4,7 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; -import org.jetbrains.idea.svn.commandLine.CommitEventHandler; +import org.jetbrains.idea.svn.checkin.CommitEventHandler; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNException; @@ -40,6 +40,7 @@ public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClien @NotNull SvnTarget destination, @Nullable SVNRevision revision, boolean makeParents, + boolean isMove, @NotNull String message, @Nullable CommitEventHandler handler) throws VcsException { @@ -53,7 +54,8 @@ public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClien SVNCommitInfo info; try { - info = client.doCopy(new SVNCopySource[]{copySource}, destination.getURL(), false, makeParents, true, message, null); + info = client + .doCopy(new SVNCopySource[]{copySource}, destination.getURL(), isMove, makeParents, true, message, null); } catch (SVNException e) { throw new VcsException(e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java index b0d2eae487be..dfd6d72cc0f9 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java @@ -1,12 +1,15 @@ package org.jetbrains.idea.svn.delete; import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.checkin.CmdCheckinClient; import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -32,9 +35,25 @@ public class CmdDeleteClient extends BaseSvnClient implements DeleteClient { File workingDirectory = CommandUtil.getHomeDirectory(); BaseUpdateCommandListener listener = new BaseUpdateCommandListener(workingDirectory, handler); - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), workingDirectory, SvnCommandName.delete, parameters, listener); + execute(myVcs, SvnTarget.fromFile(path), workingDirectory, SvnCommandName.delete, parameters, listener); listener.throwWrappedIfException(); } } + + @Override + public long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException { + SvnTarget target = SvnTarget.fromURL(url); + List parameters = ContainerUtil.newArrayList(); + + CommandUtil.put(parameters, target); + parameters.add("--message"); + parameters.add(message); + + CmdCheckinClient.CommandListener listener = new CmdCheckinClient.CommandListener(null); + + execute(myVcs, target, SvnCommandName.delete, parameters, listener); + + return listener.getCommittedRevision(); + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java index 013696e2cf6c..ec6f01481d44 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java @@ -4,6 +4,7 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import java.io.File; @@ -14,4 +15,6 @@ import java.io.File; public interface DeleteClient extends SvnClient { void delete(@NotNull File path, boolean force, boolean dryRun, @Nullable ISVNEventHandler handler) throws VcsException; + + long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java index c433b970215d..940f36e7b731 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java @@ -4,7 +4,10 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNWCClient; @@ -27,4 +30,16 @@ public class SvnKitDeleteClient extends BaseSvnClient implements DeleteClient { throw new VcsException(e); } } + + @Override + public long delete(@NotNull SVNURL url, @NotNull String message) throws VcsException { + try { + SVNCommitInfo commitInfo = myVcs.createCommitClient().doDelete(new SVNURL[]{url}, message); + + return commitInfo.getNewRevision(); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java index e0101053ad80..37dd5a5230f4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java @@ -357,7 +357,7 @@ public class CopiesPanel { SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() { @Override public void branchSelected(Project project, SvnBranchConfigurationNew configuration, String url, long revision) { - new QuickMerge(project, url, wcInfo, SVNPathUtil.tail(url), root).execute(new QuickMergeInteractionImpl(myProject)); + new QuickMerge(new MergeContext(myVcs, url, wcInfo, SVNPathUtil.tail(url), root)).execute(new QuickMergeInteractionImpl(myProject)); } }, "Select branch", mergeLabel); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java index 6591d9872ca8..880f6935d157 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java @@ -25,14 +25,13 @@ import com.intellij.util.PairConsumer; import com.intellij.util.continuation.ContinuationContext; import com.intellij.util.continuation.TaskDescriptor; import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; import org.jetbrains.idea.svn.history.TreeStructureNode; import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; @@ -45,7 +44,7 @@ import java.util.List; * Date: 3/30/13 * Time: 7:40 PM */ -class LoadRecentBranchRevisions extends TaskDescriptor { +public class LoadRecentBranchRevisions extends TaskDescriptor { public static final String PROP_BUNCH_SIZE = "idea.svn.quick.merge.bunch.size"; private final static int BUNCH_SIZE = 100; private int myBunchSize; @@ -53,21 +52,17 @@ class LoadRecentBranchRevisions extends TaskDescriptor { private boolean myLastLoaded; private OneShotMergeInfoHelper myHelper; private List myCommittedChangeLists; - private final WCInfo myWcInfo; - private final SvnVcs myVcs; - private final String mySourceUrl; private final Integer myTestBunchSize; + @NotNull private final MergeContext myMergeContext; - LoadRecentBranchRevisions(String branchName, long first, WCInfo info, SvnVcs vcs, String url) { - this(branchName, first, info, vcs, url, -1); + public LoadRecentBranchRevisions(@NotNull MergeContext mergeContext, long first) { + this(mergeContext, first, -1); } - LoadRecentBranchRevisions(String branchName, long first, WCInfo info, SvnVcs vcs, String url, final int bunchSize) { - super("Loading recent " + branchName + " revisions", Where.POOLED); + public LoadRecentBranchRevisions(@NotNull MergeContext mergeContext, long first, int bunchSize) { + super("Loading recent " + mergeContext.getBranchName() + " revisions", Where.POOLED); + myMergeContext = mergeContext; myFirst = first; - myWcInfo = info; - myVcs = vcs; - mySourceUrl = url; // for test purposes!!! myTestBunchSize = Integer.getInteger(PROP_BUNCH_SIZE); if (myTestBunchSize != null) { @@ -84,22 +79,24 @@ class LoadRecentBranchRevisions extends TaskDescriptor { @Override public void run(ContinuationContext context) { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - final SvnCommittedChangesProvider committedChangesProvider = (SvnCommittedChangesProvider)myVcs.getCommittedChangesProvider(); + final SvnCommittedChangesProvider committedChangesProvider = + (SvnCommittedChangesProvider)myMergeContext.getVcs().getCommittedChangesProvider(); final ChangeBrowserSettings settings = new ChangeBrowserSettings(); if (myFirst > 0){ settings.CHANGE_BEFORE = String.valueOf(myFirst); settings.USE_CHANGE_BEFORE_FILTER = true; } - String local = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl()); + String local = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getWcInfo().getRootUrl()); final String relativeLocal = (local.startsWith("/") ? local : "/" + local); - String relativeBranch = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), mySourceUrl); + String relativeBranch = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl()); relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch); - ProgressManager.progress2(SvnBundle.message("progress.text2.collecting.history", mySourceUrl + (myFirst > 0 ? ("@" + myFirst) : ""))); + ProgressManager.progress2( + SvnBundle.message("progress.text2.collecting.history", myMergeContext.getSourceUrl() + (myFirst > 0 ? ("@" + myFirst) : ""))); final List>> list = new ArrayList>>(); try { - committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mySourceUrl), + committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(myMergeContext.getSourceUrl()), myBunchSize + (myFirst > 0 ? 2 : 1), new PairConsumer>() { public void consume(SvnChangeList svnList, TreeStructureNode tree) { @@ -123,7 +120,7 @@ class LoadRecentBranchRevisions extends TaskDescriptor { } try { - myHelper = new OneShotMergeInfoHelper(myVcs.getProject(), myWcInfo, mySourceUrl); + myHelper = new OneShotMergeInfoHelper(myMergeContext); ProgressManager.progress2("Calculating not merged revisions"); myHelper.prepare(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeContext.java new file mode 100644 index 000000000000..bb60266aa85d --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeContext.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 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.dialogs; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeContext { + + @NotNull private final Project myProject; + @NotNull private final String myBranchName; + @NotNull private final VirtualFile myRoot; + @NotNull private final WCInfo myWcInfo; + @NotNull private String mySourceUrl; + @NotNull private final SvnVcs myVcs; + @NotNull private final String myTitle; + + public MergeContext(@NotNull SvnVcs vcs, + @NotNull String sourceUrl, + @NotNull WCInfo wcInfo, + @NotNull String branchName, + @NotNull VirtualFile root) { + myVcs = vcs; + myProject = vcs.getProject(); + myBranchName = branchName; + myRoot = root; + mySourceUrl = sourceUrl; + myWcInfo = wcInfo; + myTitle = "Merge from " + myBranchName; + } + + @NotNull + public Project getProject() { + return myProject; + } + + @NotNull + public String getBranchName() { + return myBranchName; + } + + @NotNull + public VirtualFile getRoot() { + return myRoot; + } + + @NotNull + public WCInfo getWcInfo() { + return myWcInfo; + } + + @NotNull + public String getSourceUrl() { + return mySourceUrl; + } + + @NotNull + public SvnVcs getVcs() { + return myVcs; + } + + @NotNull + public String getTitle() { + return myTitle; + } + + public void setSourceUrl(@NotNull String sourceUrl) { + mySourceUrl = sourceUrl; + } +} \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java index 230a057f6800..d699a3a53153 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java @@ -15,167 +15,46 @@ */ package org.jetbrains.idea.svn.dialogs; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.popup.util.PopupUtil; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; -import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; -import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.RefreshSessionImpl; +import com.intellij.openapi.vcs.CalledInAwt; +import com.intellij.openapi.vcs.VcsException; import com.intellij.util.Consumer; -import com.intellij.util.FilePathByPathComparator; -import com.intellij.util.PairConsumer; -import com.intellij.util.containers.MultiMap; -import com.intellij.util.continuation.*; +import com.intellij.util.continuation.Continuation; +import com.intellij.util.continuation.TaskDescriptor; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.NestedCopyType; -import org.jetbrains.idea.svn.SvnBranchConfigurationManager; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory; -import org.jetbrains.idea.svn.commandLine.SvnBindException; -import org.jetbrains.idea.svn.history.SvnChangeList; -import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; -import org.jetbrains.idea.svn.history.SvnRepositoryLocation; -import org.jetbrains.idea.svn.history.TreeStructureNode; -import org.jetbrains.idea.svn.integrate.*; -import org.jetbrains.idea.svn.mergeinfo.MergeChecker; -import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper; -import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache; -import org.jetbrains.idea.svn.update.UpdateEventHandler; +import org.jetbrains.idea.svn.integrate.CheckRepositorySupportsMergeInfoTask; +import org.jetbrains.idea.svn.integrate.MergeInitChecksTask; +import org.jetbrains.idea.svn.integrate.QuickMergeInteraction; +import org.jetbrains.idea.svn.integrate.SourceUrlCorrectionTask; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNLogEntry; -import org.tmatesoft.svn.core.SVNLogEntryPath; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.internal.util.SVNPathUtil; -import org.tmatesoft.svn.core.internal.util.SVNURLUtil; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; public class QuickMerge { - private final Project myProject; - private final String myBranchName; - private final VirtualFile myRoot; - private WCInfo myWcInfo; - private String mySourceUrl; - private SvnVcs myVcs; - private final String myTitle; + + @NotNull private final MergeContext myMergeContext; private final Continuation myContinuation; private QuickMergeInteraction myInteraction; - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.QuickMerge"); - public QuickMerge(Project project, String sourceUrl, WCInfo wcInfo, final String branchName, final VirtualFile root) { - myProject = project; - myBranchName = branchName; - myRoot = root; - myVcs = SvnVcs.getInstance(project); - mySourceUrl = sourceUrl; - myWcInfo = wcInfo; - myTitle = "Merge from " + myBranchName; - - myContinuation = Continuation.createFragmented(myProject, true); - } - - private class SourceUrlCorrection extends TaskDescriptor { - private SourceUrlCorrection() { - super("Checking branch", Where.POOLED); - } - - @Override - public void run(ContinuationContext continuationContext) { - final SVNURL branch = SvnBranchConfigurationManager.getInstance(myProject).getSvnBranchConfigManager().getWorkingBranchWithReload(myWcInfo.getUrl(), myRoot); - if (branch != null && (! myWcInfo.getUrl().equals(branch))) { - final String branchString = branch.toString(); - if (SVNPathUtil.isAncestor(branchString, myWcInfo.getRootUrl())) { - final String subPath = SVNPathUtil.getRelativePath(branchString, myWcInfo.getRootUrl()); - mySourceUrl = SVNPathUtil.append(mySourceUrl, subPath); - } - } - } - } - - private class MyInitChecks extends TaskDescriptor { - private MyInitChecks() { - super("initial checks", Where.AWT); - } - - @Override - public void run(ContinuationContext continuationContext) { - SVNURL url = parseUrl(continuationContext); - if (url == null) { - return; - } - - if (SVNURLUtil.isAncestor(url, myWcInfo.getUrl()) || SVNURLUtil.isAncestor(myWcInfo.getUrl(), url)) { - finishWithError(continuationContext, "Cannot merge from self", true); - return; - } - - if (! checkForSwitchedRoots()) { - continuationContext.cancelEverything(); - } - } - - @Nullable - private SVNURL parseUrl(ContinuationContext continuationContext) { - SVNURL url = null; - - try { - url = SvnUtil.createUrl(mySourceUrl); - } - catch (SvnBindException e) { - finishWithError(continuationContext, e.getMessage(), true); - } - - return url; - } - } - - private class CheckRepositorySupportsMergeinfo extends TaskDescriptor { - private CheckRepositorySupportsMergeinfo() { - super("Checking repository capabilities", Where.POOLED); - } - - @Override - public void run(ContinuationContext context) { - final List tasks = new LinkedList(); - final boolean supportsMergeinfo = myWcInfo.getFormat().supportsMergeInfo() && SvnUtil.checkRepositoryVersion15(myVcs, mySourceUrl); - if (! supportsMergeinfo) { - insertMergeAll(tasks); - } else { - tasks.add(new MergeAllOrSelectedChooser()); - } - context.next(tasks); - } + public QuickMerge(@NotNull MergeContext mergeContext) { + myMergeContext = mergeContext; + myContinuation = Continuation.createFragmented(mergeContext.getProject(), true); } @CalledInAwt public void execute(@NotNull final QuickMergeInteraction interaction, @NotNull final TaskDescriptor... finalTasks) { myInteraction = interaction; - myInteraction.setTitle(myTitle); + myInteraction.setTitle(myMergeContext.getTitle()); FileDocumentManager.getInstance().saveAllDocuments(); final List tasks = new LinkedList(); - tasks.add(new MyInitChecks()); - tasks.add(new SourceUrlCorrection()); - tasks.add(new CheckRepositorySupportsMergeinfo()); + tasks.add(new MergeInitChecksTask(myMergeContext, myInteraction)); + tasks.add(new SourceUrlCorrectionTask(myMergeContext, myInteraction)); + tasks.add(new CheckRepositorySupportsMergeInfoTask(myMergeContext, myInteraction)); if (finalTasks.length > 0) { tasks.addAll(Arrays.asList(finalTasks)); } @@ -183,13 +62,13 @@ public class QuickMerge { myContinuation.addExceptionHandler(VcsException.class, new Consumer() { @Override public void consume(VcsException e) { - myInteraction.showErrors(myTitle, Collections.singletonList(e)); + myInteraction.showErrors(myMergeContext.getTitle(), Collections.singletonList(e)); } }); myContinuation.addExceptionHandler(SVNException.class, new Consumer() { @Override public void consume(SVNException e) { - myInteraction.showErrors(myTitle, Collections.singletonList(new VcsException(e))); + myInteraction.showErrors(myMergeContext.getTitle(), Collections.singletonList(new VcsException(e))); } }); myContinuation.addExceptionHandler(RuntimeException.class, new Consumer() { @@ -200,664 +79,4 @@ public class QuickMerge { }); myContinuation.run(tasks); } - - private class ShowRecentInDialog extends TaskDescriptor { - private final LoadRecentBranchRevisions myLoader; - - private ShowRecentInDialog(LoadRecentBranchRevisions loader) { - super("", Where.AWT); - myLoader = loader; - } - - @Override - public void run(ContinuationContext context) { - final PairConsumer loader = new PairConsumer() { - @Override - public void consume(Long bunchSize, final MergeDialogI dialog) { - final LoadRecentBranchRevisions loader = - new LoadRecentBranchRevisions(myBranchName, dialog.getLastNumber(), myWcInfo, myVcs, mySourceUrl, bunchSize.intValue()); - final TaskDescriptor updater = new TaskDescriptor("", Where.AWT) { - @Override - public void run(ContinuationContext context) { - dialog.addMoreLists(loader.getCommittedChangeLists()); - if (loader.isLastLoaded()) { - dialog.setEverythingLoaded(true); - } - } - - @Override - public void canceled() { - dialog.addMoreLists(Collections.emptyList()); - dialog.setEverythingLoaded(true); - } - }; - final Continuation fragmented = Continuation.createFragmented(myProject, true); - fragmented.addExceptionHandler(VcsException.class, new Consumer() { - @Override - public void consume(VcsException e) { - PopupUtil.showBalloonForActiveComponent(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), MessageType.ERROR); - } - }); - fragmented.run(loader, updater); - } - }; - final List lists = myInteraction.showRecentListsForSelection(myLoader.getCommittedChangeLists(), - myTitle, myLoader.getHelper(), loader, myLoader.isLastLoaded()); - - if (lists != null && ! lists.isEmpty()){ - final MergerFactory factory = new ChangeListsMergerFactory(lists) { - @Override - public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { - return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false); - } - }; - // fictive branch point, just for - final SvnBranchPointsCalculator.BranchCopyData copyData = - new SvnBranchPointsCalculator.BranchCopyData(myWcInfo.getUrl().toString(), -1, mySourceUrl, -1); - context.next(new LocalChangesPrompt(false, lists, - new SvnBranchPointsCalculator.WrapperInvertor(false, copyData)), - new MergeTask(factory, myTitle)); - } else { - context.cancelEverything(); - } - } - } - - private class MergeAllOrSelectedChooser extends TaskDescriptor { - private MergeAllOrSelectedChooser() { - super("merge source selector", Where.AWT); - } - - @Override - public void run(final ContinuationContext context) { - final QuickMergeContentsVariants variant = myInteraction.selectMergeVariant(); - if (QuickMergeContentsVariants.cancel == variant) return; - if (QuickMergeContentsVariants.all == variant) { - insertMergeAll(context); - return; - } - if (QuickMergeContentsVariants.showLatest == variant) { - final LoadRecentBranchRevisions loader = new LoadRecentBranchRevisions(myBranchName, -1, myWcInfo, myVcs, mySourceUrl); - final ShowRecentInDialog dialog = new ShowRecentInDialog(loader); - context.next(loader, dialog); - return; - } - - final MergeCalculator calculator; - try { - calculator = new MergeCalculator(myWcInfo, mySourceUrl, myBranchName); - } - catch (VcsException e) { - finishWithError(context, e.getMessage(), true); - return; - } - context.next(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask( - myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, calculator), calculator); - } - } - - private void insertMergeAll(final ContinuationContext context) { - final List queue = new ArrayList(); - insertMergeAll(queue); - context.next(queue); - } - - private boolean checkForSwitchedRoots() { - final List infoList = myVcs.getAllWcInfos(); - boolean switchedFound = false; - for (WCInfo wcInfo : infoList) { - if (FileUtil.isAncestor(new File(myWcInfo.getPath()), new File(wcInfo.getPath()), true) - && NestedCopyType.switched.equals(wcInfo.getType())) { - switchedFound = true; - break; - } - } - if (switchedFound) { - return myInteraction.shouldContinueSwitchedRootFound(); - } - return true; - } - - @CalledInAny - private void finishWithError(final ContinuationContext context, final String message, final List exceptions) { - if (exceptions != null) { - for (VcsException exception : exceptions) { - LOG.info(message, exception); - } - } - context.cancelEverything(); - context.next(new TaskDescriptor(message, Where.AWT) { - @Override - public void run(ContinuationContext context) { - myInteraction.showErrors(message, exceptions); - } - }); - } - - @CalledInAny - private void finishWithError(final ContinuationContext context, final String message, final boolean isError) { - LOG.info((isError ? "Error: " : "Info: ") + message); - context.next(new TaskDescriptor(message, Where.AWT) { - @Override - public void run(ContinuationContext context) { - myInteraction.showErrors(message, isError); - context.cancelEverything(); - } - }); - } - - private void insertMergeAll(final List queue) { - queue.add(new LocalChangesPrompt(true, null, null)); - final MergeAllWithBranchCopyPoint mergeAllExecutor = new MergeAllWithBranchCopyPoint(); - queue.add(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask(myWcInfo.getRepositoryRoot(), mySourceUrl, myWcInfo.getRootUrl(), mergeAllExecutor)); - queue.add(mergeAllExecutor); - } - - private class MergeAllWithBranchCopyPoint extends TaskDescriptor implements Consumer, SVNException>> { - private final AtomicReference, SVNException>> myData; - - private MergeAllWithBranchCopyPoint() { - super("merge all", Where.AWT); - myData = new AtomicReference, SVNException>>(); - } - - @Override - public void consume(TransparentlyFailedValueI, SVNException> value) { - myData.set(value); - } - - @Override - public void run(ContinuationContext context) { - SvnBranchPointsCalculator.WrapperInvertor invertor; - try { - final TransparentlyFailedValueI, SVNException> - transparentlyFailedValueI = myData.get(); - if (transparentlyFailedValueI == null) { - finishWithError(context, "Merge start wasn't found", true); - return; - } - invertor = transparentlyFailedValueI.get(); - } - catch (SVNException e) { - finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e))); - return; - } - if (invertor == null) { - finishWithError(context, "Merge start wasn't found", true); - return; - } - final boolean reintegrate = invertor.isInvertedSense(); - if (reintegrate && (! myInteraction.shouldReintegrate(mySourceUrl, invertor.inverted().getTarget()))) { - context.cancelEverything(); - return; - } - final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate, invertor); - - final String title = "Merging all from " + myBranchName + (reintegrate ? " (reintegrate)" : ""); - context.next(new MergeTask(mergerFactory, title)); - } - - private MergerFactory createBranchMergerFactory(final boolean reintegrate, - final SvnBranchPointsCalculator.WrapperInvertor invertor) { - return new MergerFactory() { - public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { - return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, reintegrate, myBranchName, - reintegrate ? invertor.getWrapped().getTargetRevision() : invertor.getWrapped().getSourceRevision()); - } - @Nullable - public List getListsToMerge() { - return null; - } - public boolean isMergeAll() { - return true; - } - }; - } - } - - private class MergeTask extends TaskDescriptor { - private final MergerFactory myFactory; - - // awt since only a wrapper - private MergeTask(final MergerFactory factory, final String mergeTitle) { - super(mergeTitle, Where.AWT); - myFactory = factory; - } - - @Override - public void run(ContinuationContext context) { - final SVNURL sourceUrlUrl; - try { - sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl); - } catch (SVNException e) { - finishWithError(context, "Cannot merge: " + e.getMessage(), true); - return; - } - - final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject), - new WorkingCopyInfo(myWcInfo.getPath(), true), myFactory, sourceUrlUrl, getName(), false, myBranchName); - final TaskDescriptor taskDescriptor = TaskDescriptor.createForBackgroundableTask(task); - // merge task will be the next after... - context.next(taskDescriptor); - // ... after we create changelist - createChangelist(context); - } - - private void createChangelist(final ContinuationPause context) { - final ChangeListManager listManager = ChangeListManager.getInstance(myProject); - String name = myTitle; - int i = 1; - boolean updateDefaultList = false; - while (true) { - final LocalChangeList changeList = listManager.findChangeList(name); - if (changeList == null) { - final LocalChangeList newList = listManager.addChangeList(name, null); - listManager.setDefaultChangeList(newList); - updateDefaultList = true; - break; - } - if (changeList.getChanges().isEmpty()) { - if (! changeList.isDefault()) { - listManager.setDefaultChangeList(changeList); - updateDefaultList = true; - } - break; - } - name = myTitle + " (" + i + ")"; - ++ i; - } - if (updateDefaultList) { - context.suspend(); - listManager.invokeAfterUpdate(new Runnable() { - public void run() { - context.ping(); - } - }, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE_NOT_AWT, "", ModalityState.NON_MODAL); - } - } - } - - // true if errors found - static boolean checkListForPaths(String relativeLocal, - String relativeBranch, Pair> pair) { - // TODO: Such filtering logic is not clear enough so far (and probably not correct for all cases - for instance when we perform merge - // TODO: from branch1 to branch2 and have revision which contain merge changes from branch3 to branch1. - // TODO: In this case paths of child log entries will not contain neither urls from branch1 nor from branch2 - and checkEntry() method - // TODO: will return true => so such revision will not be used (and displayed) further. - - // TODO: Why do we check entries recursively - we have a revision - set of changes in the "merge from" branch? Why do we need to check - // TODO: where they came from - we want avoid some circular merges or what? Does subversion itself perform such checks or not? - final List> children = pair.getSecond().getChildren(); - boolean localChange = false; - for (TreeStructureNode child : children) { - if (checkForSubtree(child, relativeLocal, relativeBranch)) { - localChange = true; - break; - } - } - if (! localChange) { - // check self - return checkForEntry(pair.getSecond().getMe(), relativeLocal, relativeBranch); - } - return localChange; - } - - // true if errors found - private static boolean checkForSubtree(final TreeStructureNode tree, - String relativeBranch, final String localURL) { - final LinkedList> queue = new LinkedList>(); - queue.addLast(tree); - - while (! queue.isEmpty()) { - final TreeStructureNode element = queue.removeFirst(); - ProgressManager.checkCanceled(); - - if (checkForEntry(element.getMe(), localURL, relativeBranch)) return true; - queue.addAll(element.getChildren()); - } - return false; - } - - // true if errors found - // checks if either some changed path is in current branch => treat as local change - // or if no changed paths in current branch, checks if at least one path in "merge from" branch - // NOTE: this fails for "merge-source" log entries from other branches - when all changed paths are from some - // third branch - this logic treats such log entry as local. - private static boolean checkForEntry(final SVNLogEntry entry, final String localURL, String relativeBranch) { - boolean atLeastOneUnderBranch = false; - final Map map = entry.getChangedPaths(); - for (Object o : map.values()) { - final SVNLogEntryPath path = (SVNLogEntryPath) o; - if (SVNPathUtil.isAncestor(localURL, path.getPath())) { - return true; - } - if (! atLeastOneUnderBranch && SVNPathUtil.isAncestor(relativeBranch, path.getPath())) { - atLeastOneUnderBranch = true; - } - } - return ! atLeastOneUnderBranch; - } - - private class MergeCalculator extends TaskDescriptor implements - Consumer, SVNException>> { - private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy"; - private final WCInfo myWcInfo; - private final String mySourceUrl; - private final String myBranchName; - private final - AtomicReference, SVNException>> - myCopyData; - private boolean myIsReintegrate; - - private final List myNotMerged; - private String myMergeTitle; - private final MergeChecker myMergeChecker; - - @Override - public void consume(TransparentlyFailedValueI, SVNException> value) { - myCopyData.set(value); - } - - private MergeCalculator(WCInfo wcInfo, String sourceUrl, String branchName) throws VcsException { - super("Calculating not merged revisions", Where.POOLED); - myWcInfo = wcInfo; - mySourceUrl = sourceUrl; - myBranchName = branchName; - myNotMerged = new LinkedList(); - myMergeTitle = "Merge from " + branchName; -// if (Boolean.TRUE.equals(Boolean.getBoolean(ourOneShotStrategy))) { - myMergeChecker = new OneShotMergeInfoHelper(myProject, myWcInfo, mySourceUrl); - ((OneShotMergeInfoHelper) myMergeChecker).prepare(); -/* } else { - myMergeChecker = new BranchInfo.MyMergeCheckerWrapper(myWcInfo.getPath(), new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), - myWcInfo.getRootUrl(), mySourceUrl, - mySourceUrl, myVcs.createWCClient())); - }*/ - myCopyData = new AtomicReference, SVNException>>(); - } - - //"Calculating not merged revisions" - @Override - public void run(ContinuationContext context) { - SvnBranchPointsCalculator.WrapperInvertor copyDataValue = null; - try { - copyDataValue = myCopyData.get().get(); - } - catch (SVNException e) { - finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e))); - return; - } - if (copyDataValue == null) { - finishWithError(context, "Merge start wasn't found", true); - return; - } - - final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - myIsReintegrate = copyDataValue.isInvertedSense(); - if (!myWcInfo.getFormat().supportsMergeInfo()) return; - final SvnBranchPointsCalculator.BranchCopyData data = copyDataValue.getTrue(); - final long sourceLatest = data.getTargetRevision(); - - final SvnCommittedChangesProvider committedChangesProvider = (SvnCommittedChangesProvider)myVcs.getCommittedChangesProvider(); - final ChangeBrowserSettings settings = new ChangeBrowserSettings(); - settings.CHANGE_AFTER = Long.toString(sourceLatest); - settings.USE_CHANGE_AFTER_FILTER = true; - - String local = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl()); - final String relativeLocal = (local.startsWith("/") ? local : "/" + local); - String relativeBranch = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), mySourceUrl); - relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch); - - final LinkedList>> list = - new LinkedList>>(); - try { - committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mySourceUrl), 0, - new PairConsumer>() { - public void consume(SvnChangeList svnList, TreeStructureNode tree) { - indicator.checkCanceled(); - if (sourceLatest >= svnList.getNumber()) return; - list.add(new Pair>(svnList, tree)); - } - }); - } - catch (VcsException e) { - finishWithError(context, "Checking revisions for merge fault", Collections.singletonList(e)); - } - - indicator.setText("Checking merge information..."); - // to do not go into file system while asking something on the net - for (Pair> pair : list) { - final SvnChangeList svnList = pair.getFirst(); - final SvnMergeInfoCache.MergeCheckResult checkResult = myMergeChecker.checkList(svnList); - indicator.setText2("Processing revision " + svnList.getNumber()); - - if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) { - // additionally check for being 'local' - boolean localChange = checkListForPaths(relativeLocal, relativeBranch, pair); - - if (! localChange) { - myNotMerged.add(svnList); - } - } - } - - if (myNotMerged.isEmpty()) { - finishWithError(context, "Everything is up-to-date", false); - return; - } - context.next(new ShowRevisionSelector(copyDataValue)); - } - - private class ShowRevisionSelector extends TaskDescriptor { - private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint; - - private ShowRevisionSelector(SvnBranchPointsCalculator.WrapperInvertor copyPoint) { - super("show revisions to merge", Where.AWT); - myCopyPoint = copyPoint; - } - - @Override - public void run(ContinuationContext context) { - final QuickMergeInteraction.SelectMergeItemsResult result = myInteraction.selectMergeItems(myNotMerged, myMergeTitle, myMergeChecker); - if (QuickMergeContentsVariants.cancel == result.getResultCode()) { - context.cancelEverything(); - return; - } - if (QuickMergeContentsVariants.all == result.getResultCode()) { - insertMergeAll(context); - } else { - final List lists = result.getSelectedLists(); - if (lists.isEmpty()) return; - final MergerFactory factory = new ChangeListsMergerFactory(lists) { - @Override - public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { - return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false); - } - }; - context.next(new LocalChangesPrompt(false, lists, myCopyPoint), new MergeTask(factory, myMergeTitle)); - } - } - } - } - - private class LocalChangesPrompt extends TaskDescriptor { - private final boolean myMergeAll; - @Nullable private final List myLists; - private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint; - - private LocalChangesPrompt(final boolean mergeAll, - @Nullable final List lists, - @Nullable SvnBranchPointsCalculator.WrapperInvertor copyPoint) { - super("local changes intersection check", Where.AWT); - myMergeAll = mergeAll; - myLists = lists; - myCopyPoint = copyPoint; - } - - @Nullable - private File getLocalPath(final String relativeToRepoPath) { - // from source if not inverted - final String pathToCheck = SVNPathUtil.append(myWcInfo.getRepositoryRoot(), relativeToRepoPath); - final SvnBranchPointsCalculator.BranchCopyData wrapped = myCopyPoint.getWrapped(); - final String relativeInSource = - SVNPathUtil.getRelativePath(myCopyPoint.isInvertedSense() ? wrapped.getSource() : wrapped.getTarget(), pathToCheck); - if (StringUtil.isEmptyOrSpaces(relativeInSource)) return null; - final File local = new File(myWcInfo.getPath(), relativeInSource); - return local; - } - - @Override - public void run(ContinuationContext context) { - final Intersection intersection; - final ChangeListManager listManager = ChangeListManager.getInstance(myProject); - final List localChangeLists = listManager.getChangeListsCopy(); - - if (myMergeAll) { - intersection = getMergeAllIntersection(localChangeLists); - } else { - intersection = checkIntersection(myLists, localChangeLists); - } - if (intersection == null || intersection.getChangesSubset().isEmpty()) return; - - final LocalChangesAction action = myInteraction.selectLocalChangesAction(myMergeAll); - switch (action) { - // shelve - case shelve: - context.next(new ShelveLocalChanges(intersection)); - return; - // cancel - case cancel: - context.cancelEverything(); - return; - // continue - case continueMerge: - return; - // inspect - case inspect: - // here's cast is due to generic's bug - @SuppressWarnings("unchecked") final Collection changes = (Collection) intersection.getChangesSubset().values(); - final List paths = ChangesUtil.getPaths(changes); - Collections.sort(paths, FilePathByPathComparator.getInstance()); - myInteraction.showIntersectedLocalPaths(paths); - context.cancelEverything(); - return; - default: - } - } - - @Nullable - private Intersection checkIntersection(@Nullable final List lists, List localChangeLists) { - if (lists == null || lists.isEmpty()) { - return null; - } - final Set mergePaths = new HashSet(); - for (CommittedChangeList list : lists) { - final SvnChangeList svnList = (SvnChangeList)list; - final List paths = new ArrayList(svnList.getAddedPaths()); - paths.addAll(svnList.getChangedPaths()); - paths.addAll(svnList.getDeletedPaths()); - for (String path : paths) { - final File localPath = getLocalPath(path); - if (localPath != null) { - mergePaths.add(new FilePathImpl(localPath, false)); - } - } - } - - final Intersection intersection = new Intersection(); - for (LocalChangeList localChangeList : localChangeLists) { - final Collection localChanges = localChangeList.getChanges(); - - for (Change localChange : localChanges) { - final FilePath before = localChange.getBeforeRevision() == null ? null : localChange.getBeforeRevision().getFile(); - final FilePath after = localChange.getAfterRevision() == null ? null : localChange.getAfterRevision().getFile(); - - if ((before != null && mergePaths.contains(before)) || (after != null && mergePaths.contains(after))) { - intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange); - } - } - } - return intersection; - } - } - - private class ShelveLocalChanges extends TaskDescriptor { - private final Intersection myIntersection; - - private ShelveLocalChanges(final Intersection intersection) { - super("Shelving local changes before merge", Where.POOLED); - myIntersection = intersection; - } - - @Override - public void run(final ContinuationContext context) { - final MultiMap map = myIntersection.getChangesSubset(); - - final RefreshSessionImpl session = new RefreshSessionImpl(true, false, new Runnable() { - public void run() { - context.ping(); - } - }); - - for (String name : map.keySet()) { - try { - final Collection changes = map.get(name); - ApplicationManager.getApplication().invokeAndWait(new Runnable() { - @Override - public void run() { - FileDocumentManager.getInstance().saveAllDocuments(); - } - }, ModalityState.NON_MODAL); - ShelveChangesManager.getInstance(myProject).shelveChanges(changes, myIntersection.getComment(name) + " (auto shelve before merge)", - true); - session.addAllFiles(ChangesUtil.getFilesFromChanges(changes)); - } - catch (IOException e) { - finishWithError(context, e.getMessage(), true); - } - catch (VcsException e) { - finishWithError(context, e.getMessage(), true); - } - } - // first suspend to guarantee stop->then start back sequence - context.suspend(); - session.launch(); - } - } - - private Intersection getMergeAllIntersection(List localChangeLists) { - final Intersection intersection = new Intersection(); - - for (LocalChangeList localChangeList : localChangeLists) { - final Collection localChanges = localChangeList.getChanges(); - for (Change localChange : localChanges) { - intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange); - } - } - return intersection; - } - - private static class Intersection { - private final Map myLists; - private final MultiMap myChangesSubset; - - private Intersection() { - myLists = new HashMap(); - myChangesSubset = new MultiMap(); - } - - public void add(@NotNull final String listName, @Nullable final String comment, final Change change) { - myChangesSubset.putValue(listName, change); - final String commentToPut = comment == null ? listName : comment; - myLists.put(listName, commentToPut); - } - - public String getComment(final String listName) { - return myLists.get(listName); - } - - public MultiMap getChangesSubset() { - return myChangesSubset; - } - } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java index 4a7aa2defdea..a6da5c5f7cb3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java @@ -34,6 +34,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; @@ -66,10 +67,8 @@ import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.ISVNReporter; import org.tmatesoft.svn.core.io.ISVNReporterBaton; import org.tmatesoft.svn.core.io.SVNRepository; -import org.tmatesoft.svn.core.wc.SVNCommitClient; -import org.tmatesoft.svn.core.wc.SVNCopyClient; -import org.tmatesoft.svn.core.wc.SVNCopySource; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.swing.*; import javax.swing.tree.TreeNode; @@ -88,9 +87,6 @@ public class RepositoryBrowserDialog extends DialogWrapper { protected final SvnVcs myVCS; private RepositoryBrowserComponent myRepositoryBrowser; - @NonNls public static final String COPY_OF_PREFIX = "CopyOf"; - @NonNls public static final String NEW_FOLDER_POSTFIX = "NewFolder"; - private final DeleteAction myDeleteAction; private AnAction copyUrlAction; private AnAction mkDirAction; @@ -807,7 +803,7 @@ public class RepositoryBrowserDialog extends DialogWrapper { } } private boolean doDelete(final SVNURL url, final String comment) { - final SVNException[] exception = new SVNException[1]; + final Ref exception = new Ref(); final Project project = myBrowserComponent.getProject(); Runnable command = new Runnable() { public void run() { @@ -817,19 +813,18 @@ public class RepositoryBrowserDialog extends DialogWrapper { } SvnVcs vcs = SvnVcs.getInstance(project); try { - SVNCommitClient committer = vcs.createCommitClient(); - committer.doDelete(new SVNURL[]{url}, comment); + vcs.getFactoryFromSettings().createDeleteClient().delete(url, comment); } - catch (SVNException e) { - exception[0] = e; + catch (VcsException e) { + exception.set(e); } } }; ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.browser.delete"), false, project); - if (exception[0] != null) { - Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error")); + if (!exception.isNull()) { + Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error")); } - return exception[0] == null; + return exception.isNull(); } } @@ -1001,7 +996,7 @@ public class RepositoryBrowserDialog extends DialogWrapper { } protected static void doMkdir(final SVNURL url, final String comment, final Project project) { - final SVNException[] exception = new SVNException[1]; + final Ref exception = new Ref(); Runnable command = new Runnable() { public void run() { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); @@ -1009,23 +1004,23 @@ public class RepositoryBrowserDialog extends DialogWrapper { progress.setText(SvnBundle.message("progress.text.browser.creating", url.toString())); } SvnVcs vcs = SvnVcs.getInstance(project); + SvnTarget target = SvnTarget.fromURL(url); try { - SVNCommitClient committer = vcs.createCommitClient(); - committer.doMkDir(new SVNURL[] {url}, comment); + vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, comment, false); } - catch (SVNException e) { - exception[0] = e; + catch (VcsException e) { + exception.set(e); } } }; ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.text.create.remote.folder"), false, project); - if (exception[0] != null) { - Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error")); + if (!exception.isNull()) { + Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error")); } } private void doCopy(final SVNURL src, final SVNURL dst, final boolean move, final String comment) { - final SVNException[] exception = new SVNException[1]; + final Ref exception = new Ref(); Runnable command = new Runnable() { public void run() { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); @@ -1035,19 +1030,18 @@ public class RepositoryBrowserDialog extends DialogWrapper { } SvnVcs vcs = SvnVcs.getInstance(myProject); try { - SVNCopyClient committer = vcs.createCopyClient(); - final SVNCopySource[] copySource = new SVNCopySource[] {new SVNCopySource(SVNRevision.HEAD, SVNRevision.HEAD, src)}; - committer.doCopy(copySource, dst, move, true, true, comment, null); + vcs.getFactoryFromSettings().createCopyMoveClient().copy(SvnTarget.fromURL(src), SvnTarget.fromURL(dst), SVNRevision.HEAD, true, + move, comment, null); } - catch (SVNException e) { - exception[0] = e; + catch (VcsException e) { + exception.set(e); } } }; String progressTitle = move ? SvnBundle.message("progress.title.browser.move") : SvnBundle.message("progress.title.browser.copy"); ProgressManager.getInstance().runProcessWithProgressSynchronously(command, progressTitle, false, myProject); - if (exception[0] != null) { - Messages.showErrorDialog(exception[0].getMessage(), SvnBundle.message("message.text.error")); + if (!exception.isNull()) { + Messages.showErrorDialog(exception.get().getMessage(), SvnBundle.message("message.text.error")); } } @@ -1184,16 +1178,6 @@ public class RepositoryBrowserDialog extends DialogWrapper { return; } final Collection changesList = changes.values(); - /*final Collection changesListConverted = new ArrayList(changesList.size()); - for (Change change : changesList) { - final FilePath path = ChangesUtil.getFilePath(change); - final Change newChange = new Change( - new UrlContentRevision(change.getBeforeRevision(), - FilePathImpl.createNonLocal(SVNPathUtil.append(sourceUrl.toString(), path.getName()), path.isDirectory()), revision), - new UrlContentRevision(change.getAfterRevision(), - FilePathImpl.createNonLocal(SVNPathUtil.append(targetUrl.toString(), path.getName()), path.isDirectory()), revision)); - changesListConverted.add(newChange); - }*/ final String title = SvnBundle.message("repository.browser.compare.title", sourceTitle, targetTitle); SwingUtilities.invokeLater(new Runnable() { @@ -1231,10 +1215,6 @@ public class RepositoryBrowserDialog extends DialogWrapper { } } - public void setDefaultExpander(final NotNullFunction expanderFactory) { - myRepositoryBrowser.setLazyLoadingExpander(expanderFactory); - } - private static class UrlContentRevision implements ContentRevision { private final ContentRevision myContentRevision; private final FilePath myPath; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSHCredentialsDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSHCredentialsDialog.java index 80dfd2938f21..71860ab063bd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSHCredentialsDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSHCredentialsDialog.java @@ -75,7 +75,7 @@ public class SSHCredentialsDialog extends DialogWrapper implements ActionListene @NonNls private static final String HELP_ID = "vcs.subversion.authentication"; private boolean myKeyFileEmptyOrCorrect; - protected SSHCredentialsDialog(Project project, + public SSHCredentialsDialog(Project project, String realm, String userName, boolean allowSave, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ServerSSLDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ServerSSLDialog.java index 3506e5859a4c..fc6bb33c4db3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ServerSSLDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ServerSSLDialog.java @@ -39,7 +39,7 @@ public class ServerSSLDialog extends DialogWrapper { private int myResult; @NonNls public static final String ALGORITHM_SHA1 = "SHA1"; - protected ServerSSLDialog(final Project project, X509Certificate cert, boolean store) { + public ServerSSLDialog(final Project project, X509Certificate cert, boolean store) { super(project, true); myCertificate = cert; myResult = ISVNAuthenticationProvider.REJECTED; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java index 5d7bd6d3aa61..600eb1a04e71 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.dialogs; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI; import com.intellij.openapi.vcs.persistent.SmallMapSerializer; @@ -33,7 +34,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.history.CopyData; import org.jetbrains.idea.svn.history.FirstInBranch; -import org.tmatesoft.svn.core.SVNException; import java.io.DataInput; import java.io.DataOutput; @@ -43,7 +43,7 @@ import java.util.*; public class SvnBranchPointsCalculator { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator"); - private FactsCalculator, SVNException> myCalculator; + private FactsCalculator myCalculator; private PersistentHolder myPersistentHolder; private File myFile; private final Project myProject; @@ -58,19 +58,19 @@ public class SvnBranchPointsCalculator { } public void activate() { - ValueHolder, KeyData> cache = null; + ValueHolder cache = null; myPersistentHolder = new PersistentHolder(myFile); - cache = new ValueHolder, KeyData>() { - public WrapperInvertor getValue(KeyData dataHolder) { - final WrapperInvertor result = + cache = new ValueHolder() { + public WrapperInvertor getValue(KeyData dataHolder) { + final WrapperInvertor result = myPersistentHolder.getBestHit(dataHolder.getRepoUrl(), dataHolder.getSourceUrl(), dataHolder.getTargetUrl()); if (LOG.isDebugEnabled()) { LOG.debug("Persistent for: " + dataHolder.toString() + " returned: " + (result == null ? null : result.toString())); } return result; } - public void setValue(WrapperInvertor value, KeyData dataHolder) { + public void setValue(WrapperInvertor value, KeyData dataHolder) { if (LOG.isDebugEnabled()) { LOG.debug("Put into persistent: key: " + dataHolder.toString() + " value: " + value.toString()); } @@ -78,7 +78,7 @@ public class SvnBranchPointsCalculator { } }; - myCalculator = new FactsCalculator, SVNException>( + myCalculator = new FactsCalculator( myProject, "Looking for branch origin", cache, new Loader(myProject)); } @@ -118,11 +118,11 @@ public class SvnBranchPointsCalculator { } } - public static class WrapperInvertor> { - private final T myWrapped; + public static class WrapperInvertor { + private final BranchCopyData myWrapped; private final boolean myInvertedSense; - public WrapperInvertor(boolean invertedSense, T wrapped) { + public WrapperInvertor(boolean invertedSense, BranchCopyData wrapped) { myInvertedSense = invertedSense; myWrapped = wrapped; } @@ -131,15 +131,15 @@ public class SvnBranchPointsCalculator { return myInvertedSense; } - public T getWrapped() { + public BranchCopyData getWrapped() { return myWrapped; } - public T getTrue() { + public BranchCopyData getTrue() { return myInvertedSense ? myWrapped.invertSelf() : myWrapped; } - public T inverted() { + public BranchCopyData inverted() { return myWrapped.invertSelf(); } @@ -200,7 +200,7 @@ public class SvnBranchPointsCalculator { } @Nullable - public WrapperInvertor getBestHit(final String repoUrl, final String source, final String target) { + public WrapperInvertor getBestHit(final String repoUrl, final String source, final String target) { final List keys; synchronized (myLock) { keys = (List) myForSearchMap.get(repoUrl); @@ -217,14 +217,14 @@ public class SvnBranchPointsCalculator { final boolean sourceIsOut = sourceMatching == null; if (sourceIsOut || targetMatching == null) { // if found by "target" url - we correctly thought that target of copy is target - return sourceIsOut ? new WrapperInvertor(false, map.get(targetMatching)) : - new WrapperInvertor(true, map.get(sourceMatching)); + return sourceIsOut ? new WrapperInvertor(false, map.get(targetMatching)) : + new WrapperInvertor(true, map.get(sourceMatching)); } final BranchCopyData sourceData = map.get(sourceMatching); final BranchCopyData targetData = map.get(targetMatching); final boolean inverted = sourceData.getTargetRevision() > targetData.getTargetRevision(); - return new WrapperInvertor(inverted, inverted ? sourceData : targetData); + return new WrapperInvertor(inverted, inverted ? sourceData : targetData); } } @@ -240,7 +240,7 @@ public class SvnBranchPointsCalculator { } } - private static class Loader implements ThrowableConvertor, SVNException> { + private static class Loader implements ThrowableConvertor { private SvnVcs myVcs; private Loader(final Project project) { @@ -248,8 +248,8 @@ public class SvnBranchPointsCalculator { } @Override - public WrapperInvertor convert(final KeyData keyData) throws SVNException { - final TransparentlyFailedValue consumer = new TransparentlyFailedValue(); + public WrapperInvertor convert(final KeyData keyData) throws VcsException { + final TransparentlyFailedValue consumer = new TransparentlyFailedValue(); new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), consumer).run(); final CopyData copyData = consumer.get(); @@ -263,7 +263,7 @@ public class SvnBranchPointsCalculator { branchCopyData = new BranchCopyData(keyData.getTargetUrl(), copyData.getCopySourceRevision(), keyData.getSourceUrl(), copyData.getCopyTargetRevision()); } - WrapperInvertor invertor = new WrapperInvertor(! correct, branchCopyData); + WrapperInvertor invertor = new WrapperInvertor(! correct, branchCopyData); if (LOG.isDebugEnabled()) { LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: " + (invertor.toString())); } @@ -305,7 +305,7 @@ public class SvnBranchPointsCalculator { } } - public static class BranchCopyData implements Invertor { + public static class BranchCopyData { private final String mySource; private final String myTarget; private final long mySourceRevision; @@ -344,12 +344,8 @@ public class SvnBranchPointsCalculator { } } - private interface Invertor { - T invertSelf(); - } - public TaskDescriptor getFirstCopyPointTask(final String repoUID, final String sourceUrl, final String targetUrl, - final Consumer, SVNException>> consumer) { - return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, SVNException.class); + final Consumer> consumer) { + return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, VcsException.class); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UserNameCredentialsDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UserNameCredentialsDialog.java index 3896c020f880..212c5ce7130e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UserNameCredentialsDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UserNameCredentialsDialog.java @@ -39,7 +39,7 @@ public class UserNameCredentialsDialog extends DialogWrapper implements Document @NonNls private static final String HELP_ID = "vcs.subversion.authentication"; - protected UserNameCredentialsDialog(Project project) { + public UserNameCredentialsDialog(Project project) { super(project, true); setResizable(false); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/browserCache/RepositoryLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/browserCache/RepositoryLoader.java index 83185fef189f..d2f8b633773b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/browserCache/RepositoryLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/browserCache/RepositoryLoader.java @@ -23,7 +23,7 @@ import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.dialogs.RepositoryTreeNode; -import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider; +import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider; import org.tmatesoft.svn.core.ISVNDirEntryHandler; import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNErrorMessage; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java index b774fe6b4aac..0ad8373a0805 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java @@ -30,6 +30,7 @@ import org.jetbrains.idea.svn.SvnStatusConvertor; import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.*; import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision; +import org.jetbrains.idea.svn.status.SvnStatusHandler; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -63,7 +64,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { parameters.add("--xml"); parameters.add("--summarize"); - CommandExecutor executor = CommandUtil.execute(myVcs, target1, SvnCommandName.diff, parameters, null); + CommandExecutor executor = execute(myVcs, target1, SvnCommandName.diff, parameters, null); return parseOutput(target1, target2, executor); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java index 567c21f694e6..3494b9ac9d28 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java @@ -44,7 +44,7 @@ public class CmdHistoryClient extends BaseSvnClient implements HistoryClient { prepareCommand(target, startRevision, endRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit); try { - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.log, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.log, parameters, null); // TODO: handler should be called in parallel with command execution, but this will be in other thread // TODO: check if that is ok for current handler implementation parseOutput(command, handler); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java index 7fb35d14ca4d..f871ee09855a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java @@ -18,31 +18,35 @@ package org.jetbrains.idea.svn.history; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI; import com.intellij.util.Consumer; -import com.intellij.util.containers.hash.HashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; -import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.util.Map; -import java.util.Set; // TODO: This one seem to determine revision in which branch was created - copied from trunk. // TODO: This could be done in one command "svn log -r 0:HEAD --stop-on-copy --limit 1". // TODO: Check for 1.7 and rewrite using this approach. public class FirstInBranch implements Runnable { - private final SvnVcs myVcs; - private final String myBranchUrl; - private final String myTrunkUrl; - private final String myRepositoryRoot; - private final TransparentlyFailedValueI myConsumer; + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.FirstInBranch"); - public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl, - final TransparentlyFailedValueI consumer) { + @NotNull private final SvnVcs myVcs; + @NotNull private final String myBranchUrl; + @NotNull private final String myTrunkUrl; + @NotNull private final String myRepositoryRoot; + @NotNull private final TransparentlyFailedValueI myConsumer; + + public FirstInBranch(@NotNull SvnVcs vcs, @NotNull String repositoryRoot, @NotNull String branchUrl, @NotNull String trunkUrl, + @NotNull TransparentlyFailedValueI consumer) { myVcs = vcs; myRepositoryRoot = repositoryRoot; myConsumer = consumer; @@ -51,71 +55,83 @@ public class FirstInBranch implements Runnable { myTrunkUrl = relativePath(repositoryRoot, trunkUrl); } - private String relativePath(final String parent, final String child) { + private static String relativePath(final String parent, final String child) { String path = SVNPathUtil.getRelativePath(parent, child); - return path.startsWith("/") ? path : "/" + path; + + return SvnUtil.ensureStartSlash(path); + } + + private SVNURL getRepositoryRootUrl() { + SVNURL result = null; + + try { + result = SvnUtil.createUrl(myRepositoryRoot); + } + catch (SvnBindException e) { + LOG.info(e); + myConsumer.fail(e); + } + + return result; } public void run() { - final Set exceptions = new HashSet(); - final boolean [] called = new boolean[1]; + SVNURL repositoryRootUrl = getRepositoryRootUrl(); + + if (repositoryRootUrl != null) { + run(repositoryRootUrl); + } + } + + private void run(@NotNull SVNURL url) { try { - createTask(SVNURL.parseURIDecoded(myRepositoryRoot), exceptions).consume(new Consumer() { + run(url, new Consumer() { @Override public void consume(CopyData data) { if (data != null) { myConsumer.set(data); - called[0] = true; } } }); } - catch (SVNException e) { + catch (VcsException e) { + LOG.info(e); myConsumer.fail(e); - return; - } - if (called[0]) return; - - if (! exceptions.isEmpty()) { - LOG.info("Wasn't able to find branch point, exception(s) below"); - for (SVNException exception : exceptions) { - LOG.info(exception); - } - myConsumer.fail(exceptions.iterator().next()); - } else if (! called[0]) { - myConsumer.set(null); } } - private Consumer> createTask(final SVNURL branchURL, final Set exceptions) { - return new Consumer>() { - public void consume(final Consumer copyDataConsumer) { - final SVNLogClient logClient = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public SVNLogClient compute() { - if (myVcs.getProject().isDisposed()) return null; - return myVcs.createLogClient(); - } - }); - if (logClient == null) return; - try { - logClient.doLog(branchURL, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), false, true, -1, - new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl)); - } catch (SVNCancelException e) { - // - } catch (SVNException e) { - exceptions.add(e); - } + private void run(@NotNull SVNURL branchURL, @NotNull Consumer copyDataConsumer) throws VcsException { + final SvnTarget target = SvnTarget.fromURL(branchURL); + + HistoryClient client = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public HistoryClient compute() { + if (myVcs.getProject().isDisposed()) return null; + return myVcs.getFactory(target).createHistoryClient(); } - }; + }); + + if (client == null) return; + + try { + client.doLog(target, SVNRevision.HEAD, SVNRevision.create(0), false, true, false, -1, null, + new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl)); + } + catch (SvnBindException e) { + // do not throw cancel exception as this means corresponding copy point is found (if progress indicator was not explicitly cancelled) + if (!e.contains(SVNErrorCode.CANCELLED)) { + throw e; + } + } } private static class MyLogEntryHandler implements ISVNLogEntryHandler { - private final SvnPathThroughHistoryCorrection myTrunkCorrector; - private final SvnPathThroughHistoryCorrection myBranchCorrector; - private final Consumer myCopyDataConsumer; - public MyLogEntryHandler(Consumer copyDataConsumer, String trunkUrl, String branchUrl) { + @NotNull private final SvnPathThroughHistoryCorrection myTrunkCorrector; + @NotNull private final SvnPathThroughHistoryCorrection myBranchCorrector; + @NotNull private final Consumer myCopyDataConsumer; + + public MyLogEntryHandler(@NotNull Consumer copyDataConsumer, @NotNull String trunkUrl, @NotNull String branchUrl) { myCopyDataConsumer = copyDataConsumer; myTrunkCorrector = new SvnPathThroughHistoryCorrection(trunkUrl); myBranchCorrector = new SvnPathThroughHistoryCorrection(branchUrl); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LatestExistentSearcher.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LatestExistentSearcher.java index b878d60664a7..8aaec2aad6d0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LatestExistentSearcher.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LatestExistentSearcher.java @@ -15,8 +15,11 @@ */ package org.jetbrains.idea.svn.history; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.RootUrlInfo; import org.jetbrains.idea.svn.SvnFileUrlMapping; @@ -25,10 +28,14 @@ import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.util.Map; public class LatestExistentSearcher { + + private static final Logger LOG = Logger.getInstance(LatestExistentSearcher.class); + private long myStartNumber; private boolean myStartExistsKnown; private final SVNURL myUrl; @@ -67,24 +74,18 @@ public class LatestExistentSearcher { final String urlRelativeString = myUrl.toString().substring(repRoot.toString().length()); final SVNRevision startRevision = SVNRevision.create(myStartNumber); - myVcs.createLogClient().doLog(existingParent, new String[]{""}, startRevision, startRevision, SVNRevision.HEAD, false, true, 0, - new ISVNLogEntryHandler() { - public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException { - final Map changedPaths = logEntry.getChangedPaths(); - for (Object o : changedPaths.values()) { - final SVNLogEntryPath path = (SVNLogEntryPath) o; - if ((path.getType() == 'D') && (urlRelativeString.equals(path.getPath()))) { - latest.set(logEntry.getRevision()); - throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE); - } - } - } - }); + SvnTarget target = SvnTarget.fromURL(existingParent, startRevision); + myVcs.getFactory(target).createHistoryClient() + .doLog(target, startRevision, SVNRevision.HEAD, false, true, false, 0, null, createHandler(latest, urlRelativeString)); } } catch (SVNException e) { - // - } finally { + LOG.info(e); + } + catch (VcsException e) { + LOG.info(e); + } + finally { if (repository != null) { repository.closeSession(); } @@ -93,6 +94,22 @@ public class LatestExistentSearcher { return latest.get().longValue(); } + @NotNull + private static ISVNLogEntryHandler createHandler(@NotNull final Ref latest, @NotNull final String urlRelativeString) { + return new ISVNLogEntryHandler() { + public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException { + final Map changedPaths = logEntry.getChangedPaths(); + for (Object o : changedPaths.values()) { + final SVNLogEntryPath path = (SVNLogEntryPath)o; + if ((path.getType() == 'D') && (urlRelativeString.equals(path.getPath()))) { + latest.set(logEntry.getRevision()); + throw new SVNException(SVNErrorMessage.UNKNOWN_ERROR_MESSAGE); + } + } + } + }; + } + public long getLatestExistent() { if (! detectStartRevision()) return myStartNumber; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java index e62b8e041623..6ac6adc3b5cd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java @@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.SVNRevision; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/CmdInfoClient.java similarity index 91% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/CmdInfoClient.java index 8f4cae8f313c..475dd1a4f77d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/CmdInfoClient.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.info; import com.intellij.execution.process.ProcessOutput; import com.intellij.execution.process.ProcessOutputTypes; @@ -26,9 +26,8 @@ import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; -import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.*; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.ISVNInfoHandler; import org.tmatesoft.svn.core.wc.SVNInfo; @@ -52,16 +51,9 @@ import java.util.List; * Date: 1/27/12 * Time: 12:59 PM */ -public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { +public class CmdInfoClient extends BaseSvnClient implements InfoClient { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient"); - - @NotNull private final SvnVcs myVcs; - - public SvnCommandLineInfoClient(@NotNull final SvnVcs vcs) { - super(vcs); - myVcs = vcs; - } + private static final Logger LOG = Logger.getInstance(CmdInfoClient.class); @Override public void doInfo(File path, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException { @@ -98,7 +90,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { List parameters = new ArrayList(); fillParameters(path.getAbsolutePath(), pegRevision, revision, depth, parameters); - // TODO: Fix this check - update corresponding parameters in SvnWcClientI + // TODO: Fix this check - update corresponding parameters in InfoClient CommandUtil.putChangeLists(parameters, changeLists); parseResult(handler, base, execute(parameters, path)); @@ -117,7 +109,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { }; try { - CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.info, parameters, listener); + CommandExecutor command = execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.info, parameters, listener); return command.getOutput(); } @@ -217,10 +209,12 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { fillParameters(path, pegRevision, revision, depth, parameters); CommandExecutor command; try { - command = CommandUtil.execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.info, parameters, null); + command = execute(myVcs, SvnTarget.fromURL(url), SvnCommandName.info, parameters, null); } - catch (VcsException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); + catch (SvnBindException e) { + SVNErrorCode code = e.contains(SVNErrorCode.RA_ILLEGAL_URL) ? SVNErrorCode.RA_ILLEGAL_URL : SVNErrorCode.IO_ERROR; + + throw new SVNException(SVNErrorMessage.create(code, e), e); } parseResult(handler, null, command.getOutput()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/IdeaSVNInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/IdeaSVNInfo.java similarity index 98% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/IdeaSVNInfo.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/IdeaSVNInfo.java index 7acc08c7318c..0e2905e73016 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/IdeaSVNInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/IdeaSVNInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.info; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNDepth; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/InfoClient.java similarity index 82% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/InfoClient.java index 48ba32e5357d..f6582e603eda 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/InfoClient.java @@ -13,12 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.info; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.wc.*; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNInfoHandler; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; import java.io.File; import java.util.Collection; @@ -29,7 +34,7 @@ import java.util.Collection; * Date: 1/20/12 * Time: 6:54 PM */ -public interface SvnWcClientI extends SvnMarkerInterface { +public interface InfoClient extends SvnClient { void doInfo(File path, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException; void doInfo(File path, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SVNLockWrapper.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SVNLockWrapper.java similarity index 98% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SVNLockWrapper.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/SVNLockWrapper.java index 7cc4e24e4fa9..f5752074da77 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SVNLockWrapper.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SVNLockWrapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.info; import org.tmatesoft.svn.core.SVNLock; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoHandler.java similarity index 99% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoHandler.java index 46372c2a3ec2..7548ca1c193e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoHandler.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.info; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoStructure.java similarity index 98% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoStructure.java index 696e366490d7..6e1b1db1efa2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnInfoStructure.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.info; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.portable.IdeaSVNInfo; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.wc.SVNConflictVersion; import org.tmatesoft.svn.core.wc.*; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnWcClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnKitInfoClient.java similarity index 83% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnWcClient.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnKitInfoClient.java index 44cca7fa26c8..4cefde21d9c8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnWcClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/info/SvnKitInfoClient.java @@ -13,13 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.info; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.wc.*; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.info.InfoClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNInfoHandler; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.Collection; @@ -30,13 +36,7 @@ import java.util.Collection; * Date: 1/20/12 * Time: 6:55 PM */ -public class SvnkitSvnWcClient implements SvnWcClientI { - @NotNull - private final SvnVcs myVcs; - - public SvnkitSvnWcClient(@NotNull SvnVcs vcs) { - myVcs = vcs; - } +public class SvnKitInfoClient extends BaseSvnClient implements InfoClient { public SVNWCClient getClient() { return myVcs.createWCClient(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/BaseMergeTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/BaseMergeTask.java new file mode 100644 index 000000000000..f9c3f7821b19 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/BaseMergeTask.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vcs.CalledInAny; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.TaskDescriptor; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class BaseMergeTask extends TaskDescriptor { + + private static final Logger LOG = Logger.getInstance(BaseMergeTask.class); + + @NotNull protected final MergeContext myMergeContext; + @NotNull protected final QuickMergeInteraction myInteraction; + + public BaseMergeTask(@NotNull MergeContext mergeContext, + @NotNull QuickMergeInteraction interaction, String name, + @NotNull Where where) { + super(name, where); + myMergeContext = mergeContext; + myInteraction = interaction; + } + + protected void insertMergeAll(final ContinuationContext context) { + final List queue = new ArrayList(); + insertMergeAll(queue); + context.next(queue); + } + + protected void insertMergeAll(final List queue) { + queue.add(new LocalChangesPromptTask(myMergeContext, myInteraction, true, null, null)); + final MergeAllWithBranchCopyPointTask mergeAllExecutor = new MergeAllWithBranchCopyPointTask(myMergeContext, myInteraction); + queue.add(myMergeContext.getVcs().getSvnBranchPointsCalculator() + .getFirstCopyPointTask(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl(), + myMergeContext.getWcInfo().getRootUrl(), mergeAllExecutor)); + queue.add(mergeAllExecutor); + } + + @CalledInAny + protected void finishWithError(final ContinuationContext context, final String message, final boolean isError) { + LOG.info((isError ? "Error: " : "Info: ") + message); + context.next(new TaskDescriptor(message, Where.AWT) { + @Override + public void run(ContinuationContext context) { + myInteraction.showErrors(message, isError); + context.cancelEverything(); + } + }); + } + + @CalledInAny + protected void finishWithError(final ContinuationContext context, final String message, final List exceptions) { + if (exceptions != null) { + for (VcsException exception : exceptions) { + LOG.info(message, exception); + } + } + context.cancelEverything(); + context.next(new TaskDescriptor(message, Where.AWT) { + @Override + public void run(ContinuationContext context) { + myInteraction.showErrors(message, exceptions); + } + }); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CheckRepositorySupportsMergeInfoTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CheckRepositorySupportsMergeInfoTask.java new file mode 100644 index 000000000000..5bb178b03192 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CheckRepositorySupportsMergeInfoTask.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.TaskDescriptor; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.dialogs.MergeContext; + +import java.util.LinkedList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CheckRepositorySupportsMergeInfoTask extends BaseMergeTask { + + public CheckRepositorySupportsMergeInfoTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) { + super(mergeContext, interaction, "Checking repository capabilities", Where.POOLED); + } + + @Override + public void run(ContinuationContext context) { + final List tasks = new LinkedList(); + final boolean supportsMergeinfo = myMergeContext.getWcInfo().getFormat().supportsMergeInfo() && SvnUtil.checkRepositoryVersion15( + myMergeContext.getVcs(), myMergeContext.getSourceUrl()); + if (!supportsMergeinfo) { + insertMergeAll(tasks); + } + else { + tasks.add(new MergeAllOrSelectedChooserTask(myMergeContext, myInteraction)); + } + context.next(tasks); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java index adba7fa2fa6c..63eb66d9b002 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java @@ -105,7 +105,7 @@ public class CmdMergeClient extends BaseSvnClient implements MergeClient { private void run(File destination, ISVNEventHandler handler, List parameters) throws VcsException { BaseUpdateCommandListener listener = new BaseUpdateCommandListener(CommandUtil.correctUpToExistingParent(destination), handler); - CommandUtil.execute(myVcs, SvnTarget.fromFile(destination), SvnCommandName.merge, parameters, listener); + execute(myVcs, SvnTarget.fromFile(destination), SvnCommandName.merge, parameters, listener); listener.throwWrappedIfException(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/Intersection.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/Intersection.java new file mode 100644 index 000000000000..439fc7fecbf3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/Intersection.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** +* @author Konstantin Kolosovsky. +*/ +public class Intersection { + private final Map myLists; + private final MultiMap myChangesSubset; + + public Intersection() { + myLists = new HashMap(); + myChangesSubset = new MultiMap(); + } + + public void add(@NotNull final String listName, @Nullable final String comment, final Change change) { + myChangesSubset.putValue(listName, change); + final String commentToPut = comment == null ? listName : comment; + myLists.put(listName, commentToPut); + } + + public String getComment(final String listName) { + return myLists.get(listName); + } + + public MultiMap getChangesSubset() { + return myChangesSubset; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/LocalChangesPromptTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/LocalChangesPromptTask.java new file mode 100644 index 000000000000..2652c79b5c11 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/LocalChangesPromptTask.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.ChangesUtil; +import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.FilePathByPathComparator; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.dialogs.LocalChangesAction; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; +import org.jetbrains.idea.svn.history.SvnChangeList; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; + +import java.io.File; +import java.util.*; + +/** + * @author Konstantin Kolosovsky. + */ +public class LocalChangesPromptTask extends BaseMergeTask { + private final boolean myMergeAll; + @Nullable private final List myLists; + private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint; + + public LocalChangesPromptTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction, final boolean mergeAll, + @Nullable final List lists, + @Nullable SvnBranchPointsCalculator.WrapperInvertor copyPoint) { + super(mergeContext, interaction, "local changes intersection check", Where.AWT); + myMergeAll = mergeAll; + myLists = lists; + myCopyPoint = copyPoint; + } + + private static Intersection getMergeAllIntersection(List localChangeLists) { + final Intersection intersection = new Intersection(); + + for (LocalChangeList localChangeList : localChangeLists) { + final Collection localChanges = localChangeList.getChanges(); + for (Change localChange : localChanges) { + intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange); + } + } + return intersection; + } + + @Nullable + private File getLocalPath(final String relativeToRepoPath) { + // from source if not inverted + final String pathToCheck = SVNPathUtil.append(myMergeContext.getWcInfo().getRepositoryRoot(), relativeToRepoPath); + final SvnBranchPointsCalculator.BranchCopyData wrapped = myCopyPoint.getWrapped(); + final String relativeInSource = + SVNPathUtil.getRelativePath(myCopyPoint.isInvertedSense() ? wrapped.getSource() : wrapped.getTarget(), pathToCheck); + if (StringUtil.isEmptyOrSpaces(relativeInSource)) return null; + final File local = new File(myMergeContext.getWcInfo().getPath(), relativeInSource); + return local; + } + + @Override + public void run(ContinuationContext context) { + final Intersection intersection; + final ChangeListManager listManager = ChangeListManager.getInstance(myMergeContext.getProject()); + final List localChangeLists = listManager.getChangeListsCopy(); + + if (myMergeAll) { + intersection = getMergeAllIntersection(localChangeLists); + } + else { + intersection = checkIntersection(myLists, localChangeLists); + } + if (intersection == null || intersection.getChangesSubset().isEmpty()) return; + + final LocalChangesAction action = myInteraction.selectLocalChangesAction(myMergeAll); + switch (action) { + // shelve + case shelve: + context.next(new ShelveLocalChangesTask(myMergeContext, myInteraction, intersection)); + return; + // cancel + case cancel: + context.cancelEverything(); + return; + // continue + case continueMerge: + return; + // inspect + case inspect: + // here's cast is due to generic's bug + @SuppressWarnings("unchecked") final Collection changes = (Collection)intersection.getChangesSubset().values(); + final List paths = ChangesUtil.getPaths(changes); + Collections.sort(paths, FilePathByPathComparator.getInstance()); + myInteraction.showIntersectedLocalPaths(paths); + context.cancelEverything(); + return; + default: + } + } + + @Nullable + private Intersection checkIntersection(@Nullable final List lists, List localChangeLists) { + if (lists == null || lists.isEmpty()) { + return null; + } + final Set mergePaths = new HashSet(); + for (CommittedChangeList list : lists) { + final SvnChangeList svnList = (SvnChangeList)list; + final List paths = new ArrayList(svnList.getAddedPaths()); + paths.addAll(svnList.getChangedPaths()); + paths.addAll(svnList.getDeletedPaths()); + for (String path : paths) { + final File localPath = getLocalPath(path); + if (localPath != null) { + mergePaths.add(new FilePathImpl(localPath, false)); + } + } + } + + final Intersection intersection = new Intersection(); + for (LocalChangeList localChangeList : localChangeLists) { + final Collection localChanges = localChangeList.getChanges(); + + for (Change localChange : localChanges) { + final FilePath before = localChange.getBeforeRevision() == null ? null : localChange.getBeforeRevision().getFile(); + final FilePath after = localChange.getAfterRevision() == null ? null : localChange.getAfterRevision().getFile(); + + if ((before != null && mergePaths.contains(before)) || (after != null && mergePaths.contains(after))) { + intersection.add(localChangeList.getName(), localChangeList.getComment(), localChange); + } + } + } + return intersection; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllOrSelectedChooserTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllOrSelectedChooserTask.java new file mode 100644 index 000000000000..1d5730ee7392 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllOrSelectedChooserTask.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.LoadRecentBranchRevisions; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeAllOrSelectedChooserTask extends BaseMergeTask { + public MergeAllOrSelectedChooserTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) { + super(mergeContext, interaction, "merge source selector", Where.AWT); + } + + @Override + public void run(final ContinuationContext context) { + final QuickMergeContentsVariants variant = myInteraction.selectMergeVariant(); + if (QuickMergeContentsVariants.cancel == variant) return; + if (QuickMergeContentsVariants.all == variant) { + insertMergeAll(context); + return; + } + if (QuickMergeContentsVariants.showLatest == variant) { + final LoadRecentBranchRevisions loader = new LoadRecentBranchRevisions(myMergeContext, -1); + final ShowRecentInDialogTask dialog = new ShowRecentInDialogTask(myMergeContext, myInteraction, loader); + context.next(loader, dialog); + return; + } + + final MergeCalculatorTask calculator; + try { + calculator = new MergeCalculatorTask(myMergeContext, myInteraction); + } + catch (VcsException e) { + finishWithError(context, e.getMessage(), true); + return; + } + context.next(myMergeContext.getVcs().getSvnBranchPointsCalculator() + .getFirstCopyPointTask(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getWcInfo().getRootUrl(), + myMergeContext.getSourceUrl(), calculator), calculator); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllWithBranchCopyPointTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllWithBranchCopyPointTask.java new file mode 100644 index 000000000000..8c5921675bd5 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeAllWithBranchCopyPointTask.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI; +import com.intellij.util.Consumer; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.dialogs.BranchMerger; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; +import org.jetbrains.idea.svn.update.UpdateEventHandler; +import org.tmatesoft.svn.core.SVNURL; + +import java.io.File; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeAllWithBranchCopyPointTask extends BaseMergeTask + implements Consumer> { + + private final AtomicReference> myData; + + public MergeAllWithBranchCopyPointTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) { + super(mergeContext, interaction, "merge all", Where.AWT); + myData = new AtomicReference>(); + } + + @Override + public void consume(TransparentlyFailedValueI value) { + myData.set(value); + } + + @Override + public void run(ContinuationContext context) { + SvnBranchPointsCalculator.WrapperInvertor invertor; + try { + final TransparentlyFailedValueI + transparentlyFailedValueI = myData.get(); + if (transparentlyFailedValueI == null) { + finishWithError(context, "Merge start wasn't found", true); + return; + } + invertor = transparentlyFailedValueI.get(); + } + catch (VcsException e) { + finishWithError(context, "Merge start wasn't found", Collections.singletonList(e)); + return; + } + if (invertor == null) { + finishWithError(context, "Merge start wasn't found", true); + return; + } + final boolean reintegrate = invertor.isInvertedSense(); + if (reintegrate && (!myInteraction.shouldReintegrate(myMergeContext.getSourceUrl(), invertor.inverted().getTarget()))) { + context.cancelEverything(); + return; + } + final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate, invertor); + + final String title = "Merging all from " + myMergeContext.getBranchName() + (reintegrate ? " (reintegrate)" : ""); + context.next(new MergeTask(myMergeContext, myInteraction, mergerFactory, title)); + } + + private MergerFactory createBranchMergerFactory(final boolean reintegrate, + final SvnBranchPointsCalculator.WrapperInvertor invertor) { + return new MergerFactory() { + public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { + return new BranchMerger(vcs, currentBranchUrl, myMergeContext.getWcInfo().getUrl(), myMergeContext.getWcInfo().getPath(), handler, + reintegrate, myMergeContext.getBranchName(), + reintegrate ? invertor.getWrapped().getTargetRevision() : invertor.getWrapped().getSourceRevision()); + } + }; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeCalculatorTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeCalculatorTask.java new file mode 100644 index 000000000000..9c880b6d3dda --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeCalculatorTask.java @@ -0,0 +1,265 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI; +import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.Consumer; +import com.intellij.util.PairConsumer; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.TaskDescriptor; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants; +import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; +import org.jetbrains.idea.svn.history.SvnChangeList; +import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; +import org.jetbrains.idea.svn.history.SvnRepositoryLocation; +import org.jetbrains.idea.svn.history.TreeStructureNode; +import org.jetbrains.idea.svn.mergeinfo.MergeChecker; +import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper; +import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache; +import org.jetbrains.idea.svn.update.UpdateEventHandler; +import org.tmatesoft.svn.core.SVNLogEntry; +import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; + +import java.io.File; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeCalculatorTask extends BaseMergeTask implements + Consumer> { + private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy"; + private final + AtomicReference> + myCopyData; + private boolean myIsReintegrate; + + private final List myNotMerged; + private String myMergeTitle; + private final MergeChecker myMergeChecker; + + @Override + public void consume(TransparentlyFailedValueI value) { + myCopyData.set(value); + } + + public MergeCalculatorTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) throws VcsException { + super(mergeContext, interaction, "Calculating not merged revisions", Where.POOLED); + myNotMerged = new LinkedList(); + myMergeTitle = "Merge from " + myMergeContext.getBranchName(); + // if (Boolean.TRUE.equals(Boolean.getBoolean(ourOneShotStrategy))) { + myMergeChecker = new OneShotMergeInfoHelper(myMergeContext); + ((OneShotMergeInfoHelper)myMergeChecker).prepare(); +/* } else { + myMergeChecker = new BranchInfo.MyMergeCheckerWrapper(myWcInfo.getPath(), new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), + myWcInfo.getRootUrl(), mySourceUrl, + mySourceUrl, myVcs.createWCClient())); + }*/ + myCopyData = new AtomicReference>(); + } + + //"Calculating not merged revisions" + @Override + public void run(ContinuationContext context) { + SvnBranchPointsCalculator.WrapperInvertor copyDataValue = null; + try { + copyDataValue = myCopyData.get().get(); + } + catch (VcsException e) { + finishWithError(context, "Merge start wasn't found", Collections.singletonList(e)); + return; + } + if (copyDataValue == null) { + finishWithError(context, "Merge start wasn't found", true); + return; + } + + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + myIsReintegrate = copyDataValue.isInvertedSense(); + if (!myMergeContext.getWcInfo().getFormat().supportsMergeInfo()) return; + final SvnBranchPointsCalculator.BranchCopyData data = copyDataValue.getTrue(); + final long sourceLatest = data.getTargetRevision(); + + final SvnCommittedChangesProvider committedChangesProvider = + (SvnCommittedChangesProvider)myMergeContext.getVcs().getCommittedChangesProvider(); + final ChangeBrowserSettings settings = new ChangeBrowserSettings(); + settings.CHANGE_AFTER = Long.toString(sourceLatest); + settings.USE_CHANGE_AFTER_FILTER = true; + + String local = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getWcInfo().getRootUrl()); + final String relativeLocal = (local.startsWith("/") ? local : "/" + local); + String relativeBranch = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl()); + relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch); + + final LinkedList>> list = + new LinkedList>>(); + try { + committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(myMergeContext.getSourceUrl()), 0, + new PairConsumer>() { + public void consume(SvnChangeList svnList, + TreeStructureNode tree) { + indicator.checkCanceled(); + if (sourceLatest >= svnList.getNumber()) return; + list.add( + new Pair>(svnList, + tree) + ); + } + } + ); + } + catch (VcsException e) { + finishWithError(context, "Checking revisions for merge fault", Collections.singletonList(e)); + } + + indicator.setText("Checking merge information..."); + // to do not go into file system while asking something on the net + for (Pair> pair : list) { + final SvnChangeList svnList = pair.getFirst(); + final SvnMergeInfoCache.MergeCheckResult checkResult = myMergeChecker.checkList(svnList); + indicator.setText2("Processing revision " + svnList.getNumber()); + + if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) { + // additionally check for being 'local' + boolean localChange = checkListForPaths(relativeLocal, relativeBranch, pair); + + if (!localChange) { + myNotMerged.add(svnList); + } + } + } + + if (myNotMerged.isEmpty()) { + finishWithError(context, "Everything is up-to-date", false); + return; + } + context.next(new ShowRevisionSelector(copyDataValue)); + } + + private class ShowRevisionSelector extends TaskDescriptor { + private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint; + + private ShowRevisionSelector(SvnBranchPointsCalculator.WrapperInvertor copyPoint) { + super("show revisions to merge", Where.AWT); + myCopyPoint = copyPoint; + } + + @Override + public void run(ContinuationContext context) { + final QuickMergeInteraction.SelectMergeItemsResult result = myInteraction.selectMergeItems(myNotMerged, myMergeTitle, myMergeChecker); + if (QuickMergeContentsVariants.cancel == result.getResultCode()) { + context.cancelEverything(); + return; + } + if (QuickMergeContentsVariants.all == result.getResultCode()) { + insertMergeAll(context); + } + else { + final List lists = result.getSelectedLists(); + if (lists.isEmpty()) return; + final MergerFactory factory = new ChangeListsMergerFactory(lists) { + @Override + public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { + return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false); + } + }; + context.next(new LocalChangesPromptTask(myMergeContext, myInteraction, false, lists, myCopyPoint), new MergeTask(myMergeContext, + myInteraction, + factory, + myMergeTitle + )); + } + } + } + + // true if errors found + static boolean checkListForPaths(String relativeLocal, + String relativeBranch, Pair> pair) { + // TODO: Such filtering logic is not clear enough so far (and probably not correct for all cases - for instance when we perform merge + // TODO: from branch1 to branch2 and have revision which contain merge changes from branch3 to branch1. + // TODO: In this case paths of child log entries will not contain neither urls from branch1 nor from branch2 - and checkEntry() method + // TODO: will return true => so such revision will not be used (and displayed) further. + + // TODO: Why do we check entries recursively - we have a revision - set of changes in the "merge from" branch? Why do we need to check + // TODO: where they came from - we want avoid some circular merges or what? Does subversion itself perform such checks or not? + final List> children = pair.getSecond().getChildren(); + boolean localChange = false; + for (TreeStructureNode child : children) { + if (checkForSubtree(child, relativeLocal, relativeBranch)) { + localChange = true; + break; + } + } + if (!localChange) { + // check self + return checkForEntry(pair.getSecond().getMe(), relativeLocal, relativeBranch); + } + return localChange; + } + + // true if errors found + private static boolean checkForSubtree(final TreeStructureNode tree, + String relativeBranch, final String localURL) { + final LinkedList> queue = new LinkedList>(); + queue.addLast(tree); + + while (!queue.isEmpty()) { + final TreeStructureNode element = queue.removeFirst(); + ProgressManager.checkCanceled(); + + if (checkForEntry(element.getMe(), localURL, relativeBranch)) return true; + queue.addAll(element.getChildren()); + } + return false; + } + + // true if errors found + // checks if either some changed path is in current branch => treat as local change + // or if no changed paths in current branch, checks if at least one path in "merge from" branch + // NOTE: this fails for "merge-source" log entries from other branches - when all changed paths are from some + // third branch - this logic treats such log entry as local. + private static boolean checkForEntry(final SVNLogEntry entry, final String localURL, String relativeBranch) { + boolean atLeastOneUnderBranch = false; + final Map map = entry.getChangedPaths(); + for (Object o : map.values()) { + final SVNLogEntryPath path = (SVNLogEntryPath)o; + if (SVNPathUtil.isAncestor(localURL, path.getPath())) { + return true; + } + if (!atLeastOneUnderBranch && SVNPathUtil.isAncestor(relativeBranch, path.getPath())) { + atLeastOneUnderBranch = true; + } + } + return !atLeastOneUnderBranch; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeInitChecksTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeInitChecksTask.java new file mode 100644 index 000000000000..a5b5c8ee870b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeInitChecksTask.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.NestedCopyType; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.WCInfo; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.util.SVNURLUtil; + +import java.io.File; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeInitChecksTask extends BaseMergeTask { + + public MergeInitChecksTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) { + super(mergeContext, interaction, "initial checks", Where.AWT); + } + + @Override + public void run(ContinuationContext continuationContext) { + SVNURL url = parseUrl(continuationContext); + if (url == null) { + return; + } + + if (SVNURLUtil.isAncestor(url, myMergeContext.getWcInfo().getUrl()) || + SVNURLUtil.isAncestor(myMergeContext.getWcInfo().getUrl(), url)) { + finishWithError(continuationContext, "Cannot merge from self", true); + return; + } + + if (!checkForSwitchedRoots()) { + continuationContext.cancelEverything(); + } + } + + private boolean checkForSwitchedRoots() { + final List infoList = myMergeContext.getVcs().getAllWcInfos(); + boolean switchedFound = false; + for (WCInfo wcInfo : infoList) { + if (FileUtil.isAncestor(new File(myMergeContext.getWcInfo().getPath()), new File(wcInfo.getPath()), true) + && NestedCopyType.switched.equals(wcInfo.getType())) { + switchedFound = true; + break; + } + } + if (switchedFound) { + return myInteraction.shouldContinueSwitchedRootFound(); + } + return true; + } + + @Nullable + private SVNURL parseUrl(ContinuationContext continuationContext) { + SVNURL url = null; + + try { + url = SvnUtil.createUrl(myMergeContext.getSourceUrl()); + } + catch (SvnBindException e) { + finishWithError(continuationContext, e.getMessage(), true); + } + + return url; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeTask.java new file mode 100644 index 000000000000..6367ce4cdc0e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergeTask.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode; +import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.ContinuationPause; +import com.intellij.util.continuation.TaskDescriptor; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; + +/** + * @author Konstantin Kolosovsky. + */ +public class MergeTask extends BaseMergeTask { + private final MergerFactory myFactory; + + // awt since only a wrapper + public MergeTask(@NotNull MergeContext mergeContext, + @NotNull QuickMergeInteraction interaction, final MergerFactory factory, + final String mergeTitle) { + super(mergeContext, interaction, mergeTitle, Where.AWT); + myFactory = factory; + } + + @Override + public void run(ContinuationContext context) { + final SVNURL sourceUrlUrl; + try { + sourceUrlUrl = SVNURL.parseURIEncoded(myMergeContext.getSourceUrl()); + } + catch (SVNException e) { + finishWithError(context, "Cannot merge: " + e.getMessage(), true); + return; + } + + final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(myMergeContext.getVcs(), + new WorkingCopyInfo(myMergeContext.getWcInfo().getPath(), true), + myFactory, sourceUrlUrl, getName(), false, + myMergeContext.getBranchName()); + final TaskDescriptor taskDescriptor = TaskDescriptor.createForBackgroundableTask(task); + // merge task will be the next after... + context.next(taskDescriptor); + // ... after we create changelist + createChangelist(context); + } + + private void createChangelist(final ContinuationPause context) { + final ChangeListManager listManager = ChangeListManager.getInstance(myMergeContext.getProject()); + String name = myMergeContext.getTitle(); + int i = 1; + boolean updateDefaultList = false; + while (true) { + final LocalChangeList changeList = listManager.findChangeList(name); + if (changeList == null) { + final LocalChangeList newList = listManager.addChangeList(name, null); + listManager.setDefaultChangeList(newList); + updateDefaultList = true; + break; + } + if (changeList.getChanges().isEmpty()) { + if (!changeList.isDefault()) { + listManager.setDefaultChangeList(changeList); + updateDefaultList = true; + } + break; + } + name = myMergeContext.getTitle() + " (" + i + ")"; + ++i; + } + if (updateDefaultList) { + context.suspend(); + listManager.invokeAfterUpdate(new Runnable() { + public void run() { + context.ping(); + } + }, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE_NOT_AWT, "", ModalityState.NON_MODAL); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergerFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergerFactory.java index 42bb70bf514c..50f881e7dce0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergerFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/MergerFactory.java @@ -15,14 +15,11 @@ */ package org.jetbrains.idea.svn.integrate; -import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; -import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.update.UpdateEventHandler; import org.tmatesoft.svn.core.SVNURL; import java.io.File; -import java.util.List; public interface MergerFactory { IMerger createMerger(final SvnVcs vcs, @@ -30,7 +27,4 @@ public interface MergerFactory { final UpdateEventHandler handler, final SVNURL currentBranchUrl, String branchName); - boolean isMergeAll(); - @Nullable - List getListsToMerge(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShelveLocalChangesTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShelveLocalChangesTask.java new file mode 100644 index 000000000000..0c1f2313ca37 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShelveLocalChangesTask.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangesUtil; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vfs.newvfs.RefreshSessionImpl; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Konstantin Kolosovsky. + */ +public class ShelveLocalChangesTask extends BaseMergeTask { + private final Intersection myIntersection; + + public ShelveLocalChangesTask(@NotNull MergeContext mergeContext, + @NotNull QuickMergeInteraction interaction, + final Intersection intersection) { + super(mergeContext, interaction, "Shelving local changes before merge", Where.POOLED); + myIntersection = intersection; + } + + @Override + public void run(final ContinuationContext context) { + final MultiMap map = myIntersection.getChangesSubset(); + + final RefreshSessionImpl session = new RefreshSessionImpl(true, false, new Runnable() { + public void run() { + context.ping(); + } + }); + + for (String name : map.keySet()) { + try { + final Collection changes = map.get(name); + ApplicationManager.getApplication().invokeAndWait(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveAllDocuments(); + } + }, ModalityState.NON_MODAL); + ShelveChangesManager.getInstance(myMergeContext.getProject()).shelveChanges(changes, myIntersection.getComment(name) + + " (auto shelve before merge)", + true + ); + session.addAllFiles(ChangesUtil.getFilesFromChanges(changes)); + } + catch (IOException e) { + finishWithError(context, e.getMessage(), true); + } + catch (VcsException e) { + finishWithError(context, e.getMessage(), true); + } + } + // first suspend to guarantee stop->then start back sequence + context.suspend(); + session.launch(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShowRecentInDialogTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShowRecentInDialogTask.java new file mode 100644 index 000000000000..92277402747e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/ShowRecentInDialogTask.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.util.PopupUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.Consumer; +import com.intellij.util.PairConsumer; +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory; +import org.jetbrains.idea.svn.dialogs.LoadRecentBranchRevisions; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.dialogs.MergeDialogI; +import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; +import org.jetbrains.idea.svn.update.UpdateEventHandler; +import org.tmatesoft.svn.core.SVNURL; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class ShowRecentInDialogTask extends BaseMergeTask { + private final LoadRecentBranchRevisions myLoader; + + public ShowRecentInDialogTask(@NotNull MergeContext mergeContext, + @NotNull QuickMergeInteraction interaction, + LoadRecentBranchRevisions loader) { + super(mergeContext, interaction, "", Where.AWT); + myLoader = loader; + } + + @Override + public void run(ContinuationContext context) { + final PairConsumer loader = new PairConsumer() { + @Override + public void consume(Long bunchSize, final MergeDialogI dialog) { + final LoadRecentBranchRevisions loader = + new LoadRecentBranchRevisions(myMergeContext, dialog.getLastNumber(), bunchSize.intValue()); + final TaskDescriptor updater = new TaskDescriptor("", Where.AWT) { + @Override + public void run(ContinuationContext context) { + dialog.addMoreLists(loader.getCommittedChangeLists()); + if (loader.isLastLoaded()) { + dialog.setEverythingLoaded(true); + } + } + + @Override + public void canceled() { + dialog.addMoreLists(Collections.emptyList()); + dialog.setEverythingLoaded(true); + } + }; + final Continuation fragmented = Continuation.createFragmented(myMergeContext.getProject(), true); + fragmented.addExceptionHandler(VcsException.class, new Consumer() { + @Override + public void consume(VcsException e) { + PopupUtil.showBalloonForActiveComponent(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), MessageType.ERROR); + } + }); + fragmented.run(loader, updater); + } + }; + final List lists = myInteraction.showRecentListsForSelection(myLoader.getCommittedChangeLists(), + myMergeContext.getTitle(), myLoader.getHelper(), + loader, myLoader.isLastLoaded()); + + if (lists != null && !lists.isEmpty()) { + final MergerFactory factory = new ChangeListsMergerFactory(lists) { + @Override + public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) { + return new GroupMerger(vcs, lists, target, handler, currentBranchUrl, branchName, false, false, false); + } + }; + // fictive branch point, just for + final SvnBranchPointsCalculator.BranchCopyData copyData = + new SvnBranchPointsCalculator.BranchCopyData(myMergeContext.getWcInfo().getUrl().toString(), -1, myMergeContext.getSourceUrl(), + -1); + context.next(new LocalChangesPromptTask(myMergeContext, myInteraction, + false, lists, new SvnBranchPointsCalculator.WrapperInvertor(false, copyData) + ), + new MergeTask(myMergeContext, myInteraction, factory, myMergeContext.getTitle()) + ); + } + else { + context.cancelEverything(); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SourceUrlCorrectionTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SourceUrlCorrectionTask.java new file mode 100644 index 000000000000..39d51de62943 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SourceUrlCorrectionTask.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2014 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.integrate; + +import com.intellij.util.continuation.ContinuationContext; +import com.intellij.util.continuation.Where; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnBranchConfigurationManager; +import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; + +/** +* @author Konstantin Kolosovsky. +*/ +public class SourceUrlCorrectionTask extends BaseMergeTask { + + public SourceUrlCorrectionTask(@NotNull MergeContext mergeContext, @NotNull QuickMergeInteraction interaction) { + super(mergeContext, interaction, "Checking branch", Where.POOLED); + } + + @Override + public void run(ContinuationContext continuationContext) { + final SVNURL branch = + SvnBranchConfigurationManager.getInstance(myMergeContext.getProject()).getSvnBranchConfigManager().getWorkingBranchWithReload( + myMergeContext.getWcInfo().getUrl(), myMergeContext.getRoot()); + if (branch != null && (!myMergeContext.getWcInfo().getUrl().equals(branch))) { + final String branchString = branch.toString(); + if (SVNPathUtil.isAncestor(branchString, myMergeContext.getWcInfo().getRootUrl())) { + final String subPath = SVNPathUtil.getRelativePath(branchString, myMergeContext.getWcInfo().getRootUrl()); + myMergeContext.setSourceUrl(SVNPathUtil.append(myMergeContext.getSourceUrl(), subPath)); + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java index ff6d3f611766..abc4b5b794c2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java @@ -32,7 +32,7 @@ public class CmdLockClient extends BaseSvnClient implements LockClient { parameters.add("--message"); parameters.add(message); - CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.lock, parameters, null); + CommandExecutor command = execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.lock, parameters, null); handleCommandCompletion(command, file, SVNEventAction.LOCKED, SVNEventAction.LOCK_FAILED, handler); } @@ -40,7 +40,7 @@ public class CmdLockClient extends BaseSvnClient implements LockClient { public void unlock(@NotNull File file, boolean force, @Nullable ISVNEventHandler handler) throws VcsException { List parameters = prepareParameters(file, force); - CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.unlock, parameters, null); + CommandExecutor command = execute(myVcs, SvnTarget.fromFile(file), SvnCommandName.unlock, parameters, null); handleCommandCompletion(command, file, SVNEventAction.UNLOCKED, SVNEventAction.UNLOCK_FAILED, handler); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java index f492a434a252..bd2c41537091 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java @@ -15,15 +15,13 @@ */ package org.jetbrains.idea.svn.mergeinfo; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.AreaMap; import com.intellij.openapi.vcs.VcsException; import com.intellij.util.PairProcessor; -import org.jetbrains.idea.svn.SvnConfiguration; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.dialogs.WCInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; @@ -36,25 +34,25 @@ import java.io.File; import java.util.Map; public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { - private final Project myProject; - private final WCInfo myWCInfo; + + @NotNull private final MergeContext myMergeContext; + // subpath [file] (local) to (subpathURL - merged FROM - to ranges list) private final AreaMap> myDataMap; private final Object myLock; private final String myFromUrlRelative; - public OneRecursiveShotMergeInfoWorker(final Project project, final WCInfo WCInfo, final String fromUrl) { - myProject = project; + public OneRecursiveShotMergeInfoWorker(@NotNull MergeContext mergeContext) { + myMergeContext = mergeContext; myLock = new Object(); - myWCInfo = WCInfo; - + myDataMap = AreaMap.create(new PairProcessor() { public boolean process(String parentUrl, String childUrl) { if (".".equals(parentUrl)) return true; return SVNPathUtil.isAncestor(ensureUrlFromSlash(parentUrl), ensureUrlFromSlash(childUrl)); } }); - final String url = SVNPathUtil.getRelativePath(myWCInfo.getRepositoryRoot(), fromUrl); + final String url = SVNPathUtil.getRelativePath(myMergeContext.getWcInfo().getRepositoryRoot(), myMergeContext.getSourceUrl()); myFromUrlRelative = ensureUrlFromSlash(url); } @@ -63,7 +61,7 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { } public void prepare() throws VcsException { - final SVNDepth depth = SvnConfiguration.getInstance(myProject).isCheckNestedForQuickMerge() ? SVNDepth.INFINITY : SVNDepth.EMPTY; + final SVNDepth depth = myMergeContext.getVcs().getSvnConfiguration().isCheckNestedForQuickMerge() ? SVNDepth.INFINITY : SVNDepth.EMPTY; ISVNPropertyHandler handler = new ISVNPropertyHandler() { public void handleProperty(File path, SVNPropertyData property) throws SVNException { final String key = keyFromFile(path); @@ -80,10 +78,9 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { } }; - final SvnVcs vcs = SvnVcs.getInstance(myProject); - File path = new File(myWCInfo.getPath()); + File path = new File(myMergeContext.getWcInfo().getPath()); - vcs.getFactory(path).createPropertyClient() + myMergeContext.getVcs().getFactory(path).createPropertyClient() .getProperty(SvnTarget.fromFile(path), SVNProperty.MERGE_INFO, SVNRevision.WORKING, depth, handler); } @@ -159,8 +156,8 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { } private String keyFromFile(final File file) { - final String path = - FileUtil.getRelativePath(myWCInfo.getPath(), file.getAbsolutePath(), File.separatorChar).replace(File.separatorChar, '/'); + final String path = FileUtil.getRelativePath(myMergeContext.getWcInfo().getPath(), file.getAbsolutePath(), File.separatorChar).replace( + File.separatorChar, '/'); return keyFromPath(path); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneShotMergeInfoHelper.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneShotMergeInfoHelper.java index 3b2f6b9c379a..72810b02b2de 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneShotMergeInfoHelper.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneShotMergeInfoHelper.java @@ -15,19 +15,20 @@ */ package org.jetbrains.idea.svn.mergeinfo; -import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; -import org.jetbrains.idea.svn.dialogs.WCInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; import org.jetbrains.idea.svn.history.SvnChangeList; import java.util.*; public class OneShotMergeInfoHelper implements MergeChecker { + private OneRecursiveShotMergeInfoWorker myWorker; private final Map> myPartiallyMerged; - public OneShotMergeInfoHelper(final Project project, final WCInfo wcInfo, final String branchPath) { - myWorker = new OneRecursiveShotMergeInfoWorker(project, wcInfo, branchPath); + public OneShotMergeInfoHelper(@NotNull MergeContext mergeContext) { + myWorker = new OneRecursiveShotMergeInfoWorker(mergeContext); myPartiallyMerged = new HashMap>(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java index d9ad5a371fd0..742e3951aa72 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java @@ -20,9 +20,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.TransparentlyFailedValue; import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.SoftHashMap; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.Nullable; @@ -32,23 +34,21 @@ import org.jetbrains.idea.svn.dialogs.WCPaths; import org.jetbrains.idea.svn.history.CopyData; import org.jetbrains.idea.svn.history.FirstInBranch; import org.jetbrains.idea.svn.history.SvnChangeList; -import org.tmatesoft.svn.core.SVNException; -import java.util.HashMap; import java.util.Map; public class SvnMergeInfoCache { private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache"); private final Project myProject; - private final MyState myState; + private final Map myCurrentUrlMapping; - public static Topic SVN_MERGE_INFO_CACHE = new Topic("SVN_MERGE_INFO_CACHE", - SvnMergeInfoCacheListener.class); + public static Topic SVN_MERGE_INFO_CACHE = + new Topic("SVN_MERGE_INFO_CACHE", SvnMergeInfoCacheListener.class); private SvnMergeInfoCache(final Project project) { myProject = project; - myState = new MyState(); + myCurrentUrlMapping = ContainerUtil.newHashMap(); } public static SvnMergeInfoCache getInstance(final Project project) { @@ -58,7 +58,7 @@ public class SvnMergeInfoCache { public void clear(final WCPaths info, final String branchPath) { final String currentUrl = info.getRootUrl(); - final MyCurrentUrlData rootMapping = myState.getCurrentUrlMapping().get(currentUrl); + final MyCurrentUrlData rootMapping = myCurrentUrlMapping.get(currentUrl); if (rootMapping != null) { final BranchInfo mergeChecker = rootMapping.getBranchInfo(branchPath); if (mergeChecker != null) { @@ -71,7 +71,7 @@ public class SvnMergeInfoCache { public MergeinfoCached getCachedState(final WCPaths info, final String branchPath) { final String currentUrl = info.getRootUrl(); - MyCurrentUrlData rootMapping = myState.getCurrentUrlMapping().get(currentUrl); + MyCurrentUrlData rootMapping = myCurrentUrlMapping.get(currentUrl); if (rootMapping != null) { final BranchInfo branchInfo = rootMapping.getBranchInfo(branchPath); if (branchInfo != null) { @@ -92,11 +92,11 @@ public class SvnMergeInfoCache { final String currentUrl = info.getRootUrl(); final String branchUrl = selectedBranch.getUrl(); - MyCurrentUrlData rootMapping = myState.getCurrentUrlMapping().get(currentUrl); + MyCurrentUrlData rootMapping = myCurrentUrlMapping.get(currentUrl); BranchInfo mergeChecker = null; if (rootMapping == null) { rootMapping = new MyCurrentUrlData(); - myState.getCurrentUrlMapping().put(currentUrl, rootMapping); + myCurrentUrlMapping.put(currentUrl, rootMapping); } else { mergeChecker = rootMapping.getBranchInfo(branchPath); } @@ -110,7 +110,7 @@ public class SvnMergeInfoCache { public boolean isMixedRevisions(final WCInfoWithBranches info, final String branchPath) { final String currentUrl = info.getRootUrl(); - final MyCurrentUrlData rootMapping = myState.getCurrentUrlMapping().get(currentUrl); + final MyCurrentUrlData rootMapping = myCurrentUrlMapping.get(currentUrl); if (rootMapping != null) { final BranchInfo branchInfo = rootMapping.getBranchInfo(branchPath); if (branchInfo != null) { @@ -120,22 +120,6 @@ public class SvnMergeInfoCache { return false; } - private static class MyState { - private Map myCurrentUrlMapping; - - private MyState() { - myCurrentUrlMapping = new HashMap(); - } - - public Map getCurrentUrlMapping() { - return myCurrentUrlMapping; - } - - public void setCurrentUrlMapping(final Map currentUrlMapping) { - myCurrentUrlMapping = currentUrlMapping; - } - } - public static enum MergeCheckResult { COMMON, MERGED, @@ -160,7 +144,7 @@ public class SvnMergeInfoCache { myPath = path; myRevision = -1; - final TransparentlyFailedValueI result = new TransparentlyFailedValue() { + final TransparentlyFailedValueI result = new TransparentlyFailedValue() { @Override public void set(CopyData copyData) { if (copyData == null) return; @@ -176,7 +160,7 @@ public class SvnMergeInfoCache { } @Override - public void fail(SVNException e) { + public void fail(VcsException e) { LOG.info(e); VcsBalloonProblemNotifier.showOverChangesView(vcs.getProject(), e.getMessage(), MessageType.ERROR); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java deleted file mode 100644 index c043188a990f..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.jetbrains.idea.svn.portable; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 10/19/12 - * Time: 3:48 PM - */ -public interface SvnMarkerInterface { -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index ce6d2b219bca..f1a57b6aa5ce 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -52,7 +52,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { // is critical for some parts of merge logic parameters.add("--xml"); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.propget, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.propget, parameters, null); return parseSingleProperty(target, command.getOutput()); } @@ -67,7 +67,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { parameters.add(property); fillListParameters(target, revision, depth, parameters, false); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.propget, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.propget, parameters, null); parseOutput(target, command.getOutput(), handler); } @@ -79,7 +79,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { List parameters = new ArrayList(); fillListParameters(target, revision, depth, parameters, true); - CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.proplist, parameters, null); + CommandExecutor command = execute(myVcs, target, SvnCommandName.proplist, parameters, null); parseOutput(target, command.getOutput(), handler); } @@ -160,9 +160,8 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { // "svn propset svn:ignore *.java . --depth empty") tries to set ignore also on child files and fails with error like // "svn: E200009: Cannot set 'svn:ignore' on a file ('...File1.java')". So here we manually force home directory to be used. // NOTE: that setting other properties (not svn:ignore) does not cause such error. - CommandUtil - .execute(myVcs, target, CommandUtil.getHomeDirectory(), isDelete ? SvnCommandName.propdel : SvnCommandName.propset, parameters, - null); + execute(myVcs, target, CommandUtil.getHomeDirectory(), isDelete ? SvnCommandName.propdel : SvnCommandName.propset, parameters, + null); } private void fillListParameters(@NotNull SvnTarget target, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java index 02992c9c591f..aef5ae942687 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java @@ -40,7 +40,7 @@ public class CmdRevertClient extends BaseSvnClient implements RevertClient { // TODO: check if that is ok for current handler implementation // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed CommandExecutor command = - CommandUtil.execute(myVcs, SvnTarget.fromFile(paths[0]), CommandUtil.getHomeDirectory(), SvnCommandName.revert, parameters, null); + execute(myVcs, SvnTarget.fromFile(paths[0]), CommandUtil.getHomeDirectory(), SvnCommandName.revert, parameters, null); FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new RevertStatusConvertor()); parser.parse(command.getOutput()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/CmdStatusClient.java similarity index 91% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/status/CmdStatusClient.java index 236aeadc80dc..235f5d1ecddc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/CmdStatusClient.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.status; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -25,10 +24,11 @@ import com.intellij.util.containers.Convertor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.portable.PortableStatus; -import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; -import org.jetbrains.idea.svn.portable.SvnStatusClientI; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandExecutor; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.jetbrains.idea.svn.commandLine.SvnExceptionWrapper; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.wc.*; @@ -49,16 +49,7 @@ import java.util.*; * Date: 1/25/12 * Time: 5:21 PM */ -public class SvnCommandLineStatusClient implements SvnStatusClientI { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient"); - - private final SvnCommandLineInfoClient myInfoClient; - @NotNull private final SvnVcs myVcs; - - public SvnCommandLineStatusClient(@NotNull SvnVcs vcs) { - myVcs = vcs; - myInfoClient = new SvnCommandLineInfoClient(vcs); - } +public class CmdStatusClient extends BaseSvnClient implements StatusClient { @Override public long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, ISVNStatusHandler handler) @@ -103,14 +94,14 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { File base = path.isDirectory() ? path : path.getParentFile(); base = CommandUtil.correctUpToExistingParent(base); - final SVNInfo infoBase = myInfoClient.doInfo(base, revision); + final SVNInfo infoBase = myFactory.createInfoClient().doInfo(base, revision); List parameters = new ArrayList(); putParameters(parameters, path, depth, remote, reportAll, includeIgnored, changeLists); CommandExecutor command; try { - command = CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.st, parameters, null); + command = execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.st, parameters, null); } catch (VcsException e) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); @@ -129,7 +120,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { if (StringUtil.isEmptyOrSpaces(result)) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, "Status request returned nothing for command: " + - command.myCommandLine.getCommandLineString())); + command.getCommandText())); } try { @@ -192,7 +183,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { CommandUtil.put(parameters, remote, "-u"); CommandUtil.put(parameters, reportAll, "--verbose"); CommandUtil.put(parameters, includeIgnored, "--no-ignore"); - // TODO: Fix this check - update corresponding parameters in SvnStatusClientI + // TODO: Fix this check - update corresponding parameters in StatusClient CommandUtil.putChangeLists(parameters, changeLists); parameters.add("--xml"); } @@ -211,7 +202,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { @Override public SVNInfo convert(File o) { try { - return myInfoClient.doInfo(o, revision); + return myFactory.createInfoClient().doInfo(o, revision); } catch (SVNException e) { throw new SvnExceptionWrapper(e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/PortableStatus.java similarity index 99% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/status/PortableStatus.java index b00cb42e8afe..e78c0cd57316 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/PortableStatus.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.status; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Getter; -import org.jetbrains.idea.svn.WorkingCopyFormat; import org.tmatesoft.svn.core.SVNLock; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNURL; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/StatusClient.java similarity index 93% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/status/StatusClient.java index 3b5828239abd..4b43e6c51211 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/StatusClient.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.status; +import org.jetbrains.idea.svn.api.SvnClient; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.ISVNStatusHandler; @@ -30,7 +31,7 @@ import java.util.Collection; * Date: 1/24/12 * Time: 9:46 AM */ -public interface SvnStatusClientI extends SvnMarkerInterface { +public interface StatusClient extends SvnClient { long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, ISVNStatusHandler handler) throws SVNException; long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, boolean collectParentExternals, ISVNStatusHandler handler) throws SVNException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnKitStatusClient.java similarity index 73% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnStatusClient.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnKitStatusClient.java index 07edfa25fa61..6f4ae27fc1e7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnkitSvnStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnKitStatusClient.java @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.portable; +package org.jetbrains.idea.svn.status; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.status.StatusClient; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.wc.ISVNStatusHandler; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNStatus; -import org.tmatesoft.svn.core.wc.SVNStatusClient; +import org.tmatesoft.svn.core.wc.*; import java.io.File; import java.util.Collection; @@ -34,14 +32,19 @@ import java.util.Collection; * Date: 1/24/12 * Time: 9:47 AM */ -public class SvnkitSvnStatusClient implements SvnStatusClientI { +public class SvnKitStatusClient extends BaseSvnClient implements StatusClient { - @Nullable private final SVNStatusClient myStatusClient; - @NotNull private final SvnVcs myVcs; + private SVNStatusClient myStatusClient; + @Nullable private final ISVNStatusFileProvider myProvider; + @Nullable private final ISVNEventHandler myHandler; - public SvnkitSvnStatusClient(@NotNull SvnVcs vcs, @Nullable SVNStatusClient statusClient) { - myVcs = vcs; - myStatusClient = statusClient; + public SvnKitStatusClient() { + this(null, null); + } + + public SvnKitStatusClient(@Nullable ISVNStatusFileProvider provider, @Nullable ISVNEventHandler handler) { + myProvider = provider; + myHandler = handler; } @Override @@ -99,6 +102,18 @@ public class SvnkitSvnStatusClient implements SvnStatusClientI { @NotNull private SVNStatusClient getStatusClient() { - return myStatusClient != null ? myStatusClient : myVcs.createStatusClient(); + // if either provider or handler is specified - we reuse same status client for all further doStatus() calls + return myHandler != null || myProvider != null ? ensureStatusClient() : myVcs.createStatusClient(); + } + + @NotNull + private SVNStatusClient ensureStatusClient() { + if (myStatusClient == null) { + myStatusClient = myVcs.createStatusClient(); + myStatusClient.setFilesProvider(myProvider); + myStatusClient.setEventHandler(myHandler); + } + + return myStatusClient; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnStatusHandler.java similarity index 99% rename from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java rename to plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnStatusHandler.java index 460f7cde959a..e5e9be5c554f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/SvnStatusHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.commandLine; +package org.jetbrains.idea.svn.status; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Getter; @@ -23,7 +23,8 @@ import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.portable.PortableStatus; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.info.SVNLockWrapper; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.wc.SVNInfo; 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 967c6e5908e6..9764c4666a61 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java @@ -54,7 +54,6 @@ import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription; @@ -286,9 +285,13 @@ public class MergeFromTheirsResolver { @Override public void run(ContinuationContext context) { try { - myVcs.createWCClient().doAdd(myOldFilePath.getIOFile(), true, true, true, SVNDepth.EMPTY, false, true); + // TODO: Previously SVNKit client was invoked with mkDir=true option - so corresponding directory would be created. Now mkDir=false + // TODO: is used. Command line also does not support automatic directory creation. + // TODO: Need to check additionally if there are cases when directory does not exist and add corresponding code. + myVcs.getFactory(myOldFilePath.getIOFile()).createAddClient() + .add(myOldFilePath.getIOFile(), SVNDepth.EMPTY, true, false, true, null); } - catch (SVNException e) { + catch (VcsException e) { context.handleException(e, true); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java index 75f59ede34b3..c637ce243981 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.portable.SvnStatusClientI; +import org.jetbrains.idea.svn.status.StatusClient; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.*; @@ -123,7 +123,7 @@ public class SvnTreeConflictResolver { } else { final Set usedToBeAdded = new HashSet(); if (myPath.isDirectory()) { - SvnStatusClientI statusClient = myVcs.getFactory(ioFile).createStatusClient(); + StatusClient statusClient = myVcs.getFactory(ioFile).createStatusClient(); statusClient.doStatus(ioFile, SVNRevision.UNDEFINED, SVNDepth.INFINITY, false, false, false, false, new ISVNStatusHandler() { @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdRelocateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdRelocateClient.java index d6b86c934773..df3d9baa3a8d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdRelocateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdRelocateClient.java @@ -24,6 +24,6 @@ public class CmdRelocateClient extends BaseSvnClient implements RelocateClient { parameters.add(toPrefix); CommandUtil.put(parameters, copyRoot, false); - CommandUtil.execute(myVcs, SvnTarget.fromFile(copyRoot), SvnCommandName.relocate, parameters, null); + execute(myVcs, SvnTarget.fromFile(copyRoot), SvnCommandName.relocate, parameters, null); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdUpdateClient.java index a6a4d0f2095f..051be8945b3e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdUpdateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/CmdUpdateClient.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener; import org.jetbrains.idea.svn.commandLine.CommandUtil; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient; import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.SVNEvent; @@ -65,8 +64,7 @@ public class CmdUpdateClient extends SvnKitUpdateClient { } private void checkWorkingCopy(@NotNull File path) throws SVNException { - final SvnCommandLineInfoClient infoClient = new SvnCommandLineInfoClient(myVcs); - final SVNInfo info = infoClient.doInfo(path, SVNRevision.UNDEFINED); + final SVNInfo info = myFactory.createInfoClient().doInfo(path, SVNRevision.UNDEFINED); if (info == null || info.getURL() == null) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY, path.getPath())); @@ -82,7 +80,7 @@ public class CmdUpdateClient extends SvnKitUpdateClient { final BaseUpdateCommandListener listener = createCommandListener(paths, updatedToRevision, base); try { - CommandUtil.execute(myVcs, SvnTarget.fromFile(base), command, parameters, listener); + execute(myVcs, SvnTarget.fromFile(base), command, parameters, listener); } catch (VcsException e) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/upgrade/CmdUpgradeClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/upgrade/CmdUpgradeClient.java index cb516eb4386f..3a4e6eb6ce92 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/upgrade/CmdUpgradeClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/upgrade/CmdUpgradeClient.java @@ -51,7 +51,7 @@ public class CmdUpgradeClient extends BaseSvnClient implements UpgradeClient { FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new UpgradeStatusConvertor()); UpgradeLineCommandListener listener = new UpgradeLineCommandListener(parser); - CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.upgrade, parameters, listener); + execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.upgrade, parameters, listener); listener.throwIfException(); } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java index f3552e99c392..bf0c9eebb1bb 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnAuthenticationTest.java @@ -24,10 +24,7 @@ import com.intellij.testFramework.PlatformTestCase; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.ui.UIUtil; import junit.framework.Assert; -import org.jetbrains.idea.svn.auth.ProviderType; -import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction; -import org.jetbrains.idea.svn.auth.SvnAuthenticationListener; -import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider; +import org.jetbrains.idea.svn.auth.*; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java index 13d8202ce642..76cc3c8ce9af 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java @@ -2,8 +2,8 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.vcs.VcsException; import junit.framework.Assert; +import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.CommandExecutor; -import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.junit.Test; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -36,7 +36,7 @@ public class SvnCommandLineStabilityTest extends Svn17TestCase { SvnVcs vcs = SvnVcs.getInstance(myProject); File workingDirectory = new File(myWorkingCopyDir.getPath()); CommandExecutor command = - CommandUtil.execute(vcs, SvnTarget.fromFile(workingDirectory), workingDirectory, SvnCommandName.info, parameters, null); + BaseSvnClient.execute(vcs, SvnTarget.fromFile(workingDirectory), workingDirectory, SvnCommandName.info, parameters, null); final String result = command.getOutput(); System.out.println(result); Assert.assertNotNull(result); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnMergeInfoTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnMergeInfoTest.java index cfcb5c728b56..05c382f9e8a2 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnMergeInfoTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnMergeInfoTest.java @@ -16,14 +16,15 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vcs.CommittedChangesProvider; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.VcsConfiguration; -import com.intellij.openapi.vcs.VcsTestUtil; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.dialogs.MergeContext; import org.jetbrains.idea.svn.dialogs.WCInfo; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; @@ -33,26 +34,49 @@ import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache; import org.junit.Assert; import org.junit.Test; import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; +import java.io.IOException; import java.util.List; // TODO: Many tests in this class are written with direct SVNKit usage - could not utilize it for svn 1.8 public class SvnMergeInfoTest extends Svn17TestCase { + + private static final String CONTENT1 = "123\n456\n123"; + private static final String CONTENT2 = "123\n456\n123\n4"; + private File myBranchVcsRoot; private ProjectLevelVcsManagerImpl myProjectLevelVcsManager; private WCInfo myWCInfo; private OneShotMergeInfoHelper myOneShotMergeInfoHelper; + private SvnVcs myVcs; + private BranchInfo myMergeChecker; + + private File trunk; + private File folder; + private File folder1; + private File f1; + private File f2; + + private String myTrunkUrl; + private String myBranchUrl; + private MergeContext myMergeContext; + @Override public void setUp() throws Exception { super.setUp(); + myTrunkUrl = myRepoUrl + "/trunk"; + myBranchUrl = myRepoUrl + "/branch"; + myBranchVcsRoot = new File(myTempDirFixture.getTempDirPath(), "branch"); myBranchVcsRoot.mkdir(); @@ -60,528 +84,379 @@ public class SvnMergeInfoTest extends Svn17TestCase { myProjectLevelVcsManager.setDirectoryMapping(myBranchVcsRoot.getAbsolutePath(), SvnVcs.VCS_NAME); VirtualFile vcsRoot = LocalFileSystem.getInstance().findFileByIoFile(myBranchVcsRoot); - Node node = new Node(vcsRoot, SVNURL.parseURIEncoded(myRepoUrl + "/branch"), SVNURL.parseURIEncoded(myRepoUrl)); + Node node = new Node(vcsRoot, SVNURL.parseURIEncoded(myBranchUrl), SVNURL.parseURIEncoded(myRepoUrl)); RootUrlInfo root = new RootUrlInfo(node, WorkingCopyFormat.ONE_DOT_SIX, vcsRoot, null); myWCInfo = new WCInfo(root, true, SVNDepth.INFINITY); - myOneShotMergeInfoHelper = new OneShotMergeInfoHelper(myProject, myWCInfo, myRepoUrl + "/trunk"); + myMergeContext = new MergeContext(SvnVcs.getInstance(myProject), myTrunkUrl, myWCInfo, SVNPathUtil.tail(myTrunkUrl), vcsRoot); + myOneShotMergeInfoHelper = new OneShotMergeInfoHelper(myMergeContext); - SvnConfiguration.getInstance(myProject).setCheckNestedForQuickMerge(true); -// AbstractVcs vcsFound = myProjectLevelVcsManager.findVcsByName(SvnVcs.VCS_NAME); -// Assert.assertEquals(1, myProjectLevelVcsManager.getRootsUnderVcs(vcsFound).length); + myVcs = SvnVcs.getInstance(myProject); + myVcs.getSvnConfiguration().setCheckNestedForQuickMerge(true); + + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + + final String repoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); + myMergeChecker = new BranchInfo(myVcs, repoUrl, repoUrl + "/branch", repoUrl + "/trunk", repoUrl + "/trunk"); } @Test public void testSimpleNotMerged() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 + editAndCommit(trunk, f1); - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); - - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList = changeListList.get(0); - final BranchInfo mergeChecker = - new BranchInfo(vcs, myRepoUrl, myRepoUrl + "/branch", myRepoUrl + "/trunk", myRepoUrl + "/trunk"); - final SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, oneShotResult); + assertMergeResult(getTrunkChangeLists(), SvnMergeInfoCache.MergeCheckResult.NOT_MERGED); } @Test public void testSimpleMerged() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); + editAndCommit(trunk, f1); // rev 4: record as merged into branch - runInAndVerifyIgnoreOutput("merge", "-c", "3", myRepoUrl + "/trunk", myBranchVcsRoot.getAbsolutePath(), "--record-only"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); + recordMerge(myBranchVcsRoot, myTrunkUrl, "-c", "3"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); - - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && "/trunk:3".equals(data.getValue().getString()); - - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList = changeListList.get(0); - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - final SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.MERGED, oneShotResult); + assertMergeInfo(myBranchVcsRoot, "/trunk:3"); + assertMergeResult(getTrunkChangeLists(), SvnMergeInfoCache.MergeCheckResult.MERGED); } @Test public void testEmptyMergeinfoBlocks() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); + editAndCommit(trunk, f1); // rev 4: record as merged into branch - runInAndVerifyIgnoreOutput("merge", "-c", "3", myRepoUrl + "/trunk", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); + merge(myBranchVcsRoot, myTrunkUrl, "-c", "3"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); // rev5: put blocking empty mergeinfo //runInAndVerifyIgnoreOutput("merge", "-c", "-3", myRepoUrl + "/trunk/folder", new File(myBranchVcsRoot, "folder").getAbsolutePath(), "--record-only")); - runInAndVerifyIgnoreOutput("merge", "-r", "3:2", myRepoUrl + "/trunk/folder", new File(myBranchVcsRoot, "folder").getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); + merge(new File(myBranchVcsRoot, "folder"), myTrunkUrl + "/folder", "-r", "3:2"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); + assertMergeInfo(myBranchVcsRoot, "/trunk:3"); + assertMergeInfo(new File(myBranchVcsRoot, "folder"), ""); - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && "/trunk:3".equals(data.getValue().getString()); - final SVNPropertyData dataFolder = wcClient.doGetProperty(new File(myBranchVcsRoot, "folder"), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert dataFolder != null && dataFolder.getValue() != null && "".equals(dataFolder.getValue().getString()); - - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList = changeListList.get(0); - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - final SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, oneShotResult); // todo + assertMergeResult(getTrunkChangeLists(), SvnMergeInfoCache.MergeCheckResult.NOT_MERGED); } @Test public void testNonInheritableMergeinfo() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); + editAndCommit(trunk, f1); // rev 4: record non inheritable merge - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3*", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); + setMergeInfo(myBranchVcsRoot, "/trunk:3*"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); + assertMergeInfo(myBranchVcsRoot, "/trunk:3*"); - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && "/trunk:3*".equals(data.getValue().getString()); - - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList = changeListList.get(0); - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - final SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, oneShotResult); + assertMergeResult(getTrunkChangeLists(), SvnMergeInfoCache.MergeCheckResult.NOT_MERGED); } @Test public void testOnlyImmediateInheritableMergeinfo() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); - Thread.sleep(100); + editAndCommit(trunk, f1, CONTENT1); // rev4 - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123\n4"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); + editAndCommit(trunk, f1, CONTENT2); + + updateFile(myBranchVcsRoot); // rev 4: record non inheritable merge - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3,4", myBranchVcsRoot.getAbsolutePath()); - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3", new File(myBranchVcsRoot, "folder").getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); + setMergeInfo(myBranchVcsRoot, "/trunk:3,4"); + setMergeInfo(new File(myBranchVcsRoot, "folder"), "/trunk:3"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); + assertMergeInfo(myBranchVcsRoot, "/trunk:3-4", "/trunk:3,4"); + assertMergeInfo(new File(myBranchVcsRoot, "folder"), "/trunk:3"); - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && ("/trunk:3-4".equals(data.getValue().getString()) || - "/trunk:3,4".equals(data.getValue().getString())); - final SVNPropertyData dataFolder = wcClient.doGetProperty(new File(myBranchVcsRoot, "folder"), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert dataFolder != null && dataFolder.getValue() != null && "/trunk:3".equals(dataFolder.getValue().getString()); + final List changeListList = getTrunkChangeLists(); - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList4 = changeListList.get(0); - assert changeList4.getNumber() == 4; - final SvnChangeList changeList3 = changeListList.get(1); - assert changeList3.getNumber() == 3; - - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList3, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.MERGED.equals(result); - result = mergeChecker.checkList(changeList4, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList3); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.MERGED, oneShotResult); - - final SvnMergeInfoCache.MergeCheckResult oneShotResult1 = myOneShotMergeInfoHelper.checkList(changeList4); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, oneShotResult1); + assertRevisions(changeListList, 4, 3); + assertMergeResult(changeListList, SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, SvnMergeInfoCache.MergeCheckResult.MERGED); } @Test public void testTwoPaths() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - final File folder1 = new File(folder, "folder1"); - folder1.mkdir(); - final File f2 = new File(folder1, "f2.txt"); - f2.createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createTwoFolderStructure(myBranchVcsRoot); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f2); - VcsTestUtil.editFileInCommand(myProject, vf2, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); + editFile(f1); + editFile(f2); + commitFile(trunk); + + updateFile(myBranchVcsRoot); // rev 4: record non inheritable merge - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3", myBranchVcsRoot.getAbsolutePath()); + setMergeInfo(myBranchVcsRoot, "/trunk:3"); // this makes not merged for f2 path - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3*", new File(myBranchVcsRoot, "folder/folder1").getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); + setMergeInfo(new File(myBranchVcsRoot, "folder/folder1"), "/trunk:3*"); + commitFile(myBranchVcsRoot); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); + assertMergeInfo(myBranchVcsRoot, "/trunk:3"); + assertMergeInfo(new File(myBranchVcsRoot, "folder/folder1"), "/trunk:3*"); - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && "/trunk:3".equals(data.getValue().getString()); - final SVNPropertyData dataFolder = wcClient.doGetProperty(new File(myBranchVcsRoot, "folder/folder1"), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert dataFolder != null && dataFolder.getValue() != null && "/trunk:3*".equals(dataFolder.getValue().getString()); + final List changeListList = getTrunkChangeLists(); - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList3 = changeListList.get(0); - assert changeList3.getNumber() == 3; - - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList3, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); - - myOneShotMergeInfoHelper.prepare(); - final SvnMergeInfoCache.MergeCheckResult oneShotResult = myOneShotMergeInfoHelper.checkList(changeList3); - Assert.assertEquals(SvnMergeInfoCache.MergeCheckResult.NOT_MERGED, oneShotResult); + assertRevisions(changeListList, 3); + assertMergeResult(changeListList, SvnMergeInfoCache.MergeCheckResult.NOT_MERGED); } @Test public void testWhenInfoInRepo() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + final File fullBranch = newFolder(myTempDirFixture.getTempDirPath(), "fullBranch"); - final File fullBranch = new File(myTempDirFixture.getTempDirPath(), "fullBranch"); - fullBranch.mkdir(); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - // this will be taken as branch wc root - final File folder1 = new File(folder, "folder1"); - folder1.mkdir(); - final File f2 = new File(folder1, "f2.txt"); - f2.createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", fullBranch.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch/folder/folder1", myBranchVcsRoot.getAbsolutePath()); + createTwoFolderStructure(fullBranch); + // folder1 will be taken as branch wc root + checkOutFile(myBranchUrl + "/folder/folder1", myBranchVcsRoot); // rev 3 : f2 changed - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f2); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); + editAndCommit(trunk, f2); // rev 4: record as merged into branch using full branch WC - runInAndVerifyIgnoreOutput("merge", "-c", "3", myRepoUrl + "/trunk", fullBranch.getAbsolutePath(), "--record-only"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", fullBranch.getAbsolutePath()); + recordMerge(fullBranch, myTrunkUrl, "-c", "3"); + commitFile(fullBranch); + updateFile(myBranchVcsRoot); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); + final List changeListList = getTrunkChangeLists(); - final SvnVcs vcs = SvnVcs.getInstance(myProject); - - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - - final SvnChangeList changeList3 = changeListList.get(0); - assert changeList3.getNumber() == 3; - - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList3, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.MERGED.equals(result); + assertRevisions(changeListList, 3); + assertMergeResult(changeListList.get(0), SvnMergeInfoCache.MergeCheckResult.MERGED); } @Test public void testMixedWorkingRevisions() throws Exception { - enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); - enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); - - final File trunk = new File(myTempDirFixture.getTempDirPath(), "trunk"); - trunk.mkdir(); - Thread.sleep(100); - final File folder = new File(trunk, "folder"); - folder.mkdir(); - Thread.sleep(100); - final File f1 = new File(folder, "f1.txt"); - f1.createNewFile(); - new File(folder, "f2.txt").createNewFile(); - Thread.sleep(100); - - runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myRepoUrl + "/trunk"); - runInAndVerifyIgnoreOutput("copy", "-m", "test", myRepoUrl + "/trunk", myRepoUrl + "/branch"); - - FileUtil.delete(trunk); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/trunk", trunk.getAbsolutePath()); - runInAndVerifyIgnoreOutput("co", myRepoUrl + "/branch", myBranchVcsRoot.getAbsolutePath()); + createOneFolderStructure(); // rev 3 - final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f1); - VcsTestUtil.editFileInCommand(myProject, vf, "123\n456\n123"); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", trunk.getAbsolutePath()); + editAndCommit(trunk, f1); // rev 4: record non inheritable merge - runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", "/trunk:3", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); - runInAndVerifyIgnoreOutput("ci", "-m", "test", myBranchVcsRoot.getAbsolutePath()); - - Thread.sleep(100); + setMergeInfo(myBranchVcsRoot, "/trunk:3"); + commitFile(myBranchVcsRoot); // ! no update! - final SvnVcs vcs = SvnVcs.getInstance(myProject); - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNPropertyData data = wcClient.doGetProperty(myBranchVcsRoot, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); - assert data != null && data.getValue() != null && "/trunk:3".equals(data.getValue().getString()); + assertMergeInfo(myBranchVcsRoot, "/trunk:3"); - final SVNInfo f1info = wcClient.doInfo(new File(myBranchVcsRoot, "folder/f1.txt"), SVNRevision.UNDEFINED); + final SVNInfo f1info = myVcs.getInfo(new File(myBranchVcsRoot, "folder/f1.txt")); assert f1info.getRevision().getNumber() == 2; - final CommittedChangesProvider committedChangesProvider = vcs.getCommittedChangesProvider(); - final List changeListList = - committedChangesProvider.getCommittedChanges(committedChangesProvider.createDefaultSettings(), - new SvnRepositoryLocation(myRepoUrl + "/trunk"), 0); - + final List changeListList = getTrunkChangeLists(); final SvnChangeList changeList = changeListList.get(0); - final String encodedRepoUrl = SVNURL.parseURIDecoded(myRepoUrl).toString(); - final BranchInfo mergeChecker = - new BranchInfo(vcs, encodedRepoUrl, encodedRepoUrl + "/branch", encodedRepoUrl + "/trunk", encodedRepoUrl + "/trunk"); - SvnMergeInfoCache.MergeCheckResult result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(result); + + assertMergeResult(changeList, SvnMergeInfoCache.MergeCheckResult.NOT_MERGED); // and after update - runInAndVerifyIgnoreOutput("up", myBranchVcsRoot.getAbsolutePath()); - Thread.sleep(100); + updateFile(myBranchVcsRoot); + myMergeChecker.clear(); - mergeChecker.clear(); - result = mergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath()); - assert SvnMergeInfoCache.MergeCheckResult.MERGED.equals(result); + assertMergeResult(changeList, SvnMergeInfoCache.MergeCheckResult.MERGED); + } + + private void createOneFolderStructure() throws InterruptedException, IOException { + trunk = newFolder(myTempDirFixture.getTempDirPath(), "trunk"); + folder = newFolder(trunk, "folder"); + f1 = newFile(folder, "f1.txt"); + f2 = newFile(folder, "f2.txt"); + waitTime(); + + importAndCheckOut(trunk); + } + + private void createTwoFolderStructure(File branchFolder) throws InterruptedException, IOException { + trunk = newFolder(myTempDirFixture.getTempDirPath(), "trunk"); + folder = newFolder(trunk, "folder"); + f1 = newFile(folder, "f1.txt"); + folder1 = newFolder(folder, "folder1"); + f2 = newFile(folder1, "f2.txt"); + waitTime(); + + importAndCheckOut(trunk, branchFolder); + } + + @NotNull + private List getTrunkChangeLists() throws com.intellij.openapi.vcs.VcsException { + final CommittedChangesProvider provider = myVcs.getCommittedChangesProvider(); + + return provider.getCommittedChanges(provider.createDefaultSettings(), new SvnRepositoryLocation(myTrunkUrl), 0); + } + + private void importAndCheckOut(@NotNull File trunk) throws IOException { + importAndCheckOut(trunk, myBranchVcsRoot); + } + + private void importAndCheckOut(@NotNull File trunk, @NotNull File branch) throws IOException { + runInAndVerifyIgnoreOutput("import", "-m", "test", trunk.getAbsolutePath(), myTrunkUrl); + runInAndVerifyIgnoreOutput("copy", "-m", "test", myTrunkUrl, myBranchUrl); + + FileUtil.delete(trunk); + checkOutFile(myTrunkUrl, trunk); + checkOutFile(myBranchUrl, branch); + } + + @NotNull + private VirtualFile editAndCommit(@NotNull File trunk, @NotNull File file) throws InterruptedException, IOException { + return editAndCommit(trunk, file, CONTENT1); + } + + @NotNull + private VirtualFile editAndCommit(@NotNull File trunk, @NotNull File file, @NotNull String content) + throws InterruptedException, IOException { + final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + + return editAndCommit(trunk, vf, content); + } + + @NotNull + private VirtualFile editAndCommit(@NotNull File trunk, @NotNull VirtualFile file, @NotNull String content) + throws InterruptedException, IOException { + editFile(file, content); + commitFile(trunk); + + return file; + } + + private void editFile(@NotNull File file) throws InterruptedException { + editFile(file, CONTENT1); + } + + private void editFile(@NotNull File file, @NotNull String content) throws InterruptedException { + final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + + editFile(vf, content); + } + + private void editFile(@NotNull VirtualFile file, @NotNull String content) throws InterruptedException { + VcsTestUtil.editFileInCommand(myProject, file, content); + waitTime(); + } + + private void assertMergeInfo(@NotNull File file, @NotNull String... values) throws SVNException { + // TODO: Replace with ClientFactory model + final SvnVcs vcs = SvnVcs.getInstance(myProject); + final SVNWCClient wcClient = vcs.createWCClient(); + final SVNPropertyData data = wcClient.doGetProperty(file, "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING); + assert data != null && data.getValue() != null; + + boolean result = false; + + for (String value : values) { + result |= value.equals(data.getValue().getString()); + } + + assert result; + } + + private void assertMergeResult(@NotNull List changeLists, @NotNull SvnMergeInfoCache.MergeCheckResult... mergeResults) + throws VcsException { + myOneShotMergeInfoHelper.prepare(); + + for (int i = 0; i < mergeResults.length; i++) { + SvnChangeList changeList = changeLists.get(i); + + assertMergeResult(changeList, mergeResults[i]); + assertMergeResultOneShot(changeList, mergeResults[i]); + } + } + + private void assertMergeResult(@NotNull SvnChangeList changeList, @NotNull SvnMergeInfoCache.MergeCheckResult mergeResult) { + assert mergeResult.equals(myMergeChecker.checkList(changeList, myBranchVcsRoot.getAbsolutePath())); + } + + private void assertMergeResultOneShot(@NotNull SvnChangeList changeList, @NotNull SvnMergeInfoCache.MergeCheckResult mergeResult) { + Assert.assertEquals(mergeResult, myOneShotMergeInfoHelper.checkList(changeList)); + } + + private static void assertRevisions(@NotNull List changeLists, @NotNull int... revisions) { + for (int i = 0; i < revisions.length; i++) { + assert changeLists.get(i).getNumber() == revisions[i]; + } + } + + private void commitFile(@NotNull File file) throws IOException, InterruptedException { + runInAndVerifyIgnoreOutput("ci", "-m", "test", file.getAbsolutePath()); + waitTime(); + } + + private void updateFile(@NotNull File file) throws IOException, InterruptedException { + runInAndVerifyIgnoreOutput("up", file.getAbsolutePath()); + waitTime(); + } + + private void checkOutFile(@NotNull String url, @NotNull File directory) throws IOException { + runInAndVerifyIgnoreOutput("co", url, directory.getAbsolutePath()); + } + + private void setMergeInfo(@NotNull File file, @NotNull String value) throws IOException, InterruptedException { + runInAndVerifyIgnoreOutput("propset", "svn:mergeinfo", value, file.getAbsolutePath()); + waitTime(); + } + + private void merge(@NotNull File file, @NotNull String url, @NotNull String... revisions) throws IOException, InterruptedException { + merge(file, url, false, revisions); + } + + private void recordMerge(@NotNull File file, @NotNull String url, @NotNull String... revisions) throws IOException, InterruptedException { + merge(file, url, true, revisions); + } + + private void merge(@NotNull File file, @NotNull String url, boolean recordOnly, @NotNull String... revisions) + throws IOException, InterruptedException { + List parameters = ContainerUtil.newArrayList(); + + parameters.add("merge"); + ContainerUtil.addAll(parameters, revisions); + parameters.add(url); + parameters.add(file.getAbsolutePath()); + if (recordOnly) { + parameters.add("--record-only"); + } + + runInAndVerifyIgnoreOutput(ArrayUtil.toObjectArray(parameters, String.class)); + waitTime(); + } + + @NotNull + private static File newFile(File parent, String name) throws IOException { + final File f1 = new File(parent, name); + f1.createNewFile(); + return f1; + } + + @NotNull + private static File newFolder(String parent, String name) throws InterruptedException { + final File trunk = new File(parent, name); + trunk.mkdir(); + waitTime(); + return trunk; + } + + @NotNull + private static File newFolder(File parent, String name) throws InterruptedException { + final File trunk = new File(parent, name); + trunk.mkdir(); + waitTime(); + return trunk; + } + + private static void waitTime() throws InterruptedException { + Thread.sleep(100); } } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java index 1328b94fd7f0..6169f9415548 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java @@ -30,6 +30,8 @@ import com.intellij.util.Processor; import com.intellij.util.containers.Convertor; import junit.framework.Assert; import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; +import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; import org.junit.Before; import org.tmatesoft.svn.core.*; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java index 5bf5d7651f94..ec78b9fd5f55 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java @@ -24,11 +24,11 @@ import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import junit.framework.Assert; import junit.framework.TestCase; -import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; -import org.jetbrains.idea.svn.commandLine.SvnInfoHandler; -import org.jetbrains.idea.svn.commandLine.SvnStatusHandler; -import org.jetbrains.idea.svn.portable.IdeaSVNInfo; -import org.jetbrains.idea.svn.portable.PortableStatus; +import org.jetbrains.idea.svn.status.CmdStatusClient; +import org.jetbrains.idea.svn.info.SvnInfoHandler; +import org.jetbrains.idea.svn.status.SvnStatusHandler; +import org.jetbrains.idea.svn.info.IdeaSVNInfo; +import org.jetbrains.idea.svn.status.PortableStatus; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; @@ -671,14 +671,17 @@ public class SvnParseCommandLineParseTest extends TestCase { final String basePath = "C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main"; final SvnStatusHandler[] handler = new SvnStatusHandler[1]; final File baseFile = new File(basePath); - final SvnStatusHandler.ExternalDataCallback callback = SvnCommandLineStatusClient.createStatusCallback(new ISVNStatusHandler() { + final SvnStatusHandler.ExternalDataCallback callback = CmdStatusClient.createStatusCallback(new ISVNStatusHandler() { @Override public void handleStatus(SVNStatus status) throws SVNException { System.out.println(status.getURL()); - if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\MacMessagesParser.java").equals(status.getFile())) { + if (new File( + "C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\MacMessagesParser.java") + .equals(status.getFile())) { Assert.assertEquals("http://external/src/com/slave/MacMessagesParser.java", status.getURL().toString()); } - if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\SomeOtherClass.java").equals(status.getFile())) { + if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\SomeOtherClass.java") + .equals(status.getFile())) { Assert.assertEquals("http://external/src/com/slave/SomeOtherClass.java", status.getURL().toString()); } } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnProtocolsTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnProtocolsTest.java index 69e3b510f4b4..6c9be603e362 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnProtocolsTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnProtocolsTest.java @@ -35,6 +35,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; import com.intellij.util.containers.Convertor; import junit.framework.Assert; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; import org.junit.Before; import org.tmatesoft.svn.core.*; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java index bc1d2cd4fd25..d1d1f58a2e63 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java @@ -37,10 +37,7 @@ import org.jetbrains.idea.SvnTestCase; import org.jetbrains.idea.svn.branchConfig.InfoReliability; import org.jetbrains.idea.svn.branchConfig.InfoStorage; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; -import org.jetbrains.idea.svn.dialogs.MergeDialogI; -import org.jetbrains.idea.svn.dialogs.QuickMerge; -import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants; -import org.jetbrains.idea.svn.dialogs.WCInfo; +import org.jetbrains.idea.svn.dialogs.*; import org.jetbrains.idea.svn.integrate.SvnBranchItem; import org.jetbrains.idea.svn.mergeinfo.MergeChecker; import org.junit.Before; @@ -107,9 +104,7 @@ public class SvnQuickMergeTest extends Svn17TestCase { VcsTestUtil.editFileInCommand(myProject, myBranchTree.myS1File, "edited in branch"); runInAndVerifyIgnoreOutput(myBranchRoot, "ci", "-m", "change in branch", myBranchTree.myS1File.getPath()); - final WCInfo found = getWcInfo(); - final QuickMerge quickMerge = - new QuickMerge(myProject, myBranchUrl, found, SVNPathUtil.tail(myBranchUrl), myWorkingCopyDir); + final QuickMerge quickMerge = newQuickMerge(myBranchUrl); // by default merges all final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() { @Override @@ -174,9 +169,7 @@ public class SvnQuickMergeTest extends Svn17TestCase { } // we should get exactly 2 revisions for selection (copy and change in b2) - final WCInfo found = getWcInfo(); - final QuickMerge quickMerge = - new QuickMerge(myProject, myBranchUrl, found, SVNPathUtil.tail(myBranchUrl), myWorkingCopyDir); + final QuickMerge quickMerge = newQuickMerge(myBranchUrl); // by default merges all final AtomicReference selectionError = new AtomicReference(); final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() { @@ -268,9 +261,7 @@ public class SvnQuickMergeTest extends Svn17TestCase { runInAndVerifyIgnoreOutput(myBranchRoot, "ci", "-m", "change in b2", myBranchTree.myS2File.getPath()); // we should get exactly 2 revisions for selection (copy and change in b2) - final WCInfo found = getWcInfo(); - final QuickMerge quickMerge = - new QuickMerge(myProject, myRepoUrl + "/branches/b2", found, SVNPathUtil.tail(myRepoUrl + "/branches/b2"), myWorkingCopyDir); + final QuickMerge quickMerge = newQuickMerge(myRepoUrl + "/branches/b2"); // by default merges all final AtomicReference selectionError = new AtomicReference(); final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() { @@ -344,9 +335,7 @@ public class SvnQuickMergeTest extends Svn17TestCase { Thread.sleep(10); } - final WCInfo found = getWcInfo(); - final QuickMerge quickMerge = - new QuickMerge(myProject, myBranchUrl, found, SVNPathUtil.tail(myBranchUrl), myWorkingCopyDir); + final QuickMerge quickMerge = newQuickMerge(myBranchUrl); // by default merges all final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() { @Override @@ -436,9 +425,7 @@ public class SvnQuickMergeTest extends Svn17TestCase { myTree = new SubTree(myWorkingCopyDir); //reload refreshSvnMappingsSynchronously(); - final WCInfo found = getWcInfo(); - final QuickMerge quickMerge = - new QuickMerge(myProject, trunkUrl, found, SVNPathUtil.tail(trunkUrl), myWorkingCopyDir); + final QuickMerge quickMerge = newQuickMerge(trunkUrl); // by default merges all final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction(); final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor(); @@ -466,6 +453,12 @@ public class SvnQuickMergeTest extends Svn17TestCase { Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus()); } + private QuickMerge newQuickMerge(@NotNull String sourceUrl) { + MergeContext mergeContext = new MergeContext(myVcs, sourceUrl, getWcInfo(), SVNPathUtil.tail(sourceUrl), myWorkingCopyDir); + + return new QuickMerge(mergeContext); + } + private static class WaitingTaskDescriptor extends TaskDescriptor { private static final long TEST_TIMEOUT = TimeUnit.MINUTES.toMillis(200); private final Semaphore mySemaphore; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestInteractiveAuthentication.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestInteractiveAuthentication.java index c12cfbb055c1..3b4b827a30a3 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestInteractiveAuthentication.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestInteractiveAuthentication.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn; import com.intellij.util.containers.Convertor; import org.jetbrains.idea.svn.auth.ProviderType; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.*; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java index 9652a1f25c8a..e8068989374b 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java @@ -24,14 +24,14 @@ import com.intellij.testFramework.PlatformTestCase; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.ui.UIUtil; import junit.framework.Assert; -import org.jetbrains.idea.svn.SvnAuthenticationManager; -import org.jetbrains.idea.svn.SvnAuthenticationNotifier; +import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; +import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.auth.ProviderType; import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction; import org.jetbrains.idea.svn.auth.SvnAuthenticationListener; -import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider; +import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnMergeInfoTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnMergeInfoTest.java index e4928ef30199..986ee0748b79 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnMergeInfoTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnMergeInfoTest.java @@ -25,6 +25,7 @@ import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.dialogs.MergeContext; import org.jetbrains.idea.svn.dialogs.WCInfo; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; @@ -43,11 +44,13 @@ import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.List; +// TODO: Full duplicate of SvnMergeInfoTest in org.jetbrains.idea.svn (before latter was refactored). public class SvnMergeInfoTest extends Svn16TestCase { private File myBranchVcsRoot; private ProjectLevelVcsManagerImpl myProjectLevelVcsManager; private WCInfo myWCInfo; private OneShotMergeInfoHelper myOneShotMergeInfoHelper; + private MergeContext myMergeContext; @Override public void setUp() throws Exception { @@ -63,7 +66,8 @@ public class SvnMergeInfoTest extends Svn16TestCase { Node node = new Node(vcsRoot, SVNURL.parseURIEncoded(myRepoUrl + "/branch"), SVNURL.parseURIEncoded(myRepoUrl)); RootUrlInfo root = new RootUrlInfo(node, WorkingCopyFormat.ONE_DOT_SIX, vcsRoot, null); myWCInfo = new WCInfo(root, true, SVNDepth.INFINITY); - myOneShotMergeInfoHelper = new OneShotMergeInfoHelper(myProject, myWCInfo, myRepoUrl + "/trunk"); + myMergeContext = new MergeContext(SvnVcs.getInstance(myProject), myRepoUrl + "/trunk", myWCInfo, "trunk", vcsRoot); + myOneShotMergeInfoHelper = new OneShotMergeInfoHelper(myMergeContext); SvnConfiguration.getInstance(myProject).setCheckNestedForQuickMerge(true); // AbstractVcs vcsFound = myProjectLevelVcsManager.findVcsByName(SvnVcs.VCS_NAME);