summaryrefslogtreecommitdiff
path: root/plugins/svn4idea/bindSvn/src/org/jetbrains/idea
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2013-04-01 14:41:51 -0700
committerJean-Baptiste Queru <jbq@google.com>2013-04-01 14:41:51 -0700
commit2bd2b7c2623d4266384e890271869efc044aabff (patch)
tree0b31f50e55975b6354ed458314e17b4441bb4e17 /plugins/svn4idea/bindSvn/src/org/jetbrains/idea
parent1d526b16d476792ca7ce47616d55833115e8d6ab (diff)
downloadidea-2bd2b7c2623d4266384e890271869efc044aabff.tar.gz
Snapshot ee98b298267d0e09d2cd2f0731b6480a56dd48e7 from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: I4515f72af131fdea9fc6905a4dc0fe9532409a81
Diffstat (limited to 'plugins/svn4idea/bindSvn/src/org/jetbrains/idea')
-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.java221
-rw-r--r--plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java47
-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.java106
-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
13 files changed, 2438 insertions, 0 deletions
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/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
new file mode 100644
index 000000000000..a1e358b6f4a0
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
@@ -0,0 +1,221 @@
+/*
+ * 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.configurations.GeneralCommandLine;
+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.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.annotations.Nullable;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 12:58 PM
+ */
+public abstract class SvnCommand {
+ static final Logger LOG = Logger.getInstance(SvnCommand.class.getName());
+
+ private boolean myIsDestroyed;
+ private int myExitCode;
+ protected final GeneralCommandLine myCommandLine;
+ private final File myWorkingDirectory;
+ private Process myProcess;
+ private OSProcessHandler myHandler;
+ private final Object myLock;
+
+ private final EventDispatcher<ProcessEventListener> myListeners = EventDispatcher.create(ProcessEventListener.class);
+
+ // todo check version
+ /*c:\Program Files (x86)\CollabNet\Subversion Client17>svn --version --quiet
+ 1.7.2*/
+
+ 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();
+ myCommandLine = new GeneralCommandLine();
+ myWorkingDirectory = workingDirectory;
+ myCommandLine.setExePath(exePath);
+ myCommandLine.setWorkDirectory(workingDirectory);
+ if (configDir != null) {
+ myCommandLine.addParameters("--config-dir", configDir.getPath());
+ }
+ myCommandLine.addParameter(commandName.getName());
+ }
+
+ public void start() {
+ synchronized (myLock) {
+ checkNotStarted();
+
+ try {
+ myProcess = myCommandLine.createProcess();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(myCommandLine.toString());
+ }
+ myHandler = new OSProcessHandler(myProcess, myCommandLine.getCommandLineString());
+ startHandlingStreams();
+ } catch (Throwable t) {
+ myListeners.getMulticaster().startFailed(t);
+ }
+ }
+ }
+
+ private void startHandlingStreams() {
+ final ProcessListener processListener = new ProcessListener() {
+ public void startNotified(final ProcessEvent event) {
+ // do nothing
+ }
+
+ public void processTerminated(final ProcessEvent event) {
+ final int exitCode = event.getExitCode();
+ try {
+ setExitCode(exitCode);
+ SvnCommand.this.processTerminated(exitCode);
+ } finally {
+ listeners().processTerminated(exitCode);
+ }
+ }
+
+ public void processWillTerminate(final ProcessEvent event, final boolean willBeDestroyed) {
+ // do nothing
+ }
+
+ public void onTextAvailable(final ProcessEvent event, final Key outputType) {
+ SvnCommand.this.onTextAvailable(event.getText(), outputType);
+ }
+ };
+
+ myHandler.addProcessListener(processListener);
+ myHandler.startNotify();
+ }
+
+ /**
+ * Wait for process termination
+ */
+ public void waitFor() {
+ checkStarted();
+ final OSProcessHandler handler;
+ synchronized (myLock) {
+ if (myIsDestroyed) return;
+ handler = myHandler;
+ }
+ handler.waitFor();
+ }
+
+ protected abstract void processTerminated(int exitCode);
+ protected abstract void onTextAvailable(final String text, final Key outputType);
+
+ public void cancel() {
+ synchronized (myLock) {
+ checkStarted();
+ destroyProcess();
+ }
+ }
+
+ protected void setExitCode(final int code) {
+ synchronized (myLock) {
+ myExitCode = code;
+ }
+ }
+
+ public void addListener(final ProcessEventListener listener) {
+ synchronized (myLock) {
+ myListeners.addListener(listener);
+ }
+ }
+
+ protected ProcessEventListener listeners() {
+ synchronized (myLock) {
+ return myListeners.getMulticaster();
+ }
+ }
+
+ public void addParameters(@NonNls @NotNull String... parameters) {
+ synchronized (myLock) {
+ checkNotStarted();
+ myCommandLine.addParameters(parameters);
+ }
+ }
+
+ public void addParameters(List<String> parameters) {
+ synchronized (myLock) {
+ checkNotStarted();
+ myCommandLine.addParameters(parameters);
+ }
+ }
+
+ public void destroyProcess() {
+ synchronized (myLock) {
+ myIsDestroyed = true;
+ myHandler.destroyProcess();
+ }
+ }
+
+ /**
+ * check that process is not started yet
+ *
+ * @throws IllegalStateException if process has been already started
+ */
+ private void checkNotStarted() {
+ if (isStarted()) {
+ throw new IllegalStateException("The process has been already started");
+ }
+ }
+
+ /**
+ * check that process is started
+ *
+ * @throws IllegalStateException if process has not been started
+ */
+ protected void checkStarted() {
+ if (! isStarted()) {
+ throw new IllegalStateException("The process is not started yet");
+ }
+ }
+
+ /**
+ * @return true if process is started
+ */
+ public boolean isStarted() {
+ synchronized (myLock) {
+ return myProcess != null;
+ }
+ }
+
+ protected int getExitCode() {
+ synchronized (myLock) {
+ return myExitCode;
+ }
+ }
+
+ protected File getWorkingDirectory() {
+ return myWorkingDirectory;
+ }
+}
diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
new file mode 100644
index 000000000000..58c2ff2ab308
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.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.commandLine;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 1:49 PM
+ */
+public enum SvnCommandName {
+ 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, 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/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
new file mode 100644
index 000000000000..da5df9e6d299
--- /dev/null
+++ b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
@@ -0,0 +1,106 @@
+/*
+ * 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.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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 4:04 PM
+ */
+public class SvnSimpleCommand extends SvnCommand {
+ private final StringBuilder myStderr;
+ private final StringBuilder myStdout;
+ private VcsException myException;
+ private final Object myDataLock;
+
+ public SvnSimpleCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ super(workingDirectory, commandName, exePath);
+
+ myDataLock = new Object();
+ myStderr = new StringBuilder();
+ myStdout = new StringBuilder();
+ }
+
+ @Override
+ protected void processTerminated(int exitCode) {
+ //
+ }
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+ synchronized (myDataLock) {
+ if (ProcessOutputTypes.STDOUT.equals(outputType)) {
+ myStdout.append(text);
+ } else if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ myStderr.append(text);
+ }
+ }
+ }
+
+ public StringBuilder getStderr() {
+ synchronized (myDataLock) {
+ return myStderr;
+ }
+ }
+
+ public StringBuilder getStdout() {
+ synchronized (myDataLock) {
+ return myStdout;
+ }
+ }
+
+ public String run() throws VcsException {
+ addListener(new ProcessEventListener() {
+ @Override
+ public void processTerminated(int exitCode) {
+ }
+
+ @Override
+ public void startFailed(Throwable exception) {
+ synchronized (myDataLock) {
+ myException = new VcsException("Process failed to start (" + myCommandLine.getCommandLineString() + "): " + exception.toString(), exception);
+ }
+ }
+ });
+ start();
+ if (isStarted()) {//if wasn't started, exception is stored into a field, don't wait for process
+ waitFor();
+ }
+
+ synchronized (myDataLock) {
+ if (myException != null) throw myException;
+ final int code = getExitCode();
+ if (code == 0) {
+ return myStdout.toString();
+ } else {
+ 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);
+ }
+ }
+}