diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java index c7bd5042ec15..64248892614a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java @@ -18,11 +18,10 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.vcs.EditFileProvider; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNProperty; +import org.jetbrains.idea.svn.properties.PropertyClient; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -38,18 +37,16 @@ public class SvnEditFileProvider implements EditFileProvider { public void editFiles(VirtualFile[] files) throws VcsException { File[] ioFiles = new File[files.length]; - SVNWCClient client = myVCS.createWCClient(); + for (int i = 0; i < files.length; i++) { ioFiles[i] = new File(files[i].getPath()); - try { - SVNPropertyData property = client - .doGetProperty(ioFiles[i], SVNProperty.NEEDS_LOCK, SVNRevision.WORKING, SVNRevision.WORKING); - if (property == null || property.getValue() == null) { - throw new VcsException(SvnBundle.message("exception.text.file.miss.svn", ioFiles[i].getName())); - } - } - catch (SVNException e) { - throw new VcsException(e); + + PropertyClient client = myVCS.getFactory(ioFiles[i]).createPropertyClient(); + SVNPropertyData property = client.getProperty(SvnTarget.fromFile(ioFiles[i], SVNRevision.WORKING), SvnPropertyKeys.SVN_NEEDS_LOCK, + false, SVNRevision.WORKING); + + if (property == null || property.getValue() == null) { + throw new VcsException(SvnBundle.message("exception.text.file.miss.svn", ioFiles[i].getName())); } } SvnUtil.doLockFiles(myVCS.getProject(), myVCS, ioFiles); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java index 8b518199f5be..b246ea70e8a6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java @@ -143,12 +143,18 @@ public class SvnRecursiveStatusWalker { } public SvnStatusClientI getClient(final File file) { + // TODO: refactor to ClientFactory usage but carefully save all parameters passed in myClient - fileProvider and + // TODO: event handler (for cancel support) WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); if (format == WorkingCopyFormat.ONE_DOT_EIGHT) { return myCommandLineClient; } + if (format == WorkingCopyFormat.ONE_DOT_SIX) { + return mySvnClient; + } + // check format if (CheckJavaHL.isPresent() && SvnConfiguration.UseAcceleration.javaHL.equals(myConfiguration17.myUseAcceleration) && Svn17Detector.is17(myProject, file)) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index ef798500fb86..7594c2a56058 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -30,7 +30,10 @@ import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.Convertor; @@ -55,6 +58,8 @@ import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class SvnUtil { // TODO: ASP.NET hack behavior should be supported - http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt @@ -66,8 +71,25 @@ public class SvnUtil { @NonNls public static final String PATH_TO_LOCK_FILE = SVN_ADMIN_DIR_NAME + "/lock"; private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnUtil"); + public static final Pattern ERROR_PATTERN = Pattern.compile("^svn: (E(\\d+)): (.*)$", Pattern.MULTILINE); + public static final Pattern WARNING_PATTERN = Pattern.compile("^svn: warning: (W(\\d+)): (.*)$", Pattern.MULTILINE); + private SvnUtil() { } + @Nullable + public static SVNErrorMessage parseWarning(@NotNull String text) { + Matcher matcher = WARNING_PATTERN.matcher(text); + SVNErrorMessage error = null; + + // currently treating only first warning + if (matcher.find()) { + error = SVNErrorMessage + .create(SVNErrorCode.getErrorCode(Integer.parseInt(matcher.group(2))), matcher.group(3), SVNErrorMessage.TYPE_WARNING); + } + + return error; + } + public static boolean isSvnVersioned(final Project project, File parent) { final SVNInfo info = SvnVcs.getInstance(project).getInfo(parent); @@ -145,7 +167,7 @@ public class SvnUtil { force = false; } - final SVNException[] exception = new SVNException[1]; + final VcsException[] exception = new VcsException[1]; final Collection failedLocks = new ArrayList(); final int[] count = new int[]{ioFiles.length}; final ISVNEventHandler eventHandler = new ISVNEventHandler() { @@ -165,11 +187,8 @@ public class SvnUtil { Runnable command = new Runnable() { public void run() { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - SVNWCClient wcClient; try { - wcClient = activeVcs.createWCClient(); - wcClient.setEventHandler(eventHandler); if (progress != null) { progress.setText(SvnBundle.message("progress.text.locking.files")); } @@ -180,10 +199,10 @@ public class SvnUtil { if (progress != null) { progress.setText2(SvnBundle.message("progress.text2.processing.file", ioFile.getName())); } - wcClient.doLock(new File[]{ioFile}, force, lockMessage); + activeVcs.getFactory(ioFile).createLockClient().lock(ioFile, force, lockMessage, eventHandler); } } - catch (SVNException e) { + catch (VcsException e) { exception[0] = e; } } @@ -207,13 +226,13 @@ public class SvnUtil { StatusBarUtil.setStatusBarInfo(project, SvnBundle.message("message.text.files.locked", count[0])); if (exception[0] != null) { - throw new VcsException(exception[0]); + throw exception[0]; } } public static void doUnlockFiles(Project project, final SvnVcs activeVcs, final File[] ioFiles) throws VcsException { final boolean force = true; - final SVNException[] exception = new SVNException[1]; + final VcsException[] exception = new VcsException[1]; final Collection failedUnlocks = new ArrayList(); final int[] count = new int[]{ioFiles.length}; final ISVNEventHandler eventHandler = new ISVNEventHandler() { @@ -233,11 +252,8 @@ public class SvnUtil { Runnable command = new Runnable() { public void run() { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - SVNWCClient wcClient; try { - wcClient = activeVcs.createWCClient(); - wcClient.setEventHandler(eventHandler); if (progress != null) { progress.setText(SvnBundle.message("progress.text.unlocking.files")); } @@ -248,10 +264,10 @@ public class SvnUtil { if (progress != null) { progress.setText2(SvnBundle.message("progress.text2.processing.file", ioFile.getName())); } - wcClient.doUnlock(new File[]{ioFile}, force); + activeVcs.getFactory(ioFile).createLockClient().unlock(ioFile, force, eventHandler); } } - catch (SVNException e) { + catch (VcsException e) { exception[0] = e; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 26cffa159639..8aa0e583829b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -362,7 +362,7 @@ public class SvnVcs extends AbstractVcs { public boolean checkCommandLineVersion() { boolean isValid = true; - if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration) || isProject18()) { + if (!isProject16() && (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration) || isProject18())) { isValid = myChecker.checkExecutableAndNotifyIfNeeded(); } @@ -892,7 +892,7 @@ public class SvnVcs extends AbstractVcs { public boolean fileExistsInVcs(FilePath path) { File file = path.getIOFile(); try { - SVNStatus status = createStatusClient().doStatus(file, false); + SVNStatus status = getFactory(file).createStatusClient().doStatus(file, false); if (status != null) { if (svnStatusIs(status, SVNStatusType.STATUS_ADDED)) { return status.isCopied(); @@ -903,7 +903,7 @@ public class SvnVcs extends AbstractVcs { } } catch (SVNException e) { - // + LOG.info(e); } return false; } @@ -1341,10 +1341,20 @@ public class SvnVcs extends AbstractVcs { } public boolean isProject18() { - return WorkingCopyFormat.ONE_DOT_EIGHT.equals(getWorkingCopyFormat(new File(getProject().getBaseDir().getPath()))); + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(getProjectRootFormat()); + } + + public boolean isProject16() { + return WorkingCopyFormat.ONE_DOT_SIX.equals(getProjectRootFormat()); + } + + private WorkingCopyFormat getProjectRootFormat() { + return getWorkingCopyFormat(new File(getProject().getBaseDir().getPath())); } /** + * Detects appropriate client factory based on project root directory working copy format. + * * Try to avoid usages of this method (for now) as it could not correctly for all cases * detect svn 1.8 working copy format to guarantee command line client. * @@ -1355,18 +1365,23 @@ public class SvnVcs extends AbstractVcs { */ @NotNull public ClientFactory getFactory() { - // check working copy format of project directory - return isProject18() ? cmdClientFactory : getFactoryFromSettings(); + return getFactory(getProjectRootFormat(), false); } @NotNull public ClientFactory getFactory(@NotNull File file) { - WorkingCopyFormat format = getWorkingCopyFormat(file); + return getFactory(getWorkingCopyFormat(file), true); + } + @NotNull + private ClientFactory getFactory(@NotNull WorkingCopyFormat format, boolean useProjectRootForUnknown) { boolean is18 = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format); + boolean is16 = WorkingCopyFormat.ONE_DOT_SIX.equals(format); boolean isUnknown = WorkingCopyFormat.UNKNOWN.equals(format); - return is18 ? cmdClientFactory : (isUnknown ? getFactory() : getFactoryFromSettings()); + return is18 + ? cmdClientFactory + : (is16 ? svnKitClientFactory : (useProjectRootForUnknown && isUnknown ? getFactory() : getFactoryFromSettings())); } @NotNull @@ -1375,7 +1390,7 @@ public class SvnVcs extends AbstractVcs { } @NotNull - private ClientFactory getFactoryFromSettings() { + public ClientFactory getFactoryFromSettings() { return myConfiguration.myUseAcceleration.equals(SvnConfiguration.UseAcceleration.commandLine) ? cmdClientFactory : svnKitClientFactory; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java index 513977068b14..3f740bad1bef 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java @@ -28,9 +28,9 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.dialogs.SetPropertyDialog; -import org.tmatesoft.svn.core.SVNException; +import org.jetbrains.idea.svn.properties.PropertyClient; +import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNPropertyValue; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; @@ -73,15 +73,13 @@ public class SetPropertyAction extends BasicAction { String value = dialog.getPropertyValue(); boolean recursive = dialog.isRecursive(); - SVNWCClient wcClient = activeVcs.createWCClient(); for (int i = 0; i < ioFiles.length; i++) { File ioFile = ioFiles[i]; - try { - wcClient.doSetProperty(ioFile, name, SVNPropertyValue.create(value), false, recursive, null); - } - catch (SVNException e) { - throw new VcsException(e); - } + PropertyClient client = activeVcs.getFactory(ioFile).createPropertyClient(); + + // TODO: most likely SVNDepth.getInfinityOrEmptyDepth should be used instead of SVNDepth.fromRecursive - to have either "infinity" + // TODO: or "empty" depth, and not "infinity" or "files" depth. But previous logic used SVNDepth.fromRecursive implicitly + client.setProperty(ioFile, name, SVNPropertyValue.create(value), SVNDepth.fromRecurse(recursive), false); } for(int i = 0; i < file.length; i++) { if (recursive && file[i].isDirectory()) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java index 3d49e5158224..e7a064609fbf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java @@ -5,12 +5,14 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.add.AddClient; import org.jetbrains.idea.svn.annotate.AnnotateClient; import org.jetbrains.idea.svn.change.ChangeListClient; +import org.jetbrains.idea.svn.checkout.CheckoutClient; import org.jetbrains.idea.svn.conflict.ConflictClient; import org.jetbrains.idea.svn.content.ContentClient; import org.jetbrains.idea.svn.copy.CopyMoveClient; import org.jetbrains.idea.svn.delete.DeleteClient; import org.jetbrains.idea.svn.history.HistoryClient; import org.jetbrains.idea.svn.integrate.MergeClient; +import org.jetbrains.idea.svn.lock.LockClient; import org.jetbrains.idea.svn.portable.SvnStatusClientI; import org.jetbrains.idea.svn.portable.SvnWcClientI; import org.jetbrains.idea.svn.properties.PropertyClient; @@ -37,6 +39,8 @@ public abstract class ClientFactory { protected PropertyClient propertyClient; protected MergeClient mergeClient; protected ChangeListClient changeListClient; + protected CheckoutClient checkoutClient; + protected LockClient myLockClient; protected ClientFactory(@NotNull SvnVcs vcs) { myVcs = vcs; @@ -112,6 +116,16 @@ public abstract class ClientFactory { return prepare(changeListClient); } + @NotNull + public CheckoutClient createCheckoutClient() { + return prepare(checkoutClient); + } + + @NotNull + public LockClient createLockClient() { + return prepare(myLockClient); + } + @NotNull protected T prepare(@NotNull T client) { client.setVcs(myVcs); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java index 83c90280d359..b9fba158c718 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java @@ -5,6 +5,7 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.add.CmdAddClient; import org.jetbrains.idea.svn.annotate.CmdAnnotateClient; import org.jetbrains.idea.svn.change.CmdChangeListClient; +import org.jetbrains.idea.svn.checkout.CmdCheckoutClient; import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient; import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; import org.jetbrains.idea.svn.conflict.CmdConflictClient; @@ -13,6 +14,7 @@ import org.jetbrains.idea.svn.copy.CmdCopyMoveClient; import org.jetbrains.idea.svn.delete.CmdDeleteClient; import org.jetbrains.idea.svn.history.CmdHistoryClient; import org.jetbrains.idea.svn.integrate.CmdMergeClient; +import org.jetbrains.idea.svn.lock.CmdLockClient; import org.jetbrains.idea.svn.properties.CmdPropertyClient; import org.jetbrains.idea.svn.revert.CmdRevertClient; @@ -38,6 +40,8 @@ public class CmdClientFactory extends ClientFactory { propertyClient = new CmdPropertyClient(); mergeClient = new CmdMergeClient(); changeListClient = new CmdChangeListClient(); + checkoutClient = new CmdCheckoutClient(); + myLockClient = new CmdLockClient(); statusClient = new SvnCommandLineStatusClient(myVcs.getProject()); infoClient = new SvnCommandLineInfoClient(myVcs.getProject()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java index e94b9728c3b7..859774616dcf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java @@ -5,12 +5,14 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.add.SvnKitAddClient; import org.jetbrains.idea.svn.annotate.SvnKitAnnotateClient; import org.jetbrains.idea.svn.change.SvnKitChangeListClient; +import org.jetbrains.idea.svn.checkout.SvnKitCheckoutClient; import org.jetbrains.idea.svn.conflict.SvnKitConflictClient; import org.jetbrains.idea.svn.content.SvnKitContentClient; import org.jetbrains.idea.svn.copy.SvnKitCopyMoveClient; import org.jetbrains.idea.svn.delete.SvnKitDeleteClient; import org.jetbrains.idea.svn.history.SvnKitHistoryClient; import org.jetbrains.idea.svn.integrate.SvnKitMergeClient; +import org.jetbrains.idea.svn.lock.SvnKitLockClient; import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient; import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient; import org.jetbrains.idea.svn.properties.SvnKitPropertyClient; @@ -38,6 +40,8 @@ public class SvnKitClientFactory extends ClientFactory { propertyClient = new SvnKitPropertyClient(); mergeClient = new SvnKitMergeClient(); changeListClient = new SvnKitChangeListClient(); + checkoutClient = new SvnKitCheckoutClient(); + myLockClient = new SvnKitLockClient(); statusClient = new SvnkitSvnStatusClient(myVcs.createStatusClient()); infoClient = new SvnkitSvnWcClient(myVcs); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java new file mode 100644 index 000000000000..e7aa0299a00d --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CheckoutClient.java @@ -0,0 +1,27 @@ +package org.jetbrains.idea.svn.checkout; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.WorkingCopyFormat; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface CheckoutClient extends SvnClient { + + void checkout(@NotNull SvnTarget source, + @NotNull File destination, + @Nullable SVNRevision revision, + @Nullable SVNDepth depth, + boolean ignoreExternals, + @Nullable WorkingCopyFormat format, + @Nullable ISVNEventHandler handler) 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 new file mode 100644 index 000000000000..2ce677aa88dc --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/CmdCheckoutClient.java @@ -0,0 +1,53 @@ +package org.jetbrains.idea.svn.checkout; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.WorkingCopyFormat; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.BaseUpdateCommandListener; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdCheckoutClient extends BaseSvnClient implements CheckoutClient { + @Override + public void checkout(@NotNull SvnTarget source, + @NotNull File destination, + @Nullable SVNRevision revision, + @Nullable SVNDepth depth, + boolean ignoreExternals, + @Nullable WorkingCopyFormat format, + @Nullable ISVNEventHandler handler) throws VcsException { + List parameters = new ArrayList(); + + // TODO: check format + + CommandUtil.put(parameters, source); + CommandUtil.put(parameters, destination); + 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 + + run(destination, handler, parameters); + } + + private void run(@NotNull File destination, @Nullable ISVNEventHandler handler, @NotNull List parameters) throws VcsException { + BaseUpdateCommandListener listener = new BaseUpdateCommandListener(destination, handler); + + CommandUtil.execute(myVcs, SvnCommandName.checkout, parameters, null, listener); + + listener.throwWrappedIfException(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java index 20fe62410668..7b0ed8c35c2c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnCheckoutProvider.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.CheckoutProvider; import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx; import com.intellij.openapi.vcs.update.RefreshVFsSynchronously; import com.intellij.openapi.vfs.LocalFileSystem; @@ -42,8 +43,8 @@ import org.tmatesoft.svn.core.SVNCancelException; 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.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.swing.*; import java.io.File; @@ -78,24 +79,23 @@ public class SvnCheckoutProvider implements CheckoutProvider { final boolean ignoreExternals, final Listener listener, final WorkingCopyFormat selectedFormat) { final Ref checkoutSuccessful = new Ref(); - final SVNException[] exception = new SVNException[1]; + final Exception[] exception = new Exception[1]; 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 SVNUpdateClient client = SvnVcs.getInstance(project).createUpdateClient(); - if (! WorkingCopyFormat.ONE_DOT_SEVEN.equals(selectedFormat)) { - client.getOperationsFactory().setPrimaryWcGeneration(SvnWcGeneration.V16); - } - client.setEventHandler(new CheckoutEventHandler(SvnVcs.getInstance(project), false, ProgressManager.getInstance().getProgressIndicator())); - client.setIgnoreExternals(ignoreExternals); + 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 { - ProgressManager.progress(SvnBundle.message("progress.text.checking.out", target.getAbsolutePath())); - if (! WorkingCopyFormat.ONE_DOT_SEVEN.equals(SvnWorkingCopyFormatHolder.getPresetFormat())) { - client.getOperationsFactory().setPrimaryWcGeneration(SvnWcGeneration.V16); - } - client.doCheckout(SVNURL.parseURIEncoded(url), target, SVNRevision.UNDEFINED, revision, depth, true); + // 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); ProgressManager.checkCanceled(); checkoutSuccessful.set(Boolean.TRUE); } @@ -104,9 +104,10 @@ public class SvnCheckoutProvider implements CheckoutProvider { catch (SVNException e) { exception[0] = e; } + catch (VcsException e) { + exception[0] = e; + } finally { - client.setIgnoreExternals(false); - client.setEventHandler(null); SvnWorkingCopyFormatHolder.setPresetFormat(null); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java new file mode 100644 index 000000000000..ba46ec832b87 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkout/SvnKitCheckoutClient.java @@ -0,0 +1,55 @@ +package org.jetbrains.idea.svn.checkout; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.WorkingCopyFormat; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNUpdateClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitCheckoutClient extends BaseSvnClient implements CheckoutClient { + + @Override + public void checkout(@NotNull SvnTarget source, + @NotNull File destination, + @Nullable SVNRevision revision, + @Nullable SVNDepth depth, + boolean ignoreExternals, + @Nullable 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"); + } + + SVNUpdateClient client = myVcs.createUpdateClient(); + + // TODO: most likely we should compare directly with WorkingCopyFormat.ONE_DOT_SIX + if (!WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { + client.getOperationsFactory().setPrimaryWcGeneration(SvnWcGeneration.V16); + } + + client.setIgnoreExternals(ignoreExternals); + client.setEventHandler(handler); + + try { + client.doCheckout(source.getURL(), destination, source.getPegRevision(), revision, depth, true); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/BaseUpdateCommandListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/BaseUpdateCommandListener.java new file mode 100644 index 000000000000..d7878347bce3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/BaseUpdateCommandListener.java @@ -0,0 +1,80 @@ +package org.jetbrains.idea.svn.commandLine; + +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; + +import java.io.File; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Konstantin Kolosovsky. + */ +public class BaseUpdateCommandListener extends LineCommandListener { + + @NotNull + private final UpdateOutputLineConverter converter; + + @Nullable + private final ISVNEventHandler handler; + + @NotNull + private final AtomicReference exception; + + public BaseUpdateCommandListener(@NotNull File base, @Nullable ISVNEventHandler handler) { + this.handler = handler; + this.converter = new UpdateOutputLineConverter(base); + exception = new AtomicReference(); + } + + @Override + public void baseDirectory(File file) { + } + + @Override + public void onLineAvailable(String line, Key outputType) { + if (ProcessOutputTypes.STDOUT.equals(outputType)) { + final SVNEvent event = converter.convert(line); + if (event != null) { + beforeHandler(event); + try { + callHandler(event); + } + catch (SVNException e) { + cancel(); + exception.set(e); + } + } + } + } + + private void callHandler(SVNEvent event) throws SVNException { + if (handler != null) { + handler.handleEvent(event, 0.5); + } + } + + public void throwIfException() throws SVNException { + SVNException e = exception.get(); + + if (e != null) { + throw e; + } + } + + public void throwWrappedIfException() throws VcsException { + SVNException e = exception.get(); + + if (e != null) { + throw new VcsException(e); + } + } + + protected void beforeHandler(@NotNull SVNEvent event) { + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java index 73a8cef7707d..bcfcba50b87a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java @@ -21,12 +21,12 @@ import com.intellij.util.LineSeparator; import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnUtil; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import java.util.Map; import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. @@ -38,8 +38,6 @@ import java.util.regex.Pattern; */ public class SvnBindException extends VcsException { - private static final Pattern ERROR_PATTERN = Pattern.compile("^svn: (E(\\d+)): (.*)$", Pattern.MULTILINE); - private Map errors = new HashMap(); public SvnBindException(String message) { @@ -70,7 +68,7 @@ public class SvnBindException extends VcsException { } private void parseErrors(@NotNull String message) { - Matcher matcher = ERROR_PATTERN.matcher(message); + Matcher matcher = SvnUtil.ERROR_PATTERN.matcher(message); while (matcher.find()) { put(Integer.valueOf(matcher.group(2)), matcher.group()); 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 9e355e56f4d6..9a567d45ce7c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java @@ -143,6 +143,10 @@ public abstract class SvnCommand { return outputAdapter.getOutput().getStdout(); } + public String getErrorOutput() { + return outputAdapter.getOutput().getStderr(); + } + /** * Wait for process termination * @param timeout diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java index e0834c2f116f..33d0e867f6a1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java @@ -15,12 +15,11 @@ */ package org.jetbrains.idea.svn.commandLine; -import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; @@ -88,54 +87,8 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { base = base.isDirectory() ? base : base.getParentFile(); final List parameters = prepareParameters(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, makeParents); - - final AtomicReference excRef = new AtomicReference(); - final ISVNEventHandler handler = getEventHandler(); - final UpdateOutputLineConverter converter = new UpdateOutputLineConverter(base); + final BaseUpdateCommandListener listener = createCommandListener(paths, updatedToRevision, base); try { - final LineCommandListener listener = new LineCommandListener() { - final long[] myRevisions = new long[paths.length]; - - @Override - public void baseDirectory(File file) { - } - - @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) { - cancel(); - excRef.set(e); - } - } - } - } - - private void checkForUpdateCompleted(SVNEvent event) { - if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) { - final long eventRevision = event.getRevision(); - for (int i = 0; i < paths.length; i++) { - final File path = paths[i]; - if (FileUtil.filesEqual(path, event.getFile())) { - myRevisions[i] = eventRevision; - break; - } - } - } - } - - @Override - public void processTerminated(int exitCode) { - super.processTerminated(exitCode); - updatedToRevision.set(myRevisions); - } - }; SvnLineCommand.runWithAuthenticationAttempt(SvnApplicationSettings.getInstance().getCommandLinePath(), base, info.getURL(), SvnCommandName.up, listener, new IdeaSvnkitBasedAuthenticationCallback(SvnVcs.getInstance(myProject)), @@ -144,13 +97,40 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { catch (SvnBindException e) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); } - if (excRef.get() != null) { - throw excRef.get(); - } + + listener.throwIfException(); return updatedToRevision.get(); } + private BaseUpdateCommandListener createCommandListener(final File[] paths, + final AtomicReference updatedToRevision, + final File base) { + return new BaseUpdateCommandListener(base, getEventHandler()) { + final long[] myRevisions = new long[paths.length]; + + @Override + protected void beforeHandler(@NotNull SVNEvent event) { + if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) { + final long eventRevision = event.getRevision(); + for (int i = 0; i < paths.length; i++) { + final File path = paths[i]; + if (FileUtil.filesEqual(path, event.getFile())) { + myRevisions[i] = eventRevision; + break; + } + } + } + } + + @Override + public void processTerminated(int exitCode) { + super.processTerminated(exitCode); + updatedToRevision.set(myRevisions); + } + }; + } + private List prepareParameters(File[] paths, SVNRevision revision, SVNDepth depth, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java index 3841ec1f3258..af1ef68af323 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java @@ -29,6 +29,7 @@ public enum SvnCommandName { st("st", false), up("up", true), ci("commit", true), + checkout("checkout", true), cleanup("cleanup", true), cat("cat", false), add("add", true), @@ -40,9 +41,13 @@ public enum SvnCommandName { resolve("resolve", true), propget("propget", false), proplist("proplist", false), + propset("propset", true), + propdel("propdel", true), blame("blame", false), merge("merge", true), - changelist("changelist", true); + changelist("changelist", true), + lock("lock", true), + unlock("unlock", true); private final String myName; private final boolean myWriteable; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java index d8dbb6c19f67..4bf2786c9edf 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java @@ -281,10 +281,28 @@ public class SvnInfoHandler extends DefaultHandler { return new Lock(); } }); + myElementsMap.put("token", new Getter() { + @Override + public ElementHandlerBase get() { + return new LockToken(); + } + }); + myElementsMap.put("owner", new Getter() { + @Override + public ElementHandlerBase get() { + return new LockOwner(); + } + }); + myElementsMap.put("comment", new Getter() { + @Override + public ElementHandlerBase get() { + return new LockComment(); + } + }); myElementsMap.put("created", new Getter() { @Override public ElementHandlerBase get() { - return new Date(); + return new LockCreated(); } }); myElementsMap.put("uuid", new Getter() { @@ -778,17 +796,76 @@ public class SvnInfoHandler extends DefaultHandler { private static class Lock extends ElementHandlerBase { private Lock() { - super(new String[]{"created"}, new String[]{}); + super(new String[]{"token", "owner", "comment", "created"}, new String[]{}); } @Override protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { - // TODO: + structure.myLockWrapper = new SVNLockWrapper(); } @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - // TODO: + } + } + + private static class LockToken extends ElementHandlerBase { + private LockToken() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.myLockWrapper.setID(s); + } + } + + private static class LockOwner extends ElementHandlerBase { + private LockOwner() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.myLockWrapper.setOwner(s); + } + } + + private static class LockComment extends ElementHandlerBase { + private LockComment() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.myLockWrapper.setComment(s); + } + } + + private static class LockCreated extends ElementHandlerBase { + private LockCreated() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.myLockWrapper.setCreationDate(SVNDate.parseDate(s)); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java index 1aa5248c1939..fa47342b6270 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java @@ -53,7 +53,7 @@ public class SvnInfoStructure { public String myConflictNew; public String myConflictWorking; public String myPropRejectFile; - public SVNLock myLock; + public SVNLockWrapper myLockWrapper; public SVNDepth myDepth; public String myChangelistName; public long myWcSize; @@ -65,7 +65,18 @@ public class SvnInfoStructure { public SVNInfo convert() throws SAXException, SVNException { return new IdeaSVNInfo(myFile, myUrl, myRootURL, myRevision, myKind, myUuid, myCommittedRevision, myCommittedDate, myAuthor, mySchedule, myCopyFromURL, myCopyFromRevision, myTextTime, myPropTime, myChecksum, myConflictOld, myConflictNew, myConflictWorking, - myPropRejectFile, myLock, myDepth, myChangelistName, myWcSize, createTreeConflict()); + myPropRejectFile, getLock(), myDepth, myChangelistName, myWcSize, createTreeConflict()); + } + + private SVNLock getLock() { + SVNLock lock = null; + + if (myLockWrapper != null) { + myLockWrapper.setPath(relativeUrl); + lock = myLockWrapper.create(); + } + + return lock; } private SVNTreeConflictDescription createTreeConflict() throws SAXException, SVNException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java index 6be012bd1252..cfd6db992a03 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java @@ -53,17 +53,23 @@ public class UpdateOutputLineConverter { private final static Pattern ourAtRevision = Pattern.compile(AT_REVISION); private final static Pattern ourUpdatedToRevision = Pattern.compile(UPDATED_TO_REVISION); + private final static Pattern ourCheckedOutRevision = Pattern.compile("Checked out revision (\\d+)\\."); 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 static Pattern ourCheckedOutExternal = Pattern.compile("Checked out external at revision (\\d+)\\."); + + private final static Pattern[] ourCompletePatterns = + new Pattern[]{ourAtRevision, ourUpdatedToRevision, ourCheckedOutRevision, ourExternal, ourUpdatedExternal, ourCheckedOutExternal}; private final File myBase; private File myCurrentFile; public UpdateOutputLineConverter(File base) { myBase = base; + // checkout output does not have special line like "Updating '.'" on start - so set current file directly + // to correctly detect complete event + myCurrentFile = base; } public SVNEvent convert(final String line) { @@ -82,6 +88,7 @@ public class UpdateOutputLineConverter { 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)) { + // called, for instance, when folder is not working copy myCurrentFile = parseForPath(line); final String comment = parseComment(line); return new SVNEvent(myCurrentFile, myCurrentFile == null ? null : (myCurrentFile.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE), @@ -97,8 +104,12 @@ public class UpdateOutputLineConverter { 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); + // TODO: seems that myCurrentFile will not always be correct - complete update message could be right after complete externals update + // TODO: check this and use Stack instead + 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); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java index 34fac658afb6..b59e2b3a90ac 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; @@ -32,12 +33,13 @@ import com.intellij.ui.table.JBTable; import com.intellij.util.IconUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.swing.*; @@ -263,7 +265,30 @@ public class PropertiesComponent extends JPanel { } } - private class SetKeywordsAction extends AnAction { + private abstract class BasePropertyAction extends AnAction { + + protected void setProperty(@Nullable String property, @Nullable String value, boolean recursive, boolean force) { + if (!StringUtil.isEmpty(property)) { + try { + myVcs.getFactory(myFile).createPropertyClient() + .setProperty(myFile, property, value != null ? SVNPropertyValue.create(value) : null, + SVNDepth.getInfinityOrEmptyDepth(recursive), force); + } + catch (VcsException error) { + VcsBalloonProblemNotifier + .showOverChangesView(myVcs.getProject(), "Can not set property: " + error.getMessage(), MessageType.ERROR); + // show error message. + } + } + } + + protected void updateFileView(boolean recursive) { + setFile(myVcs, myFile); + updateFileStatus(recursive); + } + } + + private class SetKeywordsAction extends BasePropertyAction { public void update(AnActionEvent e) { e.getPresentation().setText("Edit Keywords"); @@ -276,7 +301,6 @@ public class PropertiesComponent extends JPanel { public void actionPerformed(AnActionEvent e) { Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); - SVNWCClient wcClient = myVcs.createWCClient(); SVNPropertyData propValue = null; try { propValue = myVcs.getFactory(myFile).createPropertyClient() @@ -290,21 +314,13 @@ public class PropertiesComponent extends JPanel { propValue != null ? SVNPropertyValue.getPropertyAsString(propValue.getValue()) : null); dialog.show(); if (dialog.isOK()) { - String value = dialog.getKeywords(); - try { - wcClient.doSetProperty(myFile, SVNProperty.KEYWORDS, SVNPropertyValue.create(value), false, false, null); - } - catch (SVNException err) { - // show error message - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Can not set property: " + err.getMessage(), MessageType.ERROR); - } + setProperty(SvnPropertyKeys.SVN_KEYWORDS, dialog.getKeywords(), false, false); } - setFile(myVcs, myFile); - updateFileStatus(false); + updateFileView(false); } } - private class DeletePropertyAction extends AnAction { + private class DeletePropertyAction extends BasePropertyAction { public void update(AnActionEvent e) { e.getPresentation().setText("Delete Property"); e.getPresentation().setDescription("Delete selected property"); @@ -315,18 +331,12 @@ public class PropertiesComponent extends JPanel { } public void actionPerformed(AnActionEvent e) { - try { - myVcs.createWCClient().doSetProperty(myFile, getSelectedPropertyName(), null, true, false, null); - } catch (SVNException error) { - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Can not set property: " + error.getMessage(), MessageType.ERROR); - // show error message. - } - setFile(myVcs, myFile); - updateFileStatus(false); + setProperty(getSelectedPropertyName(), null, false, true); + updateFileView(false); } } - private class AddPropertyAction extends AnAction { + private class AddPropertyAction extends BasePropertyAction { public void update(AnActionEvent e) { e.getPresentation().setText("Add Property"); @@ -344,24 +354,14 @@ public class PropertiesComponent extends JPanel { dialog.show(); boolean recursive = false; if (dialog.isOK()) { - String name = dialog.getPropertyName(); - String value = dialog.getPropertyValue(); recursive = dialog.isRecursive(); - SVNWCClient wcClient = myVcs.createWCClient(); - try { - wcClient.doSetProperty(myFile, name, SVNPropertyValue.create(value), false, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, null, null); - } - catch (SVNException err) { - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Can not set property: " + err.getMessage(), MessageType.ERROR); - // show error message - } + setProperty(dialog.getPropertyName(), dialog.getPropertyValue(), recursive, false); } - setFile(myVcs, myFile); - updateFileStatus(recursive); + updateFileView(recursive); } } - private class EditPropertyAction extends AnAction { + private class EditPropertyAction extends BasePropertyAction { public void update(AnActionEvent e) { e.getPresentation().setText("Edit Property"); e.getPresentation().setDescription("Edit selected property value"); @@ -377,20 +377,10 @@ public class PropertiesComponent extends JPanel { dialog.show(); boolean recursive = false; if (dialog.isOK()) { - String name = dialog.getPropertyName(); - String value = dialog.getPropertyValue(); recursive = dialog.isRecursive(); - SVNWCClient wcClient = myVcs.createWCClient(); - try { - wcClient.doSetProperty(myFile, name, SVNPropertyValue.create(value), false, recursive, null); - } - catch (SVNException err) { - VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "Can not set property: " + err.getMessage(), MessageType.ERROR); - // show error message - } + setProperty(dialog.getPropertyName(), dialog.getPropertyValue(), recursive, false); } - setFile(myVcs, myFile); - updateFileStatus(recursive); + updateFileView(recursive); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java index 486fe2c4355e..421f0a0ce138 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java @@ -15,22 +15,26 @@ */ package org.jetbrains.idea.svn.dialogs; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.ui.DocumentAdapter; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.SVNException; +import org.jetbrains.idea.svn.properties.PropertyClient; +import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.swing.*; import javax.swing.event.ChangeEvent; @@ -47,6 +51,9 @@ import java.util.TreeSet; * @author alex */ public class SetPropertyDialog extends DialogWrapper { + + private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.dialogs.SetPropertyDialog"); + private final String myPropertyName; private final File[] myFiles; @@ -147,14 +154,8 @@ public class SetPropertyDialog extends DialogWrapper { return; } File file = myFiles[0]; - SVNPropertyData property; - try { - SVNWCClient client = myVCS.createWCClient(); - property = client.doGetProperty(file, name, SVNRevision.WORKING, SVNRevision.WORKING); - } - catch (SVNException e) { - property = null; - } + SVNPropertyData property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; + if (property != null) { myValueText.setText(SVNPropertyValue.getPropertyAsString(property.getValue())); myValueText.selectAll(); @@ -164,6 +165,21 @@ public class SetPropertyDialog extends DialogWrapper { } } + private SVNPropertyData getProperty(@NotNull File file, @NotNull String name) { + SVNPropertyData property; + + try { + PropertyClient client = myVCS.getFactory(file).createPropertyClient(); + property = client.getProperty(SvnTarget.fromFile(file, SVNRevision.WORKING), name, false, SVNRevision.WORKING); + } + catch (VcsException e) { + LOG.info(e); + property = null; + } + + return property; + } + protected JComponent createCenterPanel() { fillPropertyNames(myFiles); if (myPropertyName != null) { @@ -189,23 +205,26 @@ public class SetPropertyDialog extends DialogWrapper { if (files.length == 1) { File file = files[0]; try { - SVNWCClient client = myVCS.createWCClient(); - client.doGetProperty(file, null, SVNRevision.WORKING, SVNRevision.WORKING, false, - new ISVNPropertyHandler() { - public void handleProperty(File path, SVNPropertyData property) { - String name = property.getName(); - if (name != null) { - names.add(name); - } - } - public void handleProperty(SVNURL url, SVNPropertyData property) { - } - public void handleProperty(long revision, SVNPropertyData property) { - } - }); + ISVNPropertyHandler handler = new ISVNPropertyHandler() { + public void handleProperty(File path, SVNPropertyData property) { + String name = property.getName(); + if (name != null) { + names.add(name); + } + } + + public void handleProperty(SVNURL url, SVNPropertyData property) { + } + + public void handleProperty(long revision, SVNPropertyData property) { + } + }; + + PropertyClient client = myVCS.getFactory(file).createPropertyClient(); + client.list(SvnTarget.fromFile(file, SVNRevision.WORKING), SVNRevision.WORKING, SVNDepth.EMPTY, handler); } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.info(e); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java index 28954a15edaa..cca0d3c3614a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CmdMergeClient.java @@ -1,27 +1,19 @@ package org.jetbrains.idea.svn.integrate; -import com.intellij.execution.process.ProcessOutputTypes; -import com.intellij.openapi.util.Key; import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; -import org.jetbrains.idea.svn.commandLine.CommandUtil; -import org.jetbrains.idea.svn.commandLine.LineCommandListener; -import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.jetbrains.idea.svn.commandLine.UpdateOutputLineConverter; +import org.jetbrains.idea.svn.commandLine.*; import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.SVNDiffOptions; -import org.tmatesoft.svn.core.wc.SVNEvent; import org.tmatesoft.svn.core.wc.SVNRevisionRange; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; /** * @author Konstantin Kolosovsky. @@ -109,52 +101,10 @@ public class CmdMergeClient extends BaseSvnClient implements MergeClient { } private void run(File destination, ISVNEventHandler handler, List parameters) throws VcsException { - final AtomicReference excRef = new AtomicReference(); - final UpdateOutputLineConverter converter = new UpdateOutputLineConverter(destination); - LineCommandListener listener = createListener(handler, excRef, converter); + BaseUpdateCommandListener listener = new BaseUpdateCommandListener(destination, handler); CommandUtil.execute(myVcs, SvnCommandName.merge, parameters, null, listener); - throwIfException(excRef); - } - - private static void throwIfException(AtomicReference exception) throws VcsException { - SVNException e = exception.get(); - - if (e != null) { - throw new VcsException(e); - } - } - - private static LineCommandListener createListener(@Nullable final ISVNEventHandler handler, - @NotNull final AtomicReference excRef, - @NotNull final UpdateOutputLineConverter converter) { - LineCommandListener result = null; - - if (handler != null) { - result = new LineCommandListener() { - @Override - public void baseDirectory(File file) { - } - - @Override - public void onLineAvailable(String line, Key outputType) { - if (ProcessOutputTypes.STDOUT.equals(outputType)) { - final SVNEvent event = converter.convert(line); - if (event != null) { - try { - handler.handleEvent(event, 0.5); - } - catch (SVNException e) { - cancel(); - excRef.set(e); - } - } - } - } - }; - } - - return result; + listener.throwWrappedIfException(); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java new file mode 100644 index 000000000000..a411a6c224f3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/CmdLockClient.java @@ -0,0 +1,85 @@ +package org.jetbrains.idea.svn.lock; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommand; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNEventAction; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdLockClient extends BaseSvnClient implements LockClient { + + @Override + public void lock(@NotNull File file, boolean force, @NotNull String message, @Nullable ISVNEventHandler handler) throws VcsException { + List parameters = prepareParameters(file, force); + + parameters.add("--message"); + parameters.add(message); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.lock, parameters, null); + handleCommandCompletion(command, file, SVNEventAction.LOCKED, SVNEventAction.LOCK_FAILED, handler); + } + + @Override + public void unlock(@NotNull File file, boolean force, @Nullable ISVNEventHandler handler) throws VcsException { + List parameters = prepareParameters(file, force); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.unlock, parameters, null); + handleCommandCompletion(command, file, SVNEventAction.UNLOCKED, SVNEventAction.UNLOCK_FAILED, handler); + } + + private static List prepareParameters(@NotNull File file, boolean force) { + List parameters = new ArrayList(); + + CommandUtil.put(parameters, file); + CommandUtil.put(parameters, force, "--force"); + + return parameters; + } + + private static void handleCommandCompletion(@NotNull SvnCommand command, + @NotNull File file, + @NotNull SVNEventAction success, + @NotNull SVNEventAction failure, + @Nullable ISVNEventHandler handler) throws VcsException { + // just warning appears in output when can not lock/unlock file for some reason (like, that file is already locked) + SVNErrorMessage error = SvnUtil.parseWarning(command.getErrorOutput()); + + try { + invokeHandler(file, error == null ? success : failure, handler, error); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private static void invokeHandler(@NotNull File file, + @NotNull SVNEventAction action, + @Nullable ISVNEventHandler handler, + @Nullable SVNErrorMessage error) + throws SVNException { + if (handler != null) { + handler.handleEvent(createEvent(file, action, error), 1); + } + } + + private static SVNEvent createEvent(@NotNull File file, @NotNull SVNEventAction action, @Nullable SVNErrorMessage error) { + return new SVNEvent(file, file.isDirectory() ? SVNNodeKind.DIR : SVNNodeKind.FILE, null, -1, null, null, null, null, action, action, + error, null, null, null, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/LockClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/LockClient.java new file mode 100644 index 000000000000..f36891f294b3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/LockClient.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.lock; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface LockClient extends SvnClient { + + void lock(@NotNull File file, + boolean force, + @NotNull String message, + @Nullable ISVNEventHandler handler) throws VcsException; + + void unlock(@NotNull File file, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/SvnKitLockClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/SvnKitLockClient.java new file mode 100644 index 000000000000..978c97dea637 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/lock/SvnKitLockClient.java @@ -0,0 +1,47 @@ +package org.jetbrains.idea.svn.lock; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitLockClient extends BaseSvnClient implements LockClient { + + @Override + public void lock(@NotNull File file, boolean force, @NotNull String message, @Nullable ISVNEventHandler handler) throws VcsException { + try { + getClient(handler).doLock(new File[]{file}, force, message); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } + + @Override + public void unlock(@NotNull File file, boolean force, @Nullable ISVNEventHandler handler) throws VcsException { + try { + getClient(handler).doUnlock(new File[]{file}, force); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } + + @NotNull + private SVNWCClient getClient(@Nullable ISVNEventHandler handler) { + SVNWCClient client = myVcs.createWCClient(); + + client.setEventHandler(handler); + + return client; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index df5230bc6a72..4013f97b7a77 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -88,6 +88,27 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { parseOutput(target, command.getOutput(), handler); } + @Override + public void setProperty(@NotNull File file, + @NotNull String property, + @Nullable SVNPropertyValue value, + @Nullable SVNDepth depth, + boolean force) throws VcsException { + List parameters = new ArrayList(); + boolean isDelete = value == null; + + parameters.add(property); + if (!isDelete) { + parameters.add(SVNPropertyValue.getPropertyAsString(value)); + // --force could only be used in "propset" command, but not in "propdel" command + CommandUtil.put(parameters, force, "--force"); + } + CommandUtil.put(parameters, file); + CommandUtil.put(parameters, depth); + + CommandUtil.execute(myVcs, isDelete ? SvnCommandName.propdel : SvnCommandName.propset, parameters, null); + } + private void fillListParameters(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNDepth depth, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 6e12f738ac87..2f1ba5ea7790 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -5,11 +5,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.SvnClient; import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; +import java.io.File; + /** * @author Konstantin Kolosovsky. */ @@ -30,4 +33,10 @@ public interface PropertyClient extends SvnClient { @Nullable SVNRevision revision, @Nullable SVNDepth depth, @Nullable ISVNPropertyHandler handler) throws VcsException; + + void setProperty(@NotNull File file, + @NotNull String property, + @Nullable SVNPropertyValue value, + @Nullable SVNDepth depth, + boolean force) throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index ef3c1b3888ee..ee72e59718ef 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -4,8 +4,10 @@ import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; import org.tmatesoft.svn.core.wc.SVNPropertyData; @@ -59,6 +61,20 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien runGetProperty(target, null, revision, depth, handler); } + @Override + public void setProperty(@NotNull File file, + @NotNull String property, + @Nullable SVNPropertyValue value, + @Nullable SVNDepth depth, + boolean force) throws VcsException { + try { + myVcs.createWCClient().doSetProperty(file, property, value, force, depth, null, null); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } + private void runGetProperty(@NotNull SvnTarget target, @Nullable String property, @Nullable SVNRevision revision,