eric6/Plugins/VcsPlugins/vcsSubversion/subversion.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7167
b3557e77314a
diff -r f99d60d6b59b -r 2602857055c5 eric6/Plugins/VcsPlugins/vcsSubversion/subversion.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/VcsPlugins/vcsSubversion/subversion.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,2389 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2003 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the version control systems interface to Subversion.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+import os
+import re
+import shutil
+
+from PyQt5.QtCore import pyqtSignal, QProcess, QRegExp, QCoreApplication
+from PyQt5.QtWidgets import QLineEdit, QDialog, QInputDialog, QApplication
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox
+
+from VCS.VersionControl import VersionControl
+
+from .SvnDialog import SvnDialog
+from .SvnUtilities import getConfigPath, amendConfig, createDefaultConfig
+
+import Preferences
+import Utilities
+
+
+class Subversion(VersionControl):
+    """
+    Class implementing the version control systems interface to Subversion.
+    
+    @signal committed() emitted after the commit action has completed
+    """
+    committed = pyqtSignal()
+    
+    def __init__(self, plugin, parent=None, name=None):
+        """
+        Constructor
+        
+        @param plugin reference to the plugin object
+        @param parent parent widget (QWidget)
+        @param name name of this object (string)
+        """
+        VersionControl.__init__(self, parent, name)
+        self.defaultOptions = {
+            'global': [''],
+            'commit': [''],
+            'checkout': [''],
+            'update': [''],
+            'add': [''],
+            'remove': [''],
+            'diff': [''],
+            'log': [''],
+            'history': [''],
+            'status': [''],
+            'tag': [''],
+            'export': ['']
+        }
+        self.interestingDataKeys = [
+            "standardLayout",
+        ]
+        
+        self.__plugin = plugin
+        self.__ui = parent
+        
+        self.options = self.defaultOptions
+        self.otherData["standardLayout"] = True
+        self.tagsList = []
+        self.branchesList = []
+        self.allTagsBranchesList = []
+        self.mergeList = [[], [], []]
+        self.showedTags = False
+        self.showedBranches = False
+        
+        self.tagTypeList = [
+            'tags',
+            'branches',
+        ]
+        
+        self.commandHistory = []
+        self.wdHistory = []
+        
+        if "SVN_ASP_DOT_NET_HACK" in os.environ:
+            self.adminDir = '_svn'
+        else:
+            self.adminDir = '.svn'
+        
+        self.log = None
+        self.diff = None
+        self.sbsDiff = None
+        self.status = None
+        self.propList = None
+        self.tagbranchList = None
+        self.blame = None
+        self.repoBrowser = None
+        self.logBrowser = None
+        
+        # regular expression object for evaluation of the status output
+        self.rx_status1 = QRegExp(
+            '(.{8})\\s+([0-9-]+)\\s+([0-9?]+)\\s+(\\S+)\\s+(.+)')
+        self.rx_status2 = QRegExp('(.{8})\\s+(.+)\\s*')
+        self.statusCache = {}
+        
+        self.__commitData = {}
+        self.__commitDialog = None
+        
+        self.__wcng = True
+        # assume new generation working copy metadata format
+        
+    def getPlugin(self):
+        """
+        Public method to get a reference to the plugin object.
+        
+        @return reference to the plugin object (VcsSubversionPlugin)
+        """
+        return self.__plugin
+        
+    def vcsShutdown(self):
+        """
+        Public method used to shutdown the Subversion interface.
+        """
+        if self.log is not None:
+            self.log.close()
+        if self.diff is not None:
+            self.diff.close()
+        if self.sbsDiff is not None:
+            self.sbsDiff.close()
+        if self.status is not None:
+            self.status.close()
+        if self.propList is not None:
+            self.propList.close()
+        if self.tagbranchList is not None:
+            self.tagbranchList.close()
+        if self.blame is not None:
+            self.blame.close()
+        if self.repoBrowser is not None:
+            self.repoBrowser.close()
+        if self.logBrowser is not None:
+            self.logBrowser.close()
+        
+    def vcsExists(self):
+        """
+        Public method used to test for the presence of the svn executable.
+        
+        @return flag indicating the existance (boolean) and an error message
+            (string)
+        """
+        self.versionStr = ''
+        errMsg = ""
+        ioEncoding = Preferences.getSystem("IOEncoding")
+        
+        process = QProcess()
+        process.start('svn', ['--version'])
+        procStarted = process.waitForStarted(5000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished and process.exitCode() == 0:
+                output = \
+                    str(process.readAllStandardOutput(), ioEncoding, 'replace')
+                self.versionStr = output.split()[2]
+                v = list(re.match(r'.*?(\d+)\.(\d+)\.?(\d+)?', self.versionStr)
+                         .groups())
+                for i in range(3):
+                    try:
+                        v[i] = int(v[i])
+                    except TypeError:
+                        v[i] = 0
+                    except IndexError:
+                        v.append(0)
+                self.version = tuple(v)
+                return True, errMsg
+            else:
+                if finished:
+                    errMsg = self.tr(
+                        "The svn process finished with the exit code {0}")\
+                        .format(process.exitCode())
+                else:
+                    errMsg = self.tr(
+                        "The svn process did not finish within 30s.")
+        else:
+            errMsg = self.tr("Could not start the svn executable.")
+        
+        return False, errMsg
+        
+    def vcsInit(self, vcsDir, noDialog=False):
+        """
+        Public method used to initialize the subversion repository.
+        
+        The subversion repository has to be initialized from outside eric6
+        because the respective command always works locally. Therefore we
+        always return TRUE without doing anything.
+        
+        @param vcsDir name of the VCS directory (string)
+        @param noDialog flag indicating quiet operations (boolean)
+        @return always TRUE
+        """
+        return True
+        
+    def vcsConvertProject(self, vcsDataDict, project):
+        """
+        Public method to convert an uncontrolled project to a version
+        controlled project.
+        
+        @param vcsDataDict dictionary of data required for the conversion
+        @param project reference to the project object
+        """
+        success = self.vcsImport(vcsDataDict, project.ppath)[0]
+        if not success:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("Create project in repository"),
+                self.tr(
+                    """The project could not be created in the repository."""
+                    """ Maybe the given repository doesn't exist or the"""
+                    """ repository server is down."""))
+        else:
+            cwdIsPpath = False
+            if os.getcwd() == project.ppath:
+                os.chdir(os.path.dirname(project.ppath))
+                cwdIsPpath = True
+            tmpProjectDir = "{0}_tmp".format(project.ppath)
+            shutil.rmtree(tmpProjectDir, True)
+            os.rename(project.ppath, tmpProjectDir)
+            os.makedirs(project.ppath)
+            self.vcsCheckout(vcsDataDict, project.ppath)
+            if cwdIsPpath:
+                os.chdir(project.ppath)
+            self.vcsCommit(project.ppath, vcsDataDict["message"], True)
+            pfn = project.pfile
+            if not os.path.isfile(pfn):
+                pfn += "z"
+            if not os.path.isfile(pfn):
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("New project"),
+                    self.tr(
+                        """The project could not be checked out of the"""
+                        """ repository.<br />"""
+                        """Restoring the original contents."""))
+                if os.getcwd() == project.ppath:
+                    os.chdir(os.path.dirname(project.ppath))
+                    cwdIsPpath = True
+                else:
+                    cwdIsPpath = False
+                shutil.rmtree(project.ppath, True)
+                os.rename(tmpProjectDir, project.ppath)
+                project.pdata["VCS"] = 'None'
+                project.vcs = None
+                project.setDirty(True)
+                project.saveProject()
+                project.closeProject()
+                return
+            shutil.rmtree(tmpProjectDir, True)
+            project.closeProject(noSave=True)
+            project.openProject(pfn)
+        
+    def vcsImport(self, vcsDataDict, projectDir, noDialog=False):
+        """
+        Public method used to import the project into the Subversion
+        repository.
+        
+        @param vcsDataDict dictionary of data required for the import
+        @param projectDir project directory (string)
+        @param noDialog flag indicating quiet operations
+        @return flag indicating an execution without errors (boolean)
+            and a flag indicating the version control status (boolean)
+        """
+        noDialog = False
+        msg = vcsDataDict["message"]
+        if not msg:
+            msg = '***'
+        
+        vcsDir = self.svnNormalizeURL(vcsDataDict["url"])
+        if vcsDir.startswith('/'):
+            vcsDir = 'file://{0}'.format(vcsDir)
+        elif vcsDir[1] in ['|', ':']:
+            vcsDir = 'file:///{0}'.format(vcsDir)
+        
+        project = vcsDir[vcsDir.rfind('/') + 1:]
+        
+        # create the dir structure to be imported into the repository
+        tmpDir = '{0}_tmp'.format(projectDir)
+        try:
+            os.makedirs(tmpDir)
+            if self.otherData["standardLayout"]:
+                os.mkdir(os.path.join(tmpDir, project))
+                os.mkdir(os.path.join(tmpDir, project, 'branches'))
+                os.mkdir(os.path.join(tmpDir, project, 'tags'))
+                shutil.copytree(
+                    projectDir, os.path.join(tmpDir, project, 'trunk'))
+            else:
+                shutil.copytree(projectDir, os.path.join(tmpDir, project))
+        except OSError:
+            if os.path.isdir(tmpDir):
+                shutil.rmtree(tmpDir, True)
+            return False, False
+        
+        args = []
+        args.append('import')
+        self.addArguments(args, self.options['global'])
+        args.append('-m')
+        args.append(msg)
+        args.append(self.__svnURL(vcsDir))
+        
+        if noDialog:
+            status = self.startSynchronizedProcess(
+                QProcess(), "svn", args, os.path.join(tmpDir, project))
+        else:
+            dia = SvnDialog(
+                self.tr('Importing project into Subversion repository'))
+            res = dia.startProcess(args, os.path.join(tmpDir, project))
+            if res:
+                dia.exec_()
+            status = dia.normalExit()
+        
+        shutil.rmtree(tmpDir, True)
+        return status, False
+        
+    def vcsCheckout(self, vcsDataDict, projectDir, noDialog=False):
+        """
+        Public method used to check the project out of the Subversion
+        repository.
+        
+        @param vcsDataDict dictionary of data required for the checkout
+        @param projectDir project directory to create (string)
+        @param noDialog flag indicating quiet operations
+        @return flag indicating an execution without errors (boolean)
+        """
+        noDialog = False
+        try:
+            tag = vcsDataDict["tag"]
+        except KeyError:
+            tag = None
+        vcsDir = self.svnNormalizeURL(vcsDataDict["url"])
+        if vcsDir.startswith('/'):
+            vcsDir = 'file://{0}'.format(vcsDir)
+        elif vcsDir[1] in ['|', ':']:
+            vcsDir = 'file:///{0}'.format(vcsDir)
+            
+        if self.otherData["standardLayout"]:
+            if tag is None or tag == '':
+                svnUrl = '{0}/trunk'.format(vcsDir)
+            else:
+                if not tag.startswith('tags') and \
+                        not tag.startswith('branches'):
+                    tagType, ok = QInputDialog.getItem(
+                        None,
+                        self.tr("Subversion Checkout"),
+                        self.tr(
+                            "The tag must be a normal tag (tags) or"
+                            " a branch tag (branches)."
+                            " Please select from the list."),
+                        self.tagTypeList,
+                        0, False)
+                    if not ok:
+                        return False
+                    tag = '{0}/{1}'.format(tagType, tag)
+                svnUrl = '{0}/{1}'.format(vcsDir, tag)
+        else:
+            svnUrl = vcsDir
+        
+        args = []
+        args.append('checkout')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['checkout'])
+        args.append(self.__svnURL(svnUrl))
+        args.append(projectDir)
+        
+        if noDialog:
+            return self.startSynchronizedProcess(QProcess(), 'svn', args)
+        else:
+            dia = SvnDialog(
+                self.tr('Checking project out of Subversion repository'))
+            res = dia.startProcess(args)
+            if res:
+                dia.exec_()
+            return dia.normalExit()
+        
+    def vcsExport(self, vcsDataDict, projectDir):
+        """
+        Public method used to export a directory from the Subversion
+        repository.
+        
+        @param vcsDataDict dictionary of data required for the checkout
+        @param projectDir project directory to create (string)
+        @return flag indicating an execution without errors (boolean)
+        """
+        try:
+            tag = vcsDataDict["tag"]
+        except KeyError:
+            tag = None
+        vcsDir = self.svnNormalizeURL(vcsDataDict["url"])
+        if vcsDir.startswith('/') or vcsDir[1] == '|':
+            vcsDir = 'file://{0}'.format(vcsDir)
+            
+        if self.otherData["standardLayout"]:
+            if tag is None or tag == '':
+                svnUrl = '{0}/trunk'.format(vcsDir)
+            else:
+                if not tag.startswith('tags') and \
+                        not tag.startswith('branches'):
+                    tagType, ok = QInputDialog.getItem(
+                        None,
+                        self.tr("Subversion Export"),
+                        self.tr(
+                            "The tag must be a normal tag (tags) or"
+                            " a branch tag (branches)."
+                            " Please select from the list."),
+                        self.tagTypeList,
+                        0, False)
+                    if not ok:
+                        return False
+                    tag = '{0}/{1}'.format(tagType, tag)
+                svnUrl = '{0}/{1}'.format(vcsDir, tag)
+        else:
+            svnUrl = vcsDir
+        
+        args = []
+        args.append('export')
+        self.addArguments(args, self.options['global'])
+        args.append("--force")
+        args.append(self.__svnURL(svnUrl))
+        args.append(projectDir)
+        
+        dia = SvnDialog(
+            self.tr('Exporting project from Subversion repository'))
+        res = dia.startProcess(args)
+        if res:
+            dia.exec_()
+        return dia.normalExit()
+        
+    def vcsCommit(self, name, message, noDialog=False):
+        """
+        Public method used to make the change of a file/directory permanent
+        in the Subversion repository.
+        
+        @param name file/directory name to be committed (string or list of
+            strings)
+        @param message message for this operation (string)
+        @param noDialog flag indicating quiet operations
+        """
+        msg = message
+        
+        if not noDialog and not msg:
+            # call CommitDialog and get message from there
+            if self.__commitDialog is None:
+                from .SvnCommitDialog import SvnCommitDialog
+                self.__commitDialog = SvnCommitDialog(self, self.__ui)
+                self.__commitDialog.accepted.connect(self.__vcsCommit_Step2)
+            self.__commitDialog.show()
+            self.__commitDialog.raise_()
+            self.__commitDialog.activateWindow()
+        
+        self.__commitData["name"] = name
+        self.__commitData["msg"] = msg
+        self.__commitData["noDialog"] = noDialog
+        
+        if noDialog:
+            self.__vcsCommit_Step2()
+        
+    def __vcsCommit_Step2(self):
+        """
+        Private slot performing the second step of the commit action.
+        """
+        name = self.__commitData["name"]
+        msg = self.__commitData["msg"]
+        noDialog = self.__commitData["noDialog"]
+        
+        if not noDialog:
+            # check, if there are unsaved changes, that should be committed
+            if isinstance(name, list):
+                nameList = name
+            else:
+                nameList = [name]
+            ok = True
+            for nam in nameList:
+                # check for commit of the project
+                if os.path.isdir(nam):
+                    project = e5App().getObject("Project")
+                    if nam == project.getProjectPath():
+                        ok &= project.checkAllScriptsDirty(
+                            reportSyntaxErrors=True) and \
+                            project.checkDirty()
+                        continue
+                elif os.path.isfile(nam):
+                    editor = e5App().getObject("ViewManager")\
+                        .getOpenEditor(nam)
+                    if editor:
+                        ok &= editor.checkDirty()
+                if not ok:
+                    break
+            
+            if not ok:
+                res = E5MessageBox.yesNo(
+                    self.__ui,
+                    self.tr("Commit Changes"),
+                    self.tr(
+                        """The commit affects files, that have unsaved"""
+                        """ changes. Shall the commit be continued?"""),
+                    icon=E5MessageBox.Warning)
+                if not res:
+                    return
+        
+        if self.__commitDialog is not None:
+            msg = self.__commitDialog.logMessage()
+            if self.__commitDialog.hasChangelists():
+                changelists, keepChangelists = \
+                    self.__commitDialog.changelistsData()
+            else:
+                changelists, keepChangelists = [], False
+            self.__commitDialog.deleteLater()
+            self.__commitDialog = None
+        else:
+            changelists, keepChangelists = [], False
+        
+        if not msg:
+            msg = '***'
+        
+        args = []
+        args.append('commit')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['commit'])
+        if keepChangelists:
+            args.append("--keep-changelists")
+        for changelist in changelists:
+            args.append("--changelist")
+            args.append(changelist)
+        args.append("-m")
+        args.append(msg)
+        if isinstance(name, list):
+            dname, fnames = self.splitPathList(name)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(name)
+            args.append(fname)
+        
+        if self.svnGetReposName(dname).startswith('http') or \
+           self.svnGetReposName(dname).startswith('svn'):
+            noDialog = False
+        
+        if noDialog:
+            self.startSynchronizedProcess(QProcess(), "svn", args, dname)
+        else:
+            dia = SvnDialog(
+                self.tr('Commiting changes to Subversion repository'))
+            res = dia.startProcess(args, dname)
+            if res:
+                dia.exec_()
+        self.committed.emit()
+        self.checkVCSStatus()
+        
+    def vcsUpdate(self, name, noDialog=False):
+        """
+        Public method used to update a file/directory with the Subversion
+        repository.
+        
+        @param name file/directory name to be updated (string or list of
+            strings)
+        @param noDialog flag indicating quiet operations (boolean)
+        @return flag indicating, that the update contained an add
+            or delete (boolean)
+        """
+        args = []
+        args.append('update')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['update'])
+        if self.version >= (1, 5, 0):
+            args.append('--accept')
+            args.append('postpone')
+        if isinstance(name, list):
+            dname, fnames = self.splitPathList(name)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(name)
+            args.append(fname)
+        
+        if noDialog:
+            self.startSynchronizedProcess(QProcess(), "svn", args, dname)
+            res = False
+        else:
+            dia = SvnDialog(
+                self.tr('Synchronizing with the Subversion repository'))
+            res = dia.startProcess(args, dname, True)
+            if res:
+                dia.exec_()
+                res = dia.hasAddOrDelete()
+        self.checkVCSStatus()
+        return res
+        
+    def vcsAdd(self, name, isDir=False, noDialog=False):
+        """
+        Public method used to add a file/directory to the Subversion
+        repository.
+        
+        @param name file/directory name to be added (string)
+        @param isDir flag indicating name is a directory (boolean)
+        @param noDialog flag indicating quiet operations
+        """
+        args = []
+        args.append('add')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['add'])
+        args.append('--non-recursive')
+        if noDialog and '--force' not in args:
+            args.append('--force')
+        
+        if isinstance(name, list):
+            if isDir:
+                dname, fname = os.path.split(name[0])
+            else:
+                dname, fnames = self.splitPathList(name)
+        else:
+            if isDir:
+                dname, fname = os.path.split(name)
+            else:
+                dname, fname = self.splitPath(name)
+        tree = []
+        wdir = dname
+        if self.__wcng:
+            repodir = dname
+            while not os.path.isdir(os.path.join(repodir, self.adminDir)):
+                repodir = os.path.dirname(repodir)
+                if os.path.splitdrive(repodir)[1] == os.sep:
+                    return  # oops, project is not version controlled
+            while os.path.normcase(dname) != os.path.normcase(repodir) and \
+                    (os.path.normcase(dname) not in self.statusCache or
+                     self.statusCache[os.path.normcase(dname)] ==
+                        self.canBeAdded):
+                # add directories recursively, if they aren't in the
+                # repository already
+                tree.insert(-1, dname)
+                dname = os.path.dirname(dname)
+                wdir = dname
+        else:
+            while not os.path.exists(os.path.join(dname, self.adminDir)):
+                # add directories recursively, if they aren't in the
+                # repository already
+                tree.insert(-1, dname)
+                dname = os.path.dirname(dname)
+                wdir = dname
+        self.addArguments(args, tree)
+        
+        if isinstance(name, list):
+            tree2 = []
+            for n in name:
+                d = os.path.dirname(n)
+                if self.__wcng:
+                    repodir = d
+                    while not os.path.isdir(
+                            os.path.join(repodir, self.adminDir)):
+                        repodir = os.path.dirname(repodir)
+                        if os.path.splitdrive(repodir)[1] == os.sep:
+                            return  # oops, project is not version controlled
+                    while os.path.normcase(d) != \
+                        os.path.normcase(repodir) and \
+                            (d not in tree2 + tree) and \
+                            (os.path.normcase(d) not in self.statusCache or
+                             self.statusCache[os.path.normcase(d)] ==
+                                self.canBeAdded):
+                        tree2.append(d)
+                        d = os.path.dirname(d)
+                else:
+                    while not os.path.exists(os.path.join(d, self.adminDir)):
+                        if d in tree2 + tree:
+                            break
+                        tree2.append(d)
+                        d = os.path.dirname(d)
+            tree2.reverse()
+            self.addArguments(args, tree2)
+            self.addArguments(args, name)
+        else:
+            args.append(name)
+        
+        if noDialog:
+            self.startSynchronizedProcess(QProcess(), "svn", args, wdir)
+        else:
+            dia = SvnDialog(
+                self.tr('Adding files/directories to the Subversion'
+                        ' repository'))
+            res = dia.startProcess(args, wdir)
+            if res:
+                dia.exec_()
+        
+    def vcsAddBinary(self, name, isDir=False):
+        """
+        Public method used to add a file/directory in binary mode to the
+        Subversion repository.
+        
+        @param name file/directory name to be added (string)
+        @param isDir flag indicating name is a directory (boolean)
+        """
+        self.vcsAdd(name, isDir)
+        
+    def vcsAddTree(self, path):
+        """
+        Public method to add a directory tree rooted at path to the Subversion
+        repository.
+        
+        @param path root directory of the tree to be added (string or list of
+            strings))
+        """
+        args = []
+        args.append('add')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['add'])
+        
+        tree = []
+        if isinstance(path, list):
+            dname, fnames = self.splitPathList(path)
+            for n in path:
+                d = os.path.dirname(n)
+                if self.__wcng:
+                    repodir = d
+                    while not os.path.isdir(
+                            os.path.join(repodir, self.adminDir)):
+                        repodir = os.path.dirname(repodir)
+                        if os.path.splitdrive(repodir)[1] == os.sep:
+                            return  # oops, project is not version controlled
+                    while os.path.normcase(d) != \
+                        os.path.normcase(repodir) and \
+                            (d not in tree) and \
+                            (os.path.normcase(d) not in self.statusCache or
+                             self.statusCache[os.path.normcase(d)] ==
+                                self.canBeAdded):
+                        tree.append(d)
+                        d = os.path.dirname(d)
+                else:
+                    while not os.path.exists(os.path.join(d, self.adminDir)):
+                        # add directories recursively,
+                        # if they aren't in the repository already
+                        if d in tree:
+                            break
+                        tree.append(d)
+                        d = os.path.dirname(d)
+            tree.reverse()
+        else:
+            dname, fname = os.path.split(path)
+            if self.__wcng:
+                repodir = dname
+                while not os.path.isdir(os.path.join(repodir, self.adminDir)):
+                    repodir = os.path.dirname(repodir)
+                    if os.path.splitdrive(repodir)[1] == os.sep:
+                        return  # oops, project is not version controlled
+                while os.path.normcase(dname) != \
+                    os.path.normcase(repodir) and \
+                        (os.path.normcase(dname) not in self.statusCache or
+                         self.statusCache[os.path.normcase(dname)] ==
+                            self.canBeAdded):
+                    # add directories recursively, if they aren't in the
+                    # repository already
+                    tree.insert(-1, dname)
+                    dname = os.path.dirname(dname)
+            else:
+                while not os.path.exists(os.path.join(dname, self.adminDir)):
+                    # add directories recursively,
+                    # if they aren't in the repository already
+                    tree.insert(-1, dname)
+                    dname = os.path.dirname(dname)
+        if tree:
+            self.vcsAdd(tree, True)
+        
+        if isinstance(path, list):
+            self.addArguments(args, path)
+        else:
+            args.append(path)
+        
+        dia = SvnDialog(
+            self.tr('Adding directory trees to the Subversion repository'))
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def vcsRemove(self, name, project=False, noDialog=False):
+        """
+        Public method used to remove a file/directory from the Subversion
+        repository.
+        
+        The default operation is to remove the local copy as well.
+        
+        @param name file/directory name to be removed (string or list of
+            strings))
+        @param project flag indicating deletion of a project tree (boolean)
+            (not needed)
+        @param noDialog flag indicating quiet operations
+        @return flag indicating successfull operation (boolean)
+        """
+        args = []
+        args.append('delete')
+        self.addArguments(args, self.options['global'])
+        self.addArguments(args, self.options['remove'])
+        if noDialog and '--force' not in args:
+            args.append('--force')
+        
+        if isinstance(name, list):
+            self.addArguments(args, name)
+        else:
+            args.append(name)
+        
+        if noDialog:
+            res = self.startSynchronizedProcess(QProcess(), "svn", args)
+        else:
+            dia = SvnDialog(
+                self.tr('Removing files/directories from the Subversion'
+                        ' repository'))
+            res = dia.startProcess(args)
+            if res:
+                dia.exec_()
+                res = dia.normalExit()
+        
+        return res
+        
+    def vcsMove(self, name, project, target=None, noDialog=False):
+        """
+        Public method used to move a file/directory.
+        
+        @param name file/directory name to be moved (string)
+        @param project reference to the project object
+        @param target new name of the file/directory (string)
+        @param noDialog flag indicating quiet operations
+        @return flag indicating successfull operation (boolean)
+        """
+        rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+')
+        opts = self.options['global'][:]
+        force = '--force' in opts
+        if force:
+            del opts[opts.index('--force')]
+        
+        res = False
+        if noDialog:
+            if target is None:
+                return False
+            force = True
+            accepted = True
+        else:
+            from .SvnCopyDialog import SvnCopyDialog
+            dlg = SvnCopyDialog(name, None, True, force)
+            accepted = (dlg.exec_() == QDialog.Accepted)
+            if accepted:
+                target, force = dlg.getData()
+            if not target:
+                return False
+        
+        if not rx_prot.exactMatch(target):
+            isDir = os.path.isdir(name)
+        else:
+            isDir = False
+        
+        if accepted:
+            args = []
+            args.append('move')
+            self.addArguments(args, opts)
+            if force:
+                args.append('--force')
+            if rx_prot.exactMatch(target):
+                args.append('--message')
+                args.append('Moving {0} to {1}'.format(name, target))
+                target = self.__svnURL(target)
+            args.append(name)
+            args.append(target)
+            
+            if noDialog:
+                res = self.startSynchronizedProcess(QProcess(), "svn", args)
+            else:
+                dia = SvnDialog(self.tr('Moving {0}')
+                                .format(name))
+                res = dia.startProcess(args)
+                if res:
+                    dia.exec_()
+                    res = dia.normalExit()
+            if res and not rx_prot.exactMatch(target):
+                if target.startswith(project.getProjectPath()):
+                    if isDir:
+                        project.moveDirectory(name, target)
+                    else:
+                        project.renameFileInPdata(name, target)
+                else:
+                    if isDir:
+                        project.removeDirectory(name)
+                    else:
+                        project.removeFile(name)
+        return res
+        
+    def vcsDiff(self, name):
+        """
+        Public method used to view the difference of a file/directory to the
+        Subversion repository.
+        
+        If name is a directory and is the project directory, all project files
+        are saved first. If name is a file (or list of files), which is/are
+        being edited and has unsaved modification, they can be saved or the
+        operation may be aborted.
+        
+        @param name file/directory name to be diffed (string)
+        """
+        if isinstance(name, list):
+            names = name[:]
+        else:
+            names = [name]
+        for nam in names:
+            if os.path.isfile(nam):
+                editor = e5App().getObject("ViewManager").getOpenEditor(nam)
+                if editor and not editor.checkDirty():
+                    return
+            else:
+                project = e5App().getObject("Project")
+                if nam == project.ppath and not project.saveAllScripts():
+                    return
+        if self.diff is None:
+            from .SvnDiffDialog import SvnDiffDialog
+            self.diff = SvnDiffDialog(self)
+        self.diff.show()
+        self.diff.raise_()
+        QApplication.processEvents()
+        self.diff.start(name, refreshable=True)
+        
+    def vcsStatus(self, name):
+        """
+        Public method used to view the status of files/directories in the
+        Subversion repository.
+        
+        @param name file/directory name(s) to show the status of
+            (string or list of strings)
+        """
+        if self.status is None:
+            from .SvnStatusDialog import SvnStatusDialog
+            self.status = SvnStatusDialog(self)
+        self.status.show()
+        self.status.raise_()
+        self.status.start(name)
+        
+    def vcsTag(self, name):
+        """
+        Public method used to set the tag of a file/directory in the
+        Subversion repository.
+        
+        @param name file/directory name to be tagged (string)
+        """
+        dname, fname = self.splitPath(name)
+        
+        reposURL = self.svnGetReposName(dname)
+        if reposURL is None:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("Subversion Error"),
+                self.tr(
+                    """The URL of the project repository could not be"""
+                    """ retrieved from the working copy. The tag operation"""
+                    """ will be aborted"""))
+            return
+        
+        if self.otherData["standardLayout"]:
+            url = None
+        else:
+            url = self.svnNormalizeURL(reposURL)
+        from .SvnTagDialog import SvnTagDialog
+        dlg = SvnTagDialog(self.allTagsBranchesList, url,
+                           self.otherData["standardLayout"])
+        if dlg.exec_() == QDialog.Accepted:
+            tag, tagOp = dlg.getParameters()
+            if tag in self.allTagsBranchesList:
+                self.allTagsBranchesList.remove(tag)
+            self.allTagsBranchesList.insert(0, tag)
+        else:
+            return
+        
+        if self.otherData["standardLayout"]:
+            rx_base = QRegExp('(.+)/(trunk|tags|branches).*')
+            if not rx_base.exactMatch(reposURL):
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("Subversion Error"),
+                    self.tr(
+                        """The URL of the project repository has an"""
+                        """ invalid format. The tag operation will"""
+                        """ be aborted"""))
+                return
+            
+            reposRoot = rx_base.cap(1)
+            if tagOp in [1, 4]:
+                url = '{0}/tags/{1}'.format(reposRoot, Utilities.quote(tag))
+            elif tagOp in [2, 8]:
+                url = '{0}/branches/{1}'.format(
+                    reposRoot, Utilities.quote(tag))
+        else:
+            url = self.__svnURL(tag)
+        
+        args = []
+        if tagOp in [1, 2]:
+            args.append('copy')
+            self.addArguments(args, self.options['global'])
+            self.addArguments(args, self.options['tag'])
+            args.append('--message')
+            args.append('Created tag <{0}>'.format(tag))
+            args.append(reposURL)
+            args.append(url)
+        else:
+            args.append('delete')
+            self.addArguments(args, self.options['global'])
+            self.addArguments(args, self.options['tag'])
+            args.append('--message')
+            args.append('Deleted tag <{0}>'.format(tag))
+            args.append(url)
+        
+        dia = SvnDialog(self.tr('Tagging {0} in the Subversion repository')
+                        .format(name))
+        res = dia.startProcess(args)
+        if res:
+            dia.exec_()
+        
+    def vcsRevert(self, name):
+        """
+        Public method used to revert changes made to a file/directory.
+        
+        @param name file/directory name to be reverted (string)
+        """
+        args = []
+        args.append('revert')
+        self.addArguments(args, self.options['global'])
+        if isinstance(name, list):
+            self.addArguments(args, name)
+            names = name[:]
+        else:
+            if os.path.isdir(name):
+                args.append('--recursive')
+            args.append(name)
+            names = [name]
+        
+        project = e5App().getObject("Project")
+        names = [project.getRelativePath(nam) for nam in names]
+        if names[0]:
+            from UI.DeleteFilesConfirmationDialog import \
+                DeleteFilesConfirmationDialog
+            dlg = DeleteFilesConfirmationDialog(
+                self.parent(),
+                self.tr("Revert changes"),
+                self.tr("Do you really want to revert all changes to"
+                        " these files or directories?"),
+                names)
+            yes = dlg.exec_() == QDialog.Accepted
+        else:
+            yes = E5MessageBox.yesNo(
+                None,
+                self.tr("Revert changes"),
+                self.tr("""Do you really want to revert all changes of"""
+                        """ the project?"""))
+        if yes:
+            dia = SvnDialog(self.tr('Reverting changes'))
+            res = dia.startProcess(args)
+            if res:
+                dia.exec_()
+            self.checkVCSStatus()
+    
+    def vcsSwitch(self, name):
+        """
+        Public method used to switch a directory to a different tag/branch.
+        
+        @param name directory name to be switched (string)
+        @return flag indicating added or changed files (boolean)
+        """
+        dname, fname = self.splitPath(name)
+        
+        reposURL = self.svnGetReposName(dname)
+        if reposURL is None:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("Subversion Error"),
+                self.tr(
+                    """The URL of the project repository could not be"""
+                    """ retrieved from the working copy. The switch"""
+                    """ operation will be aborted"""))
+            return False
+        
+        if self.otherData["standardLayout"]:
+            url = None
+        else:
+            url = self.svnNormalizeURL(reposURL)
+        from .SvnSwitchDialog import SvnSwitchDialog
+        dlg = SvnSwitchDialog(self.allTagsBranchesList, url,
+                              self.otherData["standardLayout"])
+        if dlg.exec_() == QDialog.Accepted:
+            tag, tagType = dlg.getParameters()
+            if tag in self.allTagsBranchesList:
+                self.allTagsBranchesList.remove(tag)
+            self.allTagsBranchesList.insert(0, tag)
+        else:
+            return False
+        
+        if self.otherData["standardLayout"]:
+            rx_base = QRegExp('(.+)/(trunk|tags|branches).*')
+            if not rx_base.exactMatch(reposURL):
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("Subversion Error"),
+                    self.tr(
+                        """The URL of the project repository has an"""
+                        """ invalid format. The switch operation will"""
+                        """ be aborted"""))
+                return False
+            
+            reposRoot = rx_base.cap(1)
+            tn = tag
+            if tagType == 1:
+                url = '{0}/tags/{1}'.format(reposRoot, Utilities.quote(tag))
+            elif tagType == 2:
+                url = '{0}/branches/{1}'.format(
+                    reposRoot, Utilities.quote(tag))
+            elif tagType == 4:
+                url = '{0}/trunk'.format(reposRoot)
+                tn = 'HEAD'
+        else:
+            url = self.__svnURL(tag)
+            tn = url
+        
+        args = []
+        args.append('switch')
+        if self.version >= (1, 5, 0):
+            args.append('--accept')
+            args.append('postpone')
+        args.append(url)
+        args.append(name)
+        
+        dia = SvnDialog(self.tr('Switching to {0}')
+                        .format(tn))
+        res = dia.startProcess(args, setLanguage=True)
+        if res:
+            dia.exec_()
+            res = dia.hasAddOrDelete()
+        self.checkVCSStatus()
+        return res
+        
+    def vcsMerge(self, name):
+        """
+        Public method used to merge a URL/revision into the local project.
+        
+        @param name file/directory name to be merged (string)
+        """
+        dname, fname = self.splitPath(name)
+        
+        opts = self.options['global'][:]
+        force = '--force' in opts
+        if force:
+            del opts[opts.index('--force')]
+        
+        from .SvnMergeDialog import SvnMergeDialog
+        dlg = SvnMergeDialog(
+            self.mergeList[0], self.mergeList[1], self.mergeList[2], force)
+        if dlg.exec_() == QDialog.Accepted:
+            urlrev1, urlrev2, target, force = dlg.getParameters()
+        else:
+            return
+        
+        # remember URL or revision
+        if urlrev1 in self.mergeList[0]:
+            self.mergeList[0].remove(urlrev1)
+        self.mergeList[0].insert(0, urlrev1)
+        if urlrev2 in self.mergeList[1]:
+            self.mergeList[1].remove(urlrev2)
+        self.mergeList[1].insert(0, urlrev2)
+        
+        rx_rev = QRegExp('\\d+|HEAD')
+        
+        args = []
+        args.append('merge')
+        self.addArguments(args, opts)
+        if self.version >= (1, 5, 0):
+            args.append('--accept')
+            args.append('postpone')
+        if force:
+            args.append('--force')
+        if rx_rev.exactMatch(urlrev1):
+            args.append('-r')
+            args.append('{0}:{1}'.format(urlrev1, urlrev2))
+            if not target:
+                args.append(name)
+            else:
+                args.append(target)
+                
+            # remember target
+            if target in self.mergeList[2]:
+                self.mergeList[2].remove(target)
+            self.mergeList[2].insert(0, target)
+        else:
+            args.append(self.__svnURL(urlrev1))
+            args.append(self.__svnURL(urlrev2))
+        args.append(fname)
+        
+        dia = SvnDialog(self.tr('Merging {0}').format(name))
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def vcsRegisteredState(self, name):
+        """
+        Public method used to get the registered state of a file in the vcs.
+        
+        @param name filename to check (string)
+        @return a combination of canBeCommited and canBeAdded
+        """
+        if self.__wcng:
+            return self.__vcsRegisteredState_wcng(name)
+        else:
+            return self.__vcsRegisteredState_wc(name)
+        
+    def __vcsRegisteredState_wcng(self, name):
+        """
+        Private method used to get the registered state of a file in the vcs.
+        
+        This is the variant for subversion installations using the new
+        working copy meta-data format.
+        
+        @param name filename to check (string)
+        @return a combination of canBeCommited and canBeAdded
+        """
+        if name.endswith(os.sep):
+            name = name[:-1]
+        name = os.path.normcase(name)
+        dname, fname = self.splitPath(name)
+        
+        if fname == '.' and os.path.isdir(os.path.join(dname, self.adminDir)):
+            return self.canBeCommitted
+        
+        if name in self.statusCache:
+            return self.statusCache[name]
+        
+        name = os.path.normcase(name)
+        states = {name: 0}
+        states = self.vcsAllRegisteredStates(states, dname, False)
+        if states[name] == self.canBeCommitted:
+            return self.canBeCommitted
+        else:
+            return self.canBeAdded
+        
+    def __vcsRegisteredState_wc(self, name):
+        """
+        Private method used to get the registered state of a file in the VCS.
+        
+        This is the variant for subversion installations using the old working
+        copy meta-data format.
+        
+        @param name filename to check (string)
+        @return a combination of canBeCommited and canBeAdded
+        """
+        dname, fname = self.splitPath(name)
+        
+        if fname == '.':
+            if os.path.isdir(os.path.join(dname, self.adminDir)):
+                return self.canBeCommitted
+            else:
+                return self.canBeAdded
+        
+        name = os.path.normcase(name)
+        states = {name: 0}
+        states = self.vcsAllRegisteredStates(states, dname, False)
+        if states[name] == self.canBeCommitted:
+            return self.canBeCommitted
+        else:
+            return self.canBeAdded
+        
+    def vcsAllRegisteredStates(self, names, dname, shortcut=True):
+        """
+        Public method used to get the registered states of a number of files
+        in the VCS.
+        
+        <b>Note:</b> If a shortcut is to be taken, the code will only check,
+        if the named directory has been scanned already. If so, it is assumed,
+        that the states for all files have been populated by the previous run.
+        
+        @param names dictionary with all filenames to be checked as keys
+        @param dname directory to check in (string)
+        @param shortcut flag indicating a shortcut should be taken (boolean)
+        @return the received dictionary completed with a combination of
+            canBeCommited and canBeAdded or None in order to signal an error
+        """
+        if self.__wcng:
+            return self.__vcsAllRegisteredStates_wcng(names, dname, shortcut)
+        else:
+            return self.__vcsAllRegisteredStates_wc(names, dname, shortcut)
+        
+    def __vcsAllRegisteredStates_wcng(self, names, dname, shortcut=True):
+        """
+        Private method used to get the registered states of a number of files
+        in the VCS.
+        
+        This is the variant for subversion installations using the new working
+        copy meta-data format.
+        
+        <b>Note:</b> If a shortcut is to be taken, the code will only check,
+        if the named directory has been scanned already. If so, it is assumed,
+        that the states for all files has been populated by the previous run.
+        
+        @param names dictionary with all filenames to be checked as keys
+        @param dname directory to check in (string)
+        @param shortcut flag indicating a shortcut should be taken (boolean)
+        @return the received dictionary completed with a combination of
+            canBeCommited and canBeAdded or None in order to signal an error
+        """
+        if dname.endswith(os.sep):
+            dname = dname[:-1]
+        dname = os.path.normcase(dname)
+        
+        found = False
+        for name in self.statusCache.keys():
+            if name in names:
+                found = True
+                names[name] = self.statusCache[name]
+        
+        if not found:
+            # find the root of the repo
+            repodir = dname
+            while not os.path.isdir(os.path.join(repodir, self.adminDir)):
+                repodir = os.path.dirname(repodir)
+                if os.path.splitdrive(repodir)[1] == os.sep:
+                    return names
+            
+            ioEncoding = str(Preferences.getSystem("IOEncoding"))
+            process = QProcess()
+            args = []
+            args.append('status')
+            args.append('--verbose')
+            args.append('--non-interactive')
+            args.append(dname)
+            process.start('svn', args)
+            procStarted = process.waitForStarted(5000)
+            if procStarted:
+                finished = process.waitForFinished(30000)
+                if finished and process.exitCode() == 0:
+                    output = str(process.readAllStandardOutput(), ioEncoding,
+                                 'replace')
+                    for line in output.splitlines():
+                        if self.rx_status1.exactMatch(line):
+                            flags = str(self.rx_status1.cap(1))
+                            path = self.rx_status1.cap(5).strip()
+                        elif self.rx_status2.exactMatch(line):
+                            flags = str(self.rx_status2.cap(1))
+                            path = self.rx_status2.cap(2).strip()
+                        else:
+                            continue
+                        name = os.path.normcase(path)
+                        if flags[0] not in "?I":
+                            if name in names:
+                                names[name] = self.canBeCommitted
+                            self.statusCache[name] = self.canBeCommitted
+                        else:
+                            self.statusCache[name] = self.canBeAdded
+        
+        return names
+        
+    def __vcsAllRegisteredStates_wc(self, names, dname, shortcut=True):
+        """
+        Private method used to get the registered states of a number of files
+        in the VCS.
+        
+        This is the variant for subversion installations using the old working
+        copy meta-data format.
+        
+        <b>Note:</b> If a shortcut is to be taken, the code will only check,
+        if the named directory has been scanned already. If so, it is assumed,
+        that the states for all files has been populated by the previous run.
+        
+        @param names dictionary with all filenames to be checked as keys
+        @param dname directory to check in (string)
+        @param shortcut flag indicating a shortcut should be taken (boolean)
+        @return the received dictionary completed with a combination of
+            canBeCommited and canBeAdded or None in order to signal an error
+        """
+        if not os.path.isdir(os.path.join(dname, self.adminDir)):
+            # not under version control -> do nothing
+            return names
+        
+        found = False
+        for name in list(self.statusCache.keys()):
+            if os.path.dirname(name) == dname:
+                if shortcut:
+                    found = True
+                    break
+                if name in names:
+                    found = True
+                    names[name] = self.statusCache[name]
+        
+        if not found:
+            ioEncoding = Preferences.getSystem("IOEncoding")
+            process = QProcess()
+            args = []
+            args.append('status')
+            args.append('--verbose')
+            args.append('--non-interactive')
+            args.append(dname)
+            process.start('svn', args)
+            procStarted = process.waitForStarted(5000)
+            if procStarted:
+                finished = process.waitForFinished(30000)
+                if finished and process.exitCode() == 0:
+                    output = str(process.readAllStandardOutput(), ioEncoding,
+                                 'replace')
+                    for line in output.splitlines():
+                        if self.rx_status1.exactMatch(line):
+                            flags = self.rx_status1.cap(1)
+                            path = self.rx_status1.cap(5).strip()
+                        elif self.rx_status2.exactMatch(line):
+                            flags = self.rx_status2.cap(1)
+                            path = self.rx_status2.cap(2).strip()
+                        else:
+                            continue
+                        name = os.path.normcase(path)
+                        if flags[0] not in "?I":
+                            if name in names:
+                                names[name] = self.canBeCommitted
+                            self.statusCache[name] = self.canBeCommitted
+                        else:
+                            self.statusCache[name] = self.canBeAdded
+        
+        return names
+        
+    def clearStatusCache(self):
+        """
+        Public method to clear the status cache.
+        """
+        self.statusCache = {}
+        
+    def vcsInitConfig(self, project):
+        """
+        Public method to initialize the VCS configuration.
+        
+        This method ensures, that an ignore file exists.
+        
+        @param project reference to the project (Project)
+        """
+        configPath = getConfigPath()
+        if os.path.exists(configPath):
+            amendConfig()
+        else:
+            createDefaultConfig()
+    
+    def vcsName(self):
+        """
+        Public method returning the name of the vcs.
+        
+        @return always 'Subversion' (string)
+        """
+        return "Subversion"
+
+    def vcsCleanup(self, name):
+        """
+        Public method used to cleanup the working copy.
+        
+        @param name directory name to be cleaned up (string)
+        """
+        args = []
+        args.append('cleanup')
+        self.addArguments(args, self.options['global'])
+        args.append(name)
+        
+        dia = SvnDialog(self.tr('Cleaning up {0}')
+                        .format(name))
+        res = dia.startProcess(args)
+        if res:
+            dia.exec_()
+    
+    def vcsCommandLine(self, name):
+        """
+        Public method used to execute arbitrary subversion commands.
+        
+        @param name directory name of the working directory (string)
+        """
+        from .SvnCommandDialog import SvnCommandDialog
+        dlg = SvnCommandDialog(self.commandHistory, self.wdHistory, name)
+        if dlg.exec_() == QDialog.Accepted:
+            command, wd = dlg.getData()
+            commandList = Utilities.parseOptionString(command)
+            
+            # This moves any previous occurrence of these arguments to the head
+            # of the list.
+            if command in self.commandHistory:
+                self.commandHistory.remove(command)
+            self.commandHistory.insert(0, command)
+            if wd in self.wdHistory:
+                self.wdHistory.remove(wd)
+            self.wdHistory.insert(0, wd)
+            
+            args = []
+            self.addArguments(args, commandList)
+            
+            dia = SvnDialog(self.tr('Subversion command'))
+            res = dia.startProcess(args, wd)
+            if res:
+                dia.exec_()
+        
+    def vcsOptionsDialog(self, project, archive, editable=False, parent=None):
+        """
+        Public method to get a dialog to enter repository info.
+        
+        @param project reference to the project object
+        @param archive name of the project in the repository (string)
+        @param editable flag indicating that the project name is editable
+            (boolean)
+        @param parent parent widget (QWidget)
+        @return reference to the instantiated options dialog (SvnOptionsDialog)
+        """
+        from .SvnOptionsDialog import SvnOptionsDialog
+        return SvnOptionsDialog(self, project, parent)
+        
+    def vcsNewProjectOptionsDialog(self, parent=None):
+        """
+        Public method to get a dialog to enter repository info for getting
+        a new project.
+        
+        @param parent parent widget (QWidget)
+        @return reference to the instantiated options dialog
+            (SvnNewProjectOptionsDialog)
+        """
+        from .SvnNewProjectOptionsDialog import SvnNewProjectOptionsDialog
+        return SvnNewProjectOptionsDialog(self, parent)
+        
+    def vcsRepositoryInfos(self, ppath):
+        """
+        Public method to retrieve information about the repository.
+        
+        @param ppath local path to get the repository infos (string)
+        @return string with ready formated info for display (string)
+        """
+        info = {
+            'committed-rev': '',
+            'committed-date': '',
+            'committed-time': '',
+            'url': '',
+            'last-author': '',
+            'revision': ''
+        }
+        
+        ioEncoding = Preferences.getSystem("IOEncoding")
+        
+        process = QProcess()
+        args = []
+        args.append('info')
+        args.append('--non-interactive')
+        args.append('--xml')
+        args.append(ppath)
+        process.start('svn', args)
+        procStarted = process.waitForStarted(5000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished and process.exitCode() == 0:
+                output = str(process.readAllStandardOutput(), ioEncoding,
+                             'replace')
+                entryFound = False
+                commitFound = False
+                for line in output.splitlines():
+                    line = line.strip()
+                    if line.startswith('<entry'):
+                        entryFound = True
+                    elif line.startswith('<commit'):
+                        commitFound = True
+                    elif line.startswith('</commit>'):
+                        commitFound = False
+                    elif line.startswith("revision="):
+                        rev = line[line.find('"') + 1:line.rfind('"')]
+                        if entryFound:
+                            info['revision'] = rev
+                            entryFound = False
+                        elif commitFound:
+                            info['committed-rev'] = rev
+                    elif line.startswith('<url>'):
+                        info['url'] = \
+                            line.replace('<url>', '').replace('</url>', '')
+                    elif line.startswith('<author>'):
+                        info['last-author'] = line.replace('<author>', '')\
+                            .replace('</author>', '')
+                    elif line.startswith('<date>'):
+                        value = line.replace('<date>', '')\
+                            .replace('</date>', '')
+                        date, time = value.split('T')
+                        info['committed-date'] = date
+                        info['committed-time'] = "{0}{1}".format(
+                            time.split('.')[0], time[-1])
+        
+        return QCoreApplication.translate(
+            'subversion',
+            """<h3>Repository information</h3>"""
+            """<table>"""
+            """<tr><td><b>Subversion V.</b></td><td>{0}</td></tr>"""
+            """<tr><td><b>URL</b></td><td>{1}</td></tr>"""
+            """<tr><td><b>Current revision</b></td><td>{2}</td></tr>"""
+            """<tr><td><b>Committed revision</b></td><td>{3}</td></tr>"""
+            """<tr><td><b>Committed date</b></td><td>{4}</td></tr>"""
+            """<tr><td><b>Comitted time</b></td><td>{5}</td></tr>"""
+            """<tr><td><b>Last author</b></td><td>{6}</td></tr>"""
+            """</table>"""
+        ).format(self.versionStr,
+                 info['url'],
+                 info['revision'],
+                 info['committed-rev'],
+                 info['committed-date'],
+                 info['committed-time'],
+                 info['last-author'])
+    
+    ###########################################################################
+    ## Public Subversion specific methods are below.
+    ###########################################################################
+    
+    def svnGetReposName(self, path):
+        """
+        Public method used to retrieve the URL of the subversion repository
+        path.
+        
+        @param path local path to get the svn repository path for (string)
+        @return string with the repository path URL
+        """
+        ioEncoding = Preferences.getSystem("IOEncoding")
+        
+        process = QProcess()
+        args = []
+        args.append('info')
+        args.append('--xml')
+        args.append('--non-interactive')
+        args.append(path)
+        process.start('svn', args)
+        procStarted = process.waitForStarted(5000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished and process.exitCode() == 0:
+                output = str(process.readAllStandardOutput(), ioEncoding,
+                             'replace')
+                for line in output.splitlines():
+                    line = line.strip()
+                    if line.startswith('<url>'):
+                        reposURL = line.replace('<url>', '')\
+                            .replace('</url>', '')
+                        return reposURL
+        
+        return ""
+
+    def svnResolve(self, name):
+        """
+        Public method used to resolve conflicts of a file/directory.
+        
+        @param name file/directory name to be resolved (string)
+        """
+        args = []
+        if self.version >= (1, 5, 0):
+            args.append('resolve')
+            args.append('--accept')
+            args.append('working')
+        else:
+            args.append('resolved')
+        self.addArguments(args, self.options['global'])
+        if isinstance(name, list):
+            self.addArguments(args, name)
+        else:
+            if os.path.isdir(name):
+                args.append('--recursive')
+            args.append(name)
+        
+        dia = SvnDialog(self.tr('Resolving conficts'))
+        res = dia.startProcess(args)
+        if res:
+            dia.exec_()
+        self.checkVCSStatus()
+    
+    def svnCopy(self, name, project):
+        """
+        Public method used to copy a file/directory.
+        
+        @param name file/directory name to be copied (string)
+        @param project reference to the project object
+        @return flag indicating successfull operation (boolean)
+        """
+        from .SvnCopyDialog import SvnCopyDialog
+        rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+')
+        dlg = SvnCopyDialog(name)
+        res = False
+        if dlg.exec_() == QDialog.Accepted:
+            target, force = dlg.getData()
+            
+            args = []
+            args.append('copy')
+            self.addArguments(args, self.options['global'])
+            if rx_prot.exactMatch(target):
+                args.append('--message')
+                args.append('Copying {0} to {1}'.format(name, target))
+                target = self.__svnURL(target)
+            args.append(name)
+            args.append(target)
+            
+            dia = SvnDialog(self.tr('Copying {0}')
+                            .format(name))
+            res = dia.startProcess(args)
+            if res:
+                dia.exec_()
+                res = dia.normalExit()
+                if res and \
+                   not rx_prot.exactMatch(target) and \
+                   target.startswith(project.getProjectPath()):
+                    if os.path.isdir(name):
+                        project.copyDirectory(name, target)
+                    else:
+                        project.appendFile(target)
+        return res
+    
+    def svnListProps(self, name, recursive=False):
+        """
+        Public method used to list the properties of a file/directory.
+        
+        @param name file/directory name (string or list of strings)
+        @param recursive flag indicating a recursive list is requested
+        """
+        if self.propList is None:
+            from .SvnPropListDialog import SvnPropListDialog
+            self.propList = SvnPropListDialog(self)
+        self.propList.show()
+        self.propList.raise_()
+        self.propList.start(name, recursive)
+        
+    def svnSetProp(self, name, recursive=False):
+        """
+        Public method used to add a property to a file/directory.
+        
+        @param name file/directory name (string or list of strings)
+        @param recursive flag indicating a recursive list is requested
+        """
+        from .SvnPropSetDialog import SvnPropSetDialog
+        dlg = SvnPropSetDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            propName, fileFlag, propValue = dlg.getData()
+            if not propName:
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("Subversion Set Property"),
+                    self.tr("""You have to supply a property name."""
+                            """ Aborting."""))
+                return
+            
+            args = []
+            args.append('propset')
+            self.addArguments(args, self.options['global'])
+            if recursive:
+                args.append('--recursive')
+            args.append(propName)
+            if fileFlag:
+                args.append('--file')
+            args.append(propValue)
+            if isinstance(name, list):
+                dname, fnames = self.splitPathList(name)
+                self.addArguments(args, fnames)
+            else:
+                dname, fname = self.splitPath(name)
+                args.append(fname)
+            
+            dia = SvnDialog(self.tr('Subversion Set Property'))
+            res = dia.startProcess(args, dname)
+            if res:
+                dia.exec_()
+        
+    def svnDelProp(self, name, recursive=False):
+        """
+        Public method used to delete a property of a file/directory.
+        
+        @param name file/directory name (string or list of strings)
+        @param recursive flag indicating a recursive list is requested
+        """
+        propName, ok = QInputDialog.getText(
+            None,
+            self.tr("Subversion Delete Property"),
+            self.tr("Enter property name"),
+            QLineEdit.Normal)
+        
+        if not ok:
+            return
+        
+        if not propName:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("Subversion Delete Property"),
+                self.tr("""You have to supply a property name."""
+                        """ Aborting."""))
+            return
+        
+        args = []
+        args.append('propdel')
+        self.addArguments(args, self.options['global'])
+        if recursive:
+            args.append('--recursive')
+        args.append(propName)
+        if isinstance(name, list):
+            dname, fnames = self.splitPathList(name)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(name)
+            args.append(fname)
+        
+        dia = SvnDialog(self.tr('Subversion Delete Property'))
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def svnListTagBranch(self, path, tags=True):
+        """
+        Public method used to list the available tags or branches.
+        
+        @param path directory name of the project (string)
+        @param tags flag indicating listing of branches or tags
+                (False = branches, True = tags)
+        """
+        if self.tagbranchList is None:
+            from .SvnTagBranchListDialog import SvnTagBranchListDialog
+            self.tagbranchList = SvnTagBranchListDialog(self)
+        self.tagbranchList.show()
+        self.tagbranchList.raise_()
+        if tags:
+            if not self.showedTags:
+                self.showedTags = True
+                allTagsBranchesList = self.allTagsBranchesList
+            else:
+                self.tagsList = []
+                allTagsBranchesList = None
+            self.tagbranchList.start(path, tags,
+                                     self.tagsList, allTagsBranchesList)
+        elif not tags:
+            if not self.showedBranches:
+                self.showedBranches = True
+                allTagsBranchesList = self.allTagsBranchesList
+            else:
+                self.branchesList = []
+                allTagsBranchesList = None
+            self.tagbranchList.start(
+                path, tags, self.branchesList, self.allTagsBranchesList)
+        
+    def svnBlame(self, name):
+        """
+        Public method to show the output of the svn blame command.
+        
+        @param name file name to show the blame for (string)
+        """
+        if self.blame is None:
+            from .SvnBlameDialog import SvnBlameDialog
+            self.blame = SvnBlameDialog(self)
+        self.blame.show()
+        self.blame.raise_()
+        self.blame.start(name)
+        
+    def svnExtendedDiff(self, name):
+        """
+        Public method used to view the difference of a file/directory to the
+        Subversion repository.
+        
+        If name is a directory and is the project directory, all project files
+        are saved first. If name is a file (or list of files), which is/are
+        being edited and has unsaved modification, they can be saved or the
+        operation may be aborted.
+        
+        This method gives the chance to enter the revisions to be compared.
+        
+        @param name file/directory name to be diffed (string)
+        """
+        if isinstance(name, list):
+            names = name[:]
+        else:
+            names = [name]
+        for nam in names:
+            if os.path.isfile(nam):
+                editor = e5App().getObject("ViewManager").getOpenEditor(nam)
+                if editor and not editor.checkDirty():
+                    return
+            else:
+                project = e5App().getObject("Project")
+                if nam == project.ppath and not project.saveAllScripts():
+                    return
+        from .SvnRevisionSelectionDialog import SvnRevisionSelectionDialog
+        dlg = SvnRevisionSelectionDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            revisions = dlg.getRevisions()
+            from .SvnDiffDialog import SvnDiffDialog
+            self.diff = SvnDiffDialog(self)
+            self.diff.show()
+            self.diff.start(name, revisions)
+
+    def svnUrlDiff(self, name):
+        """
+        Public method used to view the difference of a file/directory of two
+        repository URLs.
+        
+        If name is a directory and is the project directory, all project files
+        are saved first. If name is a file (or list of files), which is/are
+        being edited and has unsaved modification, they can be saved or the
+        operation may be aborted.
+        
+        This method gives the chance to enter the revisions to be compared.
+        
+        @param name file/directory name to be diffed (string)
+        """
+        if isinstance(name, list):
+            names = name[:]
+        else:
+            names = [name]
+        for nam in names:
+            if os.path.isfile(nam):
+                editor = e5App().getObject("ViewManager").getOpenEditor(nam)
+                if editor and not editor.checkDirty():
+                    return
+            else:
+                project = e5App().getObject("Project")
+                if nam == project.ppath and not project.saveAllScripts():
+                    return
+        
+        dname = self.splitPath(names[0])[0]
+        
+        from .SvnUrlSelectionDialog import SvnUrlSelectionDialog
+        dlg = SvnUrlSelectionDialog(self, self.tagsList, self.branchesList,
+                                    dname)
+        if dlg.exec_() == QDialog.Accepted:
+            urls, summary = dlg.getURLs()
+            from .SvnDiffDialog import SvnDiffDialog
+            self.diff = SvnDiffDialog(self)
+            self.diff.show()
+            QApplication.processEvents()
+            self.diff.start(name, urls=urls, summary=summary)
+        
+    def __svnGetFileForRevision(self, name, rev=""):
+        """
+        Private method to get a file for a specific revision from the
+        repository.
+        
+        @param name file name to get from the repository (string)
+        @keyparam rev revision to retrieve (integer or string)
+        @return contents of the file (string) and an error message (string)
+        """
+        args = []
+        args.append("cat")
+        if rev:
+            args.append("--revision")
+            args.append(str(rev))
+        args.append(name)
+        
+        output = ""
+        error = ""
+        
+        process = QProcess()
+        process.start('svn', args)
+        procStarted = process.waitForStarted(5000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished:
+                if process.exitCode() == 0:
+                    output = str(
+                        process.readAllStandardOutput(),
+                        Preferences.getSystem("IOEncoding"), 'replace')
+                else:
+                    error = str(
+                        process.readAllStandardError(),
+                        Preferences.getSystem("IOEncoding"), 'replace')
+            else:
+                error = self.tr(
+                    "The svn process did not finish within 30s.")
+        else:
+            error = self.tr(
+                'The process {0} could not be started. '
+                'Ensure, that it is in the search path.').format('svn')
+        
+        return output, error
+    
+    def svnSbsDiff(self, name, extended=False, revisions=None):
+        """
+        Public method used to view the difference of a file to the Mercurial
+        repository side-by-side.
+        
+        @param name file name to be diffed (string)
+        @keyparam extended flag indicating the extended variant (boolean)
+        @keyparam revisions tuple of two revisions (tuple of strings)
+        @exception ValueError raised to indicate an illegal name parameter type
+        """
+        if isinstance(name, list):
+            raise ValueError("Wrong parameter type")
+        
+        if extended:
+            from .SvnRevisionSelectionDialog import SvnRevisionSelectionDialog
+            dlg = SvnRevisionSelectionDialog()
+            if dlg.exec_() == QDialog.Accepted:
+                rev1, rev2 = dlg.getRevisions()
+                if rev1 == "WORKING":
+                    rev1 = ""
+                if rev2 == "WORKING":
+                    rev2 = ""
+            else:
+                return
+        elif revisions:
+            rev1, rev2 = revisions[0], revisions[1]
+        else:
+            rev1, rev2 = "", ""
+        
+        output1, error = self.__svnGetFileForRevision(name, rev=rev1)
+        if error:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("Subversion Side-by-Side Difference"),
+                error)
+            return
+        name1 = "{0} (rev. {1})".format(name, rev1 and rev1 or ".")
+        
+        if rev2:
+            output2, error = self.__svnGetFileForRevision(name, rev=rev2)
+            if error:
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("Subversion Side-by-Side Difference"),
+                    error)
+                return
+            name2 = "{0} (rev. {1})".format(name, rev2)
+        else:
+            try:
+                f1 = open(name, "r", encoding="utf-8")
+                output2 = f1.read()
+                f1.close()
+                name2 = name
+            except IOError:
+                E5MessageBox.critical(
+                    self.__ui,
+                    self.tr("Subversion Side-by-Side Difference"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be read.</p>""")
+                    .format(name))
+                return
+        
+        if self.sbsDiff is None:
+            from UI.CompareDialog import CompareDialog
+            self.sbsDiff = CompareDialog()
+        self.sbsDiff.show()
+        self.sbsDiff.raise_()
+        self.sbsDiff.compare(output1, output2, name1, name2)
+    
+    def vcsLogBrowser(self, name, isFile=False):
+        """
+        Public method used to browse the log of a file/directory from the
+        Subversion repository.
+        
+        @param name file/directory name to show the log of (string)
+        @param isFile flag indicating log for a file is to be shown (boolean)
+        """
+        if self.logBrowser is None:
+            from .SvnLogBrowserDialog import SvnLogBrowserDialog
+            self.logBrowser = SvnLogBrowserDialog(self)
+        self.logBrowser.show()
+        self.logBrowser.raise_()
+        self.logBrowser.start(name, isFile=isFile)
+        
+    def svnLock(self, name, stealIt=False, parent=None):
+        """
+        Public method used to lock a file in the Subversion repository.
+        
+        @param name file/directory name to be locked (string or list of
+            strings)
+        @param stealIt flag indicating a forced operation (boolean)
+        @param parent reference to the parent object of the subversion dialog
+            (QWidget)
+        """
+        args = []
+        args.append('lock')
+        self.addArguments(args, self.options['global'])
+        if stealIt:
+            args.append('--force')
+        if isinstance(name, list):
+            dname, fnames = self.splitPathList(name)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(name)
+            args.append(fname)
+        
+        dia = SvnDialog(
+            self.tr('Locking in the Subversion repository'), parent)
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def svnUnlock(self, name, breakIt=False, parent=None):
+        """
+        Public method used to unlock a file in the Subversion repository.
+        
+        @param name file/directory name to be unlocked (string or list of
+            strings)
+        @param breakIt flag indicating a forced operation (boolean)
+        @param parent reference to the parent object of the subversion dialog
+            (QWidget)
+        """
+        args = []
+        args.append('unlock')
+        self.addArguments(args, self.options['global'])
+        if breakIt:
+            args.append('--force')
+        if isinstance(name, list):
+            dname, fnames = self.splitPathList(name)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(name)
+            args.append(fname)
+        
+        dia = SvnDialog(
+            self.tr('Unlocking in the Subversion repository'), parent)
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def svnRelocate(self, projectPath):
+        """
+        Public method to relocate the working copy to a new repository URL.
+        
+        @param projectPath path name of the project (string)
+        """
+        from .SvnRelocateDialog import SvnRelocateDialog
+        currUrl = self.svnGetReposName(projectPath)
+        dlg = SvnRelocateDialog(currUrl)
+        if dlg.exec_() == QDialog.Accepted:
+            newUrl, inside = dlg.getData()
+            args = []
+            args.append('switch')
+            if not inside:
+                args.append('--relocate')
+                args.append(currUrl)
+            args.append(newUrl)
+            args.append(projectPath)
+            
+            dia = SvnDialog(self.tr('Relocating'))
+            res = dia.startProcess(args)
+            if res:
+                dia.exec_()
+        
+    def svnRepoBrowser(self, projectPath=None):
+        """
+        Public method to open the repository browser.
+        
+        @param projectPath path name of the project (string)
+        """
+        if projectPath:
+            url = self.svnGetReposName(projectPath)
+        else:
+            url = None
+        
+        if url is None:
+            url, ok = QInputDialog.getText(
+                None,
+                self.tr("Repository Browser"),
+                self.tr("Enter the repository URL."),
+                QLineEdit.Normal)
+            if not ok or not url:
+                return
+        
+        if self.repoBrowser is None:
+            from .SvnRepoBrowserDialog import SvnRepoBrowserDialog
+            self.repoBrowser = SvnRepoBrowserDialog(self)
+        self.repoBrowser.show()
+        self.repoBrowser.raise_()
+        self.repoBrowser.start(url)
+        
+    def svnRemoveFromChangelist(self, names):
+        """
+        Public method to remove a file or directory from its changelist.
+        
+        Note: Directories will be removed recursively.
+        
+        @param names name or list of names of file or directory to remove
+            (string)
+        """
+        args = []
+        args.append('changelist')
+        self.addArguments(args, self.options['global'])
+        args.append('--remove')
+        args.append('--recursive')
+        if isinstance(names, list):
+            dname, fnames = self.splitPathList(names)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(names)
+            args.append(fname)
+        
+        dia = SvnDialog(self.tr('Remove from changelist'))
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def svnAddToChangelist(self, names):
+        """
+        Public method to add a file or directory to a changelist.
+        
+        Note: Directories will be added recursively.
+        
+        @param names name or list of names of file or directory to add
+            (string)
+        """
+        clname, ok = QInputDialog.getItem(
+            None,
+            self.tr("Add to changelist"),
+            self.tr("Enter name of the changelist:"),
+            sorted(self.svnGetChangelists()),
+            0, True)
+        if not ok or not clname:
+            return
+
+        args = []
+        args.append('changelist')
+        self.addArguments(args, self.options['global'])
+        args.append('--recursive')
+        args.append(clname)
+        if isinstance(names, list):
+            dname, fnames = self.splitPathList(names)
+            self.addArguments(args, fnames)
+        else:
+            dname, fname = self.splitPath(names)
+            args.append(fname)
+        
+        dia = SvnDialog(self.tr('Remove from changelist'))
+        res = dia.startProcess(args, dname)
+        if res:
+            dia.exec_()
+        
+    def svnShowChangelists(self, path):
+        """
+        Public method used to inspect the change lists defined for the project.
+        
+        @param path directory name to show change lists for (string)
+        """
+        from .SvnChangeListsDialog import SvnChangeListsDialog
+        self.changeLists = SvnChangeListsDialog(self)
+        self.changeLists.show()
+        QApplication.processEvents()
+        self.changeLists.start(path)
+        
+    def svnGetChangelists(self):
+        """
+        Public method to get a list of all defined change lists.
+        
+        @return list of defined change list names (list of strings)
+        """
+        changelists = []
+        rx_changelist = QRegExp('--- \\S+ .([\\w\\s]+).:\\s*')
+        # three dashes, Changelist (translated), quote,
+        # changelist name, quote, :
+        
+        args = []
+        args.append("status")
+        args.append("--non-interactive")
+        args.append(".")
+        
+        ppath = e5App().getObject("Project").getProjectPath()
+        process = QProcess()
+        process.setWorkingDirectory(ppath)
+        process.start('svn', args)
+        procStarted = process.waitForStarted(5000)
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished and process.exitCode() == 0:
+                output = \
+                    str(process.readAllStandardOutput(),
+                        Preferences.getSystem("IOEncoding"),
+                        'replace')
+                if output:
+                    for line in output.splitlines():
+                        if rx_changelist.exactMatch(line):
+                            changelist = rx_changelist.cap(1)
+                            if changelist not in changelists:
+                                changelists.append(changelist)
+        
+        return changelists
+        
+    def svnUpgrade(self, path):
+        """
+        Public method to upgrade the working copy format.
+        
+        @param path directory name to show change lists for (string)
+        """
+        args = []
+        args.append("upgrade")
+        args.append(".")
+        
+        dia = SvnDialog(self.tr('Upgrade'))
+        res = dia.startProcess(args, path)
+        if res:
+            dia.exec_()
+
+    ###########################################################################
+    ## Private Subversion specific methods are below.
+    ###########################################################################
+    
+    def __svnURL(self, url):
+        """
+        Private method to format a url for subversion.
+        
+        @param url unformatted url string (string)
+        @return properly formated url for subversion (string)
+        """
+        url = self.svnNormalizeURL(url)
+        url = url.split(':', 2)
+        if len(url) == 3:
+            scheme = url[0]
+            host = url[1]
+            port, path = url[2].split("/", 1)
+            return "{0}:{1}:{2}/{3}".format(
+                scheme, host, port, Utilities.quote(path))
+        else:
+            scheme = url[0]
+            if scheme == "file":
+                return "{0}:{1}".format(scheme, Utilities.quote(url[1]))
+            else:
+                try:
+                    host, path = url[1][2:].split("/", 1)
+                except ValueError:
+                    host = url[1][2:]
+                    path = ""
+                return "{0}://{1}/{2}".format(
+                    scheme, host, Utilities.quote(path))
+
+    def svnNormalizeURL(self, url):
+        """
+        Public method to normalize a url for subversion.
+        
+        @param url url string (string)
+        @return properly normalized url for subversion (string)
+        """
+        protocol, url = url.split("://", 1)
+        if url.startswith("\\\\"):
+            url = url[2:]
+        if protocol == "file":
+            url = os.path.normcase(url)
+        url = url.replace('\\', '/')
+        if url.endswith('/'):
+            url = url[:-1]
+        if not url.startswith("/") and url[1] in [":", "|"]:
+            url = "/{0}".format(url)
+        return "{0}://{1}".format(protocol, url)
+
+    ###########################################################################
+    ## Methods to get the helper objects are below.
+    ###########################################################################
+    
+    def vcsGetProjectBrowserHelper(self, browser, project,
+                                   isTranslationsBrowser=False):
+        """
+        Public method to instanciate a helper object for the different
+        project browsers.
+        
+        @param browser reference to the project browser object
+        @param project reference to the project object
+        @param isTranslationsBrowser flag indicating, the helper is requested
+            for the translations browser (this needs some special treatment)
+        @return the project browser helper object
+        """
+        from .ProjectBrowserHelper import SvnProjectBrowserHelper
+        return SvnProjectBrowserHelper(self, browser, project,
+                                       isTranslationsBrowser)
+        
+    def vcsGetProjectHelper(self, project):
+        """
+        Public method to instanciate a helper object for the project.
+        
+        @param project reference to the project object
+        @return the project helper object
+        """
+        helper = self.__plugin.getProjectHelper()
+        helper.setObjects(self, project)
+        self.__wcng = \
+            os.path.exists(
+                os.path.join(project.getProjectPath(), ".svn", "format")) or \
+            os.path.exists(
+                os.path.join(project.getProjectPath(), "_svn", "format")) or \
+            os.path.exists(
+                os.path.join(project.getProjectPath(), ".svn", "wc.db")) or \
+            os.path.exists(
+                os.path.join(project.getProjectPath(), "_svn", "wc.db"))
+        return helper
+
+    ###########################################################################
+    ##  Status Monitor Thread methods
+    ###########################################################################
+
+    def _createStatusMonitorThread(self, interval, project):
+        """
+        Protected method to create an instance of the VCS status monitor
+        thread.
+        
+        @param interval check interval for the monitor thread in seconds
+            (integer)
+        @param project reference to the project object
+        @return reference to the monitor thread (QThread)
+        """
+        from .SvnStatusMonitorThread import SvnStatusMonitorThread
+        return SvnStatusMonitorThread(interval, project, self)

eric ide

mercurial