Thu, 27 Feb 2014 19:48:55 +0100
Started implementing the Mercurial lfconvert functionality.
--- a/Plugins/VcsPlugins/vcsMercurial/HgDialog.py Thu Feb 27 18:59:52 2014 +0100 +++ b/Plugins/VcsPlugins/vcsMercurial/HgDialog.py Thu Feb 27 19:48:55 2014 +0100 @@ -27,12 +27,14 @@ shows the output of the process. The dialog is modal, which causes a synchronized execution of the process. """ - def __init__(self, text, hg=None, parent=None): + def __init__(self, text, hg=None, useClient=True, parent=None): """ Constructor @param text text to be shown by the label (string) @param hg reference to the Mercurial interface object (Hg) + @param useClient flag indicating to use the command server client + if possible (boolean) @param parent parent widget (QWidget) """ super().__init__(parent) @@ -44,7 +46,10 @@ self.proc = None self.username = '' self.password = '' - self.__hgClient = hg.getClient() + if useClient: + self.__hgClient = hg.getClient() + else: + self.__hgClient = None self.vcs = hg self.outputGroup.setTitle(text)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/LfConvertDataDialog.py Thu Feb 27 19:48:55 2014 +0100 @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- + +""" +Module implementing a dialog to enter the data for the repo conversion. +""" + +import os + +from PyQt4.QtCore import pyqtSlot +from PyQt4.QtGui import QDialog, QDialogButtonBox + +from E5Gui import E5FileDialog +from E5Gui.E5Completers import E5DirCompleter + +from .Ui_LfConvertDataDialog import Ui_LfConvertDataDialog + +from . import getDefaults + +import Utilities +import UI.PixmapCache + + +class LfConvertDataDialog(QDialog, Ui_LfConvertDataDialog): + """ + Class implementing a dialog to enter the data for the repo conversion. + """ + def __init__(self, currentPath, mode, parent=None): + """ + Constructor + + @param currentPath directory name of the current project (string) + @param mode dialog mode (string, one of 'largefiles' or 'normal') + @param parent reference to the parent widget (QWidget) + """ + super().__init__(parent) + self.setupUi(self) + + self.newProjectButton.setIcon(UI.PixmapCache.getIcon("open.png")) + + self.__newProjectCompleter = E5DirCompleter(self.newProjectEdit) + + self.__defaults = getDefaults() + self.__currentPath = Utilities.toNativeSeparators(currentPath) + + self.currentProjectLabel.setPath(currentPath) + self.newProjectEdit.setText(os.path.dirname(currentPath)) + + self.lfFileSizeSpinBox.setValue(self.__defaults["minsize"]) + self.lfFilePatternsEdit.setText(" ".join(self.__defaults["pattern"])) + + if mode == 'normal': + self.lfFileSizeSpinBox.setEnabled(False) + self.lfFilePatternsEdit.setEnabled(False) + + self.resize(self.width(), self.minimumSizeHint().height()) + + @pyqtSlot(str) + def on_newProjectEdit_textChanged(self, txt): + """ + Private slot to handle editing of the new project directory. + + @param txt new project directory name (string) + """ + self.buttonBox.button(QDialogButtonBox.Ok).setEnabled( + txt and Utilities.toNativeSeparators(txt) != os.path.dirname( + self.__currentPath)) + + @pyqtSlot() + def on_newProjectButton_clicked(self): + """ + Private slot to select the new project directory name via a directory + selection dialog. + """ + directory = Utilities.fromNativeSeparators(self.newProjectEdit.text()) + directory = E5FileDialog.getExistingDirectory( + self, + self.tr("New Project Directory"), + directory, + E5FileDialog.Options(E5FileDialog.ShowDirsOnly)) + if directory: + self.newProjectEdit.setText( + Utilities.toNativeSeparators(directory)) + + def getData(self): + """ + Public method to retrieve the entered data. + + @return tuple containing the new project directory name (string), + minimum file size (integer) and file patterns (list of string) + """ + patterns = self.lfFilePatternsEdit.text().split() + if set(patterns) == set(self.__defaults["pattern"]): + patterns = [] + + return ( + Utilities.toNativeSeparators(self.newProjectEdit.text()), + self.lfFileSizeSpinBox.value(), + patterns, + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/LfConvertDataDialog.ui Thu Feb 27 19:48:55 2014 +0100 @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LfConvertDataDialog</class> + <widget class="QDialog" name="LfConvertDataDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>143</height> + </rect> + </property> + <property name="windowTitle"> + <string>Convert Repository Format</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="4"> + <widget class="E5SqueezeLabelPath" name="currentProjectLabel"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>New project directory:</string> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLineEdit" name="newProjectEdit"> + <property name="toolTip"> + <string>Enter the directory name of the new project directory</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QToolButton" name="newProjectButton"> + <property name="toolTip"> + <string>Press to select the new project directory name via a directory selection dialog</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Minimum file size:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="lfFileSizeSpinBox"> + <property name="toolTip"> + <string>Enter the minimum file size in MB for files to be treated as Large Files</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="suffix"> + <string> MB</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="value"> + <number>10</number> + </property> + </widget> + </item> + <item row="2" column="2" colspan="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>297</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Patterns:</string> + </property> + </widget> + </item> + <item row="3" column="1" colspan="3"> + <widget class="QLineEdit" name="lfFilePatternsEdit"> + <property name="toolTip"> + <string>Enter file patterns (space separated) for files to be treated as Large Files</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="4"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>E5SqueezeLabelPath</class> + <extends>QLabel</extends> + <header>E5Gui/E5SqueezeLabels.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>newProjectEdit</tabstop> + <tabstop>newProjectButton</tabstop> + <tabstop>lfFileSizeSpinBox</tabstop> + <tabstop>lfFilePatternsEdit</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>LfConvertDataDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>LfConvertDataDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/ProjectHelper.py Thu Feb 27 19:48:55 2014 +0100 @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the shelve extension project helper. +""" + +from PyQt4.QtGui import QMenu + +from E5Gui.E5Action import E5Action + +from ..HgExtensionProjectHelper import HgExtensionProjectHelper + + +class LargefilesProjectHelper(HgExtensionProjectHelper): + """ + Class implementing the queues extension project helper. + """ + def __init__(self): + """ + Constructor + """ + super().__init__() + + def initActions(self): + """ + Public method to generate the action objects. + """ + self.hgConvertToLargefilesAct = E5Action( + self.tr('Convert repository to largefiles'), + self.tr('Convert repository to largefiles...'), + 0, 0, self, 'mercurial_convert_to_largefiles') + self.hgConvertToLargefilesAct.setStatusTip(self.tr( + 'Convert the repository of the project to a largefiles repository.' + )) + self.hgConvertToLargefilesAct.setWhatsThis(self.tr( + """<b>Convert repository to largefiles</b>""" + """<p>This converts the repository of the project to a""" + """ largefiles repository. A new project is created. The""" + """ current one is kept as a backup.</p>""" + )) + self.hgConvertToLargefilesAct.triggered[()].connect( + lambda: self.__hgLfconvert("largefiles")) + self.actions.append(self.hgConvertToLargefilesAct) + + self.hgConvertToNormalAct = E5Action( + self.tr('Convert repository to normal'), + self.tr('Convert repository to normal...'), + 0, 0, self, 'mercurial_convert_to_normal') + self.hgConvertToNormalAct.setStatusTip(self.tr( + 'Convert the repository of the project to a normal repository.' + )) + self.hgConvertToNormalAct.setWhatsThis(self.tr( + """<b>Convert repository to normal</b>""" + """<p>This converts the repository of the project to a""" + """ normal repository. A new project is created. The current""" + """ one is kept as a backup.</p>""" + )) + self.hgConvertToNormalAct.triggered[()].connect( + lambda: self.__hgLfconvert("normal")) + self.actions.append(self.hgConvertToNormalAct) + + def initMenu(self, mainMenu): + """ + Public method to generate the extension menu. + + @param mainMenu reference to the main menu (QMenu) + @return populated menu (QMenu) + """ + menu = QMenu(self.menuTitle(), mainMenu) + menu.setTearOffEnabled(True) + + menu.addAction(self.hgConvertToLargefilesAct) + menu.addAction(self.hgConvertToNormalAct) + + return menu + + def menuTitle(self): + """ + Public method to get the menu title. + + @return title of the menu (string) + """ + return self.tr("Large Files") + + def __hgLfconvert(self, direction): + """ + Private slot to convert the repository format of the current project. + + @param direction direction of the conversion (string, one of + 'largefiles' or 'normal') + """ + assert direction in ["largefiles", "normal"] + + self.vcs.getExtensionObject("largefiles").hgLfconvert( + direction, self.project.getProjectFile())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/__init__.py Thu Feb 27 19:48:55 2014 +0100 @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing the largefiles extension support interface. +""" + +def getDefaults(): + """ + Function to get the default values of the extension. + + @return dictionary with default values and parameter as key (dict) + """ + return { + 'minsize': 10, # minimum size in MB + 'pattern': [], # file name patterns + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/largefiles.py Thu Feb 27 19:48:55 2014 +0100 @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the largefiles extension interface. +""" + +import os +import shutil + +from PyQt4.QtGui import QDialog + +from E5Gui.E5Application import e5App +from E5Gui import E5MessageBox + +from ..HgExtension import HgExtension +from ..HgDialog import HgDialog + +from . import getDefaults + + +class Largefiles(HgExtension): + """ + Class implementing the largefiles extension interface. + """ + def __init__(self, vcs): + """ + Constructor + + @param vcs reference to the Mercurial vcs object + """ + super().__init__(vcs) + + def hgLfconvert(self, direction, projectFile): + """ + Public slot to convert the repository format of the current project. + + @param direction direction of the conversion (string, one of + 'largefiles' or 'normal') + @param projectFile file name of the current project file (string) + """ + assert direction in ["largefiles", "normal"] + + projectDir = os.path.dirname(projectFile) + + # find the root of the repo + repodir = projectDir + while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): + repodir = os.path.dirname(repodir) + if os.path.splitdrive(repodir)[1] == os.sep: + return False + + from .LfConvertDataDialog import LfConvertDataDialog + dlg = LfConvertDataDialog(projectDir, direction) + if dlg.exec_() == QDialog.Accepted: + newName, minSize, patterns = dlg.getData() + newProjectFile = os.path.join( + newName, os.path.basename(projectFile)) + + # step 1: convert the current project to new project + args = self.vcs.initCommand("lfconvert") + if direction == 'normal': + args.append('--to-normal') + else: + args.append("--size") + args.append(str(minSize)) + args.append(projectDir) + args.append(newName) + if direction == 'largefiles' and patterns: + args.extend(patterns) + + dia = HgDialog(self.tr('Convert Project - Converting'), self.vcs) + res = dia.startProcess(args, repodir) + if res: + dia.exec_() + res = dia.normalExit() and os.path.isdir( + os.path.join(newName, self.vcs.adminDir)) + + # step 2: create working directory contents + if res: + args = self.vcs.initCommand("update") + if "-v" not in args and "--verbose" not in args: + args.append("-v") + dia = HgDialog(self.tr('Convert Project - Extracting'), + self.vcs, useClient=False) + res = dia.startProcess(args, newName) + if res: + dia.exec_() + res = dia.normalExit() and os.path.isfile(newProjectFile) + + # step 3: close current project and open new one + if res: + e5App().getObject("Project").openProject(newProjectFile) + + # step 3.1: copy old hgrc file + hgrc = os.path.join(repodir, self.vcs.adminDir, "hgrc") + if os.path.exists(hgrc): + ok = E5MessageBox.yesNo( + None, + self.tr("Convert Project"), + self.tr("""Shall the Mercurial repository""" + """ configuration file be copied over?""")) + if ok: + shutil.copy( + hgrc, + os.path.join(newName, self.vcs.adminDir, "hgrc")) + # TODO: write patterns to hgrc
--- a/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py Thu Feb 27 18:59:52 2014 +0100 +++ b/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py Thu Feb 27 19:48:55 2014 +0100 @@ -46,6 +46,7 @@ from .TransplantExtension.ProjectHelper import TransplantProjectHelper from .RebaseExtension.ProjectHelper import RebaseProjectHelper from .ShelveExtension.ProjectHelper import ShelveProjectHelper + from .LargefilesExtension.ProjectHelper import LargefilesProjectHelper self.__extensions = { "bookmarks": BookmarksProjectHelper(), "mq": QueuesProjectHelper(), @@ -55,6 +56,7 @@ "transplant": TransplantProjectHelper(), "rebase": RebaseProjectHelper(), "shelve": ShelveProjectHelper(), + "largefiles": LargefilesProjectHelper(), } self.__extensionMenuTitles = {}
--- a/Plugins/VcsPlugins/vcsMercurial/hg.py Thu Feb 27 18:59:52 2014 +0100 +++ b/Plugins/VcsPlugins/vcsMercurial/hg.py Thu Feb 27 19:48:55 2014 +0100 @@ -136,6 +136,7 @@ from .TransplantExtension.transplant import Transplant from .RebaseExtension.rebase import Rebase from .ShelveExtension.shelve import Shelve + from .LargefilesExtension.largefiles import Largefiles self.__extensions = { "bookmarks": Bookmarks(self), "mq": Queues(self), @@ -145,6 +146,7 @@ "transplant": Transplant(self), "rebase": Rebase(self), "shelve": Shelve(self), + "largefiles": Largefiles(self) } def getPlugin(self): @@ -3346,6 +3348,11 @@ self.version < (2, 8): # shelve extension was added as of Mercurial 2.8.0 isActive = False + if isActive and \ + extensionName == "largefiles" and \ + self.version < (2, 0): + # largefiles extension was added as of Mercurial 2.0.0 + isActive = False return isActive
--- a/eric5.e4p Thu Feb 27 18:59:52 2014 +0100 +++ b/eric5.e4p Thu Feb 27 19:48:55 2014 +0100 @@ -1121,6 +1121,10 @@ <Source>Plugins/VcsPlugins/vcsMercurial/ShelveExtension/HgShelvesSelectionDialog.py</Source> <Source>Plugins/VcsPlugins/vcsMercurial/HgExtensionProjectBrowserHelper.py</Source> <Source>Plugins/VcsPlugins/vcsMercurial/ShelveExtension/ProjectBrowserHelper.py</Source> + <Source>Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/__init__.py</Source> + <Source>Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/largefiles.py</Source> + <Source>Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/ProjectHelper.py</Source> + <Source>Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/LfConvertDataDialog.py</Source> </Sources> <Forms> <Form>PyUnit/UnittestDialog.ui</Form> @@ -1446,6 +1450,7 @@ <Form>Plugins/VcsPlugins/vcsMercurial/ShelveExtension/HgUnshelveDataDialog.ui</Form> <Form>Plugins/VcsPlugins/vcsMercurial/ShelveExtension/HgShelveBrowserDialog.ui</Form> <Form>Plugins/VcsPlugins/vcsMercurial/ShelveExtension/HgShelvesSelectionDialog.ui</Form> + <Form>Plugins/VcsPlugins/vcsMercurial/LargefilesExtension/LfConvertDataDialog.ui</Form> </Forms> <Translations> <Translation>i18n/eric5_cs.ts</Translation>