diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/ProxySvnAuthentication.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/ProxySvnAuthentication.java index 74b0a9e3bf31..25d49e231fa4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/ProxySvnAuthentication.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/ProxySvnAuthentication.java @@ -26,9 +26,15 @@ public class ProxySvnAuthentication { private ProxySvnAuthentication() { } - public static SVNAuthentication proxy(final SVNAuthentication in, final boolean storeAuth) { - if (in.isStorageAllowed() == storeAuth || (! in.isStorageAllowed())) return in; + public static SVNAuthentication proxy(final SVNAuthentication in, final boolean storeAuth, boolean forceSaving) { + if (forceSaving && storeAuth) { + return putPassedValueAsSave(in, forceSaving); + } + if (in.isStorageAllowed() == storeAuth || ( ! in.isStorageAllowed())) return in; + return putPassedValueAsSave(in, storeAuth); + } + private static SVNAuthentication putPassedValueAsSave(SVNAuthentication in, boolean storeAuth) { final String userName = in.getUserName(); if (in instanceof SVNPasswordAuthentication) { return new SVNPasswordAuthentication(userName, ((SVNPasswordAuthentication)in).getPassword(), diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationManager.java index f14dc7d2c1d0..71d7bd24a02d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationManager.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn17; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; @@ -67,6 +68,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im private IdeaSVNHostOptionsProvider myLocalHostOptionsProvider; private final ThreadLocalSavePermissions mySavePermissions; private final Map myKeyAlgorithm; + private boolean myArtificialSaving; public SvnAuthenticationManager(final Project project, final File configDirectory) { super(configDirectory, true, null, null); @@ -82,6 +84,10 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im myInteraction = new MySvnAuthenticationInteraction(myProject); } + public void setArtificialSaving(boolean artificialSaving) { + myArtificialSaving = artificialSaving; + } + private void ensureListenerCreated() { if (myListener == null) { myListener = EventDispatcher.create(SvnAuthenticationListener.class); @@ -115,6 +121,11 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im myListener.getMulticaster().saveAttemptFinished(type, url, realm, kind); } + @Override + public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + myListener.getMulticaster().acknowledge(accepted, kind, realm, message, authentication); + } + @Override public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { if (ProviderType.interactive.equals(type) && (! canceled)) { @@ -137,12 +148,18 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im String realm, SVNErrorMessage errorMessage, SVNAuthentication authentication) throws SVNException { + boolean successSaving = false; + myListener.getMulticaster().acknowledge(accepted, kind, realm, errorMessage, authentication); try { final boolean authStorageEnabled = getHostOptionsProvider().getHostOptions(authentication.getURL()).isAuthStorageEnabled(); - final SVNAuthentication proxy = ProxySvnAuthentication.proxy(authentication, authStorageEnabled); + final SVNAuthentication proxy = ProxySvnAuthentication.proxy(authentication, authStorageEnabled, myArtificialSaving); super.acknowledgeAuthentication(accepted, kind, realm, errorMessage, proxy); + successSaving = true; } finally { mySavePermissions.remove(); + if (myArtificialSaving) { + throw new CredentialsSavedException(successSaving); + } } } @@ -229,11 +246,10 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im public void saveAuthentication(final SVNAuthentication auth, final String kind, final String realm) throws SVNException { final Boolean fromInteractive = ourJustEntered.get(); ourJustEntered.set(null); - if (! Boolean.TRUE.equals(fromInteractive)) { + if (! myArtificialSaving && ! Boolean.TRUE.equals(fromInteractive)) { // not what user entered return; } - myListener.getMulticaster().saveAttemptStarted(ProviderType.persistent, auth.getURL(), realm, auth.getKind()); ((ISVNPersistentAuthenticationProvider) myDelegate).saveAuthentication(auth, kind, realm); myListener.getMulticaster().saveAttemptFinished(ProviderType.persistent, auth.getURL(), realm, auth.getKind()); @@ -822,4 +838,16 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im return s[0]; } } + + public static class CredentialsSavedException extends RuntimeException { + private final boolean mySuccess; + + public CredentialsSavedException(boolean success) { + mySuccess = success; + } + + public boolean isSuccess() { + return mySuccess; + } + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationNotifier.java index a494e24a237b..ffc2d4002b47 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/SvnAuthenticationNotifier.java @@ -178,7 +178,7 @@ public class SvnAuthenticationNotifier extends GenericNotifierImpl { return myOptions; } + public static SvnAuthenticationManager createForTmpDir(final Project project, final File dir) { + final SvnVcs17 vcs = SvnVcs17.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); + interactive.setAuthenticationProvider(interactiveProvider); + + //manager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, interactiveProvider, RUNTIME_AUTH_CACHE)); + //manager.setRuntimeStorage(RUNTIME_AUTH_CACHE); + return interactive; + } + public SvnAuthenticationManager getAuthenticationManager(final SvnVcs17 svnVcs) { if (myAuthManager == null) { // reloaded when configuration directory changes diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationInteraction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationInteraction.java index 568a72ff41d9..2d43bddd06a8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationInteraction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationInteraction.java @@ -15,7 +15,9 @@ */ package org.jetbrains.idea.svn17.auth; +import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.SVNAuthentication; import java.io.File; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationListener.java index dc556ec452e1..7f3558494b91 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/auth/SvnAuthenticationListener.java @@ -15,7 +15,9 @@ */ package org.jetbrains.idea.svn17.auth; +import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.SVNAuthentication; import java.util.EventListener; @@ -24,4 +26,5 @@ public interface SvnAuthenticationListener extends EventListener { void actualSaveWillBeTried(final ProviderType type, final SVNURL url, String realm, String kind); void saveAttemptStarted(final ProviderType type, final SVNURL url, String realm, String kind); void saveAttemptFinished(final ProviderType type, final SVNURL url, String realm, String kind); + void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/CommandLineAuthenticator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/CommandLineAuthenticator.java new file mode 100644 index 000000000000..1cffdfb64c79 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/CommandLineAuthenticator.java @@ -0,0 +1,200 @@ +/* + * 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.svn17.commandLine; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.Trinity; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import org.jetbrains.idea.svn17.SvnAuthenticationManager; +import org.jetbrains.idea.svn17.SvnConfiguration17; +import org.jetbrains.idea.svn17.SvnVcs17; +import org.jetbrains.idea.svn17.auth.ProviderType; +import org.jetbrains.idea.svn17.auth.SvnAuthenticationListener; +import org.tmatesoft.svn.core.SVNErrorCode; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.SVNAuthentication; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/1/12 + * Time: 12:28 PM + */ +public class CommandLineAuthenticator { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn17.commandLine.CommandLineAuthenticator"); + private final Project myProject; + private final AuthenticationRequiringCommand myCommand; + private final SvnConfiguration17 myConfiguration17; + private final File myConfigDir; + + public CommandLineAuthenticator(Project project, AuthenticationRequiringCommand command) { + myProject = project; + myCommand = command; + myConfiguration17 = SvnConfiguration17.getInstance(project); + final String configurationDirectory = myConfiguration17.getConfigurationDirectory(); + myConfigDir = new File(configurationDirectory); + } + + public void doWithAuthentication() throws SVNException { + try { + myCommand.run(myConfigDir); + return; + } catch (SVNException e) { + if (! e.getErrorMessage().getErrorCode().isAuthentication()) throw e; + } + File tempDirectory = null; + try { + tempDirectory = FileUtil.createTempDirectory("tmp", "Subversion"); + final SvnAuthenticationManager authenticationManager = SvnConfiguration17.createForTmpDir(myProject, tempDirectory); + //authenticationManager.setAuthenticationForced(true); + authenticationManager.setArtificialSaving(true); + myCommand.cleanup(); + tryGetCredentials(authenticationManager, tempDirectory); + + myCommand.cleanup(); + myCommand.run(tempDirectory); + } + catch (IOException e) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + } finally { + if (tempDirectory != null) { + FileUtil.delete(tempDirectory); + } + } + } + + private void tryGetCredentials(SvnAuthenticationManager manager, final File tempDirectory) throws SVNException { + final StoreListener storeListener = new StoreListener(); + manager.addListener(storeListener); + final SVNURL svnurl = myCommand.sampleUrl(); + try { + myCommand.runWithSvnkitClient(tempDirectory, manager); + LOG.assertTrue(false, "Credentials not asked"); // todo? + } catch (SvnAuthenticationManager.CredentialsSavedException e) { + // ok, check result? + if (e.isSuccess()) { + final SvnAuthenticationManager realManager = myConfiguration17.getAuthenticationManager(SvnVcs17.getInstance(myProject)); + storeListener.reStore(myProject, realManager, svnurl); + } + } + //final SVNWCClient client = new SVNWCClient(manager, myConfiguration17.getOptions(myProject)); + //client.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); + } + + public interface AuthenticationRequiringCommand { + void run(final File configDir) throws SVNException; + void runWithSvnkitClient(final File configDir, SvnAuthenticationManager manager) throws SVNException; + SVNURL sampleUrl(); + void cleanup() throws SVNException; + } + + private static class StoreListener implements SvnAuthenticationListener { + private final Set myData; + private final Set> myAuthRequested; + + private StoreListener() { + myData = new HashSet(); + myAuthRequested = new HashSet>(); + } + + @Override + public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { + if (ProviderType.interactive.equals(type)) { + myAuthRequested.add(create(kind, realm, url)); + } + } + + private Trinity create(String kind, String realm, SVNURL url) { + return new Trinity(kind, realm, url); + } + + @Override + public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) { + } + @Override + public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { + } + @Override + public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) { + } + @Override + public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + if (accepted && authentication.isStorageAllowed()) { + final Trinity trinity = create(kind, realm, authentication.getURL()); + if (myAuthRequested.contains(trinity)) { + myData.add(new StoreData(kind, realm, authentication)); + } + } + } + + public void reStore(final Project project, final SvnAuthenticationManager realManager, final SVNURL svnurl) { + for (StoreData data : myData) { + if (data.myAuthentication == null) continue; + realManager.requested(ProviderType.interactive, svnurl, data.myRealm, data.myKind, false); + try { + realManager.acknowledgeAuthentication(true, data.myKind, data.myRealm, null, data.myAuthentication); + } + catch (SVNException e) { + VcsBalloonProblemNotifier.showOverChangesView(project, "Wasn't able to store credentials: " + e.getMessage(), MessageType.ERROR); + } + } + } + } + + private static class StoreData { + public String myKind; + public String myRealm; + public SVNAuthentication myAuthentication; + + private StoreData(String kind, String realm, SVNAuthentication authentication) { + myKind = kind; + myRealm = realm; + myAuthentication = authentication; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + StoreData data = (StoreData)o; + + if (myKind != null ? !myKind.equals(data.myKind) : data.myKind != null) return false; + if (myRealm != null ? !myRealm.equals(data.myRealm) : data.myRealm != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myKind != null ? myKind.hashCode() : 0; + result = 31 * result + (myRealm != null ? myRealm.hashCode() : 0); + return result; + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/SvnCommandLineUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/SvnCommandLineUpdateClient.java new file mode 100644 index 000000000000..87f54e9b53eb --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/SvnCommandLineUpdateClient.java @@ -0,0 +1,270 @@ +/* + * 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.svn17.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.vfs.VirtualFile; +import com.intellij.util.concurrency.Semaphore; +import org.jetbrains.idea.svn17.SvnAuthenticationManager; +import org.jetbrains.idea.svn17.SvnConfiguration17; +import org.jetbrains.idea.svn17.SvnVcs17; +import org.jetbrains.idea.svn17.portable.SvnExceptionWrapper; +import org.jetbrains.idea.svn17.portable.SvnSvnkitUpdateClient; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.*; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/1/12 + * Time: 12:13 PM + */ +public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { + private static final Pattern ourExceptionPattern = Pattern.compile("svn: E(\\d{6}): .+"); + private static final String ourAuthenticationRealm = "Authentication realm:"; + private final Project myProject; + private final VirtualFile myCommonAncestor; + + public SvnCommandLineUpdateClient(final Project project, VirtualFile commonAncestor) { + super(SvnVcs17.getInstance(project).createUpdateClient()); + myProject = project; + myCommonAncestor = commonAncestor; + } + + @Override + public long doUpdate(File file, SVNRevision revision, boolean recursive) throws SVNException { + final long[] longs = doUpdate(new File[]{file}, revision, SVNDepth.fromRecurse(recursive), false, false, false); + return longs[0]; + } + + @Override + public long doUpdate(File file, SVNRevision revision, boolean recursive, boolean force) throws SVNException { + final long[] longs = doUpdate(new File[]{file}, revision, SVNDepth.fromRecurse(recursive), force, false, false); + return longs[0]; + } + + @Override + public long[] doUpdate(File[] paths, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) + throws SVNException { + return doUpdate(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, false); + } + + @Override + public long[] doUpdate(final File[] paths, final SVNRevision revision, final SVNDepth depth, final boolean allowUnversionedObstructions, + final boolean depthIsSticky, final boolean makeParents) throws SVNException { + // since one revision is passed -> I assume same repository here + final SvnCommandLineInfoClient infoClient = new SvnCommandLineInfoClient(myProject); + final SVNInfo info = infoClient.doInfo(paths[0], SVNRevision.UNDEFINED); + 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]; + + new CommandLineAuthenticator(myProject, new CommandLineAuthenticator.AuthenticationRequiringCommand() { + @Override + public void run(File configDir) throws SVNException { + final File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath()); + 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"); + } + command.addParameters("--accept", "postpone"); + command.addParameters("--config-dir", configDir.getPath()); + + for (File path : paths) { + command.addParameters(path.getPath()); + } + + 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; + } + } + } else if (ProcessOutputTypes.STDERR.equals(outputType)) { + sbError.append(line); + if (line.contains(ourAuthenticationRealm)) { + command.cancel(); + semaphore.up(); + } + } + } + + @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 = new SVNUpdateClient(manager, SvnConfiguration17.getInstance(myProject).getOptions(myProject)); + 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; + } + } + } + } + + @Override + public SVNURL sampleUrl() { + return info.getURL(); + } + + @Override + public void cleanup() throws SVNException { + final SvnVcs17 vcs17 = SvnVcs17.getInstance(myProject); + final SVNWCClient client = vcs17.createWCClient(); + for (File path : paths) { + client.doCleanup(path); + } + } + }).doWithAuthentication(); + return result; + } + + private void checkForException(final StringBuffer sbError) throws SVNException { + if (sbError.length() == 0) return; + final String message = sbError.toString(); + final Matcher matcher = ourExceptionPattern.matcher(message); + if (matcher.matches()) { + final String group = matcher.group(1); + if (group != null) { + try { + final int code = Integer.parseInt(group); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.getErrorCode(code), message)); + } catch (NumberFormatException e) { + // + } + } + } + if (message.contains(ourAuthenticationRealm)) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, message)); + } + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.UNKNOWN, message)); + } + + @Override + public long doUpdate(File path, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) + throws SVNException { + final long[] longs = doUpdate(new File[]{path}, revision, depth, allowUnversionedObstructions, depthIsSticky, false); + return longs[0]; + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision revision, boolean recursive) throws SVNException { + throw new UnsupportedOperationException(); + //return super.doSwitch(file, url, revision, recursive); + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive) throws SVNException { + throw new UnsupportedOperationException(); + //return super.doSwitch(file, url, pegRevision, revision, recursive); + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, boolean force) + throws SVNException { + throw new UnsupportedOperationException(); + //return super.doSwitch(file, url, pegRevision, revision, recursive, force); + } + + @Override + public long doSwitch(File path, + SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky) throws SVNException { + throw new UnsupportedOperationException(); + //return super.doSwitch(path, url, pegRevision, revision, depth, allowUnversionedObstructions, depthIsSticky); + } + + @Override + public long doSwitch(File path, + SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky, + boolean ignoreAncestry) throws SVNException { + throw new UnsupportedOperationException(); + // todo MAIN + //return super.doSwitch(path, url, pegRevision, revision, depth, allowUnversionedObstructions, depthIsSticky, ignoreAncestry); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/UpdateOutputLineConverter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/UpdateOutputLineConverter.java new file mode 100644 index 000000000000..80c50ea9c7dc --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/commandLine/UpdateOutputLineConverter.java @@ -0,0 +1,199 @@ +/* + * 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.svn17.commandLine; + +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNErrorCode; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNEventAction; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/1/12 + * Time: 5:13 PM + */ +public class UpdateOutputLineConverter { + private final static String UPDATING = "Updating"; + private final static String AT_REVISION = "At revision (\\d+)\\."; + private final static String UPDATED_TO_REVISION = "Updated to revision (\\d+)\\."; + private final static String SKIPPED = "Skipped"; + private final static String RESTORED = "Restored"; + + private final static String FETCHING_EXTERNAL = "Fetching external"; + private final static String EXTERNAL = "External at (\\d+)\\."; + private final static String UPDATED_EXTERNAL = "Updated external to revision (\\d+)\\."; + + private final static Pattern ourAtRevision = Pattern.compile(AT_REVISION); + private final static Pattern ourUpdatedToRevision = Pattern.compile(UPDATED_TO_REVISION); + + private final static Pattern ourExternal = Pattern.compile(EXTERNAL); + private final static Pattern ourUpdatedExternal = Pattern.compile(UPDATED_EXTERNAL); + + private final static Pattern[] ourCompletePatterns = new Pattern[] {ourAtRevision, ourUpdatedToRevision, ourExternal, ourUpdatedExternal}; + + private final File myBase; + private File myCurrentFile; + + public UpdateOutputLineConverter(File base) { + myBase = base; + } + + public SVNEvent convert(final String line) { + if (StringUtil.isEmptyOrSpaces(line)) return null; + + if (line.startsWith(UPDATING)) { + myCurrentFile = parseForPath(line); + return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), + null, -1, null, null, null, null, SVNEventAction.UPDATE_NONE, SVNEventAction.UPDATE_NONE, null, null, null, null, null); + } else if (line.startsWith(RESTORED)) { + myCurrentFile = parseForPath(line); + return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), + null, -1, null, null, null, null, SVNEventAction.RESTORE, SVNEventAction.RESTORE, null, null, null, null, null); + } else if (line.startsWith(SKIPPED)) { + myCurrentFile = parseForPath(line); + final String comment = parseComment(line); + return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), + null, -1, null, null, null, null, SVNEventAction.SKIP, SVNEventAction.SKIP, + comment == null ? null : SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, comment), null, null, null, null); + } else if (line.startsWith(FETCHING_EXTERNAL)) { + myCurrentFile = parseForPath(line); + return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), + null, -1, null, null, null, null, SVNEventAction.UPDATE_EXTERNAL, SVNEventAction.UPDATE_EXTERNAL, null, null, null, null, null); + } + + for (int i = 0; i < ourCompletePatterns.length; i++) { + final Pattern pattern = ourCompletePatterns[i]; + final long revision = matchAndGetRevision(pattern, line); + if (revision != -1) { + return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), + null, revision, null, null, null, null, SVNEventAction.UPDATE_COMPLETED, SVNEventAction.UPDATE_COMPLETED, null, null, null, null, null); + } + } + + return parseNormalString(line); + } + + private final static Set ourActions = new HashSet(Arrays.asList(new Character[] {'A', 'D', 'U', 'C', 'G', 'E', 'R'})); + + @Nullable + private SVNEvent parseNormalString(final String line) { + if (line.length() < 5) return null; + final char first = line.charAt(0); + if (' ' != first && ! ourActions.contains(first)) return null; + final SVNStatusType contentsStatus = getStatusType(first); + final char second = line.charAt(1); + final SVNStatusType propertiesStatus = getStatusType(second); + final char lock = line.charAt(2); // dont know what to do with stolen lock info + if (' ' != lock && 'B' != lock) return null; + final char treeConflict = line.charAt(3); + if (' ' != treeConflict && 'C' != treeConflict) return null; + final boolean haveTreeConflict = 'C' == treeConflict; + + final String path = line.substring(4).trim(); + if (StringUtil.isEmptyOrSpaces(path)) return null; + final File file = new File(myBase, path); + if (SVNStatusType.STATUS_OBSTRUCTED.equals(contentsStatus)) { + // obstructed + return new SVNEvent(file, file.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE, + null, -1, contentsStatus, propertiesStatus, null, null, SVNEventAction.UPDATE_SKIP_OBSTRUCTION, SVNEventAction.UPDATE_ADD, + null, null, null, null, null); + } + + SVNEventAction action; + SVNEventAction expectedAction; + if (SVNStatusType.STATUS_ADDED.equals(contentsStatus)) { + expectedAction = SVNEventAction.UPDATE_ADD; + } else if (SVNStatusType.STATUS_DELETED.equals(contentsStatus)) { + expectedAction = SVNEventAction.UPDATE_DELETE; + } else { + expectedAction = SVNEventAction.UPDATE_UPDATE; + } + action = expectedAction; + if (haveTreeConflict) { + action = SVNEventAction.TREE_CONFLICT; + } + + return new SVNEvent(file, file.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE, null, -1, contentsStatus, propertiesStatus, null, + null, action, expectedAction, null, null, null, null, null); + } + + private SVNStatusType getStatusType(char first) { + final SVNStatusType contentsStatus; + if ('A' == first) { + contentsStatus = SVNStatusType.STATUS_ADDED; + } else if ('D' == first) { + contentsStatus = SVNStatusType.STATUS_DELETED; + } else if ('U' == first) { + contentsStatus = SVNStatusType.CHANGED; + } else if ('C' == first) { + contentsStatus = SVNStatusType.CONFLICTED; + } else if ('G' == first) { + contentsStatus = SVNStatusType.MERGED; + } else if ('R' == first) { + contentsStatus = SVNStatusType.STATUS_REPLACED; + } else if ('E' == first) { + contentsStatus = SVNStatusType.STATUS_OBSTRUCTED; + } else { + contentsStatus = SVNStatusType.STATUS_NORMAL; + } + return contentsStatus; + } + + @Nullable + private long matchAndGetRevision(final Pattern pattern, final String line) { + final Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + final String group = matcher.group(1); + if (group == null) return -1; + try { + return Long.parseLong(group); + } catch (NumberFormatException e) { + // + } + } + return -1; + } + + @Nullable + private String parseComment(final String line) { + final int idx = line.lastIndexOf("--"); + if (idx != -1 && idx < (line.length() - 2)) { + return line.substring(idx + 2).trim(); + } + return null; + } + + @Nullable + private File parseForPath(final String line) { + final int idx1 = line.indexOf('\''); + if (idx1 == -1) return null; + final int idx2 = line.indexOf('\'', idx1 + 1); + if (idx2 == -1) return null; + final String substring = line.substring(idx1 + 1, idx2); + if (".".equals(substring)) return myBase; + return new File(myBase, substring); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/dialogs/SvnAuthenticationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/dialogs/SvnAuthenticationProvider.java index 44a35487ccee..93a5383534d6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/dialogs/SvnAuthenticationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/dialogs/SvnAuthenticationProvider.java @@ -17,11 +17,14 @@ package org.jetbrains.idea.svn17.dialogs; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.util.SystemProperties; import org.jetbrains.idea.svn17.SvnAuthenticationNotifier; +import org.jetbrains.idea.svn17.SvnConfiguration17; import org.jetbrains.idea.svn17.SvnVcs17; import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; @@ -40,11 +43,13 @@ public class SvnAuthenticationProvider implements ISVNAuthenticationProvider { private final Project myProject; private final SvnAuthenticationNotifier myAuthenticationNotifier; private final ISVNAuthenticationProvider mySvnInteractiveAuthenticationProvider; + private final SvnVcs17 mySvnVcs; private final ISVNAuthenticationStorage myAuthenticationStorage; private static final Set ourForceInteractive = new HashSet(); public SvnAuthenticationProvider(final SvnVcs17 svnVcs, final ISVNAuthenticationProvider provider, final ISVNAuthenticationStorage authenticationStorage) { + mySvnVcs = svnVcs; myAuthenticationStorage = authenticationStorage; myProject = svnVcs.getProject(); myAuthenticationNotifier = svnVcs.getAuthNotifier(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnSvnkitUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnSvnkitUpdateClient.java new file mode 100644 index 000000000000..dfc26591277b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnSvnkitUpdateClient.java @@ -0,0 +1,206 @@ +/* + * 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.svn17.portable; + +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNUpdateClient; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/1/12 + * Time: 12:11 PM + */ +public class SvnSvnkitUpdateClient implements SvnUpdateClientI { + private final SVNUpdateClient myClient; + private ISVNEventHandler myDispatcher; + + public SvnSvnkitUpdateClient(SVNUpdateClient client) { + myClient = client; + } + + @Override + public long doUpdate(File file, SVNRevision revision, boolean recursive) throws SVNException { + return myClient.doUpdate(file, revision, recursive); + } + + @Override + public long doUpdate(File file, SVNRevision revision, boolean recursive, boolean force) throws SVNException { + return myClient.doUpdate(file, revision, recursive, force); + } + + @Override + public long[] doUpdate(File[] paths, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky) throws SVNException { + return myClient.doUpdate(paths, revision, depth, allowUnversionedObstructions, depthIsSticky); + } + + @Override + public long[] doUpdate(File[] paths, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky, + boolean makeParents) throws SVNException { + return myClient.doUpdate(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, makeParents); + } + + @Override + public long doUpdate(File path, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) + throws SVNException { + return myClient.doUpdate(path, revision, depth, allowUnversionedObstructions, depthIsSticky); + } + + @Override + public void setUpdateLocksOnDemand(boolean locksOnDemand) { + myClient.setUpdateLocksOnDemand(locksOnDemand); + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision revision, boolean recursive) throws SVNException { + return myClient.doSwitch(file, url, revision, recursive); + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive) throws SVNException { + return myClient.doSwitch(file, url, pegRevision, revision, recursive); + } + + @Override + public long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, boolean force) + throws SVNException { + return myClient.doSwitch(file, url, pegRevision, revision, recursive, force); + } + + @Override + public long doSwitch(File path, + SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, boolean depthIsSticky) throws SVNException { + return myClient.doSwitch(path, url, pegRevision, revision, depth, allowUnversionedObstructions, depthIsSticky); + } + + @Override + public long doSwitch(File path, + SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, boolean depthIsSticky, boolean ignoreAncestry) throws SVNException { + return myClient.doSwitch(path, url, pegRevision, revision, depth, allowUnversionedObstructions, depthIsSticky, ignoreAncestry); + } + + @Override + public long doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, boolean recursive) throws SVNException { + return myClient.doCheckout(url, dstPath, pegRevision, revision, recursive); + } + + @Override + public long doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, boolean recursive, boolean force) + throws SVNException { + return myClient.doCheckout(url, dstPath, pegRevision, revision, recursive, force); + } + + @Override + public long doCheckout(SVNURL url, + File dstPath, + SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions) throws SVNException { + return myClient.doCheckout(url, dstPath, pegRevision, revision, depth, allowUnversionedObstructions); + } + + @Override + public long doExport(SVNURL url, + File dstPath, + SVNRevision pegRevision, + SVNRevision revision, + String eolStyle, + boolean force, + boolean recursive) throws SVNException { + return myClient.doExport(url, dstPath, pegRevision, revision, eolStyle, force, recursive); + } + + @Override + public long doExport(SVNURL url, + File dstPath, + SVNRevision pegRevision, + SVNRevision revision, + String eolStyle, + boolean overwrite, + SVNDepth depth) throws SVNException { + return myClient.doExport(url, dstPath, pegRevision, revision, eolStyle, overwrite, depth); + } + + @Override + public long doExport(File srcPath, + File dstPath, + SVNRevision pegRevision, + SVNRevision revision, + String eolStyle, + boolean force, + boolean recursive) throws SVNException { + return myClient.doExport(srcPath, dstPath, pegRevision, revision, eolStyle, force, recursive); + } + + @Override + public long doExport(File srcPath, + File dstPath, + SVNRevision pegRevision, + SVNRevision revision, + String eolStyle, + boolean overwrite, + SVNDepth depth) throws SVNException { + return myClient.doExport(srcPath, dstPath, pegRevision, revision, eolStyle, overwrite, depth); + } + + @Override + public void doRelocate(File dst, SVNURL oldURL, SVNURL newURL, boolean recursive) throws SVNException { + myClient.doRelocate(dst, oldURL, newURL, recursive); + } + + @Override + public void doCanonicalizeURLs(File dst, boolean omitDefaultPort, boolean recursive) throws SVNException { + myClient.doCanonicalizeURLs(dst, omitDefaultPort, recursive); + } + + @Override + public void setExportExpandsKeywords(boolean expand) { + myClient.setExportExpandsKeywords(expand); + } + + @Override + public void setEventHandler(ISVNEventHandler dispatcher) { + myDispatcher = dispatcher; + myClient.setEventHandler(dispatcher); + } + + public ISVNEventHandler getEventHandler() { + return myDispatcher; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnUpdateClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnUpdateClientI.java new file mode 100644 index 000000000000..434f657d61b9 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/portable/SvnUpdateClientI.java @@ -0,0 +1,75 @@ +/* + * 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.svn17.portable; + +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/1/12 + * Time: 11:59 AM + */ +public interface SvnUpdateClientI { + long doUpdate(File file, SVNRevision revision, boolean recursive) throws SVNException; + + long doUpdate(File file, SVNRevision revision, boolean recursive, boolean force) throws SVNException; + + long[] doUpdate(File[] paths, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) throws SVNException; + + long[] doUpdate(File[] paths, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky, boolean makeParents) throws SVNException; + + long doUpdate(File path, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) throws SVNException; + + void setUpdateLocksOnDemand(boolean locksOnDemand); + + long doSwitch(File file, SVNURL url, SVNRevision revision, boolean recursive) throws SVNException; + + long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive) throws SVNException; + + long doSwitch(File file, SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, boolean force) throws SVNException; + + long doSwitch(File path, SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky) throws SVNException; + + long doSwitch(File path, SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions, boolean depthIsSticky, boolean ignoreAncestry) throws SVNException; + + long doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, boolean recursive) throws SVNException; + + long doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, boolean recursive, boolean force) throws SVNException; + + long doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, boolean allowUnversionedObstructions) throws SVNException; + + long doExport(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, String eolStyle, boolean force, boolean recursive) throws SVNException; + + long doExport(SVNURL url, File dstPath, SVNRevision pegRevision, SVNRevision revision, String eolStyle, boolean overwrite, SVNDepth depth) throws SVNException; + + long doExport(File srcPath, File dstPath, SVNRevision pegRevision, SVNRevision revision, String eolStyle, boolean force, boolean recursive) throws SVNException; + + long doExport(File srcPath, File dstPath, SVNRevision pegRevision, SVNRevision revision, String eolStyle, boolean overwrite, SVNDepth depth) throws SVNException; + + void doRelocate(File dst, SVNURL oldURL, SVNURL newURL, boolean recursive) throws SVNException; + + void doCanonicalizeURLs(File dst, boolean omitDefaultPort, boolean recursive) throws SVNException; + + void setExportExpandsKeywords(boolean expand); + void setEventHandler(ISVNEventHandler dispatcher); +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn17/update/SvnUpdateEnvironment17.java b/plugins/svn4idea/src/org/jetbrains/idea/svn17/update/SvnUpdateEnvironment17.java index 1f9155c6719c..db44c0a19478 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn17/update/SvnUpdateEnvironment17.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn17/update/SvnUpdateEnvironment17.java @@ -23,10 +23,10 @@ import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.UpdatedFiles; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn17.SvnBundle; -import org.jetbrains.idea.svn17.SvnConfiguration17; -import org.jetbrains.idea.svn17.SvnRevisionNumber; -import org.jetbrains.idea.svn17.SvnVcs17; +import org.jetbrains.idea.svn17.*; +import org.jetbrains.idea.svn17.commandLine.SvnCommandLineUpdateClient; +import org.jetbrains.idea.svn17.portable.SvnSvnkitUpdateClient; +import org.jetbrains.idea.svn17.portable.SvnUpdateClientI; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.io.SVNRepository; @@ -82,25 +82,16 @@ public class SvnUpdateEnvironment17 extends AbstractSvnUpdateIntegrateEnvironmen final SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myVcs.getProject()); final UpdateRootInfo rootInfo = configuration.getUpdateRootInfo(root, myVcs); - final SVNUpdateClient updateClient = myVcs.createUpdateClient(); - updateClient.setEventHandler(myHandler); - updateClient.setUpdateLocksOnDemand(configuration.UPDATE_LOCK_ON_DEMAND); - if (rootInfo != null) { - final SVNURL url = rootInfo.getUrl(); - if (url != null && url.equals(getSourceUrl(myVcs, root))) { - if (rootInfo.isUpdateToRevision()) { - rev = updateClient.doUpdate(root, rootInfo.getRevision(), configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); - } else { - rev = updateClient.doUpdate(root, SVNRevision.HEAD, configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); - } + final SVNURL sourceUrl = getSourceUrl(myVcs, root); + final boolean isSwitch = rootInfo != null && rootInfo.getUrl() != null && ! rootInfo.getUrl().equals(sourceUrl); + final SVNRevision updateTo = rootInfo != null && rootInfo.isUpdateToRevision() ? rootInfo.getRevision() : SVNRevision.HEAD; - } else if (url != null) { - rev = updateClient.doSwitch(root, url, SVNRevision.UNDEFINED, rootInfo.getRevision(), configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); - } else { - rev = updateClient.doUpdate(root, SVNRevision.HEAD, configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); - } + if (isSwitch) { + final SvnUpdateClientI updateClient = createUpdateClient(configuration, root, true, sourceUrl); + rev = updateClient.doSwitch(root, rootInfo.getUrl(), SVNRevision.UNDEFINED, rootInfo.getRevision(), configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); } else { - rev = updateClient.doUpdate(root, SVNRevision.HEAD, configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); + final SvnUpdateClientI updateClient = createUpdateClient(configuration, root, false, sourceUrl); + rev = updateClient.doUpdate(root, updateTo, configuration.UPDATE_DEPTH, configuration.FORCE_UPDATE, false); } myPostUpdateFiles.setRevisions(root.getAbsolutePath(), myVcs, new SvnRevisionNumber(SVNRevision.create(rev))); @@ -108,6 +99,20 @@ public class SvnUpdateEnvironment17 extends AbstractSvnUpdateIntegrateEnvironmen return rev; } + private SvnUpdateClientI createUpdateClient(SvnConfiguration17 configuration, File root, boolean isSwitch, SVNURL sourceUrl) { + final SvnUpdateClientI updateClient; + // do not do from command line for switch now + if (! isSwitch && SvnConfiguration17.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) && + Svn17Detector.is17(myVcs.getProject(), root) && SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol())) { + updateClient = new SvnCommandLineUpdateClient(myVcs.getProject(), null); + } else { + updateClient = new SvnSvnkitUpdateClient(myVcs.createUpdateClient()); + } + updateClient.setEventHandler(myHandler); + updateClient.setUpdateLocksOnDemand(configuration.UPDATE_LOCK_ON_DEMAND); + return updateClient; + } + protected boolean isMerge() { return false; } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn17/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn17/SvnAuthenticationTest.java index 86bc1b019921..3e4d91df96bf 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn17/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn17/SvnAuthenticationTest.java @@ -1054,6 +1054,10 @@ public class SvnAuthenticationTest extends PlatformTestCase { return myCnt; } + @Override + public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + } + @Override public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { mySaved = false; @@ -1119,6 +1123,10 @@ public class SvnAuthenticationTest extends PlatformTestCase { mySaved = new HashSet>(); } + @Override + public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + } + public void reset() { mySaved.clear(); myClientRequested.clear();