summaryrefslogtreecommitdiff
path: root/plugins/svn4idea
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/svn4idea')
-rw-r--r--plugins/svn4idea/bindSvn/bindSvn.iml25
-rw-r--r--plugins/svn4idea/bindSvn/lib/javahl.jarbin0 -> 168896 bytes
-rw-r--r--plugins/svn4idea/bindSvn/lib/javahlsrc.zipbin0 -> 210516 bytes
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java109
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java30
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java49
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java925
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java104
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java217
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java40
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java (renamed from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java)32
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java (renamed from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java)20
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java415
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java (renamed from plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java)18
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java54
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java121
-rw-r--r--plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java47
-rw-r--r--plugins/svn4idea/lib/sqljet.jarbin755723 -> 757103 bytes
-rw-r--r--plugins/svn4idea/lib/svnkit-javahl.jarbin330142 -> 330141 bytes
-rw-r--r--plugins/svn4idea/lib/svnkit-javahl16.zipbin166033 -> 166032 bytes
-rw-r--r--plugins/svn4idea/lib/svnkit.jarbin3376358 -> 3378829 bytes
-rw-r--r--plugins/svn4idea/lib/svnkitsrc.zipbin2119640 -> 2120588 bytes
-rw-r--r--plugins/svn4idea/lib/trilead.jarbin247578 -> 248764 bytes
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/AuthManagerType.java26
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java155
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties10
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java86
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java27
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java24
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java98
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java85
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java62
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java591
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java165
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AddLineConverter.java38
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandFactory.java59
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java13
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java17
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java198
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java113
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java18
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.form10
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java21
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java16
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java145
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LocalChangesAction.java29
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeDialogI.java34
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java268
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMergeContentsVariants.java26
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSLCredentialsDialog.java7
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java6
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java17
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ToBeMergedDialog.java157
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java38
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/RootsAndBranches.java25
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java41
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java10
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java23
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteraction.java68
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteractionImpl.java164
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.form94
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.java85
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java3
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/lowLevel/PrimitivePool.java113
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/ListMergeStatus.java47
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/MergeInfoHolder.java21
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java12
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/networking/SSLProtocolExceptionParser.java87
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/status/DiffContentRevision.java3
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnRevisionPanel.java5
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java5
-rw-r--r--plugins/svn4idea/svn4idea-tests.iml1
-rw-r--r--plugins/svn4idea/svn4idea.iml11
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java59
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/QuickMergeTestInteraction.java149
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SSLExceptionParserTest.java41
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java3
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java492
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java27
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java630
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java3
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java510
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnRenameTest.java1
-rw-r--r--plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnExternalCommitNoticedTest.java1
88 files changed, 6792 insertions, 714 deletions
diff --git a/plugins/svn4idea/bindSvn/bindSvn.iml b/plugins/svn4idea/bindSvn/bindSvn.iml
new file mode 100644
index 000000000000..846587cef36f
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/bindSvn.iml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="platform-api" />
+ <orderEntry type="module" module-name="vcs-api" />
+ <orderEntry type="module-library">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/lib/javahl.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$MODULE_DIR$/lib/javahlsrc.zip!/src" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ </component>
+</module>
+
diff --git a/plugins/svn4idea/bindSvn/lib/javahl.jar b/plugins/svn4idea/bindSvn/lib/javahl.jar
new file mode 100644
index 000000000000..a0c5370065af
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/lib/javahl.jar
Binary files differ
diff --git a/plugins/svn4idea/bindSvn/lib/javahlsrc.zip b/plugins/svn4idea/bindSvn/lib/javahlsrc.zip
new file mode 100644
index 000000000000..9c50118203b7
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/lib/javahlsrc.zip
Binary files differ
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java
new file mode 100644
index 000000000000..0d3314f1ece5
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+/**
+ * Passed for authentication purpose to SvnLineCommand
+ * Called when svn command indicates that it needs credentials. It also means that credential was not found in standard place
+ * (Subversion config directory)
+ *
+ * Implementations should 1) ask credential from user or take it from any other storage (memory, for instance)
+ * 2) write credential in Subversion standard form into
+ * a) standard config directory if user allowed to save *all* credentials
+ * b) TMP directory and return path to the directory from getSpecialConfigDir() - if user rejected at least one credential storing
+ *
+ * Please note that several credentials could be asked during the command and therefore implementation class is used as
+ * keeping its state, and that TMP directory should be reused for all written credentials
+ *
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 1:05 PM
+ */
+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 base - file target of the operation
+ * @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
+ */
+ boolean authenticateFor(@Nullable String realm, File base, boolean previousFailed, boolean passwordRequest);
+
+ /**
+ * @return config directory if TMP was created
+ */
+ @Nullable
+ File getSpecialConfigDir();
+
+ /**
+ * Ask user or read from memory storage whether server certificate should be accepted
+ *
+ * @param url - that we used for request
+ * @param realm - realm that should be used for credential retrieval/storage.
+ * @return true is certificate was accepted
+ */
+ boolean acceptSSLServerCertificate(String url, final String realm);
+
+ /**
+ * Ask user or read from memory storage whether server certificate should be accepted
+ *
+ * @param file - that we used for request
+ * @param realm - realm that should be used for credential retrieval/storage.
+ * @return true is certificate was accepted
+ */
+ boolean acceptSSLServerCertificate(File file, final String realm);
+
+ /**
+ * Clear credentials stored anywhere - in case they were not full, wrong or anything else
+ *
+ * @param realm - required that credential
+ * @param base - file used in command
+ * @param password - whether password credential should be deleted or certificate, if protocol might demand certificate
+ */
+ void clearPassiveCredentials(String realm, File base, boolean password);
+
+ /**
+ * @return true if there's something from IDEA config that should be persisted into Subversion tmp config directory
+ * for successful call
+ * (now it's IDEA proxy settings)
+ */
+ boolean haveDataForTmpConfig();
+
+ /**
+ * writes IDEA config settings (that should be written) into tmp config directory
+ * (now it's IDEA proxy settings)
+ * @return true if have written data, false if wasn't able to determine parameters etc
+ * @throws IOException
+ * @throws URISyntaxException
+ */
+ boolean persistDataToTmpConfig(File baseFile) throws IOException, URISyntaxException;
+
+ /**
+ * Ask for IDEA-defined proxy credentials, using standard authenticator
+ * Store data into tmp config
+ *
+ * @return false if authentication was canceled or related calculations were unsuccessful
+ */
+ boolean askProxyCredentials(File base);
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java
new file mode 100644
index 000000000000..7f3bcf3bf4a5
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import java.io.File;
+
+/**
+ * Used to listen to commit events to display progress to user
+ *
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 10:12 AM
+ */
+public interface CommitEventHandler {
+ void commitEvent(final CommitEventType type, final File target);
+ void committedRevision(final long revNum);
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java
new file mode 100644
index 000000000000..46609e40532c
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 6:51 PM
+ */
+public enum CommitEventType {
+ adding("Adding"),
+ deleting("Deleting"),
+ sending("Sending"),
+ replacing("Replacing"),
+ transmittingDeltas("Transmitting file data"),
+ committedRevision("Committed revision");
+
+ private final String myText;
+
+ CommitEventType(String text) {
+ myText = text;
+ }
+
+ public String getText() {
+ return myText;
+ }
+
+ public static CommitEventType create(String text) {
+ text = text.trim();
+ for (CommitEventType value : CommitEventType.values()) {
+ if (value.getText().equals(text)) return value;
+ }
+ return null;
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java
new file mode 100644
index 000000000000..c155b78ddc53
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java
@@ -0,0 +1,925 @@
+/*
+ * 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;
+
+import org.tigris.subversion.javahl.*;
+
+import java.io.OutputStream;
+import java.util.Map;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/5/13
+ * Time: 3:08 PM
+ */
+public class SvnBindClient implements SVNClientInterface {
+ private final String myExecutablePath;
+ private CommitEventHandler myHandler;
+ private AuthenticationCallback myAuthenticationCallback;
+
+ public SvnBindClient(String path) {
+ myExecutablePath = path;
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public Version getVersion() {
+ // todo real version
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getAdminDirectoryName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAdminDirectory(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getLastPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Status singleStatus(String path, boolean onServer) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Status[] status(String path, boolean descend, boolean onServer, boolean getAll) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore, boolean ignoreExternals)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void status(String path,
+ int depth,
+ boolean onServer,
+ boolean getAll,
+ boolean noIgnore,
+ boolean ignoreExternals,
+ String[] changelists,
+ StatusCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DirEntry[] list(String url, Revision revision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DirEntry[] list(String url, Revision revision, Revision pegRevision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void list(String url,
+ Revision revision,
+ Revision pegRevision,
+ int depth,
+ int direntFields,
+ boolean fetchLocks,
+ ListCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void username(String username) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void password(String password) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPrompt(PromptUserPassword prompt) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy, boolean discoverPath)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LogMessage[] logMessages(String path,
+ Revision revisionStart,
+ Revision revisionEnd,
+ boolean stopOnCopy,
+ boolean discoverPath,
+ long limit) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void logMessages(String path,
+ Revision pegRevision,
+ Revision revisionStart,
+ Revision revisionEnd,
+ boolean stopOnCopy,
+ boolean discoverPath,
+ boolean includeMergedRevisions,
+ String[] revProps,
+ long limit,
+ LogMessageCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void logMessages(String path,
+ Revision pegRevision,
+ RevisionRange[] ranges,
+ boolean stopOnCopy,
+ boolean discoverPath,
+ boolean includeMergedRevisions,
+ String[] revProps,
+ long limit,
+ LogMessageCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long checkout(String moduleName, String destPath, Revision revision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long checkout(String moduleName,
+ String destPath,
+ Revision revision,
+ Revision pegRevision,
+ boolean recurse,
+ boolean ignoreExternals) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long checkout(String moduleName,
+ String destPath,
+ Revision revision,
+ Revision pegRevision,
+ int depth,
+ boolean ignoreExternals,
+ boolean allowUnverObstructions) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void notification(Notify notify) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void notification2(Notify2 notify) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setConflictResolver(ConflictResolverCallback listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setProgressListener(ProgressListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void commitMessageHandler(CommitMessage messageHandler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void remove(String[] path, String message, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void remove(String[] path, String message, boolean force, boolean keepLocal, Map revpropTable) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void revert(String path, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void revert(String path, int depth, String[] changelists) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(String path, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(String path, boolean recurse, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(String path, int depth, boolean force, boolean noIgnores, boolean addParents) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long update(String path, Revision revision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long[] update(String[] path, Revision revision, boolean recurse, boolean ignoreExternals) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long update(String path,
+ Revision revision,
+ int depth,
+ boolean depthIsSticky,
+ boolean ignoreExternals,
+ boolean allowUnverObstructions) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long[] update(String[] path,
+ Revision revision,
+ int depth,
+ boolean depthIsSticky,
+ boolean ignoreExternals,
+ boolean allowUnverObstructions) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long commit(String[] path, String message, boolean recurse) throws ClientException {
+ return commit(path, message, recurse? 3 : 0, false, false, null, null);
+ }
+
+ @Override
+ public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws ClientException {
+ return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null);
+ }
+
+ @Override
+ public long commit(String[] path,
+ String message,
+ int depth,
+ boolean noUnlock,
+ boolean keepChangelist,
+ String[] changelists,
+ Map revpropTable) throws ClientException {
+ final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback).
+ commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable);
+ if (commit < 0) {
+ throw new BindClientException("Wrong committed revision number: " + commit, null, -1);
+ }
+ return commit;
+ }
+
+ @Override
+ public void copy(CopySource[] sources,
+ String destPath,
+ String message,
+ boolean copyAsChild,
+ boolean makeParents,
+ boolean ignoreExternals,
+ Map revpropTable) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void copy(CopySource[] sources, String destPath, String message, boolean copyAsChild, boolean makeParents, Map revpropTable)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void copy(String srcPath, String destPath, String message, Revision revision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(String[] srcPaths,
+ String destPath,
+ String message,
+ boolean force,
+ boolean moveAsChild,
+ boolean makeParents,
+ Map revpropTable) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(String srcPath, String destPath, String message, Revision ignored, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(String srcPath, String destPath, String message, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void mkdir(String[] path, String message, boolean makeParents, Map revpropTable) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void mkdir(String[] path, String message) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void cleanup(String path) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resolve(String path, int depth, int conflictResult) throws SubversionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resolved(String path, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long doExport(String srcPath, String destPath, Revision revision, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long doExport(String srcPath,
+ String destPath,
+ Revision revision,
+ Revision pegRevision,
+ boolean force,
+ boolean ignoreExternals,
+ boolean recurse,
+ String nativeEOL) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long doExport(String srcPath,
+ String destPath,
+ Revision revision,
+ Revision pegRevision,
+ boolean force,
+ boolean ignoreExternals,
+ int depth,
+ String nativeEOL) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long doSwitch(String path, String url, Revision revision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long doSwitch(String path,
+ String url,
+ Revision revision,
+ Revision pegRevision,
+ int depth,
+ boolean depthIsSticky,
+ boolean ignoreExternals,
+ boolean allowUnverObstructions) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void doImport(String path, String url, String message, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void doImport(String path,
+ String url,
+ String message,
+ int depth,
+ boolean noIgnore,
+ boolean ignoreUnknownNodeTypes,
+ Map revpropTable) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] suggestMergeSources(String path, Revision pegRevision) throws SubversionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void merge(String path1, Revision revision1, String path2, Revision revision2, String localPath, boolean force, boolean recurse)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void merge(String path1,
+ Revision revision1,
+ String path2,
+ Revision revision2,
+ String localPath,
+ boolean force,
+ boolean recurse,
+ boolean ignoreAncestry,
+ boolean dryRun) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void merge(String path1,
+ Revision revision1,
+ String path2,
+ Revision revision2,
+ String localPath,
+ boolean force,
+ int depth,
+ boolean ignoreAncestry,
+ boolean dryRun,
+ boolean recordOnly) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void merge(String path,
+ Revision pegRevision,
+ Revision revision1,
+ Revision revision2,
+ String localPath,
+ boolean force,
+ boolean recurse,
+ boolean ignoreAncestry,
+ boolean dryRun) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void merge(String path,
+ Revision pegRevision,
+ RevisionRange[] revisions,
+ String localPath,
+ boolean force,
+ int depth,
+ boolean ignoreAncestry,
+ boolean dryRun,
+ boolean recordOnly) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void mergeReintegrate(String path, Revision pegRevision, String localPath, boolean dryRun) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Mergeinfo getMergeinfo(String path, Revision pegRevision) throws SubversionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void getMergeinfoLog(int kind,
+ String pathOrUrl,
+ Revision pegRevision,
+ String mergeSourceUrl,
+ Revision srcPegRevision,
+ boolean discoverChangedPaths,
+ int depth,
+ String[] revProps,
+ LogMessageCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void getMergeinfoLog(int kind,
+ String pathOrUrl,
+ Revision pegRevision,
+ String mergeSourceUrl,
+ Revision srcPegRevision,
+ boolean discoverChangedPaths,
+ String[] revProps,
+ LogMessageCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target1, Revision revision1, String target2, Revision revision2, String outFileName, boolean recurse)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target1,
+ Revision revision1,
+ String target2,
+ Revision revision2,
+ String outFileName,
+ boolean recurse,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target1,
+ Revision revision1,
+ String target2,
+ Revision revision2,
+ String relativeToDir,
+ String outFileName,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force,
+ boolean copiesAsAdds) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target1,
+ Revision revision1,
+ String target2,
+ Revision revision2,
+ String relativeToDir,
+ String outFileName,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target,
+ Revision pegRevision,
+ Revision startRevision,
+ Revision endRevision,
+ String outFileName,
+ boolean recurse,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target,
+ Revision pegRevision,
+ Revision startRevision,
+ Revision endRevision,
+ String relativeToDir,
+ String outFileName,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force,
+ boolean copiesAsAdds) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diff(String target,
+ Revision pegRevision,
+ Revision startRevision,
+ Revision endRevision,
+ String relativeToDir,
+ String outFileName,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ boolean noDiffDeleted,
+ boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diffSummarize(String target1,
+ Revision revision1,
+ String target2,
+ Revision revision2,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ DiffSummaryReceiver receiver) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void diffSummarize(String target,
+ Revision pegRevision,
+ Revision startRevision,
+ Revision endRevision,
+ int depth,
+ String[] changelists,
+ boolean ignoreAncestry,
+ DiffSummaryReceiver receiver) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData[] properties(String path) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData[] properties(String path, Revision revision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData[] properties(String path, Revision revision, Revision pegRevision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void properties(String path, Revision revision, Revision pegRevision, int depth, String[] changelists, ProplistCallback callback)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertySet(String path, String name, String value, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertySet(String path, String name, String value, boolean recurse, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertySet(String path, String name, byte[] value, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertySet(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertySet(String path, String name, String value, int depth, String[] changelists, boolean force, Map revpropTable)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyRemove(String path, String name, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyRemove(String path, String name, int depth, String[] changelists) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyCreate(String path, String name, String value, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyCreate(String path, String name, String value, boolean recurse, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyCreate(String path, String name, byte[] value, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyCreate(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void propertyCreate(String path, String name, String value, int depth, String[] changelists, boolean force)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData revProperty(String path, String name, Revision rev) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData[] revProperties(String path, Revision rev) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRevProperty(String path, String name, Revision rev, String value, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRevProperty(String path, String name, Revision rev, String value, String originalValue, boolean force)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData propertyGet(String path, String name) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData propertyGet(String path, String name, Revision revision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyData propertyGet(String path, String name, Revision revision, Revision pegRevision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] fileContent(String path, Revision revision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] fileContent(String path, Revision revision, Revision pegRevision) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void streamFileContent(String path, Revision revision, Revision pegRevision, int bufferSize, OutputStream stream)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void relocate(String from, String to, String path, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] blame(String path, Revision revisionStart, Revision revisionEnd) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void blame(String path, Revision revisionStart, Revision revisionEnd, BlameCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void blame(String path, Revision pegRevision, Revision revisionStart, Revision revisionEnd, BlameCallback callback)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void blame(String path,
+ Revision pegRevision,
+ Revision revisionStart,
+ Revision revisionEnd,
+ boolean ignoreMimeType,
+ boolean includeMergedRevisions,
+ BlameCallback2 callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void blame(String path,
+ Revision pegRevision,
+ Revision revisionStart,
+ Revision revisionEnd,
+ boolean ignoreMimeType,
+ boolean includeMergedRevisions,
+ BlameCallback3 callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setConfigDirectory(String configDir) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getConfigDirectory() throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void cancelOperation() throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Info info(String path) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addToChangelist(String[] paths, String changelist, int depth, String[] changelists) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeFromChangelists(String[] paths, int depth, String[] changelists) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void getChangelists(String rootPath, String[] changelists, int depth, ChangelistCallback callback) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void lock(String[] path, String comment, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unlock(String[] path, boolean force) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Info2[] info2(String pathOrUrl, Revision revision, Revision pegRevision, boolean recurse) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void info2(String pathOrUrl, Revision revision, Revision pegRevision, int depth, String[] changelists, InfoCallback callback)
+ throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getVersionInfo(String path, String trailUrl, boolean lastChanged) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void upgrade(String path) throws ClientException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setHandler(CommitEventHandler handler) {
+ myHandler = handler;
+ }
+
+ public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) {
+ myAuthenticationCallback = authenticationCallback;
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java
new file mode 100644
index 000000000000..1fec6b3bf865
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/5/13
+ * Time: 4:56 PM
+ */
+public class SvnBindUtil {
+ /**
+ * SVN_ASP_DOT_NET_HACK allows use of an alternate name for Subversion working copy
+ * administrative directories on Windows (which were formerly always
+ * named ".svn"), by setting the SVN_ASP_DOT_NET_HACK environment variable.
+ * When the variable is set (to any value), the administrative directory
+ * will be "_svn" instead of ".svn".
+ *
+ * http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt
+ */
+ public static final String ADM_NAME = System.getenv("SVN_ASP_DOT_NET_HACK") != null ? "_svn" : ".svn";
+ private final static List<DateFormat> ourFormats = new ArrayList<DateFormat>();
+
+ static {
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'"));
+ ourFormats.add(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z (EE, d MMM yyyy)", Locale.getDefault()));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss' 'ZZZZ' ('E', 'dd' 'MMM' 'yyyy')'"));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss'Z'"));
+ ourFormats.add(new SimpleDateFormat("EEE' 'MMM' 'dd' 'HH:mm:ss' 'yyyy"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'yyyy"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm:ss"));
+ }
+
+ public static Date parseDate(final String date) {
+ for (DateFormat format : ourFormats) {
+ try {
+ return format.parse(date);
+ }
+ catch (ParseException e) {
+ continue;
+ }
+ }
+ return new Date(0);
+ }
+
+ public static void changelistsToCommand(String[] changeLists, final List<String> list) {
+ if (changeLists != null) {
+ for (String name : changeLists) {
+ list.add("--cl");
+ list.add(name);
+ }
+ }
+ }
+
+ public static String getDepthName(final int i) {
+ return org.tigris.subversion.javahl.Depth.toADepth(i).name();
+ }
+
+ public static File correctUpToExistingParent(File base) {
+ while (base != null) {
+ if (base.exists() && base.isDirectory()) return base;
+ base = base.getParentFile();
+ }
+ return null;
+ }
+
+ public static File getWcRoot(File base) {
+ File current = base;
+ while (current != null) {
+ if (getWcDbUnder(current).exists()) return current;
+ current = current.getParentFile();
+ }
+ return null;
+ }
+
+ public static File getWcDbUnder(final File file) {
+ return new File(file, ADM_NAME + File.separator + "wc.db");
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java
new file mode 100644
index 000000000000..525d675303fc
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java
@@ -0,0 +1,217 @@
+/*
+ * 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;
+
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtil;
+import org.apache.subversion.javahl.types.Revision;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.svn.commandLine.LineCommandListener;
+import org.jetbrains.idea.svn.commandLine.SvnCommandName;
+import org.jetbrains.idea.svn.commandLine.SvnLineCommand;
+import org.jetbrains.idea.svn.config.SvnBindException;
+import org.tigris.subversion.javahl.BindClientException;
+import org.tigris.subversion.javahl.ClientException;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 4:56 PM
+ */
+public class SvnCommitRunner {
+ private final String myExePath;
+ @Nullable private final AuthenticationCallback myAuthenticationCallback;
+ private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnCommitRunner");
+ private SvnCommitRunner.CommandListener myCommandListener;
+
+ public SvnCommitRunner(@NotNull String path, @Nullable CommitEventHandler handler, @Nullable AuthenticationCallback authenticationCallback) {
+ myExePath = path;
+ myCommandListener = new CommandListener(handler);
+ myAuthenticationCallback = authenticationCallback;
+ }
+
+ public long commit(String[] paths,
+ String message,
+ int depth,
+ boolean noUnlock,
+ boolean keepChangelist,
+ String[] changelists,
+ Map revpropTable) throws ClientException {
+ if (paths.length == 0) return Revision.SVN_INVALID_REVNUM;
+
+ final List<String> parameters = new ArrayList<String>();
+ parameters.add("--depth");
+ parameters.add(SvnBindUtil.getDepthName(depth));
+ if (noUnlock) {
+ parameters.add("--no-unlock");
+ }
+ if (keepChangelist) {
+ parameters.add("--keep-changelists");
+ }
+ if (changelists != null && changelists.length > 0) {
+ SvnBindUtil.changelistsToCommand(changelists, parameters);
+ }
+ if (revpropTable != null && ! revpropTable.isEmpty()) {
+ final Set<Map.Entry<Object, Object>> set = revpropTable.entrySet();
+ for (Map.Entry<Object, Object> entry : set) {
+ parameters.add("--with-revprop");
+ parameters.add(entry.getKey() + "=" + entry.getValue());
+ }
+ }
+ parameters.add("-m");
+ parameters.add(message);
+ Arrays.sort(paths);
+ parameters.addAll(Arrays.asList(paths));
+
+ try {
+ SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), SvnCommandName.ci,
+ myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters));
+ }
+ catch (SvnBindException e) {
+ throw BindClientException.create(e, Revision.SVN_INVALID_REVNUM);
+ }
+ myCommandListener.throwExceptionIfOccurred();
+
+ return myCommandListener.getCommittedRevision();
+ }
+
+ private static class CommandListener extends LineCommandListener {
+ @Nullable private final CommitEventHandler myHandler;
+ private SvnBindException myException;
+ private long myCommittedRevision = Revision.SVN_INVALID_REVNUM;
+ private File myBase;
+
+ public CommandListener(@Nullable CommitEventHandler handler) {
+ myHandler = handler;
+ }
+
+ public void throwExceptionIfOccurred() throws BindClientException {
+ if (myException != null) {
+ throw BindClientException.create(myException, Revision.SVN_INVALID_REVNUM);
+ }
+ }
+
+ private long getCommittedRevision() {
+ return myCommittedRevision;
+ }
+
+ @Override
+ public void baseDirectory(File file) {
+ myBase = file;
+ }
+
+ @Override
+ public void onLineAvailable(String line, Key outputType) {
+ final String trim = line.trim();
+ if (ProcessOutputTypes.STDOUT.equals(outputType)) {
+ try {
+ parseLine(trim);
+ }
+ catch (SvnBindException e) {
+ myException = e;
+ }
+ }
+ }
+
+ private void parseLine(String line) throws SvnBindException {
+ if (StringUtil.isEmptyOrSpaces(line)) return;
+ if (line.startsWith(CommitEventType.transmittingDeltas.getText())) {
+ if (myHandler != null) {
+ myHandler.commitEvent(CommitEventType.transmittingDeltas, myBase);
+ }
+ return;
+ }
+ if (line.startsWith(CommitEventType.committedRevision.getText())) {
+ final String substring = line.substring(CommitEventType.committedRevision.getText().length());
+ int cnt = 0;
+ while (StringUtil.isWhiteSpace(substring.charAt(cnt))) {
+ ++ cnt;
+ }
+ final StringBuilder num = new StringBuilder();
+ while (Character.isDigit(substring.charAt(cnt))) {
+ num.append(substring.charAt(cnt));
+ ++ cnt;
+ }
+ if (num.length() > 0) {
+ try {
+ myCommittedRevision = Long.parseLong(num.toString());
+ if (myHandler != null) {
+ myHandler.committedRevision(myCommittedRevision);
+ }
+ } catch (NumberFormatException e) {
+ final String message = "Wrong committed revision number: " + num.toString() + ", string: " + line;
+ LOG.info(message, e);
+ throw new SvnBindException(message);
+ }
+ } else {
+ final String message = "Missing committed revision number: " + num.toString() + ", string: " + line;
+ LOG.info(message);
+ throw new SvnBindException(message);
+ }
+ } else {
+ if (myHandler == null) return;
+ final int idxSpace = line.indexOf(' ');
+ if (idxSpace == -1) {
+ LOG.info("Can not parse event type: " + line);
+ return;
+ }
+ final CommitEventType type = CommitEventType.create(line.substring(0, idxSpace));
+ if (type == null) {
+ LOG.info("Can not parse event type: " + line);
+ return;
+ }
+ final File target = new File(myBase, new String(line.substring(idxSpace + 1).trim()));
+ myHandler.commitEvent(type, target);
+ }
+ }
+ }
+
+/*C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv
+ n st
+ D gggG
+ D gggG\Rrr.java
+ D gggG\and555.txt
+ D gggG\test.txt
+ A + gggGA
+ D + gggGA\Rrr.java
+ A + gggGA\RrrAA.java
+ D + gggGA\and.txt
+ M + gggGA\and555.txt
+ A gggGA\someNewFile.txt
+
+ --- Changelist 'New changelistrwerwe':
+ A ddd.jpg
+
+ C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv
+ n ci -m 123
+ Adding ddd.jpg
+ Deleting gggG
+ Adding gggGA
+ Deleting gggGA\Rrr.java
+ Adding gggGA\RrrAA.java
+ Sending gggGA\and555.txt
+ Adding gggGA\someNewFile.txt
+ Transmitting file data ....
+ Committed revision 165.*/
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java
new file mode 100644
index 000000000000..ccbc5c37db61
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commandLine;
+
+import com.intellij.openapi.vcs.LineProcessEventListenerAdapter;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 10:38 AM
+ */
+public abstract class LineCommandListener extends LineProcessEventListenerAdapter {
+ private boolean myCanceled;
+
+ public abstract void baseDirectory(final File file);
+
+ public void cancel() {
+ myCanceled = true;
+ }
+
+ protected boolean isCanceled() {
+ return myCanceled;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
index 5ef336a98428..a1e358b6f4a0 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -20,14 +20,12 @@ import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessListener;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vcs.ProcessEventListener;
import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.idea.svn.SvnApplicationSettings;
-import org.jetbrains.idea.svn.SvnVcs;
+import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
@@ -39,9 +37,8 @@ import java.util.List;
* Time: 12:58 PM
*/
public abstract class SvnCommand {
- private static final Logger LOG = Logger.getInstance(SvnCommand.class.getName());
+ static final Logger LOG = Logger.getInstance(SvnCommand.class.getName());
- protected final Project myProject;
private boolean myIsDestroyed;
private int myExitCode;
protected final GeneralCommandLine myCommandLine;
@@ -56,14 +53,20 @@ public abstract class SvnCommand {
/*c:\Program Files (x86)\CollabNet\Subversion Client17>svn --version --quiet
1.7.2*/
- public SvnCommand(Project project, File workingDirectory, @NotNull SvnCommandName commandName) {
+ public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ this(workingDirectory, commandName, exePath, null);
+ }
+
+ public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath,
+ @Nullable File configDir) {
myLock = new Object();
- myProject = project;
myCommandLine = new GeneralCommandLine();
myWorkingDirectory = workingDirectory;
- final SvnApplicationSettings applicationSettings17 = SvnApplicationSettings.getInstance();
- myCommandLine.setExePath(applicationSettings17.getCommandLinePath());
+ myCommandLine.setExePath(exePath);
myCommandLine.setWorkDirectory(workingDirectory);
+ if (configDir != null) {
+ myCommandLine.addParameters("--config-dir", configDir.getPath());
+ }
myCommandLine.addParameter(commandName.getName());
}
@@ -73,10 +76,12 @@ public abstract class SvnCommand {
try {
myProcess = myCommandLine.createProcess();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(myCommandLine.toString());
+ }
myHandler = new OSProcessHandler(myProcess, myCommandLine.getCommandLineString());
startHandlingStreams();
} catch (Throwable t) {
- SvnVcs.getInstance(myProject).checkCommandLineVersion();
myListeners.getMulticaster().startFailed(t);
}
}
@@ -92,7 +97,6 @@ public abstract class SvnCommand {
final int exitCode = event.getExitCode();
try {
setExitCode(exitCode);
- //cleanupEnv(); todo
SvnCommand.this.processTerminated(exitCode);
} finally {
listeners().processTerminated(exitCode);
@@ -210,4 +214,8 @@ public abstract class SvnCommand {
return myExitCode;
}
}
+
+ protected File getWorkingDirectory() {
+ return myWorkingDirectory;
+ }
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
index 950ec7f9eb1b..58c2ff2ab308 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -22,18 +22,26 @@ package org.jetbrains.idea.svn.commandLine;
* Time: 1:49 PM
*/
public enum SvnCommandName {
- version("--version"),
- info("info"),
- st("st"),
- up("up");
+ version("--version", false),
+ info("info", false),
+ st("st", false),
+ up("up", true),
+ ci("commit", true),
+ cleanup("cleanup", true);
private final String myName;
+ private final boolean myWriteable;
- private SvnCommandName(String name) {
+ private SvnCommandName(String name, boolean writeable) {
myName = name;
+ myWriteable = writeable;
}
public String getName() {
return myName;
}
+
+ public boolean isWriteable() {
+ return myWriteable;
+ }
}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
new file mode 100644
index 000000000000..3554b1cd4d81
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
@@ -0,0 +1,415 @@
+/*
+ * 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.commandLine;
+
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.LineHandlerHelper;
+import com.intellij.openapi.vcs.LineProcessEventListener;
+import com.intellij.openapi.vcs.VcsException;
+import com.intellij.util.EventDispatcher;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.svn.AuthenticationCallback;
+import org.jetbrains.idea.svn.SvnBindUtil;
+import org.jetbrains.idea.svn.config.SvnBindException;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 4:05 PM
+ *
+ * honestly stolen from GitLineHandler
+ */
+public class SvnLineCommand extends SvnCommand {
+ public static final String AUTHENTICATION_REALM = "Authentication realm:";
+ public static final String CERTIFICATE_ERROR = "Error validating server certificate for";
+ public static final String PASSPHRASE_FOR = "Passphrase for";
+ public static final String UNABLE_TO_CONNECT = "svn: E170001:";
+ public static final String CANNOT_AUTHENTICATE_TO_PROXY = "Could not authenticate to proxy server";
+
+ // kept for exact text
+ //public static final String CLIENT_CERTIFICATE_FILENAME = "Client certificate filename:";
+ /**
+ * the partial line from stdout stream
+ */
+ private final StringBuilder myStdoutLine = new StringBuilder();
+ /**
+ * the partial line from stderr stream
+ */
+ private final StringBuilder myStderrLine = new StringBuilder();
+ private final EventDispatcher<LineProcessEventListener> myLineListeners;
+ private final AtomicReference<Integer> myExitCode;
+ private final StringBuffer myErr;
+
+ public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ this(workingDirectory, commandName, exePath, null);
+ }
+
+ public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, File configDir) {
+ super(workingDirectory, commandName, exePath, configDir);
+ myLineListeners = EventDispatcher.create(LineProcessEventListener.class);
+ myExitCode = new AtomicReference<Integer>();
+ myErr = new StringBuffer();
+ }
+
+ @Override
+ protected void processTerminated(int exitCode) {
+ // force newline
+ if (myStdoutLine.length() != 0) {
+ onTextAvailable("\n\r", ProcessOutputTypes.STDOUT);
+ }
+ else if (myStderrLine.length() != 0) {
+ onTextAvailable("\n\r", ProcessOutputTypes.STDERR);
+ }
+ }
+
+ public static void runWithAuthenticationAttempt(final String exePath,
+ final File firstFile,
+ SvnCommandName commandName,
+ final LineCommandListener listener,
+ @Nullable AuthenticationCallback authenticationCallback,
+ final String... parameters) throws SvnBindException {
+ File base = firstFile.isDirectory() ? firstFile : firstFile.getParentFile();
+ base = SvnBindUtil.correctUpToExistingParent(base);
+
+ listener.baseDirectory(base);
+
+ File configDir = null;
+
+ // for IDEA proxy case
+ if (authenticationCallback != null && authenticationCallback.haveDataForTmpConfig()) {
+ try {
+ if (! authenticationCallback.persistDataToTmpConfig(base)) {
+ throw new SvnBindException("Can not persist " + ApplicationNamesInfo.getInstance().getProductName() +
+ " HTTP proxy information into tmp config directory");
+ }
+ }
+ catch (IOException e) {
+ throw new SvnBindException(e);
+ }
+ catch (URISyntaxException e) {
+ throw new SvnBindException(e);
+ }
+ assert authenticationCallback.getSpecialConfigDir() != null;
+ configDir = authenticationCallback.getSpecialConfigDir();
+ }
+
+ while (true) {
+ final SvnLineCommand command = runCommand(exePath, commandName, listener, base, configDir, parameters);
+ if (command.myErr.length() > 0) {
+ final String errText = command.myErr.toString().trim();
+ if (authenticationCallback != null) {
+ final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base);
+ if (callback != null) {
+ cleanup(exePath, commandName, base);
+ if (callback.getCredentials(errText)) {
+ if (authenticationCallback.getSpecialConfigDir() != null) {
+ configDir = authenticationCallback.getSpecialConfigDir();
+ }
+ continue;
+ }
+ }
+ }
+ throw new SvnBindException(errText);
+ }
+ final Integer exitCode = command.myExitCode.get();
+ if (exitCode != 0) {
+ throw new SvnBindException("Svn process exited with error code: " + exitCode);
+ }
+ return;
+ }
+ //ok
+ }
+
+ private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base) {
+ if (errText.startsWith(CERTIFICATE_ERROR)) {
+ return new CertificateCallbackCase(callback, base);
+ }
+ if (errText.startsWith(AUTHENTICATION_REALM)) {
+ return new CredentialsCallback(callback, base);
+ }
+ if (errText.startsWith(PASSPHRASE_FOR)) {
+ return new PassphraseCallback(callback, base);
+ }
+ if (errText.startsWith(UNABLE_TO_CONNECT) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) {
+ return new ProxyCallback(callback, base);
+ }
+ return null;
+ }
+
+ private static class ProxyCallback extends AuthCallbackCase {
+ protected ProxyCallback(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ return myAuthenticationCallback.askProxyCredentials(myBase);
+ }
+ }
+
+ private static class CredentialsCallback extends AuthCallbackCase {
+ protected CredentialsCallback(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ final String realm = cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim();
+ final boolean isPassword = StringUtil.containsIgnoreCase(errText, "password");
+ if (myTried) {
+ myAuthenticationCallback.clearPassiveCredentials(realm, myBase, isPassword);
+ }
+ myTried = true;
+ if (myAuthenticationCallback.authenticateFor(realm, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, isPassword)) {
+ return true;
+ }
+ throw new SvnBindException("Authentication canceled for realm: " + realm);
+ }
+ }
+
+ private static String cutFirstLine(final String text) {
+ final int idx = text.indexOf('\n');
+ if (idx == -1) return text;
+ return text.substring(0, idx);
+ }
+
+ private static class CertificateCallbackCase extends AuthCallbackCase {
+ private CertificateCallbackCase(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ public boolean getCredentials(final String errText) throws SvnBindException {
+ String realm = cutFirstLine(errText).substring(CERTIFICATE_ERROR.length());
+ final int idx1 = realm.indexOf('\'');
+ if (idx1 == -1) {
+ throw new SvnBindException("Can not detect authentication realm name: " + errText);
+ }
+ final int idx2 = realm.indexOf('\'', idx1 + 1);
+ if (idx2== -1) {
+ throw new SvnBindException("Can not detect authentication realm name: " + errText);
+ }
+ realm = realm.substring(idx1 + 1, idx2);
+ if (! myTried && myAuthenticationCallback.acceptSSLServerCertificate(myBase, realm)) {
+ myTried = true;
+ return true;
+ }
+ throw new SvnBindException("Server SSL certificate rejected");
+ }
+ }
+
+ private static abstract class AuthCallbackCase {
+ protected boolean myTried = false;
+ protected final AuthenticationCallback myAuthenticationCallback;
+ protected final File myBase;
+
+ protected AuthCallbackCase(AuthenticationCallback callback, final File base) {
+ myAuthenticationCallback = callback;
+ myBase = base;
+ }
+
+ abstract boolean getCredentials(final String errText) throws SvnBindException;
+ }
+
+ private static void cleanup(String exePath, SvnCommandName commandName, File base) throws SvnBindException {
+ File wcRoot = SvnBindUtil.getWcRoot(base);
+ if (wcRoot == null) throw new SvnBindException("Can not find working copy root for: " + base.getPath());
+
+ //cleanup -> check command type
+ if (commandName.isWriteable()) {
+ final SvnSimpleCommand command = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath);
+ try {
+ command.run();
+ }
+ catch (VcsException e) {
+ throw new SvnBindException(e);
+ }
+ }
+ }
+
+ /*svn: E170001: Commit failed (details follow):
+ svn: E170001: Unable to connect to a repository at URL 'htt../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA'
+ svn: E170001: OPTIONS of 'htt.../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA': authorization failed: Could not authenticate to server: rejected Basic challenge (ht)*/
+ private final static String ourAuthFailed = "authorization failed";
+ private final static String ourAuthFailed2 = "Could not authenticate to server";
+
+ private static boolean isAuthenticationFailed(String s) {
+ return s.trim().startsWith(AUTHENTICATION_REALM);
+ //return s.contains(ourAuthFailed) && s.contains(ourAuthFailed2);
+ }
+
+ private static SvnLineCommand runCommand(String exePath,
+ SvnCommandName commandName,
+ final LineCommandListener listener,
+ File base, File configDir,
+ String... parameters) throws SvnBindException {
+ final SvnLineCommand command = new SvnLineCommand(base, commandName, exePath, configDir) {
+ int myErrCnt = 0;
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+
+ // we won't stop right now if got "authentication realm" -> since we want to get "password" string (that would mean password is expected
+ // or certificate maybe is expected
+ // but for certificate passphrase we get just "Passphrase for ..." - one line without line feed
+
+ // for client certificate (when no path in servers file) we get
+ // Authentication realm: <text>
+ // Client certificate filename:
+ if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ ++ myErrCnt;
+ final String trim = text.trim();
+ if (trim.startsWith(UNABLE_TO_CONNECT)) {
+ // wait for 3 lines of text then
+ if (myErrCnt >= 3) {
+ destroyProcess();
+ }
+ } else if (trim.startsWith(PASSPHRASE_FOR) || myErrCnt >= 2) {
+ destroyProcess();
+ }
+ }
+ super.onTextAvailable(text, outputType);
+ }
+ };
+
+ //command.addParameters("--non-interactive");
+ command.addParameters(parameters);
+ final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>();
+ // several threads
+ command.addLineListener(new LineProcessEventListener() {
+ @Override
+ public void onLineAvailable(String line, Key outputType) {
+ if (SvnCommand.LOG.isDebugEnabled()) {
+ SvnCommand.LOG.debug("==> " + line);
+ }
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ System.out.println("==> " + line);
+ }
+ listener.onLineAvailable(line, outputType);
+ if (listener.isCanceled()) {
+ command.destroyProcess();
+ return;
+ }
+ if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ if (command.myErr.length() > 0) {
+ command.myErr.append('\n');
+ }
+ command.myErr.append(line);
+ }
+ }
+
+ @Override
+ public void processTerminated(int exitCode) {
+ listener.processTerminated(exitCode);
+ command.myExitCode.set(exitCode);
+ }
+
+ @Override
+ public void startFailed(Throwable exception) {
+ listener.startFailed(exception);
+ exceptionRef.set(exception);
+ }
+ });
+ command.start();
+ command.waitFor();
+ if (exceptionRef.get() != null) {
+ throw new SvnBindException(exceptionRef.get());
+ }
+ return command;
+ }
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+ Iterator<String> lines = LineHandlerHelper.splitText(text).iterator();
+ if (ProcessOutputTypes.STDOUT == outputType) {
+ notifyLines(outputType, lines, myStdoutLine);
+ }
+ else if (ProcessOutputTypes.STDERR == outputType) {
+ notifyLines(outputType, lines, myStderrLine);
+ }
+ }
+
+ private void notifyLines(final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) {
+ if (!lines.hasNext()) return;
+ if (lineBuilder.length() > 0) {
+ lineBuilder.append(lines.next());
+ if (lines.hasNext()) {
+ // line is complete
+ final String line = lineBuilder.toString();
+ notifyLine(line, outputType);
+ lineBuilder.setLength(0);
+ }
+ }
+ while (true) {
+ String line = null;
+ if (lines.hasNext()) {
+ line = lines.next();
+ }
+
+ if (lines.hasNext()) {
+ notifyLine(line, outputType);
+ }
+ else {
+ if (line != null && line.length() > 0) {
+ lineBuilder.append(line);
+ }
+ break;
+ }
+ }
+ }
+
+ private void notifyLine(final String line, final Key outputType) {
+ String trimmed = LineHandlerHelper.trimLineSeparator(line);
+ myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType);
+ }
+
+ public void addLineListener(LineProcessEventListener listener) {
+ myLineListeners.addListener(listener);
+ super.addListener(listener);
+ }
+
+ private static class PassphraseCallback extends AuthCallbackCase {
+ public PassphraseCallback(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ // try to get from file
+ /*if (myTried) {
+ myAuthenticationCallback.clearPassiveCredentials(null, myBase);
+ }*/
+ myTried = true;
+ if (myAuthenticationCallback.authenticateFor(null, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, false)) {
+ return true;
+ }
+ throw new SvnBindException("Authentication canceled for : " + errText.substring(PASSPHRASE_FOR.length()));
+ }
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
index 6f68d390798e..da5df9e6d299 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -16,10 +16,10 @@
package org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.process.ProcessOutputTypes;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vcs.ProcessEventListener;
import com.intellij.openapi.vcs.VcsException;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -36,8 +36,8 @@ public class SvnSimpleCommand extends SvnCommand {
private VcsException myException;
private final Object myDataLock;
- public SvnSimpleCommand(Project project, File workingDirectory, @NotNull SvnCommandName commandName) {
- super(project, workingDirectory, commandName);
+ public SvnSimpleCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ super(workingDirectory, commandName, exePath);
myDataLock = new Object();
myStderr = new StringBuilder();
@@ -96,13 +96,9 @@ public class SvnSimpleCommand extends SvnCommand {
if (code == 0) {
return myStdout.toString();
} else {
- String msg = myStderr.toString();
- if (msg.length() == 0) {
- msg = getStdout().toString();
- }
- if (msg.length() == 0) {
- msg = "Svn process exited with error code: " + code;
- }
+ final String msg = new StringBuilder("Svn process exited with error code: ").append(code).append("\n")
+ .append("stderr: ").append(myStderr.toString()).append("\nstdout: ").append(getStdout().toString())
+ .append("\nCommand was: ").append(myCommandLine.getCommandLineString()).toString();
throw new VcsException(msg);
}
}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java
new file mode 100644
index 000000000000..8957843e911c
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java
@@ -0,0 +1,54 @@
+/*
+ * 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.config;
+
+import com.intellij.openapi.vcs.VcsException;
+
+import java.util.Collection;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 5:57 PM
+ *
+ * Marker exception
+ */
+public class SvnBindException extends VcsException {
+ public SvnBindException(String message) {
+ super(message);
+ }
+
+ public SvnBindException(Throwable throwable, boolean isWarning) {
+ super(throwable, isWarning);
+ }
+
+ public SvnBindException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public SvnBindException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SvnBindException(String message, boolean isWarning) {
+ super(message, isWarning);
+ }
+
+ public SvnBindException(Collection<String> messages) {
+ super(messages);
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java
new file mode 100644
index 000000000000..f4e0f4cf2269
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java
@@ -0,0 +1,121 @@
+/*
+ * 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.status;
+
+import org.apache.subversion.javahl.types.Lock;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Date;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/21/12
+ * Time: 2:33 PM
+ */
+public class LockWrapper {
+ private String myPath;
+ private String myID;
+ private String myOwner;
+ private String myComment;
+ private Date myCreationDate;
+ private Date myExpirationDate;
+
+ public LockWrapper(String path, String ID, String owner, String comment, Date creationDate, Date expirationDate) {
+ myPath = path;
+ myID = ID;
+ myOwner = owner;
+ myComment = comment;
+ myCreationDate = creationDate;
+ myExpirationDate = expirationDate;
+ }
+
+ public LockWrapper() {
+ }
+
+ public String getPath() {
+ return myPath;
+ }
+
+ public void setPath(String path) {
+ myPath = path;
+ }
+
+ public String getID() {
+ return myID;
+ }
+
+ public void setID(String ID) {
+ myID = ID;
+ }
+
+ public String getOwner() {
+ return myOwner;
+ }
+
+ public void setOwner(String owner) {
+ myOwner = owner;
+ }
+
+ public String getComment() {
+ return myComment;
+ }
+
+ public void setComment(String comment) {
+ myComment = comment;
+ }
+
+ public Date getCreationDate() {
+ return myCreationDate;
+ }
+
+ public void setCreationDate(Date creationDate) {
+ myCreationDate = creationDate;
+ }
+
+ public Date getExpirationDate() {
+ return myExpirationDate;
+ }
+
+ public void setExpirationDate(Date expirationDate) {
+ myExpirationDate = expirationDate;
+ }
+
+ public org.tigris.subversion.javahl.Lock create() {
+ final Date creation = getCreationDate();
+ final Date expiration = getExpirationDate();
+ final Lock newLock = new Lock(getOwner(), getPath(), getID(), getComment(), creation == null ? 0 : creation.getTime(),
+ expiration == null ? 0 : expiration.getTime());
+ try {
+ final Constructor<org.tigris.subversion.javahl.Lock> constructor = org.tigris.subversion.javahl.Lock.class.getConstructor(Lock.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(newLock);
+ }
+ catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java b/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java
new file mode 100644
index 000000000000..f8acc17d0558
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tigris.subversion.javahl;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 6:29 PM
+ */
+public class BindClientException extends ClientException {
+ private Throwable myCause;
+
+ public BindClientException(String message, String source, int aprError) {
+ super(message, source, aprError);
+ }
+
+ public BindClientException(org.apache.subversion.javahl.ClientException ex) {
+ super(ex);
+ }
+
+ public static BindClientException create(@NotNull final Throwable t, final int code) {
+ final BindClientException exception = new BindClientException(t.getMessage(), null, code);
+ exception.myCause = t;
+ return exception;
+ }
+
+ @Override
+ public Throwable getCause() {
+ return myCause;
+ }
+}
diff --git a/plugins/svn4idea/lib/sqljet.jar b/plugins/svn4idea/lib/sqljet.jar
index d9aa77b29c3f..d6e0232c1001 100644
--- a/plugins/svn4idea/lib/sqljet.jar
+++ b/plugins/svn4idea/lib/sqljet.jar
Binary files differ
diff --git a/plugins/svn4idea/lib/svnkit-javahl.jar b/plugins/svn4idea/lib/svnkit-javahl.jar
index 8fbe09f66c57..265fda0bb963 100644
--- a/plugins/svn4idea/lib/svnkit-javahl.jar
+++ b/plugins/svn4idea/lib/svnkit-javahl.jar
Binary files differ
diff --git a/plugins/svn4idea/lib/svnkit-javahl16.zip b/plugins/svn4idea/lib/svnkit-javahl16.zip
index 7661a0365e9d..da9b8269cbb5 100644
--- a/plugins/svn4idea/lib/svnkit-javahl16.zip
+++ b/plugins/svn4idea/lib/svnkit-javahl16.zip
Binary files differ
diff --git a/plugins/svn4idea/lib/svnkit.jar b/plugins/svn4idea/lib/svnkit.jar
index 203ef4a61af2..46a3ab556976 100644
--- a/plugins/svn4idea/lib/svnkit.jar
+++ b/plugins/svn4idea/lib/svnkit.jar
Binary files differ
diff --git a/plugins/svn4idea/lib/svnkitsrc.zip b/plugins/svn4idea/lib/svnkitsrc.zip
index d31e1a5a49f6..ab1a19f594c0 100644
--- a/plugins/svn4idea/lib/svnkitsrc.zip
+++ b/plugins/svn4idea/lib/svnkitsrc.zip
Binary files differ
diff --git a/plugins/svn4idea/lib/trilead.jar b/plugins/svn4idea/lib/trilead.jar
index 1351e821540a..7fca04a83626 100644
--- a/plugins/svn4idea/lib/trilead.jar
+++ b/plugins/svn4idea/lib/trilead.jar
Binary files differ
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/AuthManagerType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/AuthManagerType.java
new file mode 100644
index 000000000000..b46b7a875b51
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/AuthManagerType.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/28/13
+ * Time: 10:14 AM
+ */
+public enum AuthManagerType {
+ active, passive, usual;
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java
index 0202a3c28604..8efa61780535 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationManager.java
@@ -27,17 +27,22 @@ import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.*;
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;
+import com.intellij.util.messages.Topic;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.proxy.CommonProxy;
import com.intellij.util.ui.UIUtil;
+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.config.ProxyGroup;
+import org.jetbrains.idea.svn.config.SvnServerFileKeys;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
@@ -60,6 +65,10 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
public static final String SVN_SSH = "svn+ssh";
public static final String HTTP = "http";
public static final String HTTPS = "https";
+ public static final String HTTP_PROXY_HOST = "http-proxy-host";
+ public static final String HTTP_PROXY_PORT = "http-proxy-port";
+ public static final String HTTP_PROXY_USERNAME = "http-proxy-username";
+ public static final String HTTP_PROXY_PASSWORD = "http-proxy-password";
private Project myProject;
private File myConfigDirectory;
private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy;
@@ -71,6 +80,10 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
private final ThreadLocalSavePermissions mySavePermissions;
private final Map<Thread, String> myKeyAlgorithm;
private boolean myArtificialSaving;
+ private ISVNAuthenticationProvider myProvider;
+ public static final Topic<ISVNAuthenticationProviderListener> AUTHENTICATION_PROVIDER_LISTENER =
+ new Topic<ISVNAuthenticationProviderListener>("AUTHENTICATION_PROVIDER_LISTENER", ISVNAuthenticationProviderListener.class);
+ private final static ThreadLocal<ISVNAuthenticationProvider> ourThreadLocalProvider = new ThreadLocal<ISVNAuthenticationProvider>();
public SvnAuthenticationManager(final Project project, final File configDirectory) {
super(configDirectory, true, null, null);
@@ -106,6 +119,69 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
});
}
+ private class AuthenticationProviderProxy implements ISVNAuthenticationProvider {
+ private final ISVNAuthenticationProvider myDelegate;
+
+ private AuthenticationProviderProxy(ISVNAuthenticationProvider delegate) {
+ myDelegate = delegate;
+ }
+
+ @Override
+ public SVNAuthentication requestClientAuthentication(String kind,
+ SVNURL url,
+ String realm,
+ SVNErrorMessage errorMessage,
+ SVNAuthentication previousAuth, boolean authMayBeStored) {
+ final SVNAuthentication authentication =
+ myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
+ if (myProject != null && ! myProject.isDisposed()) {
+ myProject.getMessageBus().syncPublisher(AUTHENTICATION_PROVIDER_LISTENER)
+ .requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored, authentication);
+ }
+ return authentication;
+ }
+
+ @Override
+ public int acceptServerAuthentication(SVNURL url,
+ String realm,
+ Object certificate,
+ boolean resultMayBeStored) {
+ final int result = myDelegate.acceptServerAuthentication(url, realm, certificate, resultMayBeStored);
+ if (myProject != null && ! myProject.isDisposed()) {
+ myProject.getMessageBus().syncPublisher(AUTHENTICATION_PROVIDER_LISTENER)
+ .acceptServerAuthentication(url, realm, certificate, resultMayBeStored, result);
+ }
+ return result;
+ }
+ }
+
+ public interface ISVNAuthenticationProviderListener {
+ void requestClientAuthentication(String kind, SVNURL url, String realm, SVNErrorMessage errorMessage,
+ SVNAuthentication previousAuth, boolean authMayBeStored, SVNAuthentication authentication);
+ void acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored, @MagicConstant int acceptResult);
+ }
+
+ @Override
+ public void setAuthenticationProvider(ISVNAuthenticationProvider provider) {
+ ISVNAuthenticationProvider useProvider = provider;
+ if (! (provider instanceof AuthenticationProviderProxy)) {
+ useProvider = new AuthenticationProviderProxy(provider);
+ }
+ myProvider = useProvider;
+ super.setAuthenticationProvider(myProvider);
+ }
+
+ public ISVNAuthenticationProvider getProvider() {
+ final ISVNAuthenticationProvider threadProvider = ourThreadLocalProvider.get();
+ if (threadProvider != null) return threadProvider;
+ return myProvider;
+ }
+
+ @Override
+ public ISVNAuthenticationStorage getRuntimeAuthStorage() {
+ return super.getRuntimeAuthStorage();
+ }
+
// since set to null during dispose and we have background processes
private SvnConfiguration getConfig() {
if (myConfig == null) throw new ProcessCanceledException();
@@ -361,6 +437,16 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
public void acknowledgeConnectionSuccessful(SVNURL url) {
CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
SSLExceptionsHelper.removeInfo();
+ ourThreadLocalProvider.remove();
+ }
+
+ @Override
+ public void acknowledgeAuthentication(boolean accepted,
+ String kind,
+ String realm,
+ SVNErrorMessage errorMessage,
+ SVNAuthentication authentication) throws SVNException {
+ acknowledgeAuthentication(accepted, kind, realm, errorMessage, authentication, null);
}
@Override
@@ -371,7 +457,10 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
SVNAuthentication authentication,
SVNURL url) throws SVNException {
SSLExceptionsHelper.removeInfo();
- CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
+ ourThreadLocalProvider.remove();
+ if (url != null) {
+ CommonProxy.getInstance().removeNoProxy(url.getProtocol(), url.getHost(), url.getPort());
+ }
boolean successSaving = false;
myListener.getMulticaster().acknowledge(accepted, kind, realm, errorMessage, authentication);
try {
@@ -382,18 +471,40 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
} finally {
mySavePermissions.remove();
if (myArtificialSaving) {
+ myArtificialSaving = false;
throw new CredentialsSavedException(successSaving);
}
}
}
+ public void acknowledgeForSSL(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication proxy) {
+ if (accepted && proxy instanceof SVNSSLAuthentication && (((SVNSSLAuthentication) proxy).getCertificateFile() != null)) {
+ final SVNSSLAuthentication svnsslAuthentication = (SVNSSLAuthentication)proxy;
+ final SVNURL url = svnsslAuthentication.getURL();
+
+ final IdeaSVNHostOptionsProvider provider = getHostOptionsProvider();
+ 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);
+ }
+ serversFile.save();
+ }
+ }
+
public ISVNProxyManager getProxyManager(SVNURL url) throws SVNException {
SSLExceptionsHelper.addInfo("Accessing URL: " + url.toString());
CommonProxy.getInstance().noProxy(url.getProtocol(), url.getHost(), url.getPort());
+ ourThreadLocalProvider.set(myProvider);
// this code taken from default manager (changed for system properties reading)
String host = url.getHost();
- String proxyHost = getServersPropertyIdea(host, "http-proxy-host");
+ String proxyHost = getServersPropertyIdea(host, HTTP_PROXY_HOST);
if ((proxyHost == null) || "".equals(proxyHost.trim())) {
if (getConfig().isIsUseDefaultProxy()) {
// ! use common proxy if it is set
@@ -431,9 +542,9 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
}
}
}
- String proxyPort = getServersPropertyIdea(host, "http-proxy-port");
- String proxyUser = getServersPropertyIdea(host, "http-proxy-username");
- String proxyPassword = getServersPropertyIdea(host, "http-proxy-password");
+ String proxyPort = getServersPropertyIdea(host, HTTP_PROXY_PORT);
+ String proxyUser = getServersPropertyIdea(host, HTTP_PROXY_USERNAME);
+ String proxyPassword = getServersPropertyIdea(host, HTTP_PROXY_PASSWORD);
return new MySimpleProxyManager(proxyHost, proxyPort, proxyUser, proxyPassword);
}
@@ -601,21 +712,35 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
return false;
}
+ @Nullable
+ public static String getGroupForHost(final String host, final IdeaSVNConfigFile serversFile) {
+ final Map<String,ProxyGroup> groups = serversFile.getAllGroups();
+ for (Map.Entry<String, ProxyGroup> entry : groups.entrySet()) {
+ if (matchesGroupPattern(host, entry.getValue().getPatterns())) return entry.getKey();
+ }
+ return null;
+ }
+
// taken from default manager as is
private static String getGroupName(Map groups, String host) {
- for (Iterator names = groups.keySet().iterator(); names.hasNext();) {
- String name = (String) names.next();
- String pattern = (String) groups.get(name);
- for(StringTokenizer tokens = new StringTokenizer(pattern, ","); tokens.hasMoreTokens();) {
- String token = tokens.nextToken();
- if (DefaultSVNOptions.matches(token, host)) {
- return name;
- }
- }
- }
+ for (Object o : groups.keySet()) {
+ final String name = (String) o;
+ final String pattern = (String) groups.get(name);
+ if (matchesGroupPattern(host, pattern)) return name;
+ }
return null;
}
+ private static boolean matchesGroupPattern(String host, String pattern) {
+ for(StringTokenizer tokens = new StringTokenizer(pattern, ","); tokens.hasMoreTokens();) {
+ String token = tokens.nextToken();
+ if (DefaultSVNOptions.matches(token, host)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// default = yes
private static boolean isTurned(final String value) {
return value == null || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value);
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties
index d2e430e91b2a..0cf1b0a5eb25 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties
@@ -435,7 +435,7 @@ checkout.options.recursive.label=Checkout directories recursively
checkout.options.externals.label=Include externals locations
configure.branches.title=Configure Subversion Branches
configure.branches.error.no.connection.title=Repository root was not found
-configure.branches.error.wrong.url=Trunk location must be under repository root
+configure.branches.error.wrong.url=Trunk location must be under repository root\n''{0}''
configure.branches.trunk.location=Trunk location:
configure.branches.branch.locations=Branch locations:
configure.branches.item=Configure Branches...
@@ -648,4 +648,10 @@ svn.create.external.below.action=Create External...
svn.create.external.below.description=Select URL, add svn\:external property, and optionally checkout it
svn.edit.commit.message.title=Edit Revision #{0} Comment
svn.edit.commit.message.attention=Attention! Previous message will be lost!
-svn.edit.commit.message.prompt=New revision comment: \ No newline at end of file
+svn.edit.commit.message.prompt=New revision comment:
+quick.merge.variants.merge.all.explanation=All not merged revisions will be merged.\n\
+Subversion will find not merged revisions using svn\:mergeinfo property recorded in local copy.
+quick.merge.variants.quick.select.explanation=Shows all revisions from target branch, merged and not merged.\n\
+For manual selection. Very quick.
+quick.merge.variants.pre.select.explanation=Finds where one of involved branches was copied from another.\n\
+Loads only not yet merged revisions for selection. Can take long time for execution. \ No newline at end of file
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java
index ebdf274bb4d7..3d837368ed66 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java
@@ -113,7 +113,7 @@ public class SvnChangeProvider implements ChangeProvider {
throw new VcsException(e.getCause());
} catch (SVNException e) {
if (e.getCause() != null) {
- throw new VcsException(e.getMessage() + e.getCause().getMessage(), e);
+ throw new VcsException(e.getMessage() + " " + e.getCause().getMessage(), e);
}
throw new VcsException(e);
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java
index 14c005427be6..66132f516be6 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfiguration.java
@@ -17,6 +17,8 @@
package org.jetbrains.idea.svn;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -24,11 +26,14 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher;
import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.svn.config.ProxyGroup;
import org.jetbrains.idea.svn.config.SvnServerFileKeys;
import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider;
import org.jetbrains.idea.svn.dialogs.SvnInteractiveAuthenticationProvider;
@@ -48,6 +53,9 @@ import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.io.File;
import java.io.FilenameFilter;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
import java.util.*;
@State(
@@ -166,6 +174,47 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
myConfigFile.save();
}
+ 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 = SvnAuthenticationManager.getGroupForHost(host, configFile);
+
+ if (StringUtil.isEmptyOrSpaces(groupName)) {
+ groupName = StringUtil.replace(host, " ", "_");
+ final Map<String,ProxyGroup> groups = configFile.getAllGroups();
+ while (true) {
+ if (! groups.containsKey(groupName)) break;
+ groupName += "1";
+ }
+ }
+
+ final HashMap<String, String> map = new HashMap<String, String>();
+ 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();
+ }
+
+ 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<String, String> 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 static SvnConfiguration getInstance(final Project project) {
return ServiceManager.getService(project, SvnConfiguration.class);
}
@@ -281,19 +330,33 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
}
public static SvnAuthenticationManager createForTmpDir(final Project project, final File dir) {
+ return createForTmpDir(project, dir, null);
+ }
+
+ public static SvnAuthenticationManager createForTmpDir(final Project project, final File dir,
+ @Nullable final SvnInteractiveAuthenticationProvider provider) {
final SvnVcs vcs = SvnVcs.getInstance(project);
- //final SvnAuthenticationManager manager = new SvnAuthenticationManager(project, dir);
final SvnAuthenticationManager interactive = new SvnAuthenticationManager(project, dir);
interactive.setRuntimeStorage(RUNTIME_AUTH_CACHE);
- final SvnInteractiveAuthenticationProvider interactiveProvider = new SvnInteractiveAuthenticationProvider(vcs, interactive);
+ final SvnInteractiveAuthenticationProvider interactiveProvider = provider == null ?
+ new SvnInteractiveAuthenticationProvider(vcs, interactive) : provider;
interactive.setAuthenticationProvider(interactiveProvider);
- //manager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, interactiveProvider, RUNTIME_AUTH_CACHE));
- //manager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
return interactive;
}
+ public SvnAuthenticationManager getManager(final AuthManagerType type, final SvnVcs vcs) {
+ if (AuthManagerType.active.equals(type)) {
+ return getInteractiveManager(vcs);
+ } else if (AuthManagerType.passive.equals(type)) {
+ return getPassiveAuthenticationManager(vcs.getProject());
+ } else if (AuthManagerType.usual.equals(type)) {
+ return getAuthenticationManager(vcs);
+ }
+ throw new IllegalArgumentException();
+ }
+
public SvnAuthenticationManager getAuthenticationManager(final SvnVcs svnVcs) {
if (myAuthManager == null) {
// reloaded when configuration directory changes
@@ -565,7 +628,7 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
public void clearAuthenticationDirectory(@Nullable Project project) {
final File authDir = new File(getConfigurationDirectory(), "auth");
if (authDir.exists()) {
- ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
+ final Runnable process = new Runnable() {
public void run() {
final ProgressIndicator ind = ProgressManager.getInstance().getProgressIndicator();
if (ind != null) {
@@ -585,7 +648,13 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
FileUtil.delete(dir);
}
}
- }, "button.text.clear.authentication.cache", false, project);
+ };
+ final Application application = ApplicationManager.getApplication();
+ if (application.isUnitTestMode() || ! application.isDispatchThread()) {
+ process.run();
+ } else {
+ ProgressManager.getInstance().runProcessWithProgressSynchronously(process, "button.text.clear.authentication.cache", false, project);
+ }
}
}
@@ -601,6 +670,11 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
RUNTIME_AUTH_CACHE.putData(kind, realm, null);
}
+ public void clearRuntimeStorage() {
+ RUNTIME_AUTH_CACHE.clear();
+ }
+
+
public int getMaxAnnotateRevisions() {
return myMaxAnnotateRevisions;
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java
index 105b156a914e..38c099ba4136 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java
@@ -29,12 +29,9 @@ import com.intellij.openapi.vcs.impl.CurrentRevisionProvider;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
-import org.tmatesoft.svn.core.wc.SVNWCClient;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -116,29 +113,7 @@ public class SvnContentRevision implements ContentRevision, MarkerVcsContentRevi
if (lock.exists()) {
throw new VcsException("Can not access file base revision contents: administrative area is locked");
}
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- SVNWCClient wcClient = myVcs.createWCClient();
- try {
- wcClient.doGetFileContents(file, SVNRevision.UNDEFINED, myUseBaseRevision ? SVNRevision.BASE : myRevision, true, buffer);
- buffer.close();
- }
- catch (SVNException e) {
- /*try {
- final SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED);
- //todo
- }
- catch (SVNException e1) {
- throw new VcsException(e);
- }*/
- throw new VcsException(e);
- }
- catch (IOException e) {
- throw new VcsException(e);
- }
- final byte[] bytes = buffer.toByteArray();
- /*final Charset charset = myFile.getCharset();
- return charset == null ? bytes : SvnUtil.decode(charset, bytes);*/
- return bytes;
+ return SvnUtil.getFileContents(myVcs, file.getPath(), false, myUseBaseRevision ? SVNRevision.BASE : myRevision, SVNRevision.UNDEFINED);
}
@NotNull
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java
index 30cb2a92a6af..062c412f2336 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java
@@ -228,6 +228,8 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp
}
public boolean rename(VirtualFile file, String newName) throws IOException {
+ FileDocumentManager.getInstance().saveAllDocuments();
+
File srcFile = getIOFile(file);
File dstFile = new File(srcFile.getParentFile(), newName);
SvnVcs vcs = getVCS(file);
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java
index 22f8b05ca71f..94d21dbbbd9d 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java
@@ -173,7 +173,7 @@ public class SvnFormatSelector implements ISVNAdminAreaFactorySelector {
private static boolean displayUpgradeDialog(Project project, File path, final boolean dispay13format, String[] newMode) {
UpgradeFormatDialog dialog = new UpgradeFormatDialog(project, path, false);
- dialog.setData(dispay13format, newMode[0]);
+ dialog.setData(newMode[0]);
dialog.show();
if (dialog.isOK()) {
newMode[0] = dialog.getUpgradeMode();
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java
index f4fdfac1114d..4a49dc16a153 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java
@@ -16,6 +16,7 @@
package org.jetbrains.idea.svn;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
@@ -28,6 +29,7 @@ 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.Nullable;
import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
import org.jetbrains.idea.svn.portable.JavaHLSvnStatusClient;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
@@ -43,6 +45,7 @@ import java.io.File;
import java.util.LinkedList;
public class SvnRecursiveStatusWalker {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnRecursiveStatusWalker");
private final StatusWalkerPartner myPartner;
private final Project myProject;
private final StatusReceiver myReceiver;
@@ -71,8 +74,9 @@ public class SvnRecursiveStatusWalker {
if (path.isDirectory()) {
myHandler.setCurrentItem(item);
try {
- item.getClient(ioFile).doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, true, myHandler, null);
- myHandler.checkIfCopyRootWasReported();
+ final SvnStatusClientI client = item.getClient(ioFile);
+ client.doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, true, myHandler, null);
+ myHandler.checkIfCopyRootWasReported(null, ioFile);
}
catch (SVNException e) {
handleStatusException(item, path, e);
@@ -223,10 +227,18 @@ public class SvnRecursiveStatusWalker {
myMetCurrentItem = false;
}
- public void checkIfCopyRootWasReported() {
- if (! myMetCurrentItem) {
+ public void checkIfCopyRootWasReported(@Nullable final SVNStatus ioFileStatus, final File ioFile) {
+ if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, myCurrentItem.getPath().getIOFile())) {
myMetCurrentItem = true;
- final SVNStatus statusInner = SvnUtil.getStatus(SvnVcs.getInstance(myProject), myCurrentItem.getPath().getIOFile());
+ SVNStatus statusInner;
+ try {
+ statusInner = ioFileStatus != null ? ioFileStatus :
+ myCurrentItem.getClient().doStatus(myCurrentItem.getPath().getIOFile(), false);
+ }
+ catch (SVNException e) {
+ LOG.info(e);
+ statusInner = null;
+ }
if (statusInner == null) return;
final SVNStatusType status = statusInner.getNodeStatus();
@@ -261,7 +273,7 @@ public class SvnRecursiveStatusWalker {
public void handleStatus(final SVNStatus status) throws SVNException {
myPartner.checkCanceled();
final File ioFile = status.getFile();
- checkIfCopyRootWasReported();
+ checkIfCopyRootWasReported(status, ioFile);
final VirtualFile vFile = getVirtualFile(ioFile);
if (vFile != null) {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java
index ee25402d8ff0..0c049ac4671d 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java
@@ -30,6 +30,7 @@ import com.intellij.openapi.vcs.AbstractVcsHelper;
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.impl.ContentRevisionCache;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
@@ -38,6 +39,7 @@ import com.intellij.openapi.wm.impl.status.StatusBarUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
+import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -52,7 +54,10 @@ import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.*;
public class SvnUtil {
@@ -298,7 +303,7 @@ public class SvnUtil {
}
public static Collection<List<Change>> splitChangesIntoWc(final SvnVcs vcs, final List<Change> changes) {
- return splitIntoWc(vcs, changes, new Convertor<Change, File>() {
+ return splitIntoRepositories(vcs, changes, new Convertor<Change, File>() {
@Override
public File convert(Change o) {
return ChangesUtil.getFilePath(o).getIOFile();
@@ -306,16 +311,30 @@ public class SvnUtil {
});
}
- public static Collection<List<File>> splitFilesIntoWc(final SvnVcs vcs, final List<File> committables) {
- return splitIntoWc(vcs, committables, Convertor.SELF);
+ public static Collection<List<File>> splitFilesIntoRepositories(final SvnVcs vcs, final List<File> committables) {
+ return splitIntoRepositories(vcs, committables, Convertor.SELF);
}
- public static <T> Collection<List<T>> splitIntoWc(final SvnVcs vcs, final List<T> committables,
- Convertor<T, File> convertor) {
+ public static <T> Collection<List<T>> splitIntoRepositories(final SvnVcs vcs, final List<T> committables,
+ Convertor<T, File> convertor) {
if (committables.size() == 1) {
return Collections.singletonList(committables);
}
+ final MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> result = splitIntoRepositoriesMap(vcs, committables, convertor);
+
+ if (result.size() == 1) {
+ return Collections.singletonList(committables);
+ }
+ final Collection<List<T>> result2 = new ArrayList<List<T>>();
+ for (Map.Entry<Pair<SVNURL, WorkingCopyFormat>, Collection<T>> entry : result.entrySet()) {
+ result2.add((List<T>)entry.getValue());
+ }
+ return result2;
+ }
+
+ public static <T> MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> splitIntoRepositoriesMap(SvnVcs vcs,
+ List<T> committables, Convertor<T, File> convertor) {
final MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> result = new MultiMap<Pair<SVNURL, WorkingCopyFormat>, T>() {
@Override
protected Collection<T> createCollection() {
@@ -330,15 +349,7 @@ public class SvnUtil {
result.putValue(new Pair<SVNURL, WorkingCopyFormat>(path.getRepositoryUrlUrl(), path.getFormat()), committable);
}
}
-
- if (result.size() == 1) {
- return Collections.singletonList(committables);
- }
- final Collection<List<T>> result2 = new ArrayList<List<T>>();
- for (Map.Entry<Pair<SVNURL, WorkingCopyFormat>, Collection<T>> entry : result.entrySet()) {
- result2.add((List<T>)entry.getValue());
- }
- return result2;
+ return result;
}
private static class LocationsCrawler implements SvnWCRootCrawler {
@@ -578,6 +589,12 @@ public class SvnUtil {
return false;
}
+ public static SVNURL getCommittedURL(final SvnVcs vcs, final File file) {
+ final File root = getWorkingCopyRoot(file);
+ if (root == null) return null;
+ return getUrl(vcs, root);
+ }
+
@Nullable
public static SVNURL getUrl(final SvnVcs vcs, final File file) {
try {
@@ -626,12 +643,22 @@ public class SvnUtil {
}
public static File getWcDb(final File file) {
- return new File(file, ".svn/wc.db");
+ return new File(file, SVN_ADMIN_DIR_NAME + "/wc.db");
}
@Nullable
public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) {
File current = file;
+ boolean wcDbFound = false;
+ while (current != null) {
+ File wcDb;
+ if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) {
+ wcDbFound = true;
+ break;
+ }
+ current = current.getParentFile();
+ }
+ if (! wcDbFound) return null;
while (current != null) {
try {
final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(current, false);
@@ -675,4 +702,45 @@ public class SvnUtil {
}
return result;
}
+
+ public static byte[] getFileContents(final SvnVcs vcs, final String path, final boolean isUrl, final SVNRevision revision,
+ final SVNRevision pegRevision)
+ throws VcsException {
+ final int maxSize = VcsUtil.getMaxVcsLoadedFileSize();
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream() {
+ @Override
+ public synchronized void write(int b) {
+ if (size() > maxSize) throw new FileTooBigRuntimeException();
+ super.write(b);
+ }
+
+ @Override
+ public synchronized void write(byte[] b, int off, int len) {
+ if (size() > maxSize) throw new FileTooBigRuntimeException();
+ super.write(b, off, len);
+ }
+
+ @Override
+ public synchronized void writeTo(OutputStream out) throws IOException {
+ if (size() > maxSize) throw new FileTooBigRuntimeException();
+ super.writeTo(out);
+ }
+ };
+ SVNWCClient wcClient = vcs.createWCClient();
+ try {
+ if (isUrl) {
+ wcClient.doGetFileContents(SVNURL.parseURIEncoded(path), pegRevision, revision, true, buffer);
+ } else {
+ wcClient.doGetFileContents(new File(path), pegRevision, revision, true, buffer);
+ }
+ ContentRevisionCache.checkContentsSize(path, buffer.size());
+ } catch (FileTooBigRuntimeException e) {
+ ContentRevisionCache.checkContentsSize(path, buffer.size());
+ } catch (SVNException e) {
+ throw new VcsException(e);
+ }
+ return buffer.toByteArray();
+ }
+
+ private static class FileTooBigRuntimeException extends RuntimeException {}
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java
index 838295cb20a7..dbc78b2799d8 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java
@@ -77,7 +77,8 @@ import org.jetbrains.idea.svn.history.LoadedRevisionsCache;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider;
import org.jetbrains.idea.svn.history.SvnHistoryProvider;
-import org.jetbrains.idea.svn.lowLevel.SvnIdeaRepositoryPoolManager;
+import org.jetbrains.idea.svn.lowLevel.PrimitivePool;
+import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser;
import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment;
import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment;
import org.jetbrains.idea.svn.update.SvnUpdateEnvironment;
@@ -88,6 +89,7 @@ import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
+import org.tmatesoft.svn.core.internal.util.SVNSSLUtil;
import org.tmatesoft.svn.core.internal.util.jna.SVNJNAUtil;
import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea14;
@@ -100,10 +102,12 @@ import org.tmatesoft.svn.util.SVNDebugLogAdapter;
import org.tmatesoft.svn.util.SVNLogType;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateException;
import java.util.*;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
@@ -127,7 +131,6 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
private final Map<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>> myPropertyCache =
new SoftHashMap<String, Map<String, Pair<SVNPropertyValue, Trinity<Long, Long, Long>>>>();
- private SvnIdeaRepositoryPoolManager myPool;
private final SvnConfiguration myConfiguration;
private final SvnEntriesFileListener myEntriesFileListener;
@@ -436,7 +439,6 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
@Override
public void activate() {
- createPool();
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
if (!myProject.isDefault()) {
ChangeListManager.getInstance(myProject).addChangeListListener(myChangeListListener);
@@ -593,8 +595,6 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
mySvnBranchPointsCalculator = null;
myWorkingCopiesContent.deactivate();
myLoadedBranchesStorage.deactivate();
- myPool.dispose();
- myPool = null;
}
public VcsShowConfirmationOption getAddConfirmation() {
@@ -640,29 +640,17 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
return repos;
}
- private void createPool() {
- if (myPool != null) return;
- final String property = System.getProperty(KEEP_CONNECTIONS_KEY);
- final boolean keep;
- boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();
- // pool variant by default
- if (StringUtil.isEmptyOrSpaces(property) || unitTestMode) {
- keep = ! unitTestMode; // default
- } else {
- keep = Boolean.getBoolean(KEEP_CONNECTIONS_KEY);
- }
- myPool = new SvnIdeaRepositoryPoolManager(false, myConfiguration.getAuthenticationManager(this), myConfiguration.getOptions(myProject));
+ @NotNull
+ private ISVNRepositoryPool getPool() {
+ return getPool(myConfiguration.getAuthenticationManager(this));
}
@NotNull
- private ISVNRepositoryPool getPool() {
+ private ISVNRepositoryPool getPool(ISVNAuthenticationManager manager) {
if (myProject.isDisposed()) {
throw new ProcessCanceledException();
}
- if (myPool == null) {
- createPool();
- }
- return myPool;
+ return new PrimitivePool(manager, myConfiguration.getOptions(myProject));
}
public SVNUpdateClient createUpdateClient() {
@@ -672,7 +660,7 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
}
public SVNUpdateClient createUpdateClient(@NotNull ISVNAuthenticationManager manager) {
- final SVNUpdateClient client = new SVNUpdateClient(getPool(), myConfiguration.getOptions(myProject));
+ final SVNUpdateClient client = new SVNUpdateClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
@@ -691,7 +679,7 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
}
public SVNWCClient createWCClient(@NotNull ISVNAuthenticationManager manager) {
- final SVNWCClient client = new SVNWCClient(getPool(), myConfiguration.getOptions(myProject));
+ final SVNWCClient client = new SVNWCClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
@@ -715,7 +703,7 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
}
public SVNLogClient createLogClient(@NotNull ISVNAuthenticationManager manager) {
- final SVNLogClient client = new SVNLogClient(getPool(), myConfiguration.getOptions(myProject));
+ final SVNLogClient client = new SVNLogClient(getPool(manager), myConfiguration.getOptions(myProject));
client.getOperationsFactory().setAuthenticationManager(manager);
return client;
}
@@ -957,7 +945,7 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
private final boolean myLoggingEnabled;
private final boolean myLogNative;
private final Logger myLog;
- private final static long ourErrorNotificationInterval = 10000;
+ private final static long ourErrorNotificationInterval = TimeUnit.MINUTES.toMillis(2);
private long myPreviousTime = 0;
public JavaSVNDebugLogger(boolean loggingEnabled, boolean logNative, Logger log) {
@@ -972,23 +960,46 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
@Override
public void log(final SVNLogType logType, final Throwable th, final Level logLevel) {
+ handleSpecificSSLExceptions(th);
+ if (shouldLog(logType)) {
+ myLog.info(th);
+ }
+ }
+
+ private void handleSpecificSSLExceptions(Throwable th) {
+ final long time = System.currentTimeMillis();
+ if ((time - myPreviousTime) <= ourErrorNotificationInterval) {
+ return;
+ }
if (th instanceof SSLHandshakeException) {
- final long time = System.currentTimeMillis();
- if ((time - myPreviousTime) > ourErrorNotificationInterval) {
+ // not trusted certificate exception is not the problem, just part of normal behaviour
+ if (th.getCause() instanceof SVNSSLUtil.CertificateNotTrustedException) {
+ LOG.info(th);
+ return;
+ }
+
+ myPreviousTime = time;
+ String info = SSLExceptionsHelper.getAddInfo();
+ info = info == null ? "" : " (" + info + ") ";
+ if (th.getCause() instanceof CertificateException) {
+ PopupUtil.showBalloonForActiveFrame("Subversion: " + info + th.getCause().getMessage(), MessageType.ERROR);
+ } else {
+ final String postMessage = "\nPlease check Subversion SSL settings (Settings | Version Control | Subversion | Network)\n" +
+ "Maybe you should specify SSL protocol manually - SSLv3 or TLSv1";
+ PopupUtil.showBalloonForActiveFrame("Subversion: " + info + th.getMessage() + postMessage, MessageType.ERROR);
+ }
+ } else if (th instanceof SSLProtocolException) {
+ final String message = th.getMessage();
+ if (! StringUtil.isEmptyOrSpaces(message)) {
myPreviousTime = time;
String info = SSLExceptionsHelper.getAddInfo();
info = info == null ? "" : " (" + info + ") ";
- if (th.getCause() instanceof CertificateException) {
- PopupUtil.showBalloonForActiveComponent("Subversion: " + info + th.getCause().getMessage(), MessageType.ERROR);
- } else {
- final String postMessage = "\nPlease check Subversion SSL settings (Settings | Version Control | Subversion | Network)";
- PopupUtil.showBalloonForActiveComponent("Subversion: " + info + th.getMessage() + postMessage, MessageType.ERROR);
- }
+ final SSLProtocolExceptionParser parser = new SSLProtocolExceptionParser(message);
+ parser.parse();
+ final String errMessage = "Subversion: " + info + parser.getParsedMessage();
+ PopupUtil.showBalloonForActiveFrame(errMessage, MessageType.ERROR);
}
}
- if (shouldLog(logType)) {
- myLog.info(th);
- }
}
@Override
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java
new file mode 100644
index 000000000000..3bd8256e7b02
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java
@@ -0,0 +1,62 @@
+/*
+ * 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.progress.ProgressIndicator;
+import org.jetbrains.idea.svn.CommitEventHandler;
+import org.jetbrains.idea.svn.CommitEventType;
+import org.jetbrains.idea.svn.SvnBundle;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 11:13 AM
+ */
+public class IdeaCommitHandler implements CommitEventHandler {
+ private final ProgressIndicator myPi;
+
+ public IdeaCommitHandler(ProgressIndicator pi) {
+ myPi = pi;
+ }
+
+ @Override
+ public void commitEvent(CommitEventType type, File target) {
+ if (myPi == null) return;
+ myPi.checkCanceled();
+
+ if (CommitEventType.adding.equals(type)) {
+ myPi.setText2(SvnBundle.message("progress.text2.adding", target));
+ } else if (CommitEventType.deleting.equals(type)) {
+ myPi.setText2(SvnBundle.message("progress.text2.deleting", target));
+ } else if (CommitEventType.sending.equals(type)) {
+ myPi.setText2(SvnBundle.message("progress.text2.sending", target));
+ } else if (CommitEventType.replacing.equals(type)) {
+ myPi.setText2(SvnBundle.message("progress.text2.replacing", target));
+ } else if (CommitEventType.transmittingDeltas.equals(type)) {
+ myPi.setText2(SvnBundle.message("progress.text2.transmitting.delta", target));
+ }
+ }
+
+ @Override
+ public void committedRevision(long revNum) {
+ if (myPi == null) return;
+ myPi.checkCanceled();
+ myPi.setText2(SvnBundle.message("status.text.comitted.revision", revNum));
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java
new file mode 100644
index 000000000000..618507117bee
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java
@@ -0,0 +1,591 @@
+/*
+ * 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.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.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.messages.MessageBusConnection;
+import com.intellij.util.net.HttpConfigurable;
+import com.intellij.util.proxy.CommonProxy;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.svn.*;
+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;
+
+/**
+ * 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;
+
+ public IdeaSvnkitBasedAuthenticationCallback(SvnVcs vcs) {
+ myVcs = vcs;
+ }
+
+ @Override
+ public boolean authenticateFor(String realm, File file, boolean previousFailed, boolean passwordRequest) {
+ final File base = getExistingParent(file);
+ if (base == null) return false;
+ final SVNURL url = SvnUtil.getCommittedURL(myVcs, base);
+ if (url == null) return false;
+
+ return new CredentialsAuthenticator(myVcs).tryAuthenticate(realm, url, file, previousFailed, passwordRequest);
+ }
+
+ @Override
+ public boolean acceptSSLServerCertificate(final File file, final String realm) {
+ final File base = getExistingParent(file);
+ if (base == null) return false;
+ final SVNURL url = SvnUtil.getCommittedURL(myVcs, base);
+ if (url == null) return false;
+
+ return new SSLServerCertificateAuthenticator(myVcs).tryAuthenticate(url, realm);
+ }
+
+ @Override
+ public void clearPassiveCredentials(String realm, File file, boolean password) {
+ final File base = getExistingParent(file);
+ if (base == null) return;
+ final SVNURL url = SvnUtil.getCommittedURL(myVcs, base);
+ if (url == null) return;
+ final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
+ final List<String> kinds = getKinds(url, 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);
+ }
+
+ @Override
+ public boolean persistDataToTmpConfig(final File baseFile) throws IOException, URISyntaxException {
+ final File base = getExistingParent(baseFile);
+ if (base == null) return false;
+ final SVNURL url = SvnUtil.getCommittedURL(myVcs, base);
+ if (url == null) return false;
+
+ final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject());
+ initTmpDir(configuration);
+
+ final Proxy proxy = getIdeaDefinedProxy(url);
+ if (proxy != null){
+ SvnConfiguration.putProxyIntoServersFile(myTempDirectory, url.getHost(), proxy);
+ }
+ return true;
+ }
+
+ @Nullable
+ private static Proxy getIdeaDefinedProxy(final SVNURL url) throws URISyntaxException {
+ final List<Proxy> proxies = CommonProxy.getInstance().select(new URI(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
+ public boolean askProxyCredentials(File baseFile) {
+ final File base = getExistingParent(baseFile);
+ if (base == null) return false;
+ final SVNURL url = SvnUtil.getCommittedURL(myVcs, base);
+ if (url == null) return false;
+
+ final Proxy proxy;
+ try {
+ proxy = getIdeaDefinedProxy(url);
+ }
+ catch (URISyntaxException e) {
+ LOG.info(e);
+ return false;
+ }
+ if (proxy == null) return false;
+ 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 false;
+ }
+ final InetSocketAddress address = (InetSocketAddress)proxy.address();
+ final PasswordAuthentication authentication;
+ try {
+ authentication = Authenticator.requestPasswordAuthentication(url.getHost(), address.getAddress(),
+ url.getPort(), url.getProtocol(), url.getHost(), url.getProtocol(),
+ new URL(url.toString()), Authenticator.RequestorType.PROXY);
+ } catch (MalformedURLException e) {
+ LOG.info(e);
+ return false;
+ }
+ if (authentication != null) {
+ myProxyCredentialsWereReturned = true;
+ return SvnConfiguration.putProxyCredentialsIntoServerFile(myTempDirectory, url.getHost(), authentication);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean acceptSSLServerCertificate(final String url, final String realm) {
+ try {
+ return new SSLServerCertificateAuthenticator(myVcs).tryAuthenticate(SVNURL.parseURIEncoded(url), realm);
+ }
+ catch (SVNException e) {
+ return false;
+ }
+ }
+
+ public void reset() {
+ if (myTempDirectory != null) {
+ FileUtil.delete(myTempDirectory);
+ }
+ }
+
+ private abstract class AbstractAuthenticator<T> {
+ 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() {
+ return SvnConfiguration.createForTmpDir(myVcs.getProject(), myTempDirectory);
+ }
+
+ 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<SVNException> 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<Boolean> {
+ 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<SVNException>() {
+ @Override
+ public void run() throws SVNException {
+ myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD);
+ }
+ }
+ );
+
+ 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<SVNAuthentication> {
+ 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;
+ private File myFile;
+
+ protected CredentialsAuthenticator(SvnVcs vcs) {
+ super(vcs);
+ }
+
+ public boolean tryAuthenticate(String realm, SVNURL url, File file, boolean previousFailed, boolean passwordRequest) {
+ myFile = file;
+ realm = realm == null ? url.getHost() : realm;
+ myRealm = realm;
+ myUrl = url;
+ final List<String> 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, myFile, 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<SVNException>() {
+ @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 File getExistingParent(final File file) {
+ File current = file;
+ while (current != null) {
+ if (current.exists()) return current;
+ current = current.getParentFile();
+ }
+ return null;
+ }
+
+ private static List<String> 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;
+ }
+
+ 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/SvnCheckinEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java
index ea1a7ee6937e..433e4063e02c 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java
@@ -25,10 +25,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vcs.CheckinProjectPanel;
-import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeList;
import com.intellij.openapi.vcs.changes.ChangesUtil;
@@ -39,20 +38,19 @@ import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.ArrayUtil;
import com.intellij.util.FunctionUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.PairConsumer;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
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.SvnUtil;
-import org.jetbrains.idea.svn.SvnVcs;
-import org.tmatesoft.svn.core.SVNCancelException;
-import org.tmatesoft.svn.core.SVNCommitInfo;
-import org.tmatesoft.svn.core.SVNDepth;
-import org.tmatesoft.svn.core.SVNException;
+import org.jetbrains.idea.svn.*;
+import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient;
+import org.tigris.subversion.javahl.ClientException;
+import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.*;
import javax.swing.*;
@@ -140,18 +138,18 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
}
if (progress != null) {
- doCommit(committables, progress, committer, comment, force, recursive, exception, feedback);
+ doCommit(committables, committer, comment, force, exception, feedback);
}
else if (ApplicationManager.getApplication().isDispatchThread()) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
ProgressIndicator p = ProgressManager.getInstance().getProgressIndicator();
- doCommit(committables, p, committer, comment, force, recursive, exception, feedback);
+ doCommit(committables, committer, comment, force, exception, feedback);
}
}, SvnBundle.message("progress.title.commit"), false, mySvnVcs.getProject());
}
else {
- doCommit(committables, progress, committer, comment, force, recursive, exception, feedback);
+ doCommit(committables, committer, comment, force, exception, feedback);
}
for(VirtualFile f : deletedFiles) {
@@ -161,28 +159,39 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
}
private void doCommit(List<File> committables,
- ProgressIndicator progress,
SVNCommitClient committer,
String comment,
boolean force,
- boolean recursive,
List<VcsException> exception, final Set<String> feedback) {
- final Collection<List<File>> collections = SvnUtil.splitFilesIntoWc(mySvnVcs, committables);
- for (List<File> collection : collections) {
- doCommitOneWc(collection, progress, committer, comment, force, recursive, exception, feedback);
+ final MultiMap<Pair<SVNURL,WorkingCopyFormat>,File> map = SvnUtil.splitIntoRepositoriesMap(mySvnVcs, committables, Convertor.SELF);
+ for (Map.Entry<Pair<SVNURL, WorkingCopyFormat>, Collection<File>> entry : map.entrySet()) {
+ doCommitOneRepo(entry.getValue(), committer, comment, force, exception, feedback, entry.getKey().getSecond(), entry.getKey().getFirst());
}
}
- private void doCommitOneWc(Collection<File> committables,
- ProgressIndicator progress,
- SVNCommitClient committer,
- String comment,
- boolean force,
- boolean recursive,
- List<VcsException> exception, final Set<String> feedback) {
+ private void doCommitOneRepo(Collection<File> committables,
+ SVNCommitClient committer,
+ String comment,
+ boolean force,
+ List<VcsException> exception, final Set<String> feedback, final WorkingCopyFormat format, SVNURL url) {
if (committables.isEmpty()) {
return;
}
+ if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) &&
+ SvnConfiguration.UseAcceleration.commandLine.equals(SvnConfiguration.getInstance(mySvnVcs.getProject()).myUseAcceleration) &&
+ (SvnAuthenticationManager.HTTP.equals(url.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(url.getProtocol()))) {
+ doWithCommandLine(committables, comment, exception, feedback);
+ return;
+ }
+
+ doWithSvnkit(committables, committer, comment, force, exception, feedback);
+ }
+
+ private void doWithSvnkit(Collection<File> committables,
+ SVNCommitClient committer,
+ String comment,
+ boolean force,
+ List<VcsException> exception, Set<String> feedback) {
File[] pathsToCommit = committables.toArray(new File[committables.size()]);
boolean keepLocks = SvnConfiguration.getInstance(mySvnVcs.getProject()).isKeepLocks();
SVNCommitPacket[] commitPackets = null;
@@ -224,22 +233,98 @@ public class SvnCheckinEnvironment implements CheckinEnvironment {
}
}
if (committedRevisions.length() > 0) {
- final Project project = mySvnVcs.getProject();
- final String message = SvnBundle.message("status.text.comitted.revision", committedRevisions);
- if (feedback == null) {
- ApplicationManager.getApplication().invokeLater(new Runnable() {
- public void run() {
- new VcsBalloonProblemNotifier(project, message, MessageType.INFO).run();
- }
- }, new Condition<Object>() {
- @Override
- public boolean value(Object o) {
- return (! project.isOpen()) || project.isDisposed();
+ reportCommittedRevisions(feedback, committedRevisions.toString());
+ }
+ }
+
+ private void doWithCommandLine(Collection<File> committables, String comment, List<VcsException> exception, Set<String> 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;
+ }
+
+ final List<String> paths = ObjectsConvertor.convert(committables, new Convertor<File, String>() {
+ @Override
+ public String convert(File o) {
+ return o.getPath();
+ }
+ });
+ final IdeaSvnkitBasedAuthenticationCallback authenticationCallback = new IdeaSvnkitBasedAuthenticationCallback(mySvnVcs);
+ try {
+ final SvnBindClient client = new SvnBindClient(SvnApplicationSettings.getInstance().getCommandLinePath());
+ client.setAuthenticationCallback(authenticationCallback);
+ client.setHandler(new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator()));
+ final long revision = client.commit(ArrayUtil.toStringArray(paths), comment, false, false);
+ reportCommittedRevisions(feedback, String.valueOf(revision));
+ }
+ catch (ClientException e) {
+ exception.add(new VcsException(e));
+ } finally {
+ authenticationCallback.reset();
+ }
+ }
+
+ private Collection<File> filterCommittables(Collection<File> committables) throws SVNException {
+ final Set<File> childrenOfSomebody = new HashSet<File>();
+ new AbstractFilterChildren<File>() {
+ @Override
+ protected void sortAscending(List<File> list) {
+ Collections.sort(list);
+ }
+
+ @Override
+ protected boolean isAncestor(File parent, File child) {
+ final boolean isAncestor = FileUtil.isAncestor(parent, child, false);
+ if (isAncestor) {
+ childrenOfSomebody.add(child);
+ }
+ return isAncestor;
+ }
+ }.doFilter(new ArrayList<File>(committables));
+ if (! childrenOfSomebody.isEmpty()) {
+ final HashSet<File> result = new HashSet<File>(committables);
+ result.removeAll(childrenOfSomebody);
+ final SvnCommandLineStatusClient statusClient = new SvnCommandLineStatusClient(mySvnVcs.getProject());
+ for (File file : childrenOfSomebody) {
+ 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);
}
- });
- } else {
- feedback.add("Subversion: " + message);
+ }
+ catch (SVNException e) {
+ // not versioned
+ LOG.info(e);
+ throw e;
+ }
}
+ return result;
+ }
+ return committables;
+ }
+
+ private void reportCommittedRevisions(Set<String> feedback, String committedRevisions) {
+ final Project project = mySvnVcs.getProject();
+ final String message = SvnBundle.message("status.text.comitted.revision", committedRevisions);
+ if (feedback == null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ new VcsBalloonProblemNotifier(project, message, MessageType.INFO).run();
+ }
+ }, new Condition<Object>() {
+ @Override
+ public boolean value(Object o) {
+ return (! project.isOpen()) || project.isDisposed();
+ }
+ });
+ } else {
+ feedback.add("Subversion: " + message);
}
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AddLineConverter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AddLineConverter.java
new file mode 100644
index 000000000000..46fec54d0a79
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AddLineConverter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.commandLine;
+
+import org.tmatesoft.svn.core.wc.SVNEvent;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/4/13
+ * Time: 3:49 PM
+ */
+public class AddLineConverter {
+ private final File myBase;
+
+ public AddLineConverter(File base) {
+ myBase = base;
+ }
+
+ public SVNEvent convert(final String line) {
+ return null;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandFactory.java
new file mode 100644
index 000000000000..085c8e54dc0f
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandFactory.java
@@ -0,0 +1,59 @@
+/*
+ * 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.commandLine;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vcs.ProcessEventListener;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.svn.SvnApplicationSettings;
+import org.jetbrains.idea.svn.SvnVcs;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/4/13
+ * Time: 4:58 PM
+ */
+public class SvnCommandFactory {
+ public static SvnSimpleCommand createSimpleCommand(final Project project, File workingDirectory, @NotNull SvnCommandName commandName) {
+ final SvnSimpleCommand command =
+ new SvnSimpleCommand(workingDirectory, commandName, SvnApplicationSettings.getInstance().getCommandLinePath());
+ addStartFailedListener(project, command);
+ return command;
+ }
+
+ private static void addStartFailedListener(final Project project, SvnCommand command) {
+ command.addListener(new ProcessEventListener() {
+ @Override
+ public void processTerminated(int exitCode) {
+ }
+
+ @Override
+ public void startFailed(Throwable exception) {
+ SvnVcs.getInstance(project).checkCommandLineVersion();
+ }
+ });
+ }
+
+ public static SvnLineCommand createLineCommand(Project project, File workingDirectory, @NotNull SvnCommandName commandName) {
+ final SvnLineCommand command =
+ new SvnLineCommand(workingDirectory, commandName, SvnApplicationSettings.getInstance().getCommandLinePath(), null);
+ addStartFailedListener(project, command);
+ return command;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java
index c2a0be1e2d6d..0d2f4d00bd39 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.Consumer;
+import org.jetbrains.idea.svn.SvnBindUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.portable.SvnExceptionWrapper;
import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient;
@@ -70,12 +71,12 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
Collection changeLists,
final ISVNInfoHandler handler) throws SVNException {
File base = path.isDirectory() ? path : path.getParentFile();
- base = correctUpToExistingParent(base);
+ base = SvnBindUtil.correctUpToExistingParent(base);
if (base == null) {
// very unrealistic
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), new RuntimeException("Can not find existing parent file"));
}
- final SvnSimpleCommand command = new SvnSimpleCommand(myProject, base, SvnCommandName.info);
+ final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.info);
if (depth != null) {
command.addParameters("--depth", depth.getName());
@@ -137,14 +138,6 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
}
}
- private File correctUpToExistingParent(File base) {
- while (base != null) {
- if (base.exists() && base.isDirectory()) return base;
- base = base.getParentFile();
- }
- return null;
- }
-
@Override
public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler)
throws SVNException {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java
index f45b5c7039d0..a1a31536c8a9 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java
@@ -17,15 +17,15 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.containers.Convertor;
+import org.jetbrains.idea.svn.SvnBindUtil;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.portable.PortableStatus;
import org.jetbrains.idea.svn.portable.SvnExceptionWrapper;
import org.jetbrains.idea.svn.portable.SvnStatusClientI;
-import org.tmatesoft.sqljet.core.SqlJetErrorCode;
-import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.*;
@@ -96,12 +96,12 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI {
boolean collectParentExternals,
final ISVNStatusHandler handler,
final Collection changeLists) throws SVNException {
- final File base = path.isDirectory() ? path : path.getParentFile();
+ File base = path.isDirectory() ? path : path.getParentFile();
+ base = SvnBindUtil.correctUpToExistingParent(base);
final SVNInfo infoBase = myInfoClient.doInfo(base, revision);
- // todo can not understand why revision can be used here
- final SvnSimpleCommand command = new SvnSimpleCommand(myProject, base, SvnCommandName.st);
+ final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.st);
putParameters(depth, remote, reportAll, includeIgnored, changeLists, command);
final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1];
@@ -109,6 +109,9 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI {
try {
final String result = command.run();
+ if (StringUtil.isEmptyOrSpaces(result)) {
+ throw new VcsException("Status request returned nothing for command: " + command.myCommandLine.getCommandLineString());
+ }
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]);
if (! svnHandl[0].isAnythingReported()) {
@@ -192,10 +195,6 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI {
@Override
public void switchPath() {
final PortableStatus pending = svnHandl[0].getPending();
- if (pending.isLocked()) {
- throw new SvnExceptionWrapper(new SVNException(SVNErrorMessage.create(SVNErrorCode.SQLITE_ERROR),
- new SqlJetException(SqlJetErrorCode.BUSY)));
- }
pending.setChangelistName(changelistName[0]);
try {
//if (infoBase != null) {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java
index 4870183b9823..c0e9c8e79fa2 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java
@@ -18,17 +18,21 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
-import com.intellij.openapi.vcs.LineProcessEventListener;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.concurrency.Semaphore;
-import org.jetbrains.idea.svn.SvnAuthenticationManager;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.idea.svn.SvnApplicationSettings;
import org.jetbrains.idea.svn.SvnVcs;
-import org.jetbrains.idea.svn.portable.SvnExceptionWrapper;
+import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback;
+import org.jetbrains.idea.svn.config.SvnBindException;
import org.jetbrains.idea.svn.portable.SvnSvnkitUpdateClient;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -78,127 +82,105 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient {
if (info == null || info.getURL() == null) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY, paths[0].getPath()));
}
- final long[] result = new long[paths.length];
+ final AtomicReference<long[]> updatedToRevision = new AtomicReference<long[]>();
+ updatedToRevision.set(new long[0]);
- new CommandLineAuthenticator(myProject, new CommandLineAuthenticator.AuthenticationRequiringCommand() {
- @Override
- public void run(File configDir) throws SVNException {
- File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath());
- base = base.isDirectory() ? base : base.getParentFile();
- final SvnLineCommand command = new SvnLineCommand(myProject, base, SvnCommandName.up);
- if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) {
- command.addParameters("-r", revision.toString());
- }
- // unknown depth is not used any more for 1.7 -> why?
- if (depth != null && ! SVNDepth.UNKNOWN.equals(depth)) {
- command.addParameters("--depth", depth.toString());
- }
- if (allowUnversionedObstructions) {
- command.addParameters("--force");
- }
- if (depthIsSticky && depth != null) {// !!! not sure, but not used
- command.addParameters("--set-depth", depth.toString());
- }
- if (makeParents) {
- command.addParameters("--parents");
- }
- if (myIgnoreExternals) {
- command.addParameters("--ignore-externals");
- }
- command.addParameters("--accept", "postpone");
- command.addParameters("--config-dir", configDir.getPath());
+ File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath());
+ base = base.isDirectory() ? base : base.getParentFile();
+
+ final List<String> parameters = new ArrayList<String>();
+ if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) {
+ parameters.add("-r");
+ parameters.add(revision.toString());
+ }
+ // unknown depth is not used any more for 1.7 -> why?
+ if (depth != null && ! SVNDepth.UNKNOWN.equals(depth)) {
+ parameters.add("--depth");
+ parameters.add(depth.toString());
+ }
+ if (allowUnversionedObstructions) {
+ parameters.add("--force");
+ }
+ if (depthIsSticky && depth != null) {// !!! not sure, but not used
+ parameters.add("--set-depth");
+ parameters.add(depth.toString());
+ }
+ if (makeParents) {
+ parameters.add("--parents");
+ }
+ if (myIgnoreExternals) {
+ parameters.add("--ignore-externals");
+ }
+ parameters.add("--accept");
+ parameters.add("postpone");
- for (File path : paths) {
- command.addParameters(path.getPath());
+ for (File path : paths) {
+ parameters.add(path.getPath());
+ }
+
+ final AtomicReference<SVNException> excRef = new AtomicReference<SVNException>();
+ final ISVNEventHandler handler = getEventHandler();
+ final UpdateOutputLineConverter converter = new UpdateOutputLineConverter(base);
+ try {
+ final LineCommandListener listener = new LineCommandListener() {
+ final long[] myRevisions = new long[paths.length];
+
+ @Override
+ public void baseDirectory(File file) {
}
- final StringBuffer sbError = new StringBuffer();
- final Semaphore semaphore = new Semaphore();
- semaphore.down();
- final ISVNEventHandler handler = getEventHandler();
- final UpdateOutputLineConverter converter = new UpdateOutputLineConverter(base);
- final SVNException[] innerException = new SVNException[1];
- command.addListener(new LineProcessEventListener() {
- @Override
- public void onLineAvailable(String line, Key outputType) {
- if (ProcessOutputTypes.STDOUT.equals(outputType)) {
- final SVNEvent event = converter.convert(line);
- if (event != null) {
- checkForUpdateCompleted(event);
- try {
- handler.handleEvent(event, 0.5);
- }
- catch (SVNException e) {
- command.cancel();
- semaphore.up();
- innerException[0] = e;
- }
+ @Override
+ public void onLineAvailable(String line, Key outputType) {
+ if (ProcessOutputTypes.STDOUT.equals(outputType)) {
+ final SVNEvent event = converter.convert(line);
+ if (event != null) {
+ checkForUpdateCompleted(event);
+ try {
+ handler.handleEvent(event, 0.5);
}
- } else if (ProcessOutputTypes.STDERR.equals(outputType)) {
- sbError.append(line);
- if (line.contains(ourAuthenticationRealm)) {
- command.cancel();
- semaphore.up();
+ catch (SVNException e) {
+ cancel();
+ excRef.set(e);
}
}
}
-
- @Override
- public void processTerminated(int exitCode) {
- semaphore.up();
- }
-
- @Override
- public void startFailed(Throwable exception) {
- semaphore.up();
- }
- });
- try {
- command.start();
- semaphore.waitFor();
-
- checkForException(sbError);
- } catch (SvnExceptionWrapper e){
- throw (SVNException) e.getCause();
}
- }
- @Override
- public void runWithSvnkitClient(File configDir, SvnAuthenticationManager manager) throws SVNException {
- final SVNUpdateClient client = SvnVcs.getInstance(myProject).createUpdateClient(manager);
- client.doUpdate(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, makeParents);
- }
-
- private void checkForUpdateCompleted(SVNEvent event) {
- if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) {
- final long eventRevision = event.getRevision();
- for (int i = 0; i < paths.length; i++) {
- final File path = paths[i];
- if (path.equals(event.getFile())) {
- result[i] = eventRevision;
- break;
+ private void checkForUpdateCompleted(SVNEvent event) {
+ if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) {
+ final long eventRevision = event.getRevision();
+ for (int i = 0; i < paths.length; i++) {
+ final File path = paths[i];
+ if (FileUtil.filesEqual(path, event.getFile())) {
+ myRevisions[i] = eventRevision;
+ break;
+ }
}
}
}
- }
- @Override
- public SVNURL sampleUrl() {
- return info.getURL();
- }
-
- @Override
- public void cleanup() throws SVNException {
- final SvnVcs vcs17 = SvnVcs.getInstance(myProject);
- final SVNWCClient client = vcs17.createWCClient();
- for (File path : paths) {
- client.doCleanup(path);
+ @Override
+ public void processTerminated(int exitCode) {
+ super.processTerminated(exitCode);
+ updatedToRevision.set(myRevisions);
}
- }
- }).doWithAuthentication();
- return result;
+ };
+ SvnLineCommand.runWithAuthenticationAttempt(SvnApplicationSettings.getInstance().getCommandLinePath(),
+ base, SvnCommandName.up, listener,
+ new IdeaSvnkitBasedAuthenticationCallback(SvnVcs.getInstance(myProject)),
+ ArrayUtil.toStringArray(parameters));
+ }
+ catch (SvnBindException e) {
+ throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e));
+ }
+ if (excRef.get() != null) {
+ throw excRef.get();
+ }
+
+ return updatedToRevision.get();
}
+
private void checkForException(final StringBuffer sbError) throws SVNException {
if (sbError.length() == 0) return;
final String message = sbError.toString();
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
deleted file mode 100644
index cf31abde7f1a..000000000000
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2000-2012 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.commandLine;
-
-import com.intellij.execution.process.ProcessOutputTypes;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.vcs.LineHandlerHelper;
-import com.intellij.openapi.vcs.LineProcessEventListener;
-import com.intellij.util.EventDispatcher;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.File;
-import java.util.Iterator;
-
-/**
- * Created with IntelliJ IDEA.
- * User: Irina.Chernushina
- * Date: 1/25/12
- * Time: 4:05 PM
- *
- * honestly stolen from GitLineHandler
- */
-public class SvnLineCommand extends SvnCommand {
- /**
- * the partial line from stdout stream
- */
- private final StringBuilder myStdoutLine = new StringBuilder();
- /**
- * the partial line from stderr stream
- */
- private final StringBuilder myStderrLine = new StringBuilder();
- private final EventDispatcher<LineProcessEventListener> myLineListeners;
-
- public SvnLineCommand(Project project, File workingDirectory, @NotNull SvnCommandName commandName) {
- super(project, workingDirectory, commandName);
- myLineListeners = EventDispatcher.create(LineProcessEventListener.class);
- }
-
- @Override
- protected void processTerminated(int exitCode) {
- // force newline
- if (myStdoutLine.length() != 0) {
- onTextAvailable("\n\r", ProcessOutputTypes.STDOUT);
- }
- else if (myStderrLine.length() != 0) {
- onTextAvailable("\n\r", ProcessOutputTypes.STDERR);
- }
- }
-
- @Override
- protected void onTextAvailable(String text, Key outputType) {
- Iterator<String> lines = LineHandlerHelper.splitText(text).iterator();
- if (ProcessOutputTypes.STDOUT == outputType) {
- notifyLines(outputType, lines, myStdoutLine);
- }
- else if (ProcessOutputTypes.STDERR == outputType) {
- notifyLines(outputType, lines, myStderrLine);
- }
- }
-
- private void notifyLines(final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) {
- if (!lines.hasNext()) return;
- if (lineBuilder.length() > 0) {
- lineBuilder.append(lines.next());
- if (lines.hasNext()) {
- // line is complete
- final String line = lineBuilder.toString();
- notifyLine(line, outputType);
- lineBuilder.setLength(0);
- }
- }
- while (true) {
- String line = null;
- if (lines.hasNext()) {
- line = lines.next();
- }
-
- if (lines.hasNext()) {
- notifyLine(line, outputType);
- }
- else {
- if (line != null && line.length() > 0) {
- lineBuilder.append(line);
- }
- break;
- }
- }
- }
-
- private void notifyLine(final String line, final Key outputType) {
- String trimmed = LineHandlerHelper.trimLineSeparator(line);
- myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType);
- }
-
- public void addListener(LineProcessEventListener listener) {
- myLineListeners.addListener(listener);
- super.addListener(listener);
- }
-}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java
index 84d25f59f4b3..7b789f92e7ed 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnStatusHandler.java
@@ -284,6 +284,7 @@ public class SvnStatusHandler extends DefaultHandler {
if (createNewChild) {
assertSAX(myElementsMap.containsKey(qName));
final ElementHandlerBase newChild = myElementsMap.get(qName).get();
+ newChild.preAttributesEffect(myDataCallback);
newChild.updateStatus(attributes, myPending, myLockWrapper);
newChild.preEffect(myDataCallback);
myParseStack.add(newChild);
@@ -547,12 +548,11 @@ public class SvnStatusHandler extends DefaultHandler {
private static class Lock extends ElementHandlerBase {
private Lock() {
- super(new String[]{"token","owner","comment","created"}, new String[]{});
+ super(new String[]{"token", "owner", "comment", "created"}, new String[]{});
}
@Override
protected void updateStatus(Attributes attributes, PortableStatus status, SVNLockWrapper lock) throws SAXException {
- // todo check inside-repository path
lock.setPath(status.getPath());
}
@@ -562,11 +562,15 @@ public class SvnStatusHandler extends DefaultHandler {
}
@Override
- public void preEffect(DataCallback callback) {
+ public void preAttributesEffect(DataCallback callback) {
callback.startLock();
}
@Override
+ public void preEffect(DataCallback callback) {
+ }
+
+ @Override
public void characters(String s, PortableStatus pending, SVNLockWrapper lock) {
}
}
@@ -587,8 +591,12 @@ public class SvnStatusHandler extends DefaultHandler {
}
@Override
+ public void preAttributesEffect(DataCallback callback) {
+ callback.startLock();
+ }
+
+ @Override
public void preEffect(DataCallback callback) {
- callback.startRemoteStatus();
}
@Override
@@ -851,6 +859,8 @@ and no "mod4" under
}
public abstract void characters(String s, PortableStatus pending, SVNLockWrapper lock);
+
+ public void preAttributesEffect(DataCallback callback) {}
}
public interface ExternalDataCallback {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.form
index 170c92072c51..a416aa145505 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.form
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.form
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.svn.dialogs.BranchConfigurationDialog">
- <grid id="27dc6" binding="myTopPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <grid id="27dc6" binding="myTopPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -50,6 +50,14 @@
</component>
</children>
</grid>
+ <component id="f6743" class="javax.swing.JLabel" binding="myErrorPrompt">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Label"/>
+ </properties>
+ </component>
</children>
</grid>
</form>
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java
index aa6506917005..221ba2abbce2 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java
@@ -20,15 +20,14 @@ import com.intellij.openapi.actionSystem.ActionToolbarPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.ui.AnActionButton;
-import com.intellij.ui.AnActionButtonRunnable;
-import com.intellij.ui.DocumentAdapter;
-import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.branchConfig.InfoReliability;
@@ -54,11 +53,11 @@ public class BranchConfigurationDialog extends DialogWrapper {
private TextFieldWithBrowseButton myTrunkLocationTextField;
private JList myLocationList;
private JPanel myListPanel;
+ private JLabel myErrorPrompt;
private final SvnBranchConfigManager mySvnBranchConfigManager;
private final VirtualFile myRoot;
- public BranchConfigurationDialog(final Project project, final SvnBranchConfigurationNew configuration, final String rootUrl,
- final VirtualFile root) {
+ public BranchConfigurationDialog(@NotNull final Project project, @NotNull final SvnBranchConfigurationNew configuration, final @NotNull String rootUrl, @NotNull final VirtualFile root, @NotNull String url) {
super(project, true);
myRoot = root;
init();
@@ -66,7 +65,7 @@ public class BranchConfigurationDialog extends DialogWrapper {
final String trunkUrl = configuration.getTrunkUrl();
if (trunkUrl == null || trunkUrl.trim().length() == 0) {
- configuration.setTrunkUrl(rootUrl);
+ configuration.setTrunkUrl(url);
}
mySvnBranchConfigManager = SvnBranchConfigurationManager.getInstance(project).getSvnBranchConfigManager();
@@ -85,6 +84,9 @@ public class BranchConfigurationDialog extends DialogWrapper {
myTrunkLocationTextField.getTextField().getDocument().addDocumentListener(trunkUrlValidator);
trunkUrlValidator.textChanged(null);
+ myErrorPrompt.setUI(new MultiLineLabelUI());
+ myErrorPrompt.setForeground(SimpleTextAttributes.ERROR_ATTRIBUTES.getFgColor());
+
final MyListModel listModel = new MyListModel(configuration);
myLocationList = new JBList(listModel);
@@ -142,11 +144,10 @@ public class BranchConfigurationDialog extends DialogWrapper {
(currentValue.length() > myRootUrlPrefix.length());
myTrunkLocationTextField.getButton().setEnabled(valueOk);
- setOKActionEnabled(prefixOk);
if (prefixOk) {
myConfiguration.setTrunkUrl(currentValue.endsWith("/") ? currentValue.substring(0, currentValue.length() - 1) : currentValue);
}
- setErrorText(prefixOk ? null : SvnBundle.message("configure.branches.error.wrong.url"));
+ myErrorPrompt.setText(prefixOk ? "" : SvnBundle.message("configure.branches.error.wrong.url", myRootUrl));
}
}
@@ -197,7 +198,7 @@ public class BranchConfigurationDialog extends DialogWrapper {
}
final SvnBranchConfigurationNew clonedConfiguration = configuration.copy();
- BranchConfigurationDialog dlg = new BranchConfigurationDialog(project, clonedConfiguration, rootUrl, vcsRoot);
+ BranchConfigurationDialog dlg = new BranchConfigurationDialog(project, clonedConfiguration, rootUrl, vcsRoot, wcRoot.getUrl());
dlg.show();
if (dlg.isOK()) {
SvnBranchConfigurationManager.getInstance(project).setConfiguration(vcsRoot, clonedConfiguration);
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 d66cb6f0ae54..bfb5c645a46c 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CopiesPanel.java
@@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.DottedBorder;
+import com.intellij.ui.JBColor;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.components.labels.LinkListener;
@@ -36,6 +37,8 @@ import com.intellij.util.containers.Convertor;
import com.intellij.util.io.EqualityPolicy;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.NestedCopyType;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.WorkingCopyFormat;
@@ -43,6 +46,7 @@ import org.jetbrains.idea.svn.actions.CleanupWorker;
import org.jetbrains.idea.svn.actions.SelectBranchPopup;
import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
+import org.jetbrains.idea.svn.integrate.QuickMergeInteractionImpl;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -205,7 +209,7 @@ public class CopiesPanel {
final int result =
Messages.showOkCancelDialog(myVcs.getProject(), "You are going to checkout into '" + wcInfo.getPath() + "' with 'infinity' depth.\n" +
"This will update your working copy to HEAD revision as well.",
- "Set working copy infinity depth",
+ "Set Working Copy Infinity Depth",
Messages.getWarningIcon());
if (result == 0) {
// update of view will be triggered by roots changed event
@@ -258,6 +262,7 @@ public class CopiesPanel {
myPanel.repaint();
}
+ @SuppressWarnings("MethodMayBeStatic")
private String formatWc(WCInfo info) {
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table bgColor=\"").append(ColorUtil.toHex(UIUtil.getPanelBackground())).append("\">");
@@ -297,17 +302,18 @@ public class CopiesPanel {
return sb.toString();
}
- private void mergeFrom(final WCInfo wcInfo, final VirtualFile root, final Component mergeLabel) {
+ private void mergeFrom(@NotNull final WCInfo wcInfo, @NotNull final VirtualFile root, @Nullable final Component mergeLabel) {
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 QuickMerge(project, url, wcInfo, SVNPathUtil.tail(url), root).execute(new QuickMergeInteractionImpl(myProject));
}
}, "Select branch", mergeLabel);
}
+ @SuppressWarnings("MethodMayBeStatic")
private void setFocusableForLinks(final LinkLabel label) {
- final Border border = new DottedBorder(new Insets(1,2,1,1), Color.black);
+ final Border border = new DottedBorder(new Insets(1,2,1,1), JBColor.BLACK);
label.setFocusable(true);
label.addFocusListener(new FocusAdapter() {
@Override
@@ -334,7 +340,7 @@ public class CopiesPanel {
private void changeFormat(final WCInfo wcInfo) {
ChangeFormatDialog dialog = new ChangeFormatDialog(myProject, new File(wcInfo.getPath()), false, ! wcInfo.isIsWcRoot());
- dialog.setData(true, wcInfo.getFormat().getOption());
+ dialog.setData(wcInfo.getFormat().getOption());
dialog.show();
if (! dialog.isOK()) {
return;
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java
new file mode 100644
index 000000000000..7187613a0f86
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LoadRecentBranchRevisions.java
@@ -0,0 +1,145 @@
+/*
+ * 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.dialogs;
+
+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.versionBrowser.ChangeBrowserSettings;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+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.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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* Created with IntelliJ IDEA.
+* User: Irina.Chernushina
+* Date: 3/30/13
+* Time: 7:40 PM
+*/
+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;
+ private long myFirst;
+ private boolean myLastLoaded;
+ private OneShotMergeInfoHelper myHelper;
+ private List<CommittedChangeList> myCommittedChangeLists;
+ private final WCInfo myWcInfo;
+ private final SvnVcs myVcs;
+ private final String mySourceUrl;
+ private final Integer myTestBunchSize;
+
+ LoadRecentBranchRevisions(String branchName, long first, WCInfo info, SvnVcs vcs, String url) {
+ super("Loading recent " + branchName + " revisions", Where.POOLED);
+ myFirst = first;
+ myWcInfo = info;
+ myVcs = vcs;
+ mySourceUrl = url;
+ // for test purposes!!!
+ myTestBunchSize = Integer.getInteger(PROP_BUNCH_SIZE);
+ if (myTestBunchSize != null) {
+ myBunchSize = myTestBunchSize.intValue();
+ } else {
+ myBunchSize = BUNCH_SIZE;
+ }
+ }
+
+ void setBunchSize(int bunchSize) {
+ if (myTestBunchSize != null) return;
+ myBunchSize = bunchSize;
+ }
+
+ public boolean isLastLoaded() {
+ return myLastLoaded;
+ }
+
+ @Override
+ public void run(ContinuationContext context) {
+ final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+ final SvnCommittedChangesProvider committedChangesProvider = (SvnCommittedChangesProvider)myVcs.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());
+ final String relativeLocal = (local.startsWith("/") ? local : "/" + local);
+ String relativeBranch = SVNPathUtil.getRelativePath(myWcInfo.getRepositoryRoot(), mySourceUrl);
+ relativeBranch = (relativeBranch.startsWith("/") ? relativeBranch : "/" + relativeBranch);
+
+ ProgressManager.progress2(SvnBundle.message("progress.text2.collecting.history", mySourceUrl + (myFirst > 0 ? ("@" + myFirst) : "")));
+ final List<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>> list = new ArrayList<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>>();
+ try {
+ committedChangesProvider.getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mySourceUrl),
+ myBunchSize + (myFirst > 0 ? 2 : 1),
+ new PairConsumer<SvnChangeList, TreeStructureNode<SVNLogEntry>>() {
+ public void consume(SvnChangeList svnList, TreeStructureNode<SVNLogEntry> tree) {
+ indicator.setText2(SvnBundle.message("progress.text2.processing.revision", svnList.getNumber()));
+ list.add(new Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>(svnList, tree));
+ }
+ });
+ } catch (VcsException e) {
+ context.handleException(e, true);
+ return;
+ }
+ myCommittedChangeLists = new ArrayList<CommittedChangeList>();
+ for (Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>> pair : list) {
+ // do not take first since it's equal
+ if (myFirst > 0 && myFirst == pair.getFirst().getNumber()) continue;
+ if (! QuickMerge.checkListForPaths(relativeLocal, relativeBranch, pair)) {
+ myCommittedChangeLists.add(pair.getFirst());
+ }
+ }
+
+ try {
+ myHelper = new OneShotMergeInfoHelper(myVcs.getProject(), myWcInfo, mySourceUrl);
+ ProgressManager.progress2("Calculating not merged revisions");
+ myHelper.prepare();
+ }
+ catch (SVNException e) {
+ context.handleException(new VcsException(e), true);
+ }
+ myLastLoaded = myCommittedChangeLists.size() < myBunchSize + 1;
+ if (myCommittedChangeLists.size() > myBunchSize){
+ myCommittedChangeLists = myCommittedChangeLists.subList(0, myBunchSize);
+ }
+ }
+
+ public OneShotMergeInfoHelper getHelper() {
+ return myHelper;
+ }
+
+ public List<CommittedChangeList> getCommittedChangeLists() {
+ return myCommittedChangeLists;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LocalChangesAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LocalChangesAction.java
new file mode 100644
index 000000000000..2520d7af2b6e
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/LocalChangesAction.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dialogs;
+
+/**
+* Created with IntelliJ IDEA.
+* User: Irina.Chernushina
+* Date: 3/27/13
+* Time: 12:37 PM
+*/
+public enum LocalChangesAction {
+ cancel,
+ continueMerge,
+ shelve,
+ inspect
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeDialogI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeDialogI.java
new file mode 100644
index 000000000000..83e514aabc80
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/MergeDialogI.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dialogs;
+
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/30/13
+ * Time: 8:11 PM
+ */
+public interface MergeDialogI {
+ void setEverythingLoaded(boolean everythingLoaded);
+
+ long getLastNumber();
+
+ void addMoreLists(List<CommittedChangeList> list);
+}
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 fa3428d2afd2..01e5a1d0342a 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java
@@ -21,18 +21,15 @@ 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.progress.RunBackgroundable;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
-import com.intellij.openapi.ui.Messages;
+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.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.VirtualFile;
@@ -78,6 +75,7 @@ public class QuickMerge {
private SvnVcs myVcs;
private final String myTitle;
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) {
@@ -110,10 +108,6 @@ public class QuickMerge {
}
}
- private boolean prompt(final String question) {
- return Messages.showOkCancelDialog(myProject, question, myTitle, Messages.getQuestionIcon()) == 0;
- }
-
private class MyInitChecks extends TaskDescriptor {
private MyInitChecks() {
super("initial checks", Where.AWT);
@@ -157,17 +151,104 @@ public class QuickMerge {
}
@CalledInAwt
- public void execute() {
+ public void execute(@NotNull final QuickMergeInteraction interaction, @NotNull final TaskDescriptor... finalTasks) {
+ myInteraction = interaction;
+ myInteraction.setTitle(myTitle);
+
FileDocumentManager.getInstance().saveAllDocuments();
final List<TaskDescriptor> tasks = new LinkedList<TaskDescriptor>();
tasks.add(new MyInitChecks());
tasks.add(new SourceUrlCorrection());
tasks.add(new CheckRepositorySupportsMergeinfo());
+ if (finalTasks.length > 0) {
+ tasks.addAll(Arrays.asList(finalTasks));
+ }
+ myContinuation.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
+ @Override
+ public void consume(VcsException e) {
+ myInteraction.showErrors(myTitle, Collections.singletonList(e));
+ }
+ });
+ myContinuation.addExceptionHandler(SVNException.class, new Consumer<SVNException>() {
+ @Override
+ public void consume(SVNException e) {
+ myInteraction.showErrors(myTitle, Collections.singletonList(new VcsException(e)));
+ }
+ });
+ myContinuation.addExceptionHandler(RuntimeException.class, new Consumer<RuntimeException>() {
+ @Override
+ public void consume(RuntimeException e) {
+ myInteraction.showError(e);
+ }
+ });
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<Long, MergeDialogI> loader = new PairConsumer<Long, MergeDialogI>() {
+ @Override
+ public void consume(Long bunchSize, final MergeDialogI dialog) {
+ final LoadRecentBranchRevisions loader =
+ new LoadRecentBranchRevisions(myBranchName, dialog.getLastNumber(), myWcInfo, myVcs, mySourceUrl);
+ loader.setBunchSize(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.<CommittedChangeList>emptyList());
+ dialog.setEverythingLoaded(true);
+ }
+ };
+ final Continuation fragmented = Continuation.createFragmented(myProject, true);
+ fragmented.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
+ @Override
+ public void consume(VcsException e) {
+ PopupUtil.showBalloonForActiveComponent(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), MessageType.ERROR);
+ }
+ });
+ fragmented.run(loader, updater);
+ }
+ };
+ final List<CommittedChangeList> 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<SvnBranchPointsCalculator.BranchCopyData>(false, copyData)),
+ new MergeTask(factory, myTitle));
+ } else {
+ context.cancelEverything();
+ }
+ }
+ }
+
private class MergeAllOrSelectedChooser extends TaskDescriptor {
private MergeAllOrSelectedChooser() {
super("merge source selector", Where.AWT);
@@ -175,13 +256,18 @@ public class QuickMerge {
@Override
public void run(final ContinuationContext context) {
- final int result = Messages.showYesNoCancelDialog(myProject, "Merge all?", myTitle,
- "Merge &all", "&Select revisions to merge", "Cancel", Messages.getQuestionIcon());
- if (result == 2) return;
- if (result == 0) {
+ 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 {
@@ -197,7 +283,7 @@ public class QuickMerge {
}
private void insertMergeAll(final ContinuationContext context) {
- final List<TaskDescriptor> queue = new LinkedList<TaskDescriptor>();
+ final List<TaskDescriptor> queue = new ArrayList<TaskDescriptor>();
insertMergeAll(queue);
context.next(queue);
}
@@ -213,7 +299,7 @@ public class QuickMerge {
}
}
if (switchedFound) {
- return prompt("There are some switched paths in the working copy. Do you want to continue?");
+ return myInteraction.shouldContinueSwitchedRootFound();
}
return true;
}
@@ -229,20 +315,19 @@ public class QuickMerge {
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
- AbstractVcsHelper.getInstance(myProject).showErrors(exceptions, message);
+ myInteraction.showErrors(message, exceptions);
}
});
}
- // todo can be a very base class!
@CalledInAny
private void finishWithError(final ContinuationContext context, final String message, final boolean isError) {
LOG.info((isError ? "Error: " : "Info: ") + message);
- context.cancelEverything();
context.next(new TaskDescriptor(message, Where.AWT) {
@Override
public void run(ContinuationContext context) {
- VcsBalloonProblemNotifier.showOverChangesView(myProject, message, isError ? MessageType.ERROR : MessageType.WARNING);
+ myInteraction.showErrors(message, isError);
+ context.cancelEverything();
}
});
}
@@ -288,10 +373,7 @@ public class QuickMerge {
return;
}
final boolean reintegrate = invertor.isInvertedSense();
- if (reintegrate && (! prompt("<html><body>You are going to reintegrate changes.<br><br>This will make branch '" + mySourceUrl +
- "' <b>no longer usable for further work</b>." +
- "<br>It will not be able to correctly absorb new trunk (" + invertor.inverted().getTarget() +
- ") changes,<br>nor can this branch be properly reintegrated to trunk again.<br><br>Are you sure?</body></html>"))) {
+ if (reintegrate && (! myInteraction.shouldReintegrate(mySourceUrl, invertor.inverted().getTarget()))) {
context.cancelEverything();
return;
}
@@ -338,14 +420,12 @@ public class QuickMerge {
return;
}
- context.next(new TaskDescriptor(getName(), Where.POOLED) {
- @Override
- public void run(ContinuationContext context) {
- final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject),
- new WorkingCopyInfo(myWcInfo.getPath(), true), myFactory, sourceUrlUrl, getName(), false, myBranchName);
- RunBackgroundable.run(task);
- }
- });
+ 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);
}
@@ -383,6 +463,56 @@ public class QuickMerge {
}
}
+ // true if errors found
+ static boolean checkListForPaths(String relativeLocal,
+ String relativeBranch, Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>> pair) {
+ final List<TreeStructureNode<SVNLogEntry>> children = pair.getSecond().getChildren();
+ boolean localChange = false;
+ for (TreeStructureNode<SVNLogEntry> 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<SVNLogEntry> tree,
+ String relativeBranch, final String localURL) {
+ final LinkedList<TreeStructureNode<SVNLogEntry>> queue = new LinkedList<TreeStructureNode<SVNLogEntry>>();
+ queue.addLast(tree);
+
+ while (! queue.isEmpty()) {
+ final TreeStructureNode<SVNLogEntry> element = queue.removeFirst();
+ ProgressManager.checkCanceled();
+
+ if (checkForEntry(element.getMe(), localURL, relativeBranch)) return true;
+ queue.addAll(element.getChildren());
+ }
+ return false;
+ }
+
+ // true if errors found
+ 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<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> {
private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy";
@@ -450,6 +580,8 @@ public class QuickMerge {
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<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>> list =
new LinkedList<Pair<SvnChangeList, TreeStructureNode<SVNLogEntry>>>();
@@ -476,14 +608,7 @@ public class QuickMerge {
if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) {
// additionally check for being 'local'
- final List<TreeStructureNode<SVNLogEntry>> children = pair.getSecond().getChildren();
- boolean localChange = false;
- for (TreeStructureNode<SVNLogEntry> child : children) {
- if (isLocalRevisionMergeIteration(child, relativeLocal, indicator)) {
- localChange = true;
- break;
- }
- }
+ boolean localChange = checkListForPaths(relativeLocal, relativeBranch, pair);
if (! localChange) {
myNotMerged.add(svnList);
@@ -498,29 +623,6 @@ public class QuickMerge {
context.next(new ShowRevisionSelector(copyDataValue));
}
- private boolean isLocalRevisionMergeIteration(final TreeStructureNode<SVNLogEntry> tree,
- final String localURL,
- ProgressIndicator indicator) {
- final LinkedList<TreeStructureNode<SVNLogEntry>> queue = new LinkedList<TreeStructureNode<SVNLogEntry>>();
- queue.addLast(tree);
-
- while (! queue.isEmpty()) {
- final TreeStructureNode<SVNLogEntry> element = queue.removeFirst();
- indicator.checkCanceled();
-
- final Map map = element.getMe().getChangedPaths();
- for (Object o : map.values()) {
- final SVNLogEntryPath path = (SVNLogEntryPath) o;
- if (SVNPathUtil.isAncestor(localURL, path.getPath())) {
- return true;
- }
- break; // do not check all. first should match or fail
- }
- queue.addAll(element.getChildren());
- }
- return false;
- }
-
private class ShowRevisionSelector extends TaskDescriptor {
private final SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> myCopyPoint;
@@ -531,16 +633,15 @@ public class QuickMerge {
@Override
public void run(ContinuationContext context) {
- final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, myNotMerged, myMergeTitle, myMergeChecker);
- dialog.show();
- if (dialog.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) {
+ final QuickMergeInteraction.SelectMergeItemsResult result = myInteraction.selectMergeItems(myNotMerged, myMergeTitle, myMergeChecker);
+ if (QuickMergeContentsVariants.cancel == result.getResultCode()) {
context.cancelEverything();
return;
}
- if (dialog.getExitCode() == ToBeMergedDialog.MERGE_ALL_CODE) {
+ if (QuickMergeContentsVariants.all == result.getResultCode()) {
insertMergeAll(context);
} else {
- final List<CommittedChangeList> lists = dialog.getSelected();
+ final List<CommittedChangeList> lists = result.getSelectedLists();
if (lists.isEmpty()) return;
final MergerFactory factory = new ChangeListsMergerFactory(lists) {
@Override
@@ -582,35 +683,18 @@ public class QuickMerge {
@Override
public void run(ContinuationContext context) {
- final String message;
final Intersection intersection;
final ChangeListManager listManager = ChangeListManager.getInstance(myProject);
final List<LocalChangeList> localChangeLists = listManager.getChangeListsCopy();
if (myMergeAll) {
intersection = getMergeAllIntersection(localChangeLists);
- message = "There are local changes that can potentially intersect with merge changes.\nDo you want to continue?";
} else {
intersection = checkIntersection(myLists, localChangeLists);
- message = "There are local changes that will intersect with merge changes.\nDo you want to continue?";
}
if (intersection == null || intersection.getChangesSubset().isEmpty()) return;
- final LocalChangesAction action;
- if (! myMergeAll) {
- final LocalChangesAction[] possibleResults = {LocalChangesAction.shelve, LocalChangesAction.inspect,
- LocalChangesAction.continueMerge, LocalChangesAction.cancel};
- final int result = Messages.showDialog(message, myTitle,
- new String[]{"Shelve local changes", "Inspect changes", "Continue merge", "Cancel"},
- 0, Messages.getQuestionIcon());
- action = possibleResults[result];
- } else {
- final LocalChangesAction[] possibleResults = {LocalChangesAction.shelve, LocalChangesAction.continueMerge, LocalChangesAction.cancel};
- final int result = Messages.showDialog(message, myTitle,
- new String[]{"Shelve local changes", "Continue merge", "Cancel"},
- 0, Messages.getQuestionIcon());
- action = possibleResults[result];
- }
+ final LocalChangesAction action = myInteraction.selectLocalChangesAction(myMergeAll);
switch (action) {
// shelve
case shelve:
@@ -625,12 +709,11 @@ public class QuickMerge {
return;
// inspect
case inspect:
- final Collection<Change> changes = (Collection<Change>) intersection.getChangesSubset().values();
+ // here's cast is due to generic's bug
+ @SuppressWarnings("unchecked") final Collection<Change> changes = (Collection<Change>) intersection.getChangesSubset().values();
final List<FilePath> paths = ChangesUtil.getPaths(changes);
Collections.sort(paths, FilePathByPathComparator.getInstance());
- // todo rework message
- IntersectingLocalChangesPanel.showInVersionControlToolWindow(myProject, myTitle + ", local changes intersection",
- paths, "The following file(s) have local changes that will intersect with merge changes:");
+ myInteraction.showIntersectedLocalPaths(paths);
context.cancelEverything();
return;
default:
@@ -673,13 +756,6 @@ public class QuickMerge {
}
}
- private enum LocalChangesAction {
- cancel,
- continueMerge,
- shelve,
- inspect
- }
-
private class ShelveLocalChanges extends TaskDescriptor {
private final Intersection myIntersection;
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMergeContentsVariants.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMergeContentsVariants.java
new file mode 100644
index 000000000000..234126806081
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMergeContentsVariants.java
@@ -0,0 +1,26 @@
+/*
+ * 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.dialogs;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/27/13
+ * Time: 7:53 PM
+ */
+public enum QuickMergeContentsVariants {
+ all, select, cancel, showLatest
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSLCredentialsDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSLCredentialsDialog.java
index 44e503fe08d2..ac8dc9760cc7 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSLCredentialsDialog.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SSLCredentialsDialog.java
@@ -19,6 +19,7 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnBundle;
@@ -103,7 +104,7 @@ public class SSLCredentialsDialog extends DialogWrapper {
@Override
public JComponent getPreferredFocusedComponent() {
- return myCertificatePath;
+ return StringUtil.isEmptyOrSpaces(myCertificatePath.getText()) ? myCertificatePath : myCertificatePassword;
}
public String getCertificatePath() {
@@ -126,4 +127,8 @@ public class SSLCredentialsDialog extends DialogWrapper {
protected JComponent createCenterPanel() {
return null;
}
+
+ public void setFile(@NotNull String file) {
+ myCertificatePath.setText(file);
+ }
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java
index 8e0e4218d748..66ada39bd7b4 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java
@@ -61,6 +61,11 @@ public class SelectLocationDialog extends DialogWrapper {
try {
SVNURL.parseURIEncoded(url);
final SVNURL svnurl = initRoot(project, url);
+ if (svnurl == null) {
+ Messages.showErrorDialog(project, "Can not detect repository root for URL: " + url,
+ SvnBundle.message("dialog.title.select.repository.location"));
+ return null;
+ }
SelectLocationDialog dialog = new SelectLocationDialog(project, svnurl, null, null, true);
dialog.show();
if (!dialog.isOK()) return null;
@@ -116,6 +121,7 @@ public class SelectLocationDialog extends DialogWrapper {
return "svn.repositoryBrowser";
}
+ @Nullable
private static SVNURL initRoot(final Project project, final String urlString) throws SVNException {
final Ref<SVNURL> result = new Ref<SVNURL>();
final Ref<SVNException> excRef = new Ref<SVNException>();
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java
index 9caa86c08a2e..37b4f9d0448a 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnInteractiveAuthenticationProvider.java
@@ -21,6 +21,7 @@ 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.util.text.StringUtil;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.SystemProperties;
import com.intellij.util.WaitForProgressToShow;
@@ -32,6 +33,7 @@ import org.jetbrains.idea.svn.auth.ProviderType;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;
+import org.tmatesoft.svn.core.internal.wc.ISVNHostOptions;
import javax.swing.*;
import java.io.File;
@@ -64,7 +66,7 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
public SVNAuthentication requestClientAuthentication(final String kind,
final SVNURL url,
final String realm,
- SVNErrorMessage errorMessage,
+ final SVNErrorMessage errorMessage,
final SVNAuthentication previousAuth,
final boolean authMayBeStored) {
final MyCallState callState = new MyCallState(true, false);
@@ -85,7 +87,7 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
public void run() {
SimpleCredentialsDialog dialog = new SimpleCredentialsDialog(myProject);
dialog.setup(realm, userName, authCredsOn);
- if (previousAuth == null) {
+ if (errorMessage == null) {
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
}
else {
@@ -106,7 +108,7 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
public void run() {
UserNameCredentialsDialog dialog = new UserNameCredentialsDialog(myProject);
dialog.setup(realm, userName, authCredsOn);
- if (previousAuth == null) {
+ if (errorMessage == null) {
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
}
else {
@@ -123,7 +125,7 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
command = new Runnable() {
public void run() {
SSHCredentialsDialog dialog = new SSHCredentialsDialog(myProject, realm, userName, authCredsOn, url.getPort());
- if (previousAuth == null) {
+ if (errorMessage == null) {
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
}
else {
@@ -150,8 +152,13 @@ public class SvnInteractiveAuthenticationProvider implements ISVNAuthenticationP
} else if (ISVNAuthenticationManager.SSL.equals(kind)) {
command = new Runnable() {
public void run() {
+ final ISVNHostOptions options = myManager.getHostOptionsProvider().getHostOptions(url);
+ final String file = options.getSSLClientCertFile();
final SSLCredentialsDialog dialog = new SSLCredentialsDialog(myProject, realm, authCredsOn);
- if (previousAuth == null) {
+ if (!StringUtil.isEmptyOrSpaces(file)) {
+ dialog.setFile(file);
+ }
+ if (errorMessage == null) {
dialog.setTitle(SvnBundle.message("dialog.title.authentication.required"));
}
else {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ToBeMergedDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ToBeMergedDialog.java
index 91d8bdb4a422..97420a765ce0 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ToBeMergedDialog.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/ToBeMergedDialog.java
@@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
@@ -37,13 +38,19 @@ import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNodeRenderer;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.ui.*;
import com.intellij.ui.table.TableView;
+import com.intellij.util.Alarm;
+import com.intellij.util.PairConsumer;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.UIUtil;
+import com.intellij.vcsUtil.MoreAction;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.history.SvnChangeList;
+import org.jetbrains.idea.svn.mergeinfo.ListMergeStatus;
import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
+import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
@@ -57,11 +64,12 @@ import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
-public class ToBeMergedDialog extends DialogWrapper {
+public class ToBeMergedDialog extends DialogWrapper implements MergeDialogI {
public static final int MERGE_ALL_CODE = 222;
private final JPanel myPanel;
private final Project myProject;
private final PageEngine<List<CommittedChangeList>> myListsEngine;
+ private final Alarm myAlarm;
private TableView<CommittedChangeList> myRevisionsList;
private RepositoryChangesBrowser myRepositoryChangesBrowser;
private Splitter mySplitter;
@@ -69,11 +77,28 @@ public class ToBeMergedDialog extends DialogWrapper {
private final QuantitySelection<Long> myWiseSelection;
private final Set<Change> myAlreadyMerged;
+ private final List<CommittedChangeList> myLists;
+ private final PairConsumer<Long, MergeDialogI> myMoreLoader;
private final MergeChecker myMergeChecker;
-
- public ToBeMergedDialog(final Project project, final List<CommittedChangeList> lists, final String title, final MergeChecker mergeChecker) {
+ private final boolean myAlreadyCalculatedState;
+ private volatile boolean myEverythingLoaded;
+
+ private final Map<Long, ListMergeStatus> myStatusMap;
+ private ToBeMergedDialog.MoreXAction myMore100Action;
+ private ToBeMergedDialog.MoreXAction myMore500Action;
+
+ public ToBeMergedDialog(final Project project,
+ final List<CommittedChangeList> lists,
+ final String title,
+ final MergeChecker mergeChecker,
+ final PairConsumer<Long, MergeDialogI> moreLoader) {
super(project, true);
+ myLists = lists;
+ myMoreLoader = moreLoader;
+ myEverythingLoaded = moreLoader == null;
+ myStatusMap = Collections.synchronizedMap(new HashMap<Long, ListMergeStatus>());
myMergeChecker = mergeChecker;
+ myAlreadyCalculatedState = moreLoader == null;
setTitle(title);
myProject = project;
@@ -82,22 +107,99 @@ public class ToBeMergedDialog extends DialogWrapper {
myListsEngine = new BasePageEngine<CommittedChangeList>(lists, lists.size());
myPanel = new JPanel(new BorderLayout());
- myWiseSelection = new QuantitySelection<Long>(true);
+ myWiseSelection = new QuantitySelection<Long>(myEverythingLoaded);
myAlreadyMerged = new HashSet<Change>();
setOKButtonText("Merge Selected");
initUI();
init();
+ enableMore();
+
+ myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, getDisposable());
+ if (! myAlreadyCalculatedState) {
+ refreshListStatus();
+ }
+ }
+
+ private void enableMore() {
+ myMore100Action.setVisible(!myEverythingLoaded);
+ myMore500Action.setVisible(!myEverythingLoaded);
+ myMore100Action.setEnabled(!myEverythingLoaded);
+ myMore500Action.setEnabled(! myEverythingLoaded);
}
- @NotNull
@Override
- protected Action[] createActions() {
- return new Action[]{getOKAction(), new DialogWrapperAction("Merge All") {
+ public void setEverythingLoaded(boolean everythingLoaded) {
+ myEverythingLoaded = everythingLoaded;
+ myMore100Action.setVisible(false);
+ myMore500Action.setVisible(false);
+ }
+
+ @Override
+ public long getLastNumber() {
+ return myLists.get(myLists.size() - 1).getNumber();
+ }
+
+ @Override
+ public void addMoreLists(final List<CommittedChangeList> list) {
+ myListsEngine.getCurrent().addAll(list);
+ myRevisionsList.revalidate();
+ myRevisionsList.repaint();
+ myMore100Action.setEnabled(true);
+ myMore500Action.setEnabled(true);
+ myMore500Action.setVisible(true);
+ refreshListStatus();
+ }
+
+ public void refreshListStatus() {
+ if (myAlarm.isDisposed()) return;
+ myAlarm.addRequest(new Runnable() {
@Override
- protected void doAction(ActionEvent e) {
- close(MERGE_ALL_CODE);
+ public void run() {
+ int cnt = 10;
+ for (CommittedChangeList list : myLists) {
+ final SvnMergeInfoCache.MergeCheckResult result = myMergeChecker.checkList((SvnChangeList)list);
+ // at the moment we calculate only "merged" since we don;t have branch copy point
+ if (SvnMergeInfoCache.MergeCheckResult.MERGED.equals(result)) {
+ myStatusMap.put(list.getNumber(), ListMergeStatus.MERGED);
+ } else if (SvnMergeInfoCache.MergeCheckResult.NOT_EXISTS.equals(result)) {
+ myStatusMap.put(list.getNumber(), ListMergeStatus.ALIEN);
+ } else if (SvnMergeInfoCache.MergeCheckResult.NOT_EXISTS_PARTLY_MERGED.equals(result)) {
+ myStatusMap.put(list.getNumber(), ListMergeStatus.NOT_MERGED);
+ } else {
+ myStatusMap.put(list.getNumber(), ListMergeStatus.REFRESHING);
+ }
+
+ -- cnt;
+ if (cnt <= 0) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myRevisionsList.revalidate();
+ myRevisionsList.repaint();
+ }
+ });
+ cnt = 10;
+ }
+ }
+ myRevisionsList.revalidate();
+ myRevisionsList.repaint();
}
- }, getCancelAction()};
+ }, 0);
+ }
+
+ @NotNull
+ @Override
+ protected Action[] createActions() {
+ if (myAlreadyCalculatedState) {
+ return new Action[]{getOKAction(), new DialogWrapperAction("Merge All") {
+ @Override
+ protected void doAction(ActionEvent e) {
+ close(MERGE_ALL_CODE);
+ }
+ }, getCancelAction()};
+ } else {
+ return super.createActions();
+ }
}
public List<CommittedChangeList> getSelected() {
@@ -229,8 +331,11 @@ public class ToBeMergedDialog extends DialogWrapper {
myRevisionsList.repaint();
}
};
+ myMore100Action = new MoreXAction(100);
+ myMore500Action = new MoreXAction(500);
final PagedListWithActions<CommittedChangeList> byRevisions =
- new PagedListWithActions<CommittedChangeList>(myListsEngine, listsManager, new MySelectAll(), new MyUnselectAll());
+ new PagedListWithActions<CommittedChangeList>(myListsEngine, listsManager, new MySelectAll(), new MyUnselectAll(),
+ myMore100Action, myMore500Action);
mySplitter = new Splitter(false, 0.7f);
mySplitter.setFirstComponent(byRevisions.getComponent());
@@ -317,6 +422,23 @@ public class ToBeMergedDialog extends DialogWrapper {
return myPanel;
}
+ private class MoreXAction extends MoreAction {
+ private final int myQuantity;
+
+ private MoreXAction(final int quantity) {
+ super("Load +" + quantity);
+ myQuantity = quantity;
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ myMore500Action.setVisible(false);
+ myMore100Action.setEnabled(false);
+ myMore500Action.setEnabled(false);
+ myMoreLoader.consume(Long.valueOf(myQuantity), ToBeMergedDialog.this);
+ }
+ }
+
private class MySelectAll extends DumbAwareAction {
private MySelectAll() {
super("Select All", "Select All", AllIcons.Actions.Selectall);
@@ -351,7 +473,18 @@ public class ToBeMergedDialog extends DialogWrapper {
myCheckBox = new JCheckBox();
myCheckBox.setEnabled(true);
myCheckBox.setSelected(true);
- myRenderer = new CommittedChangeListRenderer(myProject, Collections.<CommittedChangeListDecorator>emptyList());
+ myRenderer = new CommittedChangeListRenderer(myProject, Collections.<CommittedChangeListDecorator>singletonList(new CommittedChangeListDecorator() {
+ @Nullable
+ @Override
+ public Icon decorate(CommittedChangeList list) {
+ if (myAlreadyCalculatedState) return ListMergeStatus.NOT_MERGED.getIcon();
+ final ListMergeStatus status = myStatusMap.get(list.getNumber());
+ if (status != null) {
+ return status.getIcon();
+ }
+ return ListMergeStatus.REFRESHING.getIcon();
+ }
+ }));
}
protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java
index a174c59f60d1..f9bf6bda943d 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java
@@ -31,9 +31,6 @@ import java.awt.*;
import java.io.File;
public class UpgradeFormatDialog extends DialogWrapper {
- private JRadioButton myUpgradeNoneButton;
- private JRadioButton myUpgradeAutoButton;
- private JRadioButton myUpgradeAuto15Button;
private JRadioButton myUpgradeAuto16Button;
private JRadioButton myUpgradeAuto17Button;
@@ -64,21 +61,11 @@ public class UpgradeFormatDialog extends DialogWrapper {
return "svn.upgradeDialog";
}
- public void setData(final boolean display13format, final String selectedFormat) {
+ public void setData(final String selectedFormat) {
if (SvnConfiguration.UPGRADE_AUTO_17.equals(selectedFormat)) {
myUpgradeAuto17Button.setSelected(true);
- } else if (SvnConfiguration.UPGRADE_AUTO_16.equals(selectedFormat)) {
- myUpgradeAuto16Button.setSelected(true);
- } else if (SvnConfiguration.UPGRADE_AUTO.equals(selectedFormat)) {
- myUpgradeAutoButton.setSelected(true);
- } else if (SvnConfiguration.UPGRADE_AUTO_15.equals(selectedFormat)) {
- myUpgradeAuto15Button.setSelected(true);
} else {
- myUpgradeNoneButton.setSelected(true);
- }
- myUpgradeNoneButton.setVisible(display13format);
- if (myUpgradeNoneButton.isSelected() && (! display13format)) {
- myUpgradeAutoButton.setSelected(true);
+ myUpgradeAuto16Button.setSelected(true);
}
}
@@ -114,31 +101,18 @@ public class UpgradeFormatDialog extends DialogWrapper {
panel.add(topLabel, gb);
gb.gridy += 1;
- myUpgradeNoneButton = new JRadioButton(SvnBundle.message("radio.configure." + label + ".none"));
- myUpgradeAutoButton = new JRadioButton(SvnBundle.message("radio.configure." + label + ".auto"));
- myUpgradeAuto15Button = new JRadioButton(SvnBundle.message("radio.configure." + label + ".auto.15format"));
+
myUpgradeAuto16Button = new JRadioButton(SvnBundle.message("radio.configure." + label + ".auto.16format"));
myUpgradeAuto17Button = new JRadioButton(SvnBundle.message("radio.configure." + label + ".auto.17format"));
ButtonGroup group = new ButtonGroup();
- group.add(myUpgradeNoneButton);
- group.add(myUpgradeAutoButton);
- group.add(myUpgradeAuto15Button);
group.add(myUpgradeAuto16Button);
group.add(myUpgradeAuto17Button);
- panel.add(myUpgradeNoneButton, gb);
- gb.gridy += 1;
- panel.add(myUpgradeAutoButton, gb);
- gb.gridy += 1;
- panel.add(myUpgradeAuto15Button, gb);
- gb.gridy += 1;
panel.add(myUpgradeAuto16Button, gb);
gb.gridy += 1;
panel.add(myUpgradeAuto17Button, gb);
gb.gridy += 1;
- myUpgradeNoneButton.setSelected(true);
-
final JPanel auxiliaryPanel = getBottomAuxiliaryPanel();
if (auxiliaryPanel != null) {
panel.add(auxiliaryPanel, gb);
@@ -165,12 +139,6 @@ public class UpgradeFormatDialog extends DialogWrapper {
public String getUpgradeMode() {
if (myUpgradeAuto17Button.isSelected()) {
return SvnConfiguration.UPGRADE_AUTO_17;
- } else if (myUpgradeNoneButton.isSelected()) {
- return SvnConfiguration.UPGRADE_NONE;
- } else if (myUpgradeAutoButton.isSelected()) {
- return SvnConfiguration.UPGRADE_AUTO;
- } else if (myUpgradeAuto15Button.isSelected()) {
- return SvnConfiguration.UPGRADE_AUTO_15;
} else if (myUpgradeAuto16Button.isSelected()) {
return SvnConfiguration.UPGRADE_AUTO_16;
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/RootsAndBranches.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/RootsAndBranches.java
index b0733e38717e..320c24248516 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/RootsAndBranches.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/RootsAndBranches.java
@@ -44,6 +44,7 @@ import org.jetbrains.idea.svn.actions.RecordOnlyMergerFactory;
import org.jetbrains.idea.svn.actions.ShowSvnMapAction;
import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches;
import org.jetbrains.idea.svn.integrate.*;
+import org.jetbrains.idea.svn.mergeinfo.ListMergeStatus;
import org.jetbrains.idea.svn.mergeinfo.MergeInfoHolder;
import org.jetbrains.idea.svn.update.UpdateEventHandler;
import org.tmatesoft.svn.core.SVNURL;
@@ -199,8 +200,8 @@ public class RootsAndBranches implements CommittedChangeListDecorator {
}
public Icon decorate(final CommittedChangeList list) {
- final MergeInfoHolder.ListMergeStatus status = getStatus(list, false);
- return (status == null) ? MergeInfoHolder.ListMergeStatus.ALIEN.getIcon() : status.getIcon();
+ final ListMergeStatus status = getStatus(list, false);
+ return (status == null) ? ListMergeStatus.ALIEN.getIcon() : status.getIcon();
}
private void createPanels(final RepositoryLocation location, final Runnable afterRefresh) {
@@ -566,16 +567,16 @@ public class RootsAndBranches implements CommittedChangeListDecorator {
}
private boolean mergeEnabled(final CommittedChangeList list, final boolean forMerge) {
- final MergeInfoHolder.ListMergeStatus mergeStatus = getStatus(list, true);
- if ((mergeStatus == null) || (MergeInfoHolder.ListMergeStatus.ALIEN.equals(mergeStatus))) {
+ final ListMergeStatus mergeStatus = getStatus(list, true);
+ if ((mergeStatus == null) || (ListMergeStatus.ALIEN.equals(mergeStatus))) {
return false;
- } else if (MergeInfoHolder.ListMergeStatus.REFRESHING.equals(mergeStatus)) {
+ } else if (ListMergeStatus.REFRESHING.equals(mergeStatus)) {
return true;
}
if (forMerge) {
- return MergeInfoHolder.ListMergeStatus.NOT_MERGED.equals(mergeStatus);
+ return ListMergeStatus.NOT_MERGED.equals(mergeStatus);
}
- return MergeInfoHolder.ListMergeStatus.MERGED.equals(mergeStatus);
+ return ListMergeStatus.MERGED.equals(mergeStatus);
}
private class MarkAsMerged extends AbstractIntegrateChangesAction<SelectedChangeListsChecker> {
@@ -782,7 +783,7 @@ public class RootsAndBranches implements CommittedChangeListDecorator {
}
@Nullable
- public MergeInfoHolder.ListMergeStatus getStatus(final CommittedChangeList list, final boolean ignoreEnabled) {
+ public ListMergeStatus getStatus(final CommittedChangeList list, final boolean ignoreEnabled) {
if (! (list instanceof SvnChangeList)) {
return null;
}
@@ -867,14 +868,14 @@ public class RootsAndBranches implements CommittedChangeListDecorator {
final List<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
for (CommittedChangeList list : changeLists) {
- final MergeInfoHolder.ListMergeStatus status = getStatus(list, true);
- if (MergeInfoHolder.ListMergeStatus.REFRESHING.equals(status)) {
+ final ListMergeStatus status = getStatus(list, true);
+ if (ListMergeStatus.REFRESHING.equals(status)) {
result.add(list);
- } else if ((status == null) || MergeInfoHolder.ListMergeStatus.ALIEN.equals(status)) {
+ } else if ((status == null) || ListMergeStatus.ALIEN.equals(status)) {
if (! myFilterAlien.isSelected(null)) {
result.add(list);
}
- } else if (MergeInfoHolder.ListMergeStatus.MERGED.equals(status) || MergeInfoHolder.ListMergeStatus.COMMON.equals(status)) {
+ } else if (ListMergeStatus.MERGED.equals(status) || ListMergeStatus.COMMON.equals(status)) {
if (! myFilterMerged.isSelected(null)) {
result.add(list);
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java
index bbe4f8a04362..bcb40a034ac9 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java
@@ -29,16 +29,12 @@ import com.intellij.openapi.vcs.impl.ContentRevisionCache;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.SvnRevisionNumber;
+import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
-import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
-import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNRevision;
-import org.tmatesoft.svn.core.wc.SVNWCClient;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
@@ -143,22 +139,24 @@ public class SvnFileRevision implements VcsFileRevision {
}
public byte[] loadContent() throws IOException, VcsException {
- ByteArrayOutputStream contents = new ByteArrayOutputStream();
- ContentLoader loader = new ContentLoader(myURL, contents, myRevision, myPegRevision);
+ ContentLoader loader = new ContentLoader(myURL, myRevision, myPegRevision);
if (ApplicationManager.getApplication().isDispatchThread() &&
!myRevision.isLocal()) {
- ProgressManager.getInstance().runProcessWithProgressSynchronously(loader, SvnBundle.message("progress.title.loading.file.content"), false, myVCS.getProject());
+ ProgressManager.getInstance().runProcessWithProgressSynchronously(loader, SvnBundle.message("progress.title.loading.file.content"),
+ false, myVCS.getProject());
}
else {
loader.run();
}
if (loader.getException() == null) {
- return contents.toByteArray();
+ final byte[] contents = loader.getContents();
+ ContentRevisionCache.checkContentsSize(myURL, contents.length);
+ return contents;
}
else {
- final SVNException svnException = loader.getException();
- LOG.info("Failed to load file '" + myURL + "' content at revision: " + myRevision + "\n" + svnException.getMessage(), svnException);
- throw new VcsException(svnException);
+ final VcsException vcsException = loader.getException();
+ LOG.info("Failed to load file '" + myURL + "' content at revision: " + myRevision + "\n" + vcsException.getMessage(), vcsException);
+ throw vcsException;
}
}
@@ -188,31 +186,34 @@ public class SvnFileRevision implements VcsFileRevision {
private final SVNRevision myRevision;
private final SVNRevision myPegRevision;
private final String myURL;
- private final OutputStream myDst;
- private SVNException myException;
+ private VcsException myException;
+ private byte[] myContents;
- public ContentLoader(String url, OutputStream dst, SVNRevision revision, SVNRevision pegRevision) {
+ public ContentLoader(String url, SVNRevision revision, SVNRevision pegRevision) {
myURL = url;
- myDst = dst;
myRevision = revision;
myPegRevision = pegRevision;
}
- public SVNException getException() {
+ public VcsException getException() {
return myException;
}
+ private byte[] getContents() {
+ return myContents;
+ }
+
public void run() {
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress != null) {
progress.setText(SvnBundle.message("progress.text.loading.contents", myURL));
progress.setText2(SvnBundle.message("progress.text2.revision.information", myRevision));
}
+
try {
- SVNWCClient client = myVCS.createWCClient();
- client.doGetFileContents(SVNURL.parseURIEncoded(myURL), myRevision, myRevision, true, myDst);
+ myContents = SvnUtil.getFileContents(myVCS, myURL, true, myRevision, myPegRevision);
}
- catch (SVNException e) {
+ catch (VcsException e) {
myException = e;
}
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java
index 9294fbe2ff8e..4a2c8725a898 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java
@@ -21,6 +21,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction;
import com.intellij.openapi.vcs.changes.Change;
@@ -163,7 +164,8 @@ public class SvnHistoryProvider
}
public void reportAppendableHistory(FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException {
- reportAppendableHistory(path, partner, null, null, 0, null, false);
+ // we need + 1 rows to be reported to further detect that number of rows exceeded the limit
+ reportAppendableHistory(path, partner, null, null, VcsConfiguration.getInstance(myVcs.getProject()).MAXIMUM_HISTORY_ROWS + 1, null, false);
}
public void reportAppendableHistory(FilePath path, final VcsAppendableHistorySessionPartner partner,
@@ -319,10 +321,11 @@ public class SvnHistoryProvider
final SVNRevision pegRevision = myInfo.getRevision();
SVNLogClient client = myVcs.createLogClient();
try {
+ // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows
client
.doLog(new File[]{new File(myFile.getIOFile().getAbsolutePath())},
myFrom == null ? SVNRevision.HEAD : myFrom, myTo == null ? SVNRevision.create(1) : myTo, myPeg,
- false, true, myShowMergeSources && mySupport15, myLimit, null,
+ false, true, myShowMergeSources && mySupport15, myLimit + 1, null,
new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, createConsumerAdapter(myConsumer), repoRootURL, myFile.getCharset()));
}
catch (SVNCancelException e) {
@@ -393,8 +396,9 @@ public class SvnHistoryProvider
relativeUrl = myUrl.substring(root.length());
}
SVNLogClient client = myVcs.createLogClient();
+ // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows
client.doLog(svnurl, new String[]{}, myPeg == null ? myFrom : myPeg,
- operationalFrom, myTo == null ? SVNRevision.create(1) : myTo, false, true, myShowMergeSources && mySupport15, myLimit, null,
+ operationalFrom, myTo == null ? SVNRevision.create(1) : myTo, false, true, myShowMergeSources && mySupport15, myLimit + 1, null,
new RepositoryLogEntryHandler(myVcs, myUrl, SVNRevision.UNDEFINED, relativeUrl, createConsumerAdapter(myConsumer), rootURL));
}
catch (SVNCancelException e) {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java
index 459e9ab3a30e..a668a54f953a 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java
@@ -112,6 +112,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC
if (exception != null) {
throw new VcsException(exception);
}
+ ContentRevisionCache.checkContentsSize(myPath, buffer.size());
return buffer;
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java
index 0366c21ea202..3d5bcaf0d92b 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java
@@ -16,18 +16,12 @@
package org.jetbrains.idea.svn.history;
import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.RepositoryLocation;
import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.RootUrlInfo;
-import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
-import org.tmatesoft.svn.core.SVNException;
-import org.tmatesoft.svn.core.SVNURL;
-import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import java.io.File;
@@ -68,26 +62,9 @@ public class SvnRepositoryLocation implements RepositoryLocation {
@Nullable
public static FilePath getLocalPath(final String fullPath, final NotNullFunction<File, Boolean> detector, final SvnVcs vcs) {
if (vcs.getProject().isDefault()) return null;
- final SVNURL fullPathURL;
- try {
- fullPathURL = SVNURL.parseURIEncoded(fullPath);
- }
- catch (SVNException e) {
- return null;
- }
final RootUrlInfo rootForUrl = vcs.getSvnFileUrlMapping().getWcRootForUrl(fullPath);
if (rootForUrl != null) {
return LocationDetector.filePathByUrlAndPath(fullPath, rootForUrl.getUrl().toString(), rootForUrl.getIoFile().getAbsolutePath(), detector);
- } else {
- final VirtualFile[] underVcs = ProjectLevelVcsManager.getInstance(vcs.getProject()).getRootsUnderVcs(vcs);
- if (underVcs.length == 0) return null;
- for (VirtualFile vf : underVcs) {
- final File ioFile = new File(vf.getPath());
- final SVNURL url = SvnUtil.getUrl(vcs, ioFile);
- if (url != null && SVNURLUtil.isAncestor(url, fullPathURL)) {
- return LocationDetector.filePathByUrlAndPath(fullPath, url.toString(), ioFile.getPath(), detector);
- }
- }
}
return null;
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteraction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteraction.java
new file mode 100644
index 000000000000..def31a2c3e74
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteraction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.integrate;
+
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.util.PairConsumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.svn.dialogs.LocalChangesAction;
+import org.jetbrains.idea.svn.dialogs.MergeDialogI;
+import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants;
+import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
+
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/26/13
+ * Time: 8:29 PM
+ */
+public interface QuickMergeInteraction {
+ void setTitle(@NotNull final String title);
+ /**
+ * @return {@link com.intellij.openapi.ui.Messages.CANCEL} - cancel,
+ * {@link com.intellij.openapi.ui.Messages.OK} - merge all, {@link com.intellij.openapi.ui.Messages.NO} - select revisions to merge
+ */
+ QuickMergeContentsVariants selectMergeVariant();
+ boolean shouldContinueSwitchedRootFound();
+
+ boolean shouldReintegrate(@NotNull final String sourceUrl, @NotNull final String targetUrl);
+
+ @NotNull
+ SelectMergeItemsResult selectMergeItems(final List<CommittedChangeList> lists, final String mergeTitle, final MergeChecker mergeChecker);
+
+ @NotNull
+ LocalChangesAction selectLocalChangesAction(boolean mergeAll);
+
+ void showIntersectedLocalPaths(final List<FilePath> paths);
+
+ void showError(@NotNull Exception exception);
+ void showErrors(final String message, final List<VcsException> exceptions);
+ void showErrors(final String message, final boolean isError);
+
+ List<CommittedChangeList> showRecentListsForSelection(@NotNull List<CommittedChangeList> list,
+ @NotNull String mergeTitle,
+ @NotNull MergeChecker mergeChecker,
+ @NotNull PairConsumer<Long, MergeDialogI> loader, boolean everyThingLoaded);
+
+ interface SelectMergeItemsResult {
+ QuickMergeContentsVariants getResultCode();
+ List<CommittedChangeList> getSelectedLists();
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteractionImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteractionImpl.java
new file mode 100644
index 000000000000..93790e91ba66
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeInteractionImpl.java
@@ -0,0 +1,164 @@
+/*
+ * 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.integrate;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogBuilder;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vcs.AbstractVcsHelper;
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.util.PairConsumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.svn.dialogs.*;
+import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/27/13
+ * Time: 11:40 AM
+ */
+public class QuickMergeInteractionImpl implements QuickMergeInteraction {
+ private final Project myProject;
+ private String myTitle;
+
+ public QuickMergeInteractionImpl(Project project) {
+ myProject = project;
+ }
+
+ @Override
+ public void setTitle(@NotNull String title) {
+ myTitle = title;
+ }
+
+ @Override
+ public QuickMergeContentsVariants selectMergeVariant() {
+ final QuickMergeWayOptionsPanel panel = new QuickMergeWayOptionsPanel();
+ final DialogBuilder builder = new DialogBuilder(myProject);
+ builder.removeAllActions();
+ builder.setTitle("Select Merge Variant");
+ builder.setCenterPanel(panel.getMainPanel());
+ panel.setWrapper(builder.getDialogWrapper());
+ builder.show();
+
+ return panel.getVariant();
+ }
+
+ @Override
+ public boolean shouldContinueSwitchedRootFound() {
+ return prompt("There are some switched paths in the working copy. Do you want to continue?");
+ }
+
+ @Override
+ public boolean shouldReintegrate(@NotNull final String sourceUrl, @NotNull final String targetUrl) {
+ return prompt("<html><body>You are going to reintegrate changes.<br><br>This will make branch '" + sourceUrl +
+ "' <b>no longer usable for further work</b>." +
+ "<br>It will not be able to correctly absorb new trunk (" + targetUrl +
+ ") changes,<br>nor can this branch be properly reintegrated to trunk again.<br><br>Are you sure?</body></html>");
+ }
+
+ @NotNull
+ @Override
+ public SelectMergeItemsResult selectMergeItems(List<CommittedChangeList> lists, String mergeTitle, MergeChecker mergeChecker) {
+ final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, lists, mergeTitle, mergeChecker, null);
+ dialog.show();
+ return new SelectMergeItemsResult() {
+ @Override
+ public QuickMergeContentsVariants getResultCode() {
+ final int code = dialog.getExitCode();
+ if (ToBeMergedDialog.MERGE_ALL_CODE == code) {
+ return QuickMergeContentsVariants.all;
+ }
+ return DialogWrapper.OK_EXIT_CODE == code ? QuickMergeContentsVariants.select : QuickMergeContentsVariants.cancel;
+ }
+
+ @Override
+ public List<CommittedChangeList> getSelectedLists() {
+ return dialog.getSelected();
+ }
+ };
+ }
+
+ @NotNull
+ @Override
+ public LocalChangesAction selectLocalChangesAction(final boolean mergeAll) {
+ if (! mergeAll) {
+ final LocalChangesAction[] possibleResults = {LocalChangesAction.shelve, LocalChangesAction.inspect,
+ LocalChangesAction.continueMerge, LocalChangesAction.cancel};
+ final int result = Messages.showDialog("There are local changes that will intersect with merge changes.\nDo you want to continue?", myTitle,
+ new String[]{"Shelve local changes", "Inspect changes", "Continue merge", "Cancel"},
+ 0, Messages.getQuestionIcon());
+ return possibleResults[result];
+ } else {
+ final LocalChangesAction[] possibleResults = {LocalChangesAction.shelve, LocalChangesAction.continueMerge, LocalChangesAction.cancel};
+ final int result = Messages.showDialog("There are local changes that can potentially intersect with merge changes.\nDo you want to continue?", myTitle,
+ new String[]{"Shelve local changes", "Continue merge", "Cancel"},
+ 0, Messages.getQuestionIcon());
+ return possibleResults[result];
+ }
+ }
+
+ @Override
+ public void showIntersectedLocalPaths(final List<FilePath> paths) {
+ IntersectingLocalChangesPanel.showInVersionControlToolWindow(myProject, myTitle + ", local changes intersection",
+ paths, "The following file(s) have local changes that will intersect with merge changes:");
+ }
+
+ @Override
+ public void showError(@NotNull Exception exception) {
+ AbstractVcsHelper.getInstance(myProject).showErrors(Collections.singletonList(new VcsException(exception)),
+ exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
+ }
+
+ @Override
+ public void showErrors(String message, List<VcsException> exceptions) {
+ AbstractVcsHelper.getInstance(myProject).showErrors(exceptions, message);
+ }
+
+ @Override
+ public void showErrors(String message, boolean isError) {
+ VcsBalloonProblemNotifier.showOverChangesView(myProject, message, isError ? MessageType.ERROR : MessageType.WARNING);
+ }
+
+ @Override
+ public List<CommittedChangeList> showRecentListsForSelection(@NotNull List<CommittedChangeList> list,
+ @NotNull String mergeTitle,
+ @NotNull MergeChecker mergeChecker,
+ @NotNull PairConsumer<Long, MergeDialogI> loader,
+ boolean everyThingLoaded) {
+ final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, list, mergeTitle, mergeChecker, loader);
+ if (everyThingLoaded) {
+ dialog.setEverythingLoaded(true);
+ }
+ dialog.show();
+ if (DialogWrapper.OK_EXIT_CODE == dialog.getExitCode()) {
+ return dialog.getSelected();
+ }
+ return null;
+ }
+
+ private boolean prompt(final String question) {
+ return Messages.showOkCancelDialog(myProject, question, myTitle, Messages.getQuestionIcon()) == 0;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.form
new file mode 100644
index 000000000000..a48870b200f0
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.form
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.svn.integrate.QuickMergeWayOptionsPanel">
+ <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="8" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="754" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="3c8e5" class="javax.swing.JButton" binding="myMergeAllButton" default-binding="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Merge All"/>
+ </properties>
+ </component>
+ <vspacer id="66429">
+ <constraints>
+ <grid row="7" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <hspacer id="54e1">
+ <constraints>
+ <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <component id="2abc7" class="javax.swing.JButton" binding="myQuickManualSelectButton" default-binding="true">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Quick Manual Select"/>
+ </properties>
+ </component>
+ <component id="76374" class="javax.swing.JButton" binding="mySelectWithPreFilterButton" default-binding="true">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Select With Pre-Filter"/>
+ </properties>
+ </component>
+ <component id="e43e9" class="javax.swing.JButton" binding="myCancelButton" default-binding="true">
+ <constraints>
+ <grid row="6" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Cancel"/>
+ </properties>
+ </component>
+ <component id="54a5b" class="javax.swing.JLabel" binding="myAllNotMergedRevisionsLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/svn/SvnBundle" key="quick.merge.variants.merge.all.explanation"/>
+ </properties>
+ </component>
+ <component id="18321" class="javax.swing.JLabel" binding="myShowsAllRevisionsFromLabel">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/svn/SvnBundle" key="quick.merge.variants.quick.select.explanation"/>
+ </properties>
+ </component>
+ <component id="f8a4d" class="javax.swing.JLabel" binding="myFindsWhereOneOfLabel">
+ <constraints>
+ <grid row="5" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/svn/SvnBundle" key="quick.merge.variants.pre.select.explanation"/>
+ </properties>
+ </component>
+ <hspacer id="3de54">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="2" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <hspacer id="4ab9a">
+ <constraints>
+ <grid row="3" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ <hspacer id="a4a34">
+ <constraints>
+ <grid row="5" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </hspacer>
+ </children>
+ </grid>
+</form>
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.java
new file mode 100644
index 000000000000..430da5256080
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/QuickMergeWayOptionsPanel.java
@@ -0,0 +1,85 @@
+/*
+ * 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.integrate;
+
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/29/13
+ * Time: 7:27 PM
+ */
+public class QuickMergeWayOptionsPanel {
+ private JButton myMergeAllButton;
+ private JButton myQuickManualSelectButton;
+ private JButton mySelectWithPreFilterButton;
+ private JButton myCancelButton;
+ private JPanel myMainPanel;
+ private JLabel myAllNotMergedRevisionsLabel;
+ private JLabel myShowsAllRevisionsFromLabel;
+ private JLabel myFindsWhereOneOfLabel;
+ private DialogWrapper myWrapper;
+
+ private QuickMergeContentsVariants myVariant = QuickMergeContentsVariants.cancel;
+
+ public QuickMergeWayOptionsPanel() {
+ myMergeAllButton.addActionListener(setCodeAndClose(QuickMergeContentsVariants.all));
+ myQuickManualSelectButton.addActionListener(setCodeAndClose(QuickMergeContentsVariants.showLatest));
+ mySelectWithPreFilterButton.addActionListener(setCodeAndClose(QuickMergeContentsVariants.select));
+ myCancelButton.addActionListener(setCodeAndClose(QuickMergeContentsVariants.cancel));
+
+ myAllNotMergedRevisionsLabel.setUI(new MultiLineLabelUI());
+ myShowsAllRevisionsFromLabel.setUI(new MultiLineLabelUI());
+ myFindsWhereOneOfLabel.setUI(new MultiLineLabelUI());
+
+ myAllNotMergedRevisionsLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
+ myShowsAllRevisionsFromLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
+ myFindsWhereOneOfLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
+ }
+
+ private ActionListener setCodeAndClose(final QuickMergeContentsVariants variant) {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ myVariant = variant;
+ close();
+ }
+ };
+ }
+
+ private void close() {
+ myWrapper.close(DialogWrapper.OK_EXIT_CODE);
+ }
+
+ public void setWrapper(DialogWrapper wrapper) {
+ myWrapper = wrapper;
+ }
+
+ public QuickMergeContentsVariants getVariant() {
+ return myVariant;
+ }
+
+ public JPanel getMainPanel() {
+ return myMainPanel;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java
index 1133e8b32ef7..00f839776120 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SvnIntegrateChangesTask.java
@@ -215,7 +215,8 @@ public class SvnIntegrateChangesTask extends Task.Backgroundable {
private void finishActions(final boolean wasCanceled) {
if (! wasCanceled) {
- if ((! myDryRun) && (myExceptions.isEmpty()) && (! myAccomulatedFiles.containErrors()) &&
+ if (! ApplicationManager.getApplication().isUnitTestMode() &&
+ (! myDryRun) && (myExceptions.isEmpty()) && (! myAccomulatedFiles.containErrors()) &&
((! myAccomulatedFiles.isEmpty()) || (myMergeTarget != null))) {
if (myInfo.isUnderProjectRoot()) {
showLocalCommit();
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/lowLevel/PrimitivePool.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/lowLevel/PrimitivePool.java
new file mode 100644
index 000000000000..0b7e7ae67f05
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/lowLevel/PrimitivePool.java
@@ -0,0 +1,113 @@
+/*
+ * 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.lowLevel;
+
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import org.tmatesoft.svn.core.ISVNCanceller;
+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.io.ISVNSession;
+import org.tmatesoft.svn.core.io.ISVNTunnelProvider;
+import org.tmatesoft.svn.core.io.SVNRepository;
+import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
+import org.tmatesoft.svn.core.wc.ISVNRepositoryPool;
+import org.tmatesoft.svn.util.ISVNDebugLog;
+import org.tmatesoft.svn.util.SVNDebugLog;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/27/13
+ * Time: 10:41 PM
+ */
+public class PrimitivePool implements ISVNRepositoryPool, ISVNSession {
+ private final ISVNAuthenticationManager myManager;
+ private final ISVNTunnelProvider myTunnelProvider;
+
+ public PrimitivePool(ISVNAuthenticationManager manager, ISVNTunnelProvider tunnelProvider) {
+ myManager = manager;
+ myTunnelProvider = tunnelProvider;
+ }
+
+ @Override
+ public SVNRepository createRepository(SVNURL url, boolean mayReuse) throws SVNException {
+ final SVNRepository repos = SVNRepositoryFactory.create(url, this);
+ repos.setAuthenticationManager(myManager);
+ repos.setTunnelProvider(myTunnelProvider);
+ repos.setDebugLog(new ProxySvnLog(SVNDebugLog.getDefaultLog()));
+ repos.setCanceller(new MyCanceller());
+ return repos;
+ }
+
+ @Override
+ public void setAuthenticationManager(ISVNAuthenticationManager authManager) {
+ }
+
+ @Override
+ public void setCanceller(ISVNCanceller canceller) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setDebugLog(ISVNDebugLog log) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void shutdownConnections(boolean shutdownAll) {
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public boolean keepConnection(SVNRepository repository) {
+ return false;
+ }
+
+ @Override
+ public void saveCommitMessage(SVNRepository repository, long revision, String message) {
+ }
+
+ @Override
+ public String getCommitMessage(SVNRepository repository, long revision) {
+ return null;
+ }
+
+ @Override
+ public boolean hasCommitMessage(SVNRepository repository, long revision) {
+ return false;
+ }
+
+ private static class MyCanceller implements ISVNCanceller {
+ @Override
+ public void checkCancelled() throws SVNCancelException {
+ final ProgressManager pm = ProgressManager.getInstance();
+ final ProgressIndicator pi = pm.getProgressIndicator();
+ if (pi != null) {
+ if (pi.isCanceled()) throw new SVNCancelException();
+ }
+ ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+ if (indicator != null && indicator.isCanceled()) {
+ throw new SVNCancelException();
+ }
+ }
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/ListMergeStatus.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/ListMergeStatus.java
new file mode 100644
index 000000000000..750a1fc46ff9
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/ListMergeStatus.java
@@ -0,0 +1,47 @@
+/*
+ * 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.mergeinfo;
+
+import icons.SvnIcons;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+* Created with IntelliJ IDEA.
+* User: Irina.Chernushina
+* Date: 3/30/13
+* Time: 2:41 PM
+*/
+public enum ListMergeStatus {
+ COMMON(SvnIcons.Common),
+ MERGED(SvnIcons.Integrated),
+ NOT_MERGED(SvnIcons.Notintegrated),
+ ALIEN(null),
+ REFRESHING(SvnIcons.IntegrationStatusUnknown);
+
+ @Nullable
+ private final Icon myIcon;
+
+ ListMergeStatus(@Nullable final Icon icon) {
+ myIcon = icon;
+ }
+
+ @Nullable
+ public Icon getIcon() {
+ return myIcon;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/MergeInfoHolder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/MergeInfoHolder.java
index a08bc68ea618..fb0f52362cdf 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/MergeInfoHolder.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/MergeInfoHolder.java
@@ -24,14 +24,12 @@ import com.intellij.openapi.vcs.changes.committed.DecoratorManager;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Consumer;
-import icons.SvnIcons;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches;
import org.jetbrains.idea.svn.dialogs.WCPaths;
import org.jetbrains.idea.svn.history.SvnChangeList;
-import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.HashMap;
@@ -165,25 +163,6 @@ public class MergeInfoHolder {
}
}
- public static enum ListMergeStatus {
- COMMON(SvnIcons.Common),
- MERGED(SvnIcons.Integrated),
- NOT_MERGED(SvnIcons.Notintegrated),
- //ALIEN(IconLoader.getIcon("/icons/OnDefault.png")),
- ALIEN(null),
- REFRESHING(SvnIcons.IntegrationStatusUnknown);
-
- private final Icon myIcon;
-
- private ListMergeStatus(final Icon icon) {
- myIcon = icon;
- }
-
- public Icon getIcon() {
- return myIcon;
- }
- }
-
public static interface ListChecker {
ListMergeStatus check(final CommittedChangeList list, final boolean ignoreEnabled);
}
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 5eda9a30ff54..e2962ed58222 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java
@@ -39,10 +39,12 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker {
private final WCInfo myWCInfo;
// subpath [file] (local) to (subpathURL - merged FROM - to ranges list)
private final AreaMap<String, Map<String, SVNMergeRangeList>> myDataMap;
- private String myFromUrlRelative;
+ private final Object myLock;
+ private final String myFromUrlRelative;
public OneRecursiveShotMergeInfoWorker(final Project project, final WCInfo WCInfo, final String fromUrl) {
myProject = project;
+ myLock = new Object();
myWCInfo = WCInfo;
myDataMap = AreaMap.create(new PairProcessor<String, String>() {
@@ -68,7 +70,9 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker {
depth, new ISVNPropertyHandler() {
public void handleProperty(File path, SVNPropertyData property) throws SVNException {
final String key = keyFromFile(path);
- myDataMap.put(key, SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(replaceSeparators(property.getValue().getString())), null));
+ synchronized (myLock) {
+ myDataMap.put(key, SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(replaceSeparators(property.getValue().getString())), null));
+ }
}
public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException {
@@ -84,7 +88,9 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker {
if (relativeToWc == null) return SvnMergeInfoCache.MergeCheckResult.NOT_EXISTS;
final InfoProcessor processor = new InfoProcessor(relativeToWc, myFromUrlRelative, revisionNumber);
- myDataMap.getSimiliar(keyFromPath(relativeToWc), processor);
+ synchronized (myLock) {
+ myDataMap.getSimiliar(keyFromPath(relativeToWc), processor);
+ }
return SvnMergeInfoCache.MergeCheckResult.getInstance(processor.isMerged());
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/networking/SSLProtocolExceptionParser.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/networking/SSLProtocolExceptionParser.java
new file mode 100644
index 000000000000..51813274257b
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/networking/SSLProtocolExceptionParser.java
@@ -0,0 +1,87 @@
+/*
+ * 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.networking;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/25/13
+ * Time: 5:44 PM
+ */
+public class SSLProtocolExceptionParser {
+ private final String myMessage;
+ private String myParsedMessage;
+ private byte myFieldValue;
+ private String myFieldName;
+
+ public SSLProtocolExceptionParser(@NotNull String message) {
+ myMessage = message;
+ }
+
+ public void parse() {
+ myParsedMessage = myMessage;
+ myFieldValue = 0;
+ myFieldName = null;
+
+ final List<String> words = StringUtil.split(myMessage.trim(), " ");
+ if (words.isEmpty()) return;
+ // we'll try to parse by last word - it's just an attempt so ok if failed - just will show the real message
+
+ final String[] possiblePlaces = {"com.sun.net.ssl.internal.ssl.Alerts", "sun.security.ssl.Alerts"};
+ for (String place : possiblePlaces) {
+ try {
+ final Class<?> clazz = Class.forName(place);
+ if (tryByStaticField(clazz, words.get(words.size() - 1))) {
+ return;
+ }
+ }
+ catch (ClassNotFoundException e) {
+ //
+ }
+ }
+ }
+
+ public String getParsedMessage() {
+ return myParsedMessage;
+ }
+
+ private boolean tryByStaticField(Class<?> clazz, String word) {
+ try {
+ final Field field = clazz.getDeclaredField("alert_" + word);
+ field.setAccessible(true);
+ myFieldValue = field.getByte(clazz);
+ myFieldName = field.getName();
+ myParsedMessage = "SSLProtocolException: alert code: " + Byte.toString(myFieldValue) + " alert name: " + myFieldName +
+ ", original message: " + myMessage;
+ if ("alert_unrecognized_name".equals(myFieldName)) {
+ myParsedMessage += "\nThis may be JDK bug 7127374 : JSSE creates SSLProtocolException on (common) warning: unrecognized_name for SNI";
+ }
+ }
+ catch (NoSuchFieldException e) {
+ return false;
+ }
+ catch (IllegalAccessException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/status/DiffContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/DiffContentRevision.java
index 2d280b21d3ec..bec2f3e963e9 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/status/DiffContentRevision.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/status/DiffContentRevision.java
@@ -21,6 +21,7 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.CharsetToolkit;
+import com.intellij.openapi.vfs.encoding.EncodingRegistry;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -59,7 +60,7 @@ public class DiffContentRevision implements ContentRevision {
}
final byte[] bytes = bos.toByteArray();
final Charset charset = myFilePath.getCharset();
- myContents = charset == null ? CharsetToolkit.bytesToString(bytes) : CharsetToolkit.bytesToString(bytes, charset);
+ myContents = charset == null ? CharsetToolkit.bytesToString(bytes, EncodingRegistry.getInstance().getDefaultCharset()) : CharsetToolkit.bytesToString(bytes, charset);
}
return myContents;
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnRevisionPanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnRevisionPanel.java
index 45cdaf080ca4..897f5b76f72b 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnRevisionPanel.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnRevisionPanel.java
@@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter;
+import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnBundle;
import org.jetbrains.idea.svn.history.SvnChangeList;
@@ -33,7 +34,7 @@ import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.ArrayList;
+import java.util.List;
public class SvnRevisionPanel extends JPanel {
private JRadioButton mySpecified;
@@ -42,7 +43,7 @@ public class SvnRevisionPanel extends JPanel {
private TextFieldWithBrowseButton myRevisionField;
private Project myProject;
private UrlProvider myUrlProvider;
- private final ArrayList<ChangeListener> myChangeListeners = new ArrayList<ChangeListener>();
+ private final List<ChangeListener> myChangeListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private VirtualFile myRoot;
public SvnRevisionPanel() {
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java
index 559c8b72e2d9..80e43aaf7828 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java
@@ -106,7 +106,10 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment
final SvnUpdateClientI updateClient;
// do not do from command line for switch now
if (! isSwitch && SvnConfiguration.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) &&
- Svn17Detector.is17(myVcs.getProject(), root) && SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol())) {
+ Svn17Detector.is17(myVcs.getProject(), root) && (
+ SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol()) ||
+ SvnAuthenticationManager.HTTPS.equals(sourceUrl.getProtocol())
+ )) {
updateClient = new SvnCommandLineUpdateClient(myVcs.getProject(), null);
} else {
updateClient = new SvnSvnkitUpdateClient(myVcs.createUpdateClient());
diff --git a/plugins/svn4idea/svn4idea-tests.iml b/plugins/svn4idea/svn4idea-tests.iml
index 0b826ed87489..9a537c253abf 100644
--- a/plugins/svn4idea/svn4idea-tests.iml
+++ b/plugins/svn4idea/svn4idea-tests.iml
@@ -38,6 +38,7 @@
</SOURCES>
</library>
</orderEntry>
+ <orderEntry type="module" module-name="bindSvn" />
</component>
</module>
diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml
index e27f7e52a099..a2785e1261f5 100644
--- a/plugins/svn4idea/svn4idea.iml
+++ b/plugins/svn4idea/svn4idea.iml
@@ -94,23 +94,22 @@
<orderEntry type="module-library">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/lib/javahl.jar!/" />
+ <root url="jar://$MODULE_DIR$/lib/svnkit-javahl.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$MODULE_DIR$/lib/javahlsrc.zip!/src" />
+ <root url="jar://$MODULE_DIR$/lib/svnkit-javahl16.zip!/" />
</SOURCES>
</library>
</orderEntry>
+ <orderEntry type="module" module-name="bindSvn" />
<orderEntry type="module-library">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/lib/svnkit-javahl.jar!/" />
+ <root url="jar://$MODULE_DIR$/bindSvn/lib/javahl.jar!/" />
</CLASSES>
<JAVADOC />
- <SOURCES>
- <root url="jar://$MODULE_DIR$/lib/svnkit-javahl16.zip!/" />
- </SOURCES>
+ <SOURCES />
</library>
</orderEntry>
</component>
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java
index 25e5f71f7826..a1b7360206e7 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java
@@ -83,11 +83,12 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
protected String myWcRootName;
protected boolean myUseNativeAcceleration = new GregorianCalendar().get(Calendar.HOUR_OF_DAY) % 2 == 0;
- private final String myTestDataDir;
+ protected final String myTestDataDir;
private File myRepoRoot;
private File myWcRoot;
private ChangeListManagerGate myGate;
protected String myAnotherRepoUrl;
+ protected File myPluginRoot;
protected SvnTestCase(@NotNull String testDataDir) {
PlatformTestCase.initPlatformLangPrefix();
@@ -128,15 +129,15 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
myRepoRoot = new File(myTempDirFixture.getTempDirPath(), "svnroot");
assert myRepoRoot.mkdir() || myRepoRoot.isDirectory() : myRepoRoot;
- File pluginRoot = new File(PluginPathManager.getPluginHomePath("svn4idea"));
- if (!pluginRoot.isDirectory()) {
+ myPluginRoot = new File(PluginPathManager.getPluginHomePath("svn4idea"));
+ if (!myPluginRoot.isDirectory()) {
// try standalone mode
Class aClass = SvnTestCase.class;
String rootPath = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
- pluginRoot = new File(rootPath).getParentFile().getParentFile().getParentFile();
+ myPluginRoot = new File(rootPath).getParentFile().getParentFile().getParentFile();
}
- File svnBinDir = new File(pluginRoot, myTestDataDir + "/svn/bin");
+ File svnBinDir = new File(myPluginRoot, myTestDataDir + "/svn/bin");
File svnExecutable = null;
if (SystemInfo.isWindows) {
svnExecutable = new File(svnBinDir, "windows/svn.exe");
@@ -154,7 +155,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
? createClientRunner(Collections.singletonMap("DYLD_LIBRARY_PATH", myClientBinaryPath.getPath()))
: createClientRunner();
- ZipUtil.extract(new File(pluginRoot, myTestDataDir + "/svn/newrepo.zip"), myRepoRoot, null);
+ ZipUtil.extract(new File(myPluginRoot, myTestDataDir + "/svn/newrepo.zip"), myRepoRoot, null);
myWcRoot = new File(myTempDirFixture.getTempDirPath(), myWcRootName);
assert myWcRoot.mkdir() || myWcRoot.isDirectory() : myWcRoot;
@@ -280,6 +281,43 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
});
}
+ protected void prepareInnerCopy(final boolean anotherRepository) throws Exception {
+ final String mainUrl = myRepoUrl + "/root/source";
+ final String externalURL;
+ if (anotherRepository) {
+ createAnotherRepo();
+ externalURL = myAnotherRepoUrl + "/root/target";
+ } else {
+ externalURL = myRepoUrl + "/root/target";
+ }
+
+ final ChangeListManagerImpl clManager = (ChangeListManagerImpl)ChangeListManager.getInstance(myProject);
+ final SubTree subTree = new SubTree(myWorkingCopyDir);
+ checkin();
+ clManager.stopEveryThingIfInTestMode();
+ sleep(100);
+ final File rootFile = new File(subTree.myRootDir.getPath());
+ FileUtil.delete(rootFile);
+ FileUtil.delete(new File(myWorkingCopyDir.getPath() + File.separator + ".svn"));
+ Assert.assertTrue(!rootFile.exists());
+ sleep(200);
+ myWorkingCopyDir.refresh(false, true);
+
+ runInAndVerifyIgnoreOutput("co", mainUrl);
+ final File sourceDir = new File(myWorkingCopyDir.getPath(), "source");
+ final File innerDir = new File(sourceDir, "inner1/inner2/inner");
+ runInAndVerifyIgnoreOutput("co", externalURL, innerDir.getPath());
+ sleep(100);
+ myWorkingCopyDir.refresh(false, true);
+ // above is preparation
+
+ // start change list manager again
+ clManager.forceGoInTestMode();
+ refreshSvnMappingsSynchronously();
+ //clManager.ensureUpToDate(false);
+ //clManager.ensureUpToDate(false);
+ }
+
protected class SubTree {
public VirtualFile myRootDir;
public VirtualFile mySourceDir;
@@ -412,7 +450,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
//clManager.ensureUpToDate(false);
}
- private void createAnotherRepo() throws Exception {
+ protected void createAnotherRepo() throws Exception {
final File repo = FileUtil.createTempDirectory("anotherRepo", "");
FileUtil.delete(repo);
FileUtil.copyDir(myRepoRoot, repo);
@@ -547,4 +585,11 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase {
final ProcessOutput output = runner.runClient("svn", null, workingDir, input);
primitiveVerifier.process(output);
}
+
+ protected void setNativeAcceleration(final boolean value) {
+ System.out.println("Set native acceleration to " + value);
+ SvnConfiguration.getInstance(myProject).myUseAcceleration =
+ value ? SvnConfiguration.UseAcceleration.commandLine : SvnConfiguration.UseAcceleration.nothing;
+ SvnApplicationSettings.getInstance().setCommandLinePath(myClientBinaryPath + File.separator + "svn");
+ }
}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/QuickMergeTestInteraction.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/QuickMergeTestInteraction.java
new file mode 100644
index 000000000000..6336da4b0606
--- /dev/null
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/QuickMergeTestInteraction.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.util.PairConsumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.svn.dialogs.LocalChangesAction;
+import org.jetbrains.idea.svn.dialogs.MergeDialogI;
+import org.jetbrains.idea.svn.dialogs.QuickMergeContentsVariants;
+import org.jetbrains.idea.svn.integrate.QuickMergeInteraction;
+import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/27/13
+ * Time: 6:56 PM
+ */
+public class QuickMergeTestInteraction implements QuickMergeInteraction {
+ private QuickMergeContentsVariants myMergeVariant = QuickMergeContentsVariants.all;
+ private boolean myReintegrateAnswer = false;
+ private LocalChangesAction myLocalChangesAction = LocalChangesAction.continueMerge;
+ private QuickMergeContentsVariants mySelectMergeAction2ndStep = QuickMergeContentsVariants.all;
+
+ private final List<Exception> myExceptions;
+
+ public QuickMergeTestInteraction() {
+ myExceptions = new ArrayList<Exception>();
+ }
+
+ @Override
+ public void setTitle(@NotNull String title) {
+ }
+
+ @Override
+ public QuickMergeContentsVariants selectMergeVariant() {
+ return myMergeVariant;
+ }
+
+ public void setMergeVariant(QuickMergeContentsVariants mergeVariant) {
+ myMergeVariant = mergeVariant;
+ }
+
+ @Override
+ public boolean shouldContinueSwitchedRootFound() {
+ // not gonna test this at the moment
+ return false;
+ }
+
+ @Override
+ public boolean shouldReintegrate(@NotNull String sourceUrl, @NotNull String targetUrl) {
+ return myReintegrateAnswer;
+ }
+
+ public void setReintegrateAnswer(boolean reintegrateAnswer) {
+ myReintegrateAnswer = reintegrateAnswer;
+ }
+
+ public void setSelectMergeAction2ndStep(QuickMergeContentsVariants selectMergeAction2ndStep) {
+ mySelectMergeAction2ndStep = selectMergeAction2ndStep;
+ }
+
+ @NotNull
+ @Override
+ public SelectMergeItemsResult selectMergeItems(List<CommittedChangeList> lists, String mergeTitle, MergeChecker mergeChecker) {
+ return new SelectMergeItemsResult() {
+ @Override
+ public QuickMergeContentsVariants getResultCode() {
+ return mySelectMergeAction2ndStep;
+ }
+
+ @Override
+ public List<CommittedChangeList> getSelectedLists() {
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public List<CommittedChangeList> showRecentListsForSelection(@NotNull List<CommittedChangeList> list,
+ @NotNull String mergeTitle,
+ @NotNull MergeChecker mergeChecker,
+ @NotNull PairConsumer<Long, MergeDialogI> loader,
+ boolean everyThingLoaded) {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public LocalChangesAction selectLocalChangesAction(boolean mergeAll) {
+ return myLocalChangesAction;
+ }
+
+ public void setLocalChangesAction(LocalChangesAction localChangesAction) {
+ myLocalChangesAction = localChangesAction;
+ }
+
+ @Override
+ public void showIntersectedLocalPaths(List<FilePath> paths) {
+ }
+
+ @Override
+ public void showError(@NotNull Exception exception) {
+ myExceptions.add(exception);
+ }
+
+ @Override
+ public void showErrors(String message, List<VcsException> exceptions) {
+ if (exceptions != null && ! exceptions.isEmpty()) {
+ myExceptions.addAll(exceptions);
+ return;
+ }
+ myExceptions.add(new RuntimeException(message));
+ }
+
+ @Override
+ public void showErrors(String message, boolean isError) {
+ if (isError) {
+ myExceptions.add(new RuntimeException(message));
+ } else {
+ System.out.println("merge warning: " + message);
+ }
+ }
+
+ public void throwIfExceptions() throws Exception {
+ for (Exception exception : myExceptions) {
+ throw exception;
+ }
+ }
+}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SSLExceptionParserTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SSLExceptionParserTest.java
new file mode 100644
index 000000000000..3e83843c7d7b
--- /dev/null
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SSLExceptionParserTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.net.ssl.SSLProtocolException;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/25/13
+ * Time: 6:09 PM
+ */
+public class SSLExceptionParserTest {
+ @Test
+ public void testRealLifeCase() throws Exception {
+ final String original = "handshake alert: unrecognized_name";
+ final SSLProtocolException exception = new SSLProtocolException(original);
+ final SSLProtocolExceptionParser parser = new SSLProtocolExceptionParser(exception.getMessage());
+ parser.parse();
+ final String message = parser.getParsedMessage();
+ System.out.println(message);
+ Assert.assertNotSame(original, message);
+ }
+}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java
index e699a1a8069b..b6ffb7e7e7fb 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommandLineStabilityTest.java
@@ -2,6 +2,7 @@ package org.jetbrains.idea.svn;
import com.intellij.openapi.vcs.VcsException;
import junit.framework.Assert;
+import org.jetbrains.idea.svn.commandLine.SvnCommandFactory;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.jetbrains.idea.svn.commandLine.SvnSimpleCommand;
import org.junit.Test;
@@ -24,7 +25,7 @@ public class SvnCommandLineStabilityTest extends Svn17TestCase {
}
private void call() throws VcsException {
- final SvnSimpleCommand command = new SvnSimpleCommand(myProject, new File(myWorkingCopyDir.getPath()), SvnCommandName.info);
+ final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, new File(myWorkingCopyDir.getPath()), SvnCommandName.info);
command.addParameters("--xml");
final String result = command.run();
System.out.println(result);
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java
new file mode 100644
index 000000000000..f4da04ea201a
--- /dev/null
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java
@@ -0,0 +1,492 @@
+/*
+ * 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;
+
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vcs.*;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.NullableFunction;
+import junit.framework.Assert;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/28/13
+ * Time: 11:59 AM
+ */
+public class SvnCommitTest extends Svn17TestCase {
+ private SvnVcs myVcs;
+ private VcsDirtyScopeManager myDirtyScopeManager;
+ private ChangeListManager myChangeListManager;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ myVcs = SvnVcs.getInstance(myProject);
+ myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
+ myChangeListManager = ChangeListManager.getInstance(myProject);
+ }
+
+ @Test
+ public void testSimpleCommit() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ run2variants(new MyRunner() {
+ private String myName = "a.txt";
+
+ @Override
+ protected void run() throws Exception {
+ final VirtualFile file = createFileInCommand(myWorkingCopyDir, myName, "123");
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFile(file, FileStatus.ADDED);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ myName = "b.txt";
+ }
+ });
+ }
+
+ @Test
+ public void testCommitRename() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ run2variants(new MyRunner() {
+ private String myName = "a.txt";
+ private String myRenamedName = "aRenamed.txt";
+
+ @Override
+ protected void run() throws Exception {
+ final VirtualFile file = createFileInCommand(myWorkingCopyDir, myName, "123");
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFile(file, FileStatus.ADDED);
+
+ renameFileInCommand(file, myRenamedName);
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFile(file, FileStatus.MODIFIED);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ myName = "b.txt";
+ myRenamedName = "bRenamed.txt";
+ }
+ });
+ }
+
+ @Test
+ public void testRenameReplace() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ run2variants(new MyRunner() {
+ private String myName = "a.txt";
+ private String myName2 = "aRenamed.txt";
+
+ @Override
+ protected void run() throws Exception {
+ final VirtualFile file = createFileInCommand(myWorkingCopyDir, myName, "123");
+ final VirtualFile file2 = createFileInCommand(myWorkingCopyDir, myName2, "1235");
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(file, file2);
+
+ renameFileInCommand(file, file.getName() + "7.txt");
+ renameFileInCommand(file2, myName);
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(file, file2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ myName = "b.txt";
+ myName2 = "bRenamed.txt";
+ }
+ });
+ }
+
+ @Test
+ public void testRenameFolder() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ run2variants(new MyRunner() {
+ private String folder = "f";
+
+ @Override
+ protected void run() throws Exception {
+ final VirtualFile dir = createDirInCommand(myWorkingCopyDir, folder);
+ final VirtualFile file = createFileInCommand(dir, "a.txt", "123");
+ final VirtualFile file2 = createFileInCommand(dir, "b.txt", "1235");
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(dir, file, file2);
+
+ renameFileInCommand(dir, dir.getName() + "dd");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(dir, file, file2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ folder = "f1";
+ }
+ });
+ }
+
+ @Test
+ public void testCommitDeletion() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ run2variants(new MyRunner() {
+ private String folder = "f";
+
+ @Override
+ protected void run() throws Exception {
+ final VirtualFile dir = createDirInCommand(myWorkingCopyDir, folder);
+ final VirtualFile file = createFileInCommand(dir, "a.txt", "123");
+ final VirtualFile file2 = createFileInCommand(dir, "b.txt", "1235");
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(dir, file, file2);
+
+ final FilePath dirPath = new FilePathImpl(new File(dir.getPath()), true);
+ deleteFileInCommand(dir);
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinPaths(dirPath);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ folder = "f1";
+ }
+ });
+ }
+
+ @Test
+ public void testSameRepoPlusInnerCopyCommitNative() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareInnerCopy(false);
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/inner1/inner2/inner/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ final HashSet<String> strings = checkinFiles(vf1, vf2);
+ System.out.println("" + StringUtil.join(strings, "\n"));
+ Assert.assertEquals(1, strings.size());
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ @Test
+ public void testSameRepoPlusInnerCopyCommitSvnkit() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareInnerCopy(false);
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/inner1/inner2/inner/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ final HashSet<String> strings = checkinFiles(vf1, vf2);
+ System.out.println("" + StringUtil.join(strings, "\n"));
+ Assert.assertEquals(1, strings.size());
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ @Test
+ public void testAnotherRepoPlusInnerCopyCommitNative() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareInnerCopy(true);
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/inner1/inner2/inner/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(vf1, vf2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ @Test
+ public void testAnotherRepoPlusInnerCopyCommitSvnkit() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareInnerCopy(true);
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/inner1/inner2/inner/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(vf1, vf2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ @Test
+ public void testPlusExternalCopyCommitNative() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareExternal();
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/external/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(vf1, vf2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ @Test
+ public void testPlusExternalCopyCommitSvnkit() throws Exception {
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD);
+ enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE);
+ prepareExternal();
+ final MyRunner runner = new MyRunner() {
+ @Override
+ protected void run() throws Exception {
+ final File file1 = new File(myWorkingCopyDir.getPath(), "source/s1.txt");
+ final File fileInner = new File(myWorkingCopyDir.getPath(), "source/external/t11.txt");
+
+ Assert.assertTrue(file1.exists());
+ Assert.assertTrue(fileInner.exists());
+ final VirtualFile vf1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file1);
+ final VirtualFile vf2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(fileInner);
+ Assert.assertNotNull(vf1);
+ Assert.assertNotNull(vf2);
+
+ editFileInCommand(vf1, "2317468732ghdwwe7y348rf");
+ editFileInCommand(vf2, "2317468732ghdwwe7y348rf csdjcjksw");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ checkinFiles(vf1, vf2);
+ }
+
+ @Override
+ protected void cleanup() throws Exception {
+ }
+ };
+ setNativeAcceleration(false);
+ runner.run();
+ }
+
+ private void checkinPaths(FilePath... files) {
+ final List<Change> changes = new ArrayList<Change>();
+ for (FilePath file : files) {
+ final Change change = myChangeListManager.getChange(file);
+ Assert.assertNotNull(change);
+ changes.add(change);
+ }
+ final List<VcsException> exceptions = myVcs.getCheckinEnvironment().commit(changes, "test comment list");
+ Assert.assertTrue(exceptions == null || exceptions.isEmpty());
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ for (FilePath file : files) {
+ final Change changeA = myChangeListManager.getChange(file);
+ Assert.assertNull(changeA);
+ }
+ }
+
+ private HashSet<String> checkinFiles(VirtualFile... files) {
+ final List<Change> changes = new ArrayList<Change>();
+ for (VirtualFile file : files) {
+ final Change change = myChangeListManager.getChange(file);
+ Assert.assertNotNull(change);
+ changes.add(change);
+ }
+ final HashSet<String> feedback = new HashSet<String>();
+ final List<VcsException> exceptions = myVcs.getCheckinEnvironment().commit(changes, "test comment list",
+ new NullableFunction<Object, Object>() {
+ @Nullable
+ @Override
+ public Object fun(Object o) {
+ return null;
+ }
+ }, feedback);
+ if (exceptions !=null && ! exceptions.isEmpty()) {
+ exceptions.get(0).printStackTrace();
+ }
+ Assert.assertTrue(exceptions == null || exceptions.isEmpty());
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ for (VirtualFile file : files) {
+ final Change changeA = myChangeListManager.getChange(file);
+ Assert.assertNull(changeA);
+ }
+ return feedback;
+ }
+
+ private void checkinFile(VirtualFile file, FileStatus status) {
+ final Change change = myChangeListManager.getChange(file);
+ Assert.assertNotNull(change);
+ Assert.assertEquals(status, change.getFileStatus());
+ final List<VcsException> exceptions = myVcs.getCheckinEnvironment().commit(Collections.singletonList(change), "test comment");
+ Assert.assertTrue(exceptions == null || exceptions.isEmpty());
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+ final Change changeA = myChangeListManager.getChange(file);
+ Assert.assertNull(changeA);
+ }
+
+ protected void run2variants(final MyRunner runner) throws Exception {
+ setNativeAcceleration(false);
+ runner.run();
+ runner.cleanup();
+ setNativeAcceleration(true);
+ runner.run();
+ }
+
+ private static abstract class MyRunner {
+ protected abstract void run() throws Exception;
+ protected abstract void cleanup() throws Exception;
+ }
+}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java
index 177f608ea768..500acbdff4c9 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java
@@ -91,31 +91,8 @@ public class SvnExternalTests extends Svn17TestCase {
Assert.assertTrue(expectedUrls.isEmpty());
}
- private void prepareInnerCopy() throws Exception {
- final SubTree subTree = new SubTree(myWorkingCopyDir);
- checkin();
- clManager.stopEveryThingIfInTestMode();
- sleep(100);
- final File rootFile = new File(subTree.myRootDir.getPath());
- FileUtil.delete(rootFile);
- FileUtil.delete(new File(myWorkingCopyDir.getPath() + File.separator + ".svn"));
- Assert.assertTrue(!rootFile.exists());
- sleep(200);
- myWorkingCopyDir.refresh(false, true);
-
- runInAndVerifyIgnoreOutput("co", myMainUrl);
- final File sourceDir = new File(myWorkingCopyDir.getPath(), "source");
- final File innerDir = new File(sourceDir, "inner1/inner2/inner");
- runInAndVerifyIgnoreOutput("co", myExternalURL, innerDir.getPath());
- sleep(100);
- myWorkingCopyDir.refresh(false, true);
- // above is preparation
-
- // start change list manager again
- clManager.forceGoInTestMode();
- refreshSvnMappingsSynchronously();
- //clManager.ensureUpToDate(false);
- //clManager.ensureUpToDate(false);
+ protected void prepareInnerCopy() throws Exception {
+ prepareInnerCopy(false);
}
@Test
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java
new file mode 100644
index 000000000000..61aca5670867
--- /dev/null
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java
@@ -0,0 +1,630 @@
+/*
+ * 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;
+
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vcs.*;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.CurrentContentRevision;
+import com.intellij.openapi.vcs.update.SequentialUpdatesContext;
+import com.intellij.openapi.vcs.update.UpdateSession;
+import com.intellij.openapi.vcs.update.UpdatedFiles;
+import com.intellij.openapi.vfs.LocalFileSystem;
+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.checkout.SvnCheckoutProvider;
+import org.junit.Before;
+import org.tmatesoft.svn.core.*;
+import org.tmatesoft.svn.core.auth.*;
+import org.tmatesoft.svn.core.io.SVNRepository;
+import org.tmatesoft.svn.core.wc.SVNRevision;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/4/13
+ * Time: 9:55 PM
+ */
+public class SvnNativeClientAuthTest extends Svn17TestCase {
+ private SvnVcs myVcs;
+ private int myCertificateAnswer = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY;
+ private boolean myCredentialsCorrect = true;
+ private boolean mySaveCredentials = false;
+ private boolean myCancelAuth = false;
+
+ private String outHttpUser = "test";
+ private String outHttpPassword = "test";
+
+ private final static String ourHTTP_URL = "http://svnsrvtest/stuff/autoTest";
+ private final static String ourHTTPS_URL = "https://svnsrvtest:443/TestSSL/autoTest";
+
+ private int myCertificateAskedInteractivelyCount = 0;
+ private int myCredentialsAskedInteractivelyCount = 0;
+
+ private int myExpectedCreds = 0;
+ private int myExpectedCert = 0;
+ private boolean myIsSecure;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ final File certFile = new File(myPluginRoot, myTestDataDir + "/svn/____.pfx");
+ setNativeAcceleration(true);
+ myVcs = SvnVcs.getInstance(myProject);
+ // replace authentication provider so that pass credentials without dialogs
+ final SvnConfiguration configuration = SvnConfiguration.getInstance(myProject);
+ final File svnconfig = FileUtil.createTempDirectory("svnconfig", "");
+ configuration.setConfigurationDirParameters(false, svnconfig.getPath());
+
+ final SvnAuthenticationManager interactiveManager = configuration.getInteractiveManager(myVcs);
+ final SvnTestInteractiveAuthentication authentication = new SvnTestInteractiveAuthentication(interactiveManager) {
+ @Override
+ public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) {
+ ++ myCertificateAskedInteractivelyCount;
+ return myCertificateAnswer;
+ }
+
+ @Override
+ public SVNAuthentication requestClientAuthentication(String kind,
+ SVNURL url,
+ String realm,
+ SVNErrorMessage errorMessage,
+ SVNAuthentication previousAuth,
+ boolean authMayBeStored) {
+ if (myCancelAuth) return null;
+ return super.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
+ }
+ };
+ interactiveManager.setAuthenticationProvider(authentication);
+
+ final SvnAuthenticationManager manager = configuration.getAuthenticationManager(myVcs);
+ // will be the same as in interactive -> authentication notifier is not used
+ manager.setAuthenticationProvider(authentication);
+
+ authentication.addAuthentication(ISVNAuthenticationManager.PASSWORD,
+ new Convertor<SVNURL, SVNAuthentication>() {
+ @Override
+ public SVNAuthentication convert(SVNURL o) {
+ ++ myCredentialsAskedInteractivelyCount;
+ if (myCancelAuth) return null;
+ if (myCredentialsCorrect) {
+ return new SVNPasswordAuthentication(outHttpUser, outHttpPassword, mySaveCredentials, o, false);
+ } else {
+ myCredentialsCorrect = true;// only once
+ return new SVNPasswordAuthentication("1234214 23 4234", "324324", mySaveCredentials, o, false);
+ }
+ }
+ });
+ authentication.addAuthentication(ISVNAuthenticationManager.SSL,
+ new Convertor<SVNURL, SVNAuthentication>() {
+ @Override
+ public SVNAuthentication convert(SVNURL o) {
+ ++ myCredentialsAskedInteractivelyCount;
+ if (myCancelAuth) return null;
+ if (myCredentialsCorrect) {
+ return new SVNSSLAuthentication(certFile, "12345", mySaveCredentials, o, false);
+ } else {
+ myCredentialsCorrect = true;// only once
+ return new SVNSSLAuthentication(new File("1232432423"), "3245321532534235445", mySaveCredentials, o, false);
+ }
+ }
+ });
+ myCertificateAskedInteractivelyCount = 0;
+ myCredentialsAskedInteractivelyCount = 0;
+ }
+
+ @Test
+ public void testTmpHttpUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testTmpSSLUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testPermanentSSLUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = true;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testPermanentHttpUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = true;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testTmpHttpCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testTmpSSLCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testPermanentSSLCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = true;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void test2PermanentSSLCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = true;
+ myCertificateAnswer = ISVNAuthenticationProvider.ACCEPTED;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testMixedSSLCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCertificateAnswer = ISVNAuthenticationProvider.ACCEPTED;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ //------------
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+ mySaveCredentials = true;
+ myCertificateAnswer = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY;
+
+ testCommitImpl(wc1);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ testCommitImpl(wc1);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testPermanentHttpCommit() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = true;
+
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ testCommitImpl(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testFailedThenSuccessTmpHttpUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCredentialsCorrect = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testFailedThenSuccessTmpSSLUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCredentialsCorrect = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ // credentials wrong, but certificate was ok accepted
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testCertificateRejectedThenCredentialsFailedThenSuccessTmpSSLUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCertificateAnswer = ISVNAuthenticationProvider.REJECTED;
+
+ updateExpectAuthCanceled(wc1, "Authentication canceled");
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ myCertificateAnswer = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY;
+ myCredentialsCorrect = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ // credentials wrong, but certificate was ok accepted
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testCanceledThenFailedThenSuccessTmpHttpUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTP_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCredentialsCorrect = false;
+ myCancelAuth = true;
+ updateExpectAuthCanceled(wc1, "Authentication canceled");
+ myCancelAuth = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ }
+
+ @Test
+ public void testCanceledThenFailedThenSuccessTmpSSLUpdate() throws Exception {
+ final File wc1 = testCheckoutImpl(ourHTTPS_URL);
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ final SvnConfiguration instance = SvnConfiguration.getInstance(myProject);
+ instance.clearAuthenticationDirectory(myProject);
+ instance.clearRuntimeStorage();
+
+ Assert.assertEquals(SvnConfiguration.UseAcceleration.commandLine, instance.myUseAcceleration);
+ mySaveCredentials = false;
+ myCredentialsCorrect = false;
+ myCancelAuth = true;
+ updateExpectAuthCanceled(wc1, "Authentication canceled");
+ myCancelAuth = false;
+
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+
+ // credentials are cached now only
+ updateSimple(wc1);
+
+ //Assert.assertEquals(myExpectedCreds, myCredentialsAskedInteractivelyCount);
+ //Assert.assertEquals(myExpectedCert, myCertificateAskedInteractivelyCount);
+ }
+
+ private File testCommitImpl(File wc1) throws IOException {
+ Assert.assertTrue(wc1.isDirectory());
+ final File file = FileUtil.createTempFile(wc1, "file", ".txt");
+ final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
+ Assert.assertNotNull(vf);
+ final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
+ files.add(vf);
+ final List<VcsException> exceptions = myVcs.getCheckinEnvironment().scheduleUnversionedFilesForAddition(files);
+ Assert.assertTrue(exceptions.isEmpty());
+
+ final Change change = new Change(null, new CurrentContentRevision(new FilePathImpl(vf)));
+ final List<VcsException> commit = myVcs.getCheckinEnvironment().commit(Collections.singletonList(change), "commit");
+ Assert.assertTrue(commit.isEmpty());
+ ++ myExpectedCreds;
+ ++ myExpectedCert;
+ return file;
+ }
+
+ private File testCheckoutImpl(final String url) throws IOException {
+ final File root = FileUtil.createTempDirectory("checkoutRoot", "");
+ root.deleteOnExit();
+ Assert.assertTrue(root.exists());
+ SvnCheckoutProvider
+ .checkout(myProject, root, url, SVNRevision.HEAD, SVNDepth.INFINITY, false, new CheckoutProvider.Listener() {
+ @Override
+ public void directoryCheckedOut(File directory, VcsKey vcs) {
+ }
+
+ @Override
+ public void checkoutCompleted() {
+ }
+ }, WorkingCopyFormat.ONE_DOT_SEVEN);
+ final int[] cnt = new int[1];
+ cnt[0] = 0;
+ FileUtil.processFilesRecursively(root, new Processor<File>() {
+ @Override
+ public boolean process(File file) {
+ ++ cnt[0];
+ return ! (cnt[0] > 1);
+ }
+ });
+ Assert.assertTrue(cnt[0] > 1);
+ myIsSecure = url.contains("https:");
+ if (myIsSecure) {
+ ++ myExpectedCreds;
+ ++ myExpectedCert;
+ }
+ ProjectLevelVcsManager.getInstance(myProject).setDirectoryMapping(root.getPath(), SvnVcs.VCS_NAME);
+ refreshSvnMappingsSynchronously();
+ return root;
+ }
+
+ private void updateExpectAuthCanceled(File wc1, String expectedText) {
+ Assert.assertTrue(wc1.isDirectory());
+ final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(wc1);
+ final UpdatedFiles files = UpdatedFiles.create();
+ final UpdateSession session =
+ myVcs.getUpdateEnvironment().updateDirectories(new FilePath[]{new FilePathImpl(vf)}, files, new EmptyProgressIndicator(),
+ new Ref<SequentialUpdatesContext>());
+ Assert.assertTrue(session.getExceptions() != null && ! session.getExceptions().isEmpty());
+ Assert.assertTrue(!session.isCanceled());
+ Assert.assertTrue(session.getExceptions().get(0).getMessage().contains(expectedText));
+
+ if (myIsSecure) {
+ ++ myExpectedCreds;
+ ++ myExpectedCert;
+ }
+ }
+
+ private void updateSimple(File wc1) {
+ Assert.assertTrue(wc1.isDirectory());
+ final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(wc1);
+ final UpdatedFiles files = UpdatedFiles.create();
+ final UpdateSession session =
+ myVcs.getUpdateEnvironment().updateDirectories(new FilePath[]{new FilePathImpl(vf)}, files, new EmptyProgressIndicator(),
+ new Ref<SequentialUpdatesContext>());
+ Assert.assertTrue(session.getExceptions() == null || session.getExceptions().isEmpty());
+ Assert.assertTrue(!session.isCanceled());
+ if (myIsSecure) {
+ ++ myExpectedCreds;
+ ++ myExpectedCert;
+ }
+ }
+
+ private void testBrowseRepositoryImpl(final String url) throws SVNException {
+ final List<SVNDirEntry> list = new ArrayList<SVNDirEntry>();
+ final SVNRepository repository = myVcs.createRepository(url);
+ repository.getDir(".", -1, null, new ISVNDirEntryHandler() {
+ @Override
+ public void handleDirEntry(SVNDirEntry dirEntry) throws SVNException {
+ list.add(dirEntry);
+ }
+ });
+
+ Assert.assertTrue(!list.isEmpty());
+ }
+
+ private static @interface Test {}
+}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java
index 3870bd3c6142..5bf5d7651f94 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java
@@ -611,7 +611,8 @@ public class SvnParseCommandLineParseTest extends TestCase {
}
private String changePathsIfNix(String s) {
- s = StringUtil.replace(s, "\\", "/");
+ if (SystemInfo.isWindows) return s;
+ s = FileUtil.toSystemIndependentName(s);
return StringUtil.replace(s, "C:/", LINUX_ROOT);
}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java
new file mode 100644
index 000000000000..d7b48df7b479
--- /dev/null
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnQuickMergeTest.java
@@ -0,0 +1,510 @@
+/*
+ * 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;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vcs.FileStatus;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
+import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PairConsumer;
+import com.intellij.util.SmartList;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.util.continuation.ContinuationContext;
+import com.intellij.util.continuation.TaskDescriptor;
+import com.intellij.util.continuation.Where;
+import junit.framework.Assert;
+import org.jetbrains.annotations.NotNull;
+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.integrate.SvnBranchItem;
+import org.jetbrains.idea.svn.mergeinfo.MergeChecker;
+import org.junit.Before;
+import org.junit.Test;
+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 java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 3/27/13
+ * Time: 12:58 PM
+ */
+public class SvnQuickMergeTest extends Svn17TestCase {
+ private SvnVcs myVcs;
+ private String myBranchUrl;
+ private File myBranchRoot;
+ private VirtualFile myBranchVf;
+ private SubTree myBranchTree;
+ private ChangeListManager myChangeListManager;
+ private SvnTestCase.SubTree myTree;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ myVcs = SvnVcs.getInstance(myProject);
+ myChangeListManager = ChangeListManager.getInstance(myProject);
+ myBranchUrl = prepareBranchesStructure();
+ myBranchRoot = new File(myTempDirFixture.getTempDirPath(), "b1");
+
+ runInAndVerifyIgnoreOutput("co", myBranchUrl, myBranchRoot.getPath());
+ Assert.assertTrue(myBranchRoot.exists());
+ myBranchVf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myBranchRoot);
+ Assert.assertNotNull(myBranchVf);
+
+ myBranchTree = new SubTree(myBranchVf);
+ myTree = new SubTree(myWorkingCopyDir);
+
+ final SvnBranchConfigurationManager branchConfigurationManager = SvnBranchConfigurationManager.getInstance(myProject);
+ final SvnBranchConfigurationNew configuration = new SvnBranchConfigurationNew();
+ configuration.setTrunkUrl(myRepoUrl + "/trunk");
+ configuration.addBranches(myRepoUrl + "/branches",
+ new InfoStorage<List<SvnBranchItem>>(new ArrayList<SvnBranchItem>(), InfoReliability.empty));
+ branchConfigurationManager.setConfiguration(myWorkingCopyDir, configuration);
+
+ //((ApplicationImpl) ApplicationManager.getApplication()).setRunPooledInTest(true);
+
+ runInAndVerifyIgnoreOutput(new File(myWorkingCopyDir.getPath()), "up");
+ Thread.sleep(10);
+ }
+
+ @Test
+ public void testSimpleMergeAllFromB1ToTrunk() throws Exception {
+ 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);
+ // by default merges all
+ final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() {
+ @Override
+ public boolean shouldReintegrate(@NotNull String sourceUrl, @NotNull String targetUrl) {
+ return true;
+ }
+ };
+ final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ quickMerge.execute(testInteraction, descriptor);
+ }
+ });
+ descriptor.waitForCompletion();
+ testInteraction.throwIfExceptions();
+
+ Assert.assertTrue(descriptor.isCompleted());
+
+ VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ // should have changed svn:mergeinfo on wc root and s1 file
+ final Change fileChange = myChangeListManager.getChange(myTree.myS1File);
+ Assert.assertNotNull(fileChange);
+ Assert.assertEquals(FileStatus.MODIFIED, fileChange.getFileStatus());
+
+ final Change dirChange = myChangeListManager.getChange(myWorkingCopyDir);
+ Assert.assertNotNull(dirChange);
+ Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus());
+ }
+
+ // if we create branches like this:
+ // trunk -> b1, b1->b2, b2->b3, b1->b4, then we should be able to merge between b1 and b2. some time before we had bug with it
+ @Test
+ public void testMergeBetweenDifferentTimeCreatedBranches() throws Exception {
+ // b1 -> b2
+ runInAndVerifyIgnoreOutput("copy", "-q", "-m", "copy1", myBranchUrl, myRepoUrl + "/branches/b2");
+ // b2 -> b3
+ runInAndVerifyIgnoreOutput("copy", "-q", "-m", "copy1", myRepoUrl + "/branches/b2", myRepoUrl + "/branches/b3");
+ // b1 -> b4
+ runInAndVerifyIgnoreOutput("copy", "-q", "-m", "copy1", myBranchUrl, myRepoUrl + "/branches/b4");
+
+ testSimpleMergeAllFromB1ToTrunk();
+ }
+
+ @Test
+ public void testSelectRevisionsWithQuickSelectCheckForLocalChanges() throws Exception {
+ // get revision #
+ final SVNInfo info = myVcs.createWCClient().doInfo(new File(myBranchTree.myS1File.getPath()), SVNRevision.WORKING);
+ Assert.assertNotNull(info);
+
+ final long numberBefore = info.getRevision().getNumber();
+ final int totalChanges = 3;
+
+ final StringBuilder sb = new StringBuilder(FileUtil.loadFile(new File(myBranchTree.myS1File.getPath())));
+ for (int i = 0; i < totalChanges; i++) {
+ sb.append("\nedited in branch ").append(i);
+ editFileInCommand(myProject, myBranchTree.myS1File, sb.toString());
+ runInAndVerifyIgnoreOutput(myBranchRoot, "ci", "-m", "change in branch " + i, myBranchTree.myS1File.getPath());
+ Thread.sleep(10);
+ }
+
+ // 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);
+ // by default merges all
+ final AtomicReference<String> selectionError = new AtomicReference<String>();
+ final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() {
+ @Override
+ public boolean shouldReintegrate(@NotNull String sourceUrl, @NotNull String targetUrl) {
+ return true;
+ }
+
+ @Override
+ public List<CommittedChangeList> showRecentListsForSelection(@NotNull List<CommittedChangeList> list,
+ @NotNull String mergeTitle,
+ @NotNull MergeChecker mergeChecker,
+ @NotNull PairConsumer<Long, MergeDialogI> loader,
+ boolean everyThingLoaded) {
+ if (list.size() != 4) {
+ selectionError.set("List size: " + list.size());
+ } else if (list.get(3).getNumber() != numberBefore) {
+ selectionError.set("wrong revision for copy statement: " + list.get(3).getNumber());
+ }
+ return new SmartList<CommittedChangeList>(list.get(2)); // get a change
+ }
+ };
+ testInteraction.setMergeVariant(QuickMergeContentsVariants.showLatest);
+ final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ quickMerge.execute(testInteraction, descriptor);
+ }
+ });
+ descriptor.waitForCompletion();
+ testInteraction.throwIfExceptions();
+ if (selectionError.get() != null){
+ throw new RuntimeException(selectionError.get());
+ }
+
+ Assert.assertTrue(descriptor.isCompleted());
+
+ VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ // should have changed svn:mergeinfo on wc root and s1 file
+ final Change fileChange = myChangeListManager.getChange(myTree.myS1File);
+ Assert.assertNotNull(fileChange);
+ Assert.assertEquals(FileStatus.MODIFIED, fileChange.getFileStatus());
+
+ final Change dirChange = myChangeListManager.getChange(myWorkingCopyDir);
+ Assert.assertNotNull(dirChange);
+ Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus());
+
+ final SVNPropertyData data = myVcs.createWCClient()
+ .doGetProperty(new File(myWorkingCopyDir.getPath()), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING);
+ System.out.println(data.getValue().getString());
+ Assert.assertEquals("/branches/b1:" + (numberBefore + 1), data.getValue().getString());
+ }
+
+ // this test is mainly to check revisions selection. at the moment we are not sure whether we support
+ // trunk->b1->b2 merges between trunk and b2
+ @Test
+ public void testSelectRevisionsWithQuickSelect() throws Exception {
+ // get revision #
+ final SVNInfo info = myVcs.createWCClient().doInfo(new File(myBranchTree.myS1File.getPath()), SVNRevision.WORKING);
+ Assert.assertNotNull(info);
+
+ final long numberBefore = info.getRevision().getNumber();
+ final int totalChanges = 3;
+
+ final StringBuilder sb = new StringBuilder(FileUtil.loadFile(new File(myBranchTree.myS1File.getPath())));
+ for (int i = 0; i < totalChanges; i++) {
+ sb.append("\nedited in branch ").append(i);
+ editFileInCommand(myProject, myBranchTree.myS1File, sb.toString());
+ runInAndVerifyIgnoreOutput(myBranchRoot, "ci", "-m", "change in branch " + i, myBranchTree.myS1File.getPath());
+ Thread.sleep(10);
+ }
+
+ // before copy
+ final SVNInfo info2 = myVcs.createWCClient().doInfo(new File(myBranchTree.myS1File.getPath()), SVNRevision.WORKING);
+ Assert.assertNotNull(info2);
+ final long numberBeforeCopy = info2.getRevision().getNumber();
+
+ runInAndVerifyIgnoreOutput("copy", "-q", "-m", "copy1", myBranchUrl, myRepoUrl + "/branches/b2");
+
+ // switch b1 to b2
+ runInAndVerifyIgnoreOutput(myBranchRoot, "switch", myRepoUrl + "/branches/b2", myBranchRoot.getPath());
+ myBranchTree = new SubTree(myBranchVf); //reload
+
+ // one commit in b2 in s2 file
+ editFileInCommand(myProject, myBranchTree.myS2File, "completely changed");
+ 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);
+ // by default merges all
+ final AtomicReference<String> selectionError = new AtomicReference<String>();
+ final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() {
+ @Override
+ public boolean shouldReintegrate(@NotNull String sourceUrl, @NotNull String targetUrl) {
+ return true;
+ }
+
+ @Override
+ public List<CommittedChangeList> showRecentListsForSelection(@NotNull List<CommittedChangeList> list,
+ @NotNull String mergeTitle,
+ @NotNull MergeChecker mergeChecker,
+ @NotNull PairConsumer<Long, MergeDialogI> loader,
+ boolean everyThingLoaded) {
+ if (list.size() != 2) {
+ selectionError.set("List size: " + list.size());
+ } else if (list.get(1).getNumber() != numberBeforeCopy + 1) {
+ selectionError.set("wrong revision for copy statement: " + list.get(1).getNumber());
+ }
+ return new SmartList<CommittedChangeList>(list.get(0)); // get a change
+ }
+ };
+ testInteraction.setMergeVariant(QuickMergeContentsVariants.showLatest);
+ final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ quickMerge.execute(testInteraction, descriptor);
+ }
+ });
+ descriptor.waitForCompletion();
+ testInteraction.throwIfExceptions();
+ if (selectionError.get() != null){
+ throw new RuntimeException(selectionError.get());
+ }
+
+ Assert.assertTrue(descriptor.isCompleted());
+
+ VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ // should have changed svn:mergeinfo on wc root and s1 file
+ final Change fileChange = myChangeListManager.getChange(myTree.myS2File);
+ Assert.assertNotNull(fileChange);
+ Assert.assertEquals(FileStatus.MODIFIED, fileChange.getFileStatus());
+
+ final Change dirChange = myChangeListManager.getChange(myWorkingCopyDir);
+ Assert.assertNotNull(dirChange);
+ Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus());
+
+ final SVNPropertyData data = myVcs.createWCClient()
+ .doGetProperty(new File(myWorkingCopyDir.getPath()), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING);
+ System.out.println(data.getValue().getString());
+ Assert.assertEquals("/branches/b2:" + (numberBeforeCopy + 2), data.getValue().getString());
+ }
+
+ @Test
+ public void testSelectRevisions() throws Exception {
+ // get revision #
+ final SVNInfo info = myVcs.createWCClient().doInfo(new File(myBranchTree.myS1File.getPath()), SVNRevision.WORKING);
+ Assert.assertNotNull(info);
+
+ final long numberBefore = info.getRevision().getNumber();
+ final int totalChanges = 10;
+
+ final StringBuilder sb = new StringBuilder(FileUtil.loadFile(new File(myBranchTree.myS1File.getPath())));
+ for (int i = 0; i < totalChanges; i++) {
+ sb.append("\nedited in branch ").append(i);
+ editFileInCommand(myProject, myBranchTree.myS1File, sb.toString());
+ runInAndVerifyIgnoreOutput(myBranchRoot, "ci", "-m", "change in branch " + i, myBranchTree.myS1File.getPath());
+ Thread.sleep(10);
+ }
+
+ final WCInfo found = getWcInfo();
+ final QuickMerge quickMerge =
+ new QuickMerge(myProject, myBranchUrl, found, SVNPathUtil.tail(myBranchUrl), myWorkingCopyDir);
+ // by default merges all
+ final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction() {
+ @Override
+ public boolean shouldReintegrate(@NotNull String sourceUrl, @NotNull String targetUrl) {
+ return true;
+ }
+
+ @NotNull
+ @Override
+ public SelectMergeItemsResult selectMergeItems(final List<CommittedChangeList> lists,
+ String mergeTitle,
+ MergeChecker mergeChecker) {
+ return new SelectMergeItemsResult() {
+ @Override
+ public QuickMergeContentsVariants getResultCode() {
+ return QuickMergeContentsVariants.select;
+ }
+
+ @Override
+ public List<CommittedChangeList> getSelectedLists() {
+ final List<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
+ for (CommittedChangeList list : lists) {
+ if (numberBefore + 1 == list.getNumber() || numberBefore + 2 == list.getNumber()) {
+ result.add(list);
+ }
+ }
+ return result;
+ }
+ };
+ }
+ };
+ testInteraction.setMergeVariant(QuickMergeContentsVariants.select);
+ final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ quickMerge.execute(testInteraction, descriptor);
+ }
+ });
+ descriptor.waitForCompletion();
+ testInteraction.throwIfExceptions();
+
+ Assert.assertTrue(descriptor.isCompleted());
+
+ VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ // should have changed svn:mergeinfo on wc root and s1 file
+ final Change fileChange = myChangeListManager.getChange(myTree.myS1File);
+ Assert.assertNotNull(fileChange);
+ Assert.assertEquals(FileStatus.MODIFIED, fileChange.getFileStatus());
+
+ final Change dirChange = myChangeListManager.getChange(myWorkingCopyDir);
+ Assert.assertNotNull(dirChange);
+ Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus());
+
+ final SVNPropertyData data = myVcs.createWCClient()
+ .doGetProperty(new File(myWorkingCopyDir.getPath()), "svn:mergeinfo", SVNRevision.UNDEFINED, SVNRevision.WORKING);
+ System.out.println(data.getValue().getString());
+ Assert.assertEquals("/branches/b1:" + (numberBefore + 1) + "-" + (numberBefore + 2), data.getValue().getString());
+ }
+
+ private WCInfo getWcInfo() {
+ WCInfo found = null;
+ final File workingIoFile = new File(myWorkingCopyDir.getPath());
+ final List<WCInfo> infos = myVcs.getAllWcInfos();
+ for (WCInfo info : infos) {
+ if (FileUtil.filesEqual(workingIoFile, new File(info.getPath()))) {
+ found = info;
+ break;
+ }
+ }
+ Assert.assertNotNull(found);
+ return found;
+ }
+
+ @Test
+ public void testSimpleMergeFromTrunkToB1() throws Exception {
+ // change in trunk
+ editFileInCommand(myProject, myTree.myS1File, "903403240328");
+ final File workingIoFile = new File(myWorkingCopyDir.getPath());
+ runInAndVerifyIgnoreOutput(workingIoFile, "ci", "-m", "change in trunk", myTree.myS1File.getPath());
+
+ final String trunkUrl = myRepoUrl + "/trunk";
+ // switch this copy to b1
+ runInAndVerifyIgnoreOutput(workingIoFile, "switch", myBranchUrl, workingIoFile.getPath());
+ myTree = new SubTree(myWorkingCopyDir); //reload
+
+ refreshSvnMappingsSynchronously();
+ final WCInfo found = getWcInfo();
+ final QuickMerge quickMerge =
+ new QuickMerge(myProject, trunkUrl, found, SVNPathUtil.tail(trunkUrl), myWorkingCopyDir);
+ // by default merges all
+ final QuickMergeTestInteraction testInteraction = new QuickMergeTestInteraction();
+ final WaitingTaskDescriptor descriptor = new WaitingTaskDescriptor();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ quickMerge.execute(testInteraction, descriptor);
+ }
+ });
+ descriptor.waitForCompletion();
+ testInteraction.throwIfExceptions();
+
+ Assert.assertTrue(descriptor.isCompleted());
+
+ VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ // should have changed svn:mergeinfo on wc root and s1 file
+ final Change fileChange = myChangeListManager.getChange(myTree.myS1File);
+ Assert.assertNotNull(fileChange);
+ Assert.assertEquals(FileStatus.MODIFIED, fileChange.getFileStatus());
+
+ final Change dirChange = myChangeListManager.getChange(myWorkingCopyDir);
+ Assert.assertNotNull(dirChange);
+ Assert.assertEquals(FileStatus.MODIFIED, dirChange.getFileStatus());
+ }
+
+ private static class WaitingTaskDescriptor extends TaskDescriptor {
+ private static final long TEST_TIMEOUT = TimeUnit.MINUTES.toMillis(200);
+ private final Semaphore mySemaphore;
+ private volatile boolean myCompleted = false;
+ private volatile boolean myCanceled = false;
+
+ public WaitingTaskDescriptor() {
+ super("waiting", Where.POOLED);
+ mySemaphore = new Semaphore();
+ mySemaphore.down();
+ }
+
+ // will survive in Continuation if cancel occurred
+ @Override
+ public boolean isHaveMagicCure() {
+ return true;
+ }
+
+ @Override
+ public void run(ContinuationContext context) {
+ myCompleted = true;
+ mySemaphore.up();
+ }
+
+ public void waitForCompletion() {
+ mySemaphore.waitFor(TEST_TIMEOUT);
+ }
+
+ @Override
+ public void canceled() {
+ myCanceled = true;
+ mySemaphore.up();
+ }
+
+ private boolean isCompleted() {
+ return myCompleted;
+ }
+
+ private boolean isCanceled() {
+ return myCanceled;
+ }
+ }
+}
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnRenameTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnRenameTest.java
index 486d97823815..c86ed0cc04aa 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnRenameTest.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnRenameTest.java
@@ -284,6 +284,7 @@ public class SvnRenameTest extends Svn17TestCase {
// IDEA-13824
@Test
public void testRenameFileRenameDir() throws Exception {
+ setNativeAcceleration(true); //todo debug
final VirtualFile child = prepareDirectoriesForRename();
final VirtualFile f = child.findChild("a.txt");
renameFileInCommand(f, "anew.txt");
diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnExternalCommitNoticedTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnExternalCommitNoticedTest.java
index 079ca66955d8..b686c94df7e4 100644
--- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnExternalCommitNoticedTest.java
+++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnExternalCommitNoticedTest.java
@@ -121,6 +121,7 @@ public class SvnExternalCommitNoticedTest extends Svn17TestCase {
runInAndVerifyIgnoreOutput("switch", branchUrl + "/root/source/s1.txt", tree.myS1File.getPath());
runInAndVerifyIgnoreOutput("switch", branchUrl + "/root/target", tree.myTargetDir.getPath());
+ sleep(50);
myWorkingCopyDir.refresh(false, true);
imitateEvent(myWorkingCopyDir);
// no dirty scope externally provided! just VFS refresh