diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index bbb8e54c6704..65de1e5bc8d3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -330,6 +330,7 @@ progress.text2.exported=Exported {1} files, exporting {0} progress.text2.checked.out.revision=Checked out revision {0}. progress.text2.exported.revision=Exported revision {0}. status.text.checked.out.revision=Checked out revision {0}. +message.text.cannot.load.supported.formats=Cannot load supported formats: {0} message.text.cannot.checkout=Cannot checkout from svn: {0} message.text.cannot.export=Cannot export from svn: {0} message.title.check.out=Check Out from Subversion diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java index 2595631bac4b..12513893c5af 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java @@ -17,12 +17,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Ref; -import com.intellij.util.WaitForProgressToShow; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.dialogs.UpgradeFormatDialog; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; @@ -136,22 +131,6 @@ public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { return result; } - public static WorkingCopyFormat showUpgradeDialog(final File path, - final Project project, - final boolean display13format, - @NotNull final WorkingCopyFormat defaultSelection, - @NotNull final Ref wasOk) { - assert ! ApplicationManager.getApplication().isUnitTestMode(); - final Ref format = new Ref(defaultSelection); - WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(new Runnable() { - public void run() { - wasOk.set(displayUpgradeDialog(project, path, display13format, format)); - } - }); - ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run(); - return format.get(); - } - public static WorkingCopyFormat findRootAndGetFormat(final File path) { File root = SvnUtil.getWorkingCopyRootNew(path); @@ -189,14 +168,4 @@ public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { return WorkingCopyFormat.getInstance(format); } - - private static boolean displayUpgradeDialog(Project project, File path, final boolean dispay13format, Ref format) { - UpgradeFormatDialog dialog = new UpgradeFormatDialog(project, path, false); - dialog.setData(format.get()); - dialog.show(); - if (dialog.isOK()) { - format.set(dialog.getUpgradeMode()); - } - return dialog.isOK(); - } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 07301c0829f1..083bb7840cdf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -647,6 +647,7 @@ public class SvnUtil { } public static boolean remoteFolderIsEmpty(final SvnVcs vcs, final String url) throws SVNException { + // TODO: Implement with command line client SVNRepository repository = null; try { repository = vcs.createRepository(url); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java index 041ee2811a63..595abe10a83c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java @@ -100,4 +100,23 @@ public enum WorkingCopyFormat { public int getFormat() { return myFormat; } + + @NotNull + public static WorkingCopyFormat from(@NotNull Version version) { + WorkingCopyFormat result = UNKNOWN; + + for (WorkingCopyFormat format : WorkingCopyFormat.values()) { + if (format.getVersion().is(version.major, version.minor)) { + result = format; + break; + } + } + + return result; + } + + @Override + public String toString() { + return getName(); + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java index 975a1641b0c5..99b2e0cc8449 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/ShareProjectAction.java @@ -31,18 +31,19 @@ import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; import org.jetbrains.idea.svn.dialogs.ShareDialog; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNUpdateClient; -import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -104,7 +105,7 @@ public class ShareProjectAction extends BasicAction { final String parent = shareDialog.getSelectedURL(); if (shareDialog.isOK() && parent != null) { final Ref actionStarted = new Ref(Boolean.TRUE); - final SVNException[] error = new SVNException[1]; + final Exception[] error = new Exception[1]; final ShareDialog.ShareTarget shareTarget = shareDialog.getShareTarget(); final ProgressManager progressManager = ProgressManager.getInstance(); @@ -120,19 +121,23 @@ public class ShareProjectAction extends BasicAction { } } + final WorkingCopyFormat format = SvnCheckoutProvider.promptForWCopyFormat(VfsUtilCore.virtualToIoFile(file), project); + actionStarted.set(format != WorkingCopyFormat.UNKNOWN); + // means operation cancelled + if (format == WorkingCopyFormat.UNKNOWN) { + return true; + } + ExclusiveBackgroundVcsAction.run(project, new Runnable() { public void run() { progressManager.runProcessWithProgressSynchronously(new Runnable() { public void run() { try { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - final File path = new File(file.getPath()); - if (! SvnCheckoutProvider.promptForWCFormatAndSelect(path, project)) { - // action cancelled - actionStarted.set(Boolean.FALSE); - return; - } + + SvnWorkingCopyFormatHolder.setPresetFormat(format); + final SVNURL parenUrl = SVNURL.parseURIEncoded(parent); final SVNURL checkoutUrl; final SVNRevision revision; @@ -160,16 +165,17 @@ public class ShareProjectAction extends BasicAction { indicator.checkCanceled(); indicator.setText(SvnBundle.message("share.directory.checkout.back.progress.text", checkoutUrl.toString())); } - final SVNUpdateClient client = activeVcs.createUpdateClient(); - if (! WorkingCopyFormat.ONE_DOT_SEVEN.equals(SvnWorkingCopyFormatHolder.getPresetFormat())) { - client.getOperationsFactory().setPrimaryWcGeneration(SvnWcGeneration.V16); - } - client.doCheckout(checkoutUrl, path, SVNRevision.UNDEFINED, revision, SVNDepth.INFINITY, false); - SvnWorkingCopyFormatHolder.setPresetFormat(null); - addRecursively(activeVcs, file); + final ClientFactory factory = activeVcs.getFactoryFromSettings(); + + factory.createCheckoutClient() + .checkout(SvnTarget.fromURL(checkoutUrl), path, revision, SVNDepth.INFINITY, false, false, format, null); + addRecursively(activeVcs, factory, file); } catch (SVNException e) { error[0] = e; + } + catch (VcsException e) { + error[0] = e; } finally { activeVcs.invokeRefreshSvnRoots(); SvnWorkingCopyFormatHolder.setPresetFormat(null); @@ -226,6 +232,7 @@ public class ShareProjectAction extends BasicAction { indicator.checkCanceled(); indicator.setText(SvnBundle.message("share.directory.create.dir.progress.text", urlText)); } + // TODO: Implement with command line client final SVNCommitInfo info = vcs.createCommitClient().doMkDir(new SVNURL[]{url}, SvnBundle.message("share.directory.commit.message", folderName, ApplicationNamesInfo.getInstance().getFullProductName(), commitText)); @@ -237,17 +244,17 @@ public class ShareProjectAction extends BasicAction { VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(file); } - private static void addRecursively(final SvnVcs activeVcs, final VirtualFile file) throws SVNException { - final SVNWCClient wcClient = activeVcs.createWCClient(); + private static void addRecursively(@NotNull final SvnVcs activeVcs, @NotNull final ClientFactory factory, final VirtualFile file) + throws VcsException { final SvnExcludingIgnoredOperation operation = new SvnExcludingIgnoredOperation(activeVcs.getProject(), new SvnExcludingIgnoredOperation.Operation() { - public void doOperation(final VirtualFile virtualFile) throws SVNException { + public void doOperation(final VirtualFile virtualFile) throws VcsException { final File ioFile = new File(virtualFile.getPath()); final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.checkCanceled(); indicator.setText(SvnBundle.message("share.or.import.add.progress.text", virtualFile.getPath())); } - wcClient.doAdd(ioFile, true, false, false, SVNDepth.EMPTY, false, false); + factory.createAddClient().add(ioFile, SVNDepth.EMPTY, false, false, true, null); } }, SVNDepth.INFINITY); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnExcludingIgnoredOperation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnExcludingIgnoredOperation.java index f60e553332e3..49fdc0279dd5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnExcludingIgnoredOperation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnExcludingIgnoredOperation.java @@ -20,13 +20,13 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileVisitor; import org.jetbrains.annotations.NotNull; import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; public class SvnExcludingIgnoredOperation { private final Operation myImportAction; @@ -79,28 +79,28 @@ public class SvnExcludingIgnoredOperation { } } - private boolean operation(final VirtualFile file) throws SVNException { + private boolean operation(final VirtualFile file) throws VcsException { if (! myFilter.accept(file)) return false; myImportAction.doOperation(file); return true; } - private void executeDown(final VirtualFile file) throws SVNException { + private void executeDown(final VirtualFile file) throws VcsException { VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { try { return operation(file); } - catch (SVNException e) { + catch (VcsException e) { throw new VisitorException(e); } } - }, SVNException.class); + }, VcsException.class); } - public void execute(final VirtualFile file) throws SVNException { + public void execute(final VirtualFile file) throws VcsException { if (SVNDepth.INFINITY.equals(myDepth)) { executeDown(file); return; @@ -123,6 +123,6 @@ public class SvnExcludingIgnoredOperation { } public interface Operation { - void doOperation(final VirtualFile file) throws SVNException; + void doOperation(final VirtualFile file) throws VcsException; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java index 133bc6489c4e..e6bca33d617f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java @@ -1,9 +1,14 @@ package org.jetbrains.idea.svn.api; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.WorkingCopyFormat; import org.tmatesoft.svn.core.wc2.SvnTarget; +import java.util.Collection; + /** * @author Konstantin Kolosovsky. */ @@ -44,4 +49,11 @@ public abstract class BaseSvnClient implements SvnClient { throw new IllegalArgumentException("Target should be file " + target); } } + + protected void validateFormat(@NotNull WorkingCopyFormat format, @NotNull Collection supported) throws VcsException { + if (!supported.contains(format)) { + throw new VcsException( + String.format("%s format is not supported. Supported formats are: %s.", format.getName(), StringUtil.join(supported, ","))); + } + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java index e7aa0299a00d..b31d0da1dc48 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java @@ -11,6 +11,7 @@ import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; +import java.util.List; /** * @author Konstantin Kolosovsky. @@ -22,6 +23,9 @@ public interface CheckoutClient extends SvnClient { @Nullable SVNRevision revision, @Nullable SVNDepth depth, boolean ignoreExternals, - @Nullable WorkingCopyFormat format, + boolean force, + @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException; + + List getSupportedFormats() throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java index fe22fc817182..6e151e4a6ae8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java @@ -1,5 +1,6 @@ package org.jetbrains.idea.svn.checkout; +import com.intellij.openapi.util.Version; import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,22 +28,33 @@ public class CmdCheckoutClient extends BaseSvnClient implements CheckoutClient { @Nullable SVNRevision revision, @Nullable SVNDepth depth, boolean ignoreExternals, - @Nullable WorkingCopyFormat format, + boolean force, + @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException { - List parameters = new ArrayList(); + validateFormat(format, getSupportedFormats()); - // TODO: check format + List parameters = new ArrayList(); CommandUtil.put(parameters, source); CommandUtil.put(parameters, destination, false); CommandUtil.put(parameters, depth); CommandUtil.put(parameters, revision); CommandUtil.put(parameters, ignoreExternals, "--ignore-externals"); - parameters.add("--force"); // this is to conform to currently used SVNKit behavior - allowUnversionedObstructions + CommandUtil.put(parameters, force, "--force"); // corresponds to "allowUnversionedObstructions" in SVNKit run(source, destination, handler, parameters); } + @Override + public List getSupportedFormats() throws VcsException { + ArrayList result = new ArrayList(); + + Version version = myFactory.createVersionClient().getVersion(); + result.add(WorkingCopyFormat.from(version)); + + return result; + } + private void run(@NotNull SvnTarget source, @NotNull File destination, @Nullable ISVNEventHandler handler, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java index ff415a0c116f..4e703ddee543 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn.checkout; import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; @@ -26,6 +27,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.CalledInAwt; import com.intellij.openapi.vcs.CheckoutProvider; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsException; @@ -34,6 +36,8 @@ import com.intellij.openapi.vcs.update.RefreshVFsSynchronously; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.StatusBar; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; @@ -42,6 +46,7 @@ import org.jetbrains.idea.svn.actions.SvnExcludingIgnoredOperation; import org.jetbrains.idea.svn.checkin.IdeaCommitHandler; import org.jetbrains.idea.svn.commandLine.CommitEventHandler; import org.jetbrains.idea.svn.dialogs.CheckoutDialog; +import org.jetbrains.idea.svn.dialogs.UpgradeFormatDialog; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; @@ -51,6 +56,9 @@ import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.swing.*; import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; public class SvnCheckoutProvider implements CheckoutProvider { @@ -84,19 +92,16 @@ public class SvnCheckoutProvider implements CheckoutProvider { final Task.Backgroundable checkoutBackgroundTask = new Task.Backgroundable(project, SvnBundle.message("message.title.check.out"), true, VcsConfiguration.getInstance(project).getCheckoutOption()) { public void run(@NotNull final ProgressIndicator indicator) { - SvnWorkingCopyFormatHolder.setPresetFormat(selectedFormat); + final WorkingCopyFormat format = selectedFormat == null ? WorkingCopyFormat.UNKNOWN : selectedFormat; + + SvnWorkingCopyFormatHolder.setPresetFormat(format); SvnVcs vcs = SvnVcs.getInstance(project); - // TODO: made this way to preserve existing logic, but probably this check could be omitted as setPresetFormat(selectedFormat) invoked above - WorkingCopyFormat format = !WorkingCopyFormat.ONE_DOT_SEVEN.equals(SvnWorkingCopyFormatHolder.getPresetFormat()) - ? WorkingCopyFormat.ONE_DOT_SIX - : selectedFormat; ISVNEventHandler handler = new CheckoutEventHandler(vcs, false, ProgressManager.getInstance().getProgressIndicator()); ProgressManager.progress(SvnBundle.message("progress.text.checking.out", target.getAbsolutePath())); try { - // TODO: probably rewrite some logic to force ClientFactory provide supported versions (or create special client for that) vcs.getFactoryFromSettings().createCheckoutClient() - .checkout(SvnTarget.fromURL(SVNURL.parseURIEncoded(url)), target, revision, depth, ignoreExternals, format, handler); + .checkout(SvnTarget.fromURL(SVNURL.parseURIEncoded(url)), target, revision, depth, ignoreExternals, true, format, handler); ProgressManager.checkCanceled(); checkoutSuccessful.set(Boolean.TRUE); } @@ -168,22 +173,10 @@ public class SvnCheckoutProvider implements CheckoutProvider { } } - public static boolean promptForWCFormatAndSelect(final File target, final Project project) { - final WorkingCopyFormat result = promptForWCopyFormat(target, project); - if (result != WorkingCopyFormat.UNKNOWN) { - SvnWorkingCopyFormatHolder.setPresetFormat(result); - } - return result != WorkingCopyFormat.UNKNOWN; - } - + @CalledInAwt @NotNull - private static WorkingCopyFormat promptForWCopyFormat(final File target, final Project project) { - WorkingCopyFormat format = WorkingCopyFormat.UNKNOWN; - final Ref wasOk = new Ref(); - while ((format == WorkingCopyFormat.UNKNOWN) && (! Boolean.FALSE.equals(wasOk.get()))) { - format = SvnFormatSelector.showUpgradeDialog(target, project, true, WorkingCopyFormat.ONE_DOT_SEVEN, wasOk); - } - return Boolean.TRUE.equals(wasOk.get()) ? format : WorkingCopyFormat.UNKNOWN; + public static WorkingCopyFormat promptForWCopyFormat(final File target, final Project project) { + return new CheckoutFormatFromUserProvider(project, target).prompt(); } public static void doExport(final Project project, final File target, final SVNURL url, final SVNDepth depth, @@ -286,6 +279,79 @@ public class SvnCheckoutProvider implements CheckoutProvider { return "_Subversion"; } -} + public static class CheckoutFormatFromUserProvider { + @NotNull private final Project myProject; + @NotNull private final SvnVcs myVcs; + @NotNull private final File myPath; + @NotNull private final AtomicReference error; + + public CheckoutFormatFromUserProvider(@NotNull Project project, @NotNull File path) { + myProject = project; + myVcs = SvnVcs.getInstance(project); + myPath = path; + + error = new AtomicReference(); + } + + @CalledInAwt + public WorkingCopyFormat prompt() { + assert !ApplicationManager.getApplication().isUnitTestMode(); + + final WorkingCopyFormat result = displayUpgradeDialog(WorkingCopyFormat.ONE_DOT_SEVEN); + + ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run(); + + return result; + } + + private WorkingCopyFormat displayUpgradeDialog(@NotNull WorkingCopyFormat defaultSelection) { + final UpgradeFormatDialog dialog = new UpgradeFormatDialog(myProject, myPath, false); + dialog.startLoading(); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + final List formats = loadSupportedFormats(); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + final String errorMessage = error.get(); + + if (errorMessage != null) { + dialog.doCancelAction(); + Messages.showErrorDialog(SvnBundle.message("message.text.cannot.load.supported.formats", errorMessage), + SvnBundle.message("message.title.check.out")); + } + else { + dialog.setSupported(formats); + dialog.setData(ContainerUtil.getFirstItem(formats, WorkingCopyFormat.UNKNOWN)); + dialog.stopLoading(); + } + } + }, ModalityState.stateForComponent(dialog.getWindow())); + } + }); + + dialog.show(); + + return dialog.isOK() ? dialog.getUpgradeMode() : WorkingCopyFormat.UNKNOWN; + } + + private List loadSupportedFormats() { + List result; + + try { + result = myVcs.getFactoryFromSettings().createCheckoutClient().getSupportedFormats(); + } + catch (VcsException e) { + result = Collections.emptyList(); + error.set(e.getMessage()); + } + + return result; + } + } +} \ No newline at end of file diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java index ba46ec832b87..54e9063e1937 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java @@ -15,30 +15,41 @@ import org.tmatesoft.svn.core.wc.SVNUpdateClient; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * @author Konstantin Kolosovsky. */ public class SvnKitCheckoutClient extends BaseSvnClient implements CheckoutClient { + private static final List SUPPORTED_FORMATS; + + static { + List supportedFormats = new ArrayList(); + + supportedFormats.add(WorkingCopyFormat.ONE_DOT_SEVEN); + supportedFormats.add(WorkingCopyFormat.ONE_DOT_SIX); + + SUPPORTED_FORMATS = Collections.unmodifiableList(supportedFormats); + } + @Override public void checkout(@NotNull SvnTarget source, @NotNull File destination, @Nullable SVNRevision revision, @Nullable SVNDepth depth, boolean ignoreExternals, - @Nullable WorkingCopyFormat format, + boolean force, + @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException { assertUrl(source); - - if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format)) { - throw new IllegalArgumentException("could not check out 1.8 format with SVNKit"); - } + validateFormat(format, getSupportedFormats()); SVNUpdateClient client = myVcs.createUpdateClient(); - // TODO: most likely we should compare directly with WorkingCopyFormat.ONE_DOT_SIX - if (!WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { + if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { client.getOperationsFactory().setPrimaryWcGeneration(SvnWcGeneration.V16); } @@ -46,10 +57,15 @@ public class SvnKitCheckoutClient extends BaseSvnClient implements CheckoutClien client.setEventHandler(handler); try { - client.doCheckout(source.getURL(), destination, source.getPegRevision(), revision, depth, true); + client.doCheckout(source.getURL(), destination, source.getPegRevision(), revision, depth, force); } catch (SVNException e) { throw new SvnBindException(e); } } + + @Override + public List getSupportedFormats() throws VcsException { + return SUPPORTED_FORMATS; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java index 2f667d363067..8ae61d51df22 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java @@ -42,6 +42,7 @@ public abstract class SvnCommand { private final File myConfigDir; private boolean myIsDestroyed; + private boolean myNeedsDestroy; private int myExitCode; protected final GeneralCommandLine myCommandLine; private final File myWorkingDirectory; @@ -210,13 +211,31 @@ public abstract class SvnCommand { public void destroyProcess() { synchronized (myLock) { - if (! myIsDestroyed) { + myNeedsDestroy = true; + } + } + + /** + * ProcessHandler.destroyProcess() implementations could acquire read lock in its implementation - like OSProcessManager.getInstance(). + * Some commands are called under write lock - which is generally bad idea, but such logic is not refactored yet. + * To prevent deadlocks this method should only be called from thread that started the process. + */ + public void doDestroyProcess() { + synchronized (myLock) { + if (!myIsDestroyed) { + LOG.info("Destroying process by command: " + getCommandText()); myIsDestroyed = true; myHandler.destroyProcess(); } } } + public boolean needsDestroy() { + synchronized (myLock) { + return myNeedsDestroy; + } + } + public String getCommandText() { synchronized (myLock) { return myCommandLine.getCommandLineString(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java index 9cf56bbd5500..75ff08525af4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java @@ -62,7 +62,8 @@ 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 UNABLE_TO_CONNECT_CODE = "svn: E170001:"; + public static final String UNABLE_TO_CONNECT_MESSAGE = "Unable to connect to a repository"; public static final String CANNOT_AUTHENTICATE_TO_PROXY = "Could not authenticate to proxy server"; public static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; @@ -216,7 +217,7 @@ public class SvnLineCommand extends SvnCommand { if (errText.startsWith(PASSPHRASE_FOR)) { return new PassphraseCallback(callback, url); } - if (errText.startsWith(UNABLE_TO_CONNECT) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) { + if (errText.startsWith(UNABLE_TO_CONNECT_CODE) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) { return new ProxyCallback(callback, url); } // http/https protocol invalid credentials @@ -228,6 +229,10 @@ public class SvnLineCommand extends SvnCommand { // svn protocol invalid credentials return new UsernamePasswordCallback(callback, url); } + // http/https protocol, svn 1.7, non-interactive + if (errText.contains(UNABLE_TO_CONNECT_MESSAGE)) { + return new UsernamePasswordCallback(callback, url); + } // https one-way protocol untrusted server certificate if (errText.contains(UNTRUSTED_SERVER_CERTIFICATE)) { return new CertificateCallbackCase(callback, url); @@ -469,23 +474,13 @@ public class SvnLineCommand extends SvnCommand { final String trim = text.trim(); // should end in 1 second errorReceived.set(true); - // TODO: destroy process here is called despite --non-interactive flag (so it is called even for 1.8) and then unnecessary - // TODO: cleanup is invoked - fix this - 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); + command.addParameters("--non-interactive"); final AtomicReference exceptionRef = new AtomicReference(); // several threads command.addLineListener(new LineProcessEventListener() { @@ -503,6 +498,7 @@ public class SvnLineCommand extends SvnCommand { } listener.onLineAvailable(line, outputType); if (listener.isCanceled()) { + LOG.info("Cancelling command: " + command.getCommandText()); command.destroyProcess(); return; } @@ -530,9 +526,9 @@ public class SvnLineCommand extends SvnCommand { boolean finished; do { finished = command.waitFor(500); - if (!finished && errorReceived.get()) { + if (!finished && (errorReceived.get() || command.needsDestroy())) { command.waitFor(1000); - command.destroyProcess(); + command.doDestroyProcess(); break; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java index cb2fcdaba0f1..28645fd623eb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/UpgradeFormatDialog.java @@ -19,6 +19,7 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.MultiLineLabelUI; +import com.intellij.ui.components.JBLoadingPanel; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,6 +31,7 @@ import javax.swing.*; import java.awt.*; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class UpgradeFormatDialog extends DialogWrapper { @@ -37,6 +39,8 @@ public class UpgradeFormatDialog extends DialogWrapper { private ButtonGroup formatGroup = new ButtonGroup(); private List formatButtons = new ArrayList(); + private JBLoadingPanel myLoadingPanel; + protected File myPath; public UpgradeFormatDialog(Project project, File path, boolean canBeParent) { @@ -64,7 +68,7 @@ public class UpgradeFormatDialog extends DialogWrapper { return "svn.upgradeDialog"; } - public void setData(final WorkingCopyFormat selectedFormat) { + public void setData(@NotNull final WorkingCopyFormat selectedFormat) { for (JRadioButton button : formatButtons) { if (selectedFormat == getFormat(button)) { button.setSelected(true); @@ -73,6 +77,29 @@ public class UpgradeFormatDialog extends DialogWrapper { } } + public void setSupported(@NotNull Collection supported) { + for (JRadioButton button : formatButtons) { + button.setEnabled(supported.contains(getFormat(button))); + } + } + + public void startLoading() { + enableFormatButtons(false); + getOKAction().setEnabled(false); + myLoadingPanel.startLoading(); + } + + private void enableFormatButtons(boolean enabled) { + for (JRadioButton button : formatButtons) { + button.setEnabled(enabled); + } + } + + public void stopLoading() { + getOKAction().setEnabled(true); + myLoadingPanel.stopLoading(); + } + protected String getTopMessage(final String label) { return SvnBundle.message("label.configure." + label + ".label", ApplicationNamesInfo.getInstance().getFullProductName()); } @@ -115,7 +142,10 @@ public class UpgradeFormatDialog extends DialogWrapper { gb.gridy += 1; } - return panel; + myLoadingPanel = new JBLoadingPanel(new BorderLayout(), getDisposable()); + myLoadingPanel.add(panel, BorderLayout.CENTER); + + return myLoadingPanel; } private void registerFormat(@NotNull WorkingCopyFormat format,