svn: Implemented "Change" (upgrade) action for command line client

This commit is contained in:
Konstantin Kolosovsky
2013-09-30 13:31:28 +04:00
parent 2b41af82f6
commit 9196b27213
12 changed files with 288 additions and 33 deletions
@@ -101,6 +101,10 @@ public enum WorkingCopyFormat {
return myFormat;
}
public boolean isOrGreater(@NotNull WorkingCopyFormat format) {
return myVersion.isOrGreaterThan(format.getVersion().major, format.getVersion().minor);
}
@NotNull
public static WorkingCopyFormat from(@NotNull Version version) {
WorkingCopyFormat result = UNKNOWN;
@@ -3,10 +3,17 @@ 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.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.WorkingCopyFormat;
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.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.Collection;
/**
@@ -56,4 +63,20 @@ public abstract class BaseSvnClient implements SvnClient {
String.format("%s format is not supported. Supported formats are: %s.", format.getName(), StringUtil.join(supported, ",")));
}
}
protected static void callHandler(@Nullable ISVNEventHandler handler, @NotNull SVNEvent event) throws VcsException {
if (handler != null) {
try {
handler.handleEvent(event, 0);
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
}
@NotNull
protected static SVNEvent createEvent(@NotNull File path, @Nullable SVNEventAction action) {
return new SVNEvent(path, null, null, 0, null, null, null, null, action, null, null, null, null, null, null);
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.idea.svn.portable.SvnWcClientI;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.jetbrains.idea.svn.revert.RevertClient;
import org.jetbrains.idea.svn.update.RelocateClient;
import org.jetbrains.idea.svn.upgrade.UpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -51,6 +52,7 @@ public abstract class ClientFactory {
protected VersionClient myVersionClient;
protected ImportClient myImportClient;
protected ExportClient myExportClient;
protected UpgradeClient myUpgradeClient;
protected ClientFactory(@NotNull SvnVcs vcs) {
myVcs = vcs;
@@ -166,6 +168,11 @@ public abstract class ClientFactory {
return prepare(myExportClient);
}
@NotNull
public UpgradeClient createUpgradeClient() {
return prepare(myUpgradeClient);
}
@NotNull
protected <T extends SvnClient> T prepare(@NotNull T client) {
client.setVcs(myVcs);
@@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.update.UpdateClient;
import org.jetbrains.idea.svn.properties.CmdPropertyClient;
import org.jetbrains.idea.svn.revert.CmdRevertClient;
import org.jetbrains.idea.svn.update.CmdRelocateClient;
import org.jetbrains.idea.svn.upgrade.CmdUpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -53,6 +54,7 @@ public class CmdClientFactory extends ClientFactory {
myVersionClient = new CmdVersionClient();
myImportClient = new CmdImportClient();
myExportClient = new CmdExportClient();
myUpgradeClient = new CmdUpgradeClient();
statusClient = new SvnCommandLineStatusClient(myVcs);
infoClient = new SvnCommandLineInfoClient(myVcs);
}
@@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient;
import org.jetbrains.idea.svn.properties.SvnKitPropertyClient;
import org.jetbrains.idea.svn.revert.SvnKitRevertClient;
import org.jetbrains.idea.svn.update.SvnKitRelocateClient;
import org.jetbrains.idea.svn.upgrade.SvnKitUpgradeClient;
/**
* @author Konstantin Kolosovsky.
@@ -53,6 +54,7 @@ public class SvnKitClientFactory extends ClientFactory {
myVersionClient = new SvnKitVersionClient();
myImportClient = new SvnKitImportClient();
myExportClient = new SvnKitExportClient();
myUpgradeClient = new SvnKitUpgradeClient();
statusClient = new SvnkitSvnStatusClient(myVcs, null);
infoClient = new SvnkitSvnWcClient(myVcs);
}
@@ -24,7 +24,7 @@ import java.util.List;
*/
public class SvnKitCheckoutClient extends BaseSvnClient implements CheckoutClient {
private static final List<WorkingCopyFormat> SUPPORTED_FORMATS;
public static final List<WorkingCopyFormat> SUPPORTED_FORMATS;
static {
List<WorkingCopyFormat> supportedFormats = new ArrayList<WorkingCopyFormat>();
@@ -49,7 +49,8 @@ public enum SvnCommandName {
lock("lock", true),
unlock("unlock", true),
importFolder("import", false),
export("export", false);
export("export", false),
upgrade("upgrade", true);
private final String myName;
private final boolean myWriteable;
@@ -17,12 +17,14 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
@@ -61,11 +63,13 @@ import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.*;
import java.util.List;
public class CopiesPanel {
private static final Logger LOG = Logger.getInstance(CopiesPanel.class);
private final Project myProject;
private MessageBusConnection myConnection;
private SvnVcs myVcs;
@@ -98,6 +102,7 @@ public class CopiesPanel {
@Override
public void run() {
final List<WCInfo> infoList = myVcs.getAllWcInfos();
final List<WorkingCopyFormat> supportedFormats = getSupportedFormats();
Runnable runnable = new Runnable() {
@Override
public void run() {
@@ -117,7 +122,7 @@ public class CopiesPanel {
myCurrentInfoList = newList;
}
Collections.sort(infoList, WCComparator.getInstance());
updateList(infoList);
updateList(infoList, supportedFormats);
myRefreshLabel.setEnabled(true);
SwingUtilities.invokeLater(focus);
}
@@ -178,7 +183,7 @@ public class CopiesPanel {
return myRefreshLabel;
}
private void updateList(final List<WCInfo> infoList) {
private void updateList(@NotNull final List<WCInfo> infoList, @NotNull final List<WorkingCopyFormat> supportedFormats) {
myPanel.removeAll();
final Insets nullIndent = new Insets(1, 3, 1, 0);
final GridBagConstraints gb =
@@ -217,7 +222,7 @@ public class CopiesPanel {
SVNDepth.INFINITY, false, null, wcInfo.getFormat());
}
} else if (CHANGE_FORMAT.equals(e.getDescription())) {
changeFormat(wcInfo);
changeFormat(wcInfo, supportedFormats);
} else if (MERGE_FROM.equals(e.getDescription())) {
if (! checkRoot(root, wcInfo.getPath(), " invoke Merge From")) return;
mergeFrom(wcInfo, root, editorPane);
@@ -237,7 +242,7 @@ public class CopiesPanel {
}
});
editorPane.setBorder(null);
editorPane.setText(formatWc(wcInfo));
editorPane.setText(formatWc(wcInfo, supportedFormats));
final JPanel copyPanel = new JPanel(new GridBagLayout());
@@ -263,14 +268,14 @@ public class CopiesPanel {
}
@SuppressWarnings("MethodMayBeStatic")
private String formatWc(WCInfo info) {
private String formatWc(@NotNull WCInfo info, @NotNull List<WorkingCopyFormat> supportedFormats) {
final StringBuilder sb = new StringBuilder().append("<html><head>").append(UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()))
.append("</head><body><table bgColor=\"").append(ColorUtil.toHex(UIUtil.getPanelBackground())).append("\">");
sb.append("<tr valign=\"top\"><td colspan=\"3\"><b>").append(info.getPath()).append("</b></td></tr>");
sb.append("<tr valign=\"top\"><td>URL:</td><td colspan=\"2\">").append(info.getRootUrl()).append("</td></tr>");
if (! WorkingCopyFormat.ONE_DOT_SEVEN.equals(info.getFormat())) {
// can convert
Collection<WorkingCopyFormat> upgradeFormats = getUpgradeFormats(info, supportedFormats);
if (upgradeFormats.size() > 1) {
sb.append("<tr valign=\"top\"><td>Format:</td><td>").append(info.getFormat().getName()).append("</td><td><a href=\"").
append(CHANGE_FORMAT).append("\">Change</a></td></tr>");
} else {
@@ -302,6 +307,33 @@ public class CopiesPanel {
return sb.toString();
}
@NotNull
private List<WorkingCopyFormat> getSupportedFormats() {
List<WorkingCopyFormat> result = Collections.emptyList();
try {
result = myVcs.getFactory().createUpgradeClient().getSupportedFormats();
}
catch (VcsException e) {
LOG.info(e);
}
return result;
}
public static Set<WorkingCopyFormat> getUpgradeFormats(@NotNull WCInfo info, @NotNull List<WorkingCopyFormat> supportedFormats) {
Set<WorkingCopyFormat> canUpgradeTo = EnumSet.noneOf(WorkingCopyFormat.class);
for (WorkingCopyFormat format : supportedFormats) {
if (format.isOrGreater(info.getFormat())) {
canUpgradeTo.add(format);
}
}
canUpgradeTo.add(info.getFormat());
return canUpgradeTo;
}
private void mergeFrom(@NotNull final WCInfo wcInfo, @NotNull final VirtualFile root, @Nullable final Component mergeLabel) {
SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() {
@Override
@@ -338,8 +370,10 @@ public class CopiesPanel {
});
}
private void changeFormat(final WCInfo wcInfo) {
private void changeFormat(@NotNull final WCInfo wcInfo, @NotNull final List<WorkingCopyFormat> supportedFormats) {
ChangeFormatDialog dialog = new ChangeFormatDialog(myProject, new File(wcInfo.getPath()), false, ! wcInfo.isIsWcRoot());
dialog.setSupported(supportedFormats);
dialog.setData(wcInfo.getFormat());
dialog.show();
if (! dialog.isOK()) {
@@ -37,7 +37,6 @@ import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.ArrayList;
@@ -122,20 +121,7 @@ public class SvnFormatWorker extends Task.Backgroundable {
if (supportsChangelists) {
myBeforeChangeLists = ChangeListManager.getInstance(myProject).getChangeListsCopy();
}
final SVNWCClient wcClient = myVcs.createWCClient();
wcClient.setEventHandler(new ISVNEventHandler() {
@Override
public void handleEvent(SVNEvent event, double progress) throws SVNException {
if (SVNEventAction.UPGRADED_PATH.equals(event.getAction()) && event.getFile() != null) {
indicator.setText2("Upgraded path " + VcsUtil.getPathForProgressPresentation(event.getFile()));
}
}
@Override
public void checkCancelled() throws SVNCancelException {
indicator.checkCanceled();
}
});
try {
for (WCInfo wcInfo : myWcInfos) {
File path = new File(wcInfo.getPath());
@@ -143,13 +129,13 @@ public class SvnFormatWorker extends Task.Backgroundable {
path = SvnUtil.getWorkingCopyRoot(path);
}
try {
if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(myNewFormat)) {
indicator.setText(SvnBundle.message("action.Subversion.cleanup.progress.text", path.getAbsolutePath()));
wcClient.doCleanup(path);
}
indicator.setText(SvnBundle.message("action.change.wcopy.format.task.progress.text", path.getAbsolutePath(),
SvnUtil.formatRepresentation(wcInfo.getFormat()), SvnUtil.formatRepresentation(myNewFormat)));
wcClient.doSetWCFormat(path, myNewFormat.getFormat());
String cleanupMessage = SvnBundle.message("action.Subversion.cleanup.progress.text", path.getAbsolutePath());
String upgradeMessage = SvnBundle.message("action.change.wcopy.format.task.progress.text", path.getAbsolutePath(),
SvnUtil.formatRepresentation(wcInfo.getFormat()),
SvnUtil.formatRepresentation(myNewFormat));
ISVNEventHandler handler = createUpgradeHandler(indicator, cleanupMessage, upgradeMessage);
myVcs.getFactory(path).createUpgradeClient().upgrade(path, myNewFormat, handler);
} catch (Throwable e) {
myExceptions.add(e);
}
@@ -166,4 +152,32 @@ public class SvnFormatWorker extends Task.Backgroundable {
ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs.WC_CONVERTED).run();
}
}
private static ISVNEventHandler createUpgradeHandler(@NotNull final ProgressIndicator indicator,
@NotNull final String cleanupMessage,
@NotNull final String upgradeMessage) {
return new ISVNEventHandler() {
@Override
public void handleEvent(SVNEvent event, double progress) throws SVNException {
if (event.getFile() != null) {
if (SVNEventAction.UPGRADED_PATH.equals(event.getAction())) {
indicator.setText2("Upgraded path " + VcsUtil.getPathForProgressPresentation(event.getFile()));
}
// fake event indicating cleanup start
if (SVNEventAction.UPDATE_STARTED.equals(event.getAction())) {
indicator.setText(cleanupMessage);
}
// fake event indicating upgrade start
if (SVNEventAction.UPDATE_COMPLETED.equals(event.getAction())) {
indicator.setText(upgradeMessage);
}
}
}
@Override
public void checkCancelled() throws SVNCancelException {
indicator.checkCanceled();
}
};
}
}
@@ -0,0 +1,83 @@
package org.jetbrains.idea.svn.upgrade;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.util.containers.Convertor;
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.api.FileStatusResultParser;
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.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Konstantin Kolosovsky.
*/
public class CmdUpgradeClient extends BaseSvnClient implements UpgradeClient {
private static final String STATUS = "\\s*(.+?)\\s*";
private static final String PATH = "\\s*\'(.*?)\'\\s*";
private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH);
@Override
public void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException {
validateFormat(format, getSupportedFormats());
// fake event indicating upgrade start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_COMPLETED));
List<String> parameters = new ArrayList<String>();
CommandUtil.put(parameters, path);
// TODO: handler should be called in parallel with command execution, but this will be in other thread
// TODO: check if that is ok for current handler implementation
// TODO: add possibility to invoke "handler.checkCancelled" - process should be killed
// for 1.8 - no output
// for 1.7 - output in format "Upgraded '<path>'"
SvnCommand command = CommandUtil.execute(myVcs, SvnTarget.fromFile(path), SvnCommandName.upgrade, parameters, null);
FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new UpgradeStatusConvertor());
parser.parse(command.getOutput());
}
@Override
public List<WorkingCopyFormat> getSupportedFormats() throws VcsException {
List<WorkingCopyFormat> result = new ArrayList<WorkingCopyFormat>();
result.add(WorkingCopyFormat.from(myFactory.createVersionClient().getVersion()));
return result;
}
private static class UpgradeStatusConvertor implements Convertor<Matcher, SVNEvent> {
public SVNEvent convert(@NotNull Matcher matcher) {
String statusMessage = matcher.group(1);
String path = matcher.group(2);
return createEvent(new File(path), createAction(statusMessage));
}
@Nullable
public static SVNEventAction createAction(@NotNull String code) {
SVNEventAction result = null;
if ("Upgraded".equals(code)) {
result = SVNEventAction.UPGRADED_PATH;
}
return result;
}
}
}
@@ -0,0 +1,64 @@
package org.jetbrains.idea.svn.upgrade;
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.checkout.SvnKitCheckoutClient;
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.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public class SvnKitUpgradeClient extends BaseSvnClient implements UpgradeClient {
@Override
public void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException {
validateFormat(format, getSupportedFormats());
SVNWCClient client = myVcs.createWCClient();
client.setEventHandler(handler);
try {
cleanupIfNecessary(path, format, client, handler);
upgrade(path, format, client, handler);
}
catch (SVNException e) {
throw new SvnBindException(e);
}
}
@Override
public List<WorkingCopyFormat> getSupportedFormats() throws VcsException {
return SvnKitCheckoutClient.SUPPORTED_FORMATS;
}
private static void cleanupIfNecessary(@NotNull File path,
@NotNull WorkingCopyFormat format,
@NotNull SVNWCClient client,
@Nullable ISVNEventHandler handler) throws SVNException, VcsException {
// cleanup is executed only for SVNKit as it could handle both 1.6 and 1.7 formats
if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) {
// fake event indicating cleanup start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_STARTED));
client.doCleanup(path);
}
}
private static void upgrade(@NotNull File path,
@NotNull WorkingCopyFormat format,
@NotNull SVNWCClient client,
@Nullable ISVNEventHandler handler) throws SVNException, VcsException {
// fake event indicating upgrade start
callHandler(handler, createEvent(path, SVNEventAction.UPDATE_COMPLETED));
client.doSetWCFormat(path, format.getFormat());
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.idea.svn.upgrade;
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.wc.ISVNEventHandler;
import java.io.File;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public interface UpgradeClient extends SvnClient {
void upgrade(@NotNull File path, @NotNull WorkingCopyFormat format, @Nullable ISVNEventHandler handler) throws VcsException;
List<WorkingCopyFormat> getSupportedFormats() throws VcsException;
}