From 27ea3a3073d4e0876c942ba1a622db1c2bef9a63 Mon Sep 17 00:00:00 2001 From: irengrig Date: Thu, 15 Nov 2012 15:11:04 +0400 Subject: [PATCH] IDEA-94873 Subversion: renamed/moved file located in root added as svn:externals gets incorrect status when command line client is used when parsing CL status, take into account that there could be externals in the way, and URLs should be calculated with respect to them + test --- .../SvnCommandLineStatusClient.java | 166 ++++++++++++------ .../idea/svn/portable/PortableStatus.java | 4 + .../svn/SvnParseCommandLineParseTest.java | 95 +++++++++- 3 files changed, 204 insertions(+), 61 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java index e4aa6b3f2da1..0c441e2af3fa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java @@ -26,10 +26,7 @@ import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; import org.jetbrains.idea.svn.portable.SvnStatusClientI; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; -import org.tmatesoft.svn.core.wc.ISVNStatusHandler; -import org.tmatesoft.svn.core.wc.SVNInfo; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.*; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -39,6 +36,8 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; /** * Created with IntelliJ IDEA. @@ -101,62 +100,10 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { // todo can not understand why revision can be used here final SvnSimpleCommand command = new SvnSimpleCommand(myProject, base, SvnCommandName.st); + putParameters(depth, remote, reportAll, includeIgnored, changeLists, command); - if (depth != null) { - command.addParameters("--depth", depth.getName()); - } - if (remote) { - command.addParameters("-u"); - } - if (reportAll) { - command.addParameters("-v"); - } - if (includeIgnored) { - command.addParameters("--no-ignore"); - } - // no way in interface to ignore externals - /*if (! collectParentExternals) { - command.addParameters("--ignore-externals"); - }*/ - - //--changelist (--cl) ARG - changelistsToCommand(changeLists, command); - command.addParameters("--xml"); - - final String[] changelistName = new String[1]; final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; - svnHandl[0] = new SvnStatusHandler(new SvnStatusHandler.ExternalDataCallback() { - @Override - public void switchPath() { - final PortableStatus pending = svnHandl[0].getPending(); - pending.setChangelistName(changelistName[0]); - try { - if (infoBase != null) { - final String append = SVNPathUtil.append(infoBase.getURL().toString(), FileUtil.toSystemIndependentName(pending.getPath())); - pending.setURL(SVNURL.parseURIEncoded(append)); - } - handler.handleStatus(pending); - } - catch (SVNException e) { - throw new SvnExceptionWrapper(e); - } - } - - @Override - public void switchChangeList(String newList) { - changelistName[0] = newList; - } - }, base, new Convertor() { - @Override - public SVNInfo convert(File o) { - try { - return myInfoClient.doInfo(o, revision); - } - catch (SVNException e) { - throw new SvnExceptionWrapper(e); - } - } - }); + svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); try { final String result = command.run(); @@ -185,6 +132,109 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { return 0; } + private void putParameters(SVNDepth depth, + boolean remote, + boolean reportAll, + boolean includeIgnored, + Collection changeLists, + SvnSimpleCommand command) { + if (depth != null) { + command.addParameters("--depth", depth.getName()); + } + if (remote) { + command.addParameters("-u"); + } + if (reportAll) { + command.addParameters("-v"); + } + if (includeIgnored) { + command.addParameters("--no-ignore"); + } + // no way in interface to ignore externals + /*if (! collectParentExternals) { + command.addParameters("--ignore-externals"); + }*/ + + //--changelist (--cl) ARG + changelistsToCommand(changeLists, command); + command.addParameters("--xml"); + } + + public SvnStatusHandler createStatusHandler(final SVNRevision revision, + final ISVNStatusHandler handler, + final File base, + final SVNInfo infoBase, final SvnStatusHandler[] svnHandl) { + final SvnStatusHandler.ExternalDataCallback callback = createStatusCallback(handler, base, infoBase, svnHandl); + + return new SvnStatusHandler(callback, base, new Convertor() { + @Override + public SVNInfo convert(File o) { + try { + return myInfoClient.doInfo(o, revision); + } + catch (SVNException e) { + throw new SvnExceptionWrapper(e); + } + } + }); + } + + public static SvnStatusHandler.ExternalDataCallback createStatusCallback(final ISVNStatusHandler handler, + final File base, + final SVNInfo infoBase, + final SvnStatusHandler[] svnHandl) { + final Map externalsMap = new HashMap(); + final String[] changelistName = new String[1]; + + return new SvnStatusHandler.ExternalDataCallback() { + @Override + public void switchPath() { + final PortableStatus pending = svnHandl[0].getPending(); + pending.setChangelistName(changelistName[0]); + try { + //if (infoBase != null) { + SVNInfo baseInfo = infoBase; + File baseFile = base; + final File pendingFile = new File(pending.getPath()); + if (! externalsMap.isEmpty()) { + for (File file : externalsMap.keySet()) { + if (FileUtil.isAncestor(file, pendingFile, false)) { + baseInfo = externalsMap.get(file); + baseFile = file; + break; + } + } + } + if (baseInfo != null) { + final String append; + final String systemIndependentPath = FileUtil.toSystemIndependentName(pending.getPath()); + if (pendingFile.isAbsolute()) { + final String relativePath = + FileUtil.getRelativePath(FileUtil.toSystemIndependentName(baseFile.getPath()), systemIndependentPath, '/'); + append = SVNPathUtil.append(baseInfo.getURL().toString(), FileUtil.toSystemIndependentName(relativePath)); + } + else { + append = SVNPathUtil.append(baseInfo.getURL().toString(), systemIndependentPath); + } + pending.setURL(SVNURL.parseURIEncoded(append)); + } + if (SVNStatusType.STATUS_EXTERNAL.equals(pending.getNodeStatus())) { + externalsMap.put(pending.getFile(), pending.getInfo()); + } + handler.handleStatus(pending); + } + catch (SVNException e) { + throw new SvnExceptionWrapper(e); + } + } + + @Override + public void switchChangeList(String newList) { + changelistName[0] = newList; + } + }; + } + public static void changelistsToCommand(Collection changeLists, SvnSimpleCommand command) { if (changeLists != null) { for (Object o : changeLists) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java index 667bfd2390ed..c834e3a17dbb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java @@ -150,6 +150,10 @@ public class PortableStatus extends SVNStatus { return myInfo; } + public SVNInfo getInfo() { + return initInfo(); + } + @Override public SVNNodeKind getKind() { if (myFileExists) return super.getKind(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java index 612e63660704..3af195443b4d 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnParseCommandLineParseTest.java @@ -21,6 +21,7 @@ import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; import junit.framework.Assert; import junit.framework.TestCase; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; import org.jetbrains.idea.svn.commandLine.SvnInfoHandler; import org.jetbrains.idea.svn.commandLine.SvnStatusHandler; import org.jetbrains.idea.svn.portable.IdeaSVNInfo; @@ -29,8 +30,10 @@ import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNStatusHandler; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatus; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -551,7 +554,8 @@ public class SvnParseCommandLineParseTest extends TestCase { "\n"; final SvnStatusHandler[] handlerArr = new SvnStatusHandler[1]; - final SvnStatusHandler handler = new SvnStatusHandler(new SvnStatusHandler.ExternalDataCallback() { + final SvnStatusHandler handler = new + SvnStatusHandler(new SvnStatusHandler.ExternalDataCallback() { @Override public void switchPath() { handlerArr[0].getPending().getKind(); @@ -574,8 +578,7 @@ public class SvnParseCommandLineParseTest extends TestCase { final int secondIdx = o.getPath().indexOf(":", idx + 1); Assert.assertTrue(o.getPath(), secondIdx == -1); try { - return new IdeaSVNInfo("C:/base/1", SVNURL.parseURIEncoded("http://a.b.c"), SVNRevision.HEAD, SVNNodeKind.FILE, "", - SVNURL.parseURIEncoded("http://a.b.c"), 1, new Date(), "me", null, SVNDepth.EMPTY, 1); + return createStubInfo("C:/base/1", "http://a.b.c"); } catch (SVNException e) { // @@ -589,4 +592,90 @@ public class SvnParseCommandLineParseTest extends TestCase { parser.parse(new ByteArrayInputStream(s.getBytes(CharsetToolkit.UTF8_CHARSET)), handler); final MultiMap changes = handler.getCurrentListChanges(); } + + private IdeaSVNInfo createStubInfo(final String basePath, final String baseUrl) throws SVNException { + return new IdeaSVNInfo(basePath, SVNURL.parseURIEncoded(baseUrl), SVNRevision.HEAD, SVNNodeKind.FILE, "", + SVNURL.parseURIEncoded("http://a.b.c"), 1, new Date(), "me", null, SVNDepth.EMPTY, 1); + } + + public void testStatusInExternalMove() throws Exception { + final String status = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "admin\n" + + "2012-09-12T12:16:51.621000Z\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "admin\n" + + "2012-09-12T12:16:51.621000Z\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + final String basePath = "C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main"; + final SvnStatusHandler[] handler = new SvnStatusHandler[1]; + final File baseFile = new File(basePath); + final SvnStatusHandler.ExternalDataCallback callback = SvnCommandLineStatusClient.createStatusCallback(new ISVNStatusHandler() { + @Override + public void handleStatus(SVNStatus status) throws SVNException { + System.out.println(status.getURL()); + if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\MacMessagesParser.java").equals(status.getFile())) { + Assert.assertEquals("http://external/src/com/slave/MacMessagesParser.java", status.getURL().toString()); + } + if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave\\src\\com\\slave\\SomeOtherClass.java").equals(status.getFile())) { + Assert.assertEquals("http://external/src/com/slave/SomeOtherClass.java", status.getURL().toString()); + } + } + }, baseFile, createStubInfo(basePath, "http://mainurl/"), handler); + handler[0] = new SvnStatusHandler(callback, baseFile, new Convertor() { + @Override + public SVNInfo convert(File o) { + try { + if (new File("C:\\TestProjects\\sortedProjects\\Subversion\\local2\\sep12main\\main\\slave").equals(o)) { + return createStubInfo(o.getPath(), "http://external"); + } + return createStubInfo(o.getPath(), "http://12345"); + } + catch (SVNException e) { + throw new RuntimeException(e); + } + } + }); + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + parser.parse(new ByteArrayInputStream(status.getBytes(CharsetToolkit.UTF8_CHARSET)), handler[0]); + final MultiMap changes = handler[0].getCurrentListChanges(); + } }