Mercurial Interface eric7

Tue, 03 Dec 2024 14:21:36 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 03 Dec 2024 14:21:36 +0100
branch
eric7
changeset 11075
282fc28b44ee
parent 11074
97f6160548a6
child 11076
2989645b2618

Mercurial Interface
- Added support for the `fastexport` extension.

docs/changelog.md file | annotate | diff | comparison | revisions
eric7.epj file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.ui file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/ProjectHelper.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/Ui_HgFastexportConfigDialog.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/__init__.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/fastexport.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py file | annotate | diff | comparison | revisions
src/eric7/Plugins/VcsPlugins/vcsMercurial/hg.py file | annotate | diff | comparison | revisions
--- a/docs/changelog.md	Tue Dec 03 14:20:10 2024 +0100
+++ b/docs/changelog.md	Tue Dec 03 14:21:36 2024 +0100
@@ -6,6 +6,7 @@
     - Removed support for the `strip` extension (does not exist anymore).
     - Moved `purge` and `shelve` extensions to the `Other Functions` menu
       because they are builtin functions.
+    - Added support for the `fastexport` extension.
 
 ### Version 24.12
 - bug fixes
--- a/eric7.epj	Tue Dec 03 14:20:10 2024 +0100
+++ b/eric7.epj	Tue Dec 03 14:21:36 2024 +0100
@@ -455,6 +455,7 @@
       "src/eric7/Plugins/VcsPlugins/vcsGit/GitWorktreePathsDialog.ui",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/CloseheadExtension/HgCloseHeadSelectionDialog.ui",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/ConfigurationPage/MercurialPage.ui",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.ui",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/GpgExtension/HgGpgSignDialog.ui",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/GpgExtension/HgGpgSignaturesDialog.ui",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/HgAddSubrepositoryDialog.ui",
@@ -1848,6 +1849,11 @@
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/ConfigurationPage/MercurialPage.py",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/ConfigurationPage/Ui_MercurialPage.py",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/ConfigurationPage/__init__.py",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.py",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/ProjectHelper.py",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/Ui_HgFastexportConfigDialog.py",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/__init__.py",
+      "src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/fastexport.py",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/GpgExtension/HgGpgSignDialog.py",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/GpgExtension/HgGpgSignaturesDialog.py",
       "src/eric7/Plugins/VcsPlugins/vcsMercurial/GpgExtension/ProjectHelper.py",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.py	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the fastexport configuration dialog.
+"""
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtWidgets import QDialog, QDialogButtonBox
+
+from eric7.EricWidgets.EricPathPicker import EricPathPickerModes
+
+from .Ui_HgFastexportConfigDialog import Ui_HgFastexportConfigDialog
+
+
+class HgFastexportConfigDialog(QDialog, Ui_HgFastexportConfigDialog):
+    """
+    Class implementing the fastexport configuration dialog.
+    """
+
+    def __init__(self, revisions=None, parent=None):
+        """
+        Constructor
+
+        @param revisions list of revisions, tags or branches to be exported
+            (defaults to None)
+        @type list of str (optional)
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        self.outputPicker.setMode(EricPathPickerModes.SAVE_FILE_ENSURE_EXTENSION_MODE)
+        self.authormapPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
+        self.importMarksPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
+        self.exportMarksPicker.setMode(
+            EricPathPickerModes.SAVE_FILE_ENSURE_EXTENSION_MODE
+        )
+        fileFilters = self.tr("Text Files (*.txt);;All Files (*)")
+        for picker in (
+            self.outputPicker,
+            self.authormapPicker,
+            self.importMarksPicker,
+            self.exportMarksPicker,
+        ):
+            picker.setFilters(fileFilters)
+
+        self.outputPicker.textChanged.connect(self.__updateOK)
+
+        if revisions:
+            self.revisionsEdit.setText(", ".join(revisions))
+
+        self.__updateOK()
+
+    @pyqtSlot()
+    def __updateOK(self):
+        """
+        Private slot to updated the enabled state of the OK button.
+        """
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(
+            bool(self.outputPicker.text())
+        )
+
+    def getData(self):
+        """
+        Public method to get the entered fastexport configuration data.
+
+        @return tuple containing the fastexport configuration (output file,
+            list of revisions, author map file, import marks file, export marks
+            file)
+        @rtype tuple of (str, list of str, str, str, str)
+        """
+        return (
+            self.outputPicker.text(),
+            [r.strip() for r in self.revisionsEdit.text().split(",") if r.strip()],
+            self.authormapPicker.text(),
+            self.importMarksPicker.text(),
+            self.exportMarksPicker.text(),
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.ui	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HgFastexportConfigDialog</class>
+ <widget class="QDialog" name="HgFastexportConfigDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>172</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>fastexport Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="4" column="1">
+    <widget class="EricPathPicker" name="exportMarksPicker" native="true">
+     <property name="focusPolicy">
+      <enum>Qt::FocusPolicy::StrongFocus</enum>
+     </property>
+     <property name="toolTip">
+      <string>Enter the path of the file for the exported marks.</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="EricPathPicker" name="outputPicker" native="true">
+     <property name="focusPolicy">
+      <enum>Qt::FocusPolicy::StrongFocus</enum>
+     </property>
+     <property name="toolTip">
+      <string>Enter the path of the output file.</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Revisions:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="EricPathPicker" name="importMarksPicker" native="true">
+     <property name="focusPolicy">
+      <enum>Qt::FocusPolicy::StrongFocus</enum>
+     </property>
+     <property name="toolTip">
+      <string>Enter the path of the file containing already exported marks.</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Author Map:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Import Marks:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="label_5">
+     <property name="text">
+      <string>Export Marks:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Output File:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="revisionsEdit">
+     <property name="toolTip">
+      <string>Enter the revisions, tags or branches to be exported (separated by comma).</string>
+     </property>
+     <property name="clearButtonEnabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="EricPathPicker" name="authormapPicker" native="true">
+     <property name="focusPolicy">
+      <enum>Qt::FocusPolicy::StrongFocus</enum>
+     </property>
+     <property name="toolTip">
+      <string>Enter the path of the author map file.</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>EricPathPicker</class>
+   <extends>QWidget</extends>
+   <header>eric7/EricWidgets/EricPathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>outputPicker</tabstop>
+  <tabstop>revisionsEdit</tabstop>
+  <tabstop>authormapPicker</tabstop>
+  <tabstop>importMarksPicker</tabstop>
+  <tabstop>exportMarksPicker</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HgFastexportConfigDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>177</x>
+     <y>151</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>179</x>
+     <y>168</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HgFastexportConfigDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>289</x>
+     <y>151</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>289</x>
+     <y>170</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/ProjectHelper.py	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the fastexport extension project helper.
+"""
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtWidgets import QMenu
+
+from eric7.EricGui.EricAction import EricAction
+
+from ..HgExtensionProjectHelper import HgExtensionProjectHelper
+
+
+class FastexportProjectHelper(HgExtensionProjectHelper):
+    """
+    Class implementing the fastexport extension project helper.
+    """
+
+    def __init__(self):
+        """
+        Constructor
+        """
+        super().__init__()
+
+    def initActions(self):
+        """
+        Public method to generate the action objects.
+        """
+        self.hgFastexportAct = EricAction(
+            self.tr("Export to git"),
+            self.tr("Export to git"),
+            0,
+            0,
+            self,
+            "mercurial_fastexport",
+        )
+        self.hgFastexportAct.setStatusTip(
+            self.tr("Export the repository as git fast-import stream.")
+        )
+        self.hgFastexportAct.setWhatsThis(
+            self.tr(
+                """<b>Export to git</b>"""
+                """<p>This exports the repository as a git fast-import stream.</p>"""
+            )
+        )
+        self.hgFastexportAct.triggered.connect(self.__hgFastexport)
+        self.actions.append(self.hgFastexportAct)
+
+    def initMenu(self, mainMenu):
+        """
+        Public method to generate the extension menu.
+
+        @param mainMenu reference to the main menu
+        @type QMenu
+        @return populated menu
+        @rtype QMenu
+        """
+        menu = QMenu(self.menuTitle(), mainMenu)
+        menu.setTearOffEnabled(True)
+
+        menu.addAction(self.hgFastexportAct)
+
+        return menu
+
+    def menuTitle(self):
+        """
+        Public method to get the menu title.
+
+        @return title of the menu
+        @rtype str
+        """
+        return self.tr("Fastexport")
+
+    @pyqtSlot()
+    def __hgFastexport(self):
+        """
+        Private slot used to generate a git fast-import file.
+        """
+        self.vcs.getExtensionObject("fastexport").hgFastexport()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/Ui_HgFastexportConfigDialog.py	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,81 @@
+# Form implementation generated from reading ui file 'src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/HgFastexportConfigDialog.ui'
+#
+# Created by: PyQt6 UI code generator 6.7.1
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again.  Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_HgFastexportConfigDialog(object):
+    def setupUi(self, HgFastexportConfigDialog):
+        HgFastexportConfigDialog.setObjectName("HgFastexportConfigDialog")
+        HgFastexportConfigDialog.resize(600, 172)
+        HgFastexportConfigDialog.setSizeGripEnabled(True)
+        self.gridLayout = QtWidgets.QGridLayout(HgFastexportConfigDialog)
+        self.gridLayout.setObjectName("gridLayout")
+        self.exportMarksPicker = EricPathPicker(parent=HgFastexportConfigDialog)
+        self.exportMarksPicker.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
+        self.exportMarksPicker.setObjectName("exportMarksPicker")
+        self.gridLayout.addWidget(self.exportMarksPicker, 4, 1, 1, 1)
+        self.outputPicker = EricPathPicker(parent=HgFastexportConfigDialog)
+        self.outputPicker.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
+        self.outputPicker.setObjectName("outputPicker")
+        self.gridLayout.addWidget(self.outputPicker, 0, 1, 1, 1)
+        self.label = QtWidgets.QLabel(parent=HgFastexportConfigDialog)
+        self.label.setObjectName("label")
+        self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
+        self.importMarksPicker = EricPathPicker(parent=HgFastexportConfigDialog)
+        self.importMarksPicker.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
+        self.importMarksPicker.setObjectName("importMarksPicker")
+        self.gridLayout.addWidget(self.importMarksPicker, 3, 1, 1, 1)
+        self.label_3 = QtWidgets.QLabel(parent=HgFastexportConfigDialog)
+        self.label_3.setObjectName("label_3")
+        self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
+        self.label_4 = QtWidgets.QLabel(parent=HgFastexportConfigDialog)
+        self.label_4.setObjectName("label_4")
+        self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
+        self.label_5 = QtWidgets.QLabel(parent=HgFastexportConfigDialog)
+        self.label_5.setObjectName("label_5")
+        self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
+        self.label_2 = QtWidgets.QLabel(parent=HgFastexportConfigDialog)
+        self.label_2.setObjectName("label_2")
+        self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
+        self.revisionsEdit = QtWidgets.QLineEdit(parent=HgFastexportConfigDialog)
+        self.revisionsEdit.setClearButtonEnabled(True)
+        self.revisionsEdit.setObjectName("revisionsEdit")
+        self.gridLayout.addWidget(self.revisionsEdit, 1, 1, 1, 1)
+        self.buttonBox = QtWidgets.QDialogButtonBox(parent=HgFastexportConfigDialog)
+        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
+        self.buttonBox.setObjectName("buttonBox")
+        self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 2)
+        self.authormapPicker = EricPathPicker(parent=HgFastexportConfigDialog)
+        self.authormapPicker.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus)
+        self.authormapPicker.setObjectName("authormapPicker")
+        self.gridLayout.addWidget(self.authormapPicker, 2, 1, 1, 1)
+
+        self.retranslateUi(HgFastexportConfigDialog)
+        self.buttonBox.accepted.connect(HgFastexportConfigDialog.accept) # type: ignore
+        self.buttonBox.rejected.connect(HgFastexportConfigDialog.reject) # type: ignore
+        QtCore.QMetaObject.connectSlotsByName(HgFastexportConfigDialog)
+        HgFastexportConfigDialog.setTabOrder(self.outputPicker, self.revisionsEdit)
+        HgFastexportConfigDialog.setTabOrder(self.revisionsEdit, self.authormapPicker)
+        HgFastexportConfigDialog.setTabOrder(self.authormapPicker, self.importMarksPicker)
+        HgFastexportConfigDialog.setTabOrder(self.importMarksPicker, self.exportMarksPicker)
+
+    def retranslateUi(self, HgFastexportConfigDialog):
+        _translate = QtCore.QCoreApplication.translate
+        HgFastexportConfigDialog.setWindowTitle(_translate("HgFastexportConfigDialog", "fastexport Configuration"))
+        self.exportMarksPicker.setToolTip(_translate("HgFastexportConfigDialog", "Enter the path of the file for the exported marks."))
+        self.outputPicker.setToolTip(_translate("HgFastexportConfigDialog", "Enter the path of the output file."))
+        self.label.setText(_translate("HgFastexportConfigDialog", "Revisions:"))
+        self.importMarksPicker.setToolTip(_translate("HgFastexportConfigDialog", "Enter the path of the file containing already exported marks."))
+        self.label_3.setText(_translate("HgFastexportConfigDialog", "Author Map:"))
+        self.label_4.setText(_translate("HgFastexportConfigDialog", "Import Marks:"))
+        self.label_5.setText(_translate("HgFastexportConfigDialog", "Export Marks:"))
+        self.label_2.setText(_translate("HgFastexportConfigDialog", "Output File:"))
+        self.revisionsEdit.setToolTip(_translate("HgFastexportConfigDialog", "Enter the revisions, tags or branches to be exported (separated by comma)."))
+        self.authormapPicker.setToolTip(_translate("HgFastexportConfigDialog", "Enter the path of the author map file."))
+from eric7.EricWidgets.EricPathPicker import EricPathPicker
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/__init__.py	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the fastexport extension support interface.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/FastexportExtension/fastexport.py	Tue Dec 03 14:21:36 2024 +0100
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the fastexport extension interface.
+"""
+
+import os
+
+from PyQt6.QtCore import QProcess, pyqtSlot
+from PyQt6.QtWidgets import QDialog
+
+from eric7.EricWidgets import EricMessageBox
+from eric7.EricWidgets.EricProgressDialog import EricProgressDialog
+
+from ..HgExtension import HgExtension
+from ..HgUtilities import getHgExecutable
+
+
+class Fastexport(HgExtension):
+    """
+    Class implementing the fastexport extension interface.
+    """
+
+    def __init__(self, vcs, ui=None):
+        """
+        Constructor
+
+        @param vcs reference to the Mercurial vcs object
+        @type Hg
+        @param ui reference to a UI widget (defaults to None)
+        @type QWidget
+        """
+        super().__init__(vcs, ui=ui)
+
+        self.__process = None
+        self.__progress = None
+
+    def hgFastexport(self, revisions=None):
+        """
+        Public method to export the repository as a git fast-import stream.
+
+        @param revisions list of revisions to be exported
+        @type list of str
+        """
+        from .HgFastexportConfigDialog import HgFastexportConfigDialog
+
+        dlg = HgFastexportConfigDialog(revisions=revisions, parent=self.ui)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            outputFile, revisions, authormap, importMarks, exportMarks = dlg.getData()
+
+            if os.path.exists(outputFile):
+                overwrite = EricMessageBox.yesNo(
+                    self.ui,
+                    self.tr("Mercurial Fastexport"),
+                    self.tr(
+                        "<p>The output file <b>{0}</b> exists already. Overwrite it?"
+                        "</p>"
+                    ).format(outputFile),
+                )
+                if not overwrite:
+                    return
+
+            repoPath = self.vcs.getClient().getRepository()
+            hgExecutable = getHgExecutable()
+
+            args = self.vcs.initCommand("fastexport")
+            args.extend(["--config", "progress.assume-tty=True"])
+            args.extend(["--config", "progress.format=topic number estimate"])
+            if authormap:
+                args.extend(["--authormap", authormap])
+            if importMarks:
+                args.extend(["--import-marks", importMarks])
+            if exportMarks:
+                args.extend(["--export-marks", exportMarks])
+            for revision in revisions:
+                args.extend(["--rev", revision])
+
+            self.__progress = None
+
+            self.__process = QProcess(parent=self)
+            self.__process.setStandardOutputFile(outputFile)
+            self.__process.setWorkingDirectory(repoPath)
+            self.__process.readyReadStandardError.connect(self.__readStderr)
+            self.__process.finished.connect(self.__processFinished)
+            self.__process.start(hgExecutable, args)
+
+    @pyqtSlot()
+    def __readStderr(self):
+        """
+        Private slot to handle the readyReadStandardError signal.
+        """
+        if self.__process is not None:
+            output = str(
+                self.__process.readAllStandardError(), self.vcs.getEncoding(), "replace"
+            )
+            if output.lstrip().startswith("exporting "):
+                msg = output.splitlines()[-1]
+                topic, number, estimate = msg.split(None, 2)
+                value, maximum = number.split("/", 1)
+                if self.__progress is None:
+                    self.__progress = EricProgressDialog(
+                        labelText="",
+                        cancelButtonText=self.tr("Cancel"),
+                        minimum=0,
+                        maximum=int(maximum),
+                        labelFormat=self.tr("%v/%m Changesets"),
+                        parent=self.ui,
+                    )
+                    self.__progress.setWindowTitle(self.tr("Mercurial Fastexport"))
+                    self.__progress.show()
+                self.__progress.setLabelText(
+                    self.tr("Exporting repository (time remaining: {0}) ...").format(
+                        estimate
+                    )
+                )
+                self.__progress.setValue(int(value))
+
+                if self.__progress.wasCanceled() and self.__process is not None:
+                    self.__process.terminate()
+
+            else:
+                if (
+                    self.__progress
+                    and not self.__progress.wasCanceled()
+                    and self.output.strip()
+                ):
+                    EricMessageBox.warning(
+                        self.ui,
+                        self.tr("Mercurial Fastexport"),
+                        self.tr(
+                            "<p>The repository fastexport process sent an error"
+                            " message.</p><p>{0}</p>"
+                        ).format(output.strip()),
+                    )
+
+    @pyqtSlot(int, QProcess.ExitStatus)
+    def __processFinished(self, exitCode, exitStatus):
+        """
+        Private slot to handle the process finished signal.
+
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status
+        @type QProcess.ExitStatus
+        """
+        if self.__progress is not None:
+            self.__progress.hide()
+            self.__progress.deleteLater()
+            self.__progress = None
+
+        if exitStatus == QProcess.ExitStatus.NormalExit:
+            if exitCode == 0:
+                EricMessageBox.information(
+                    self.ui,
+                    self.tr("Mercurial Fastexport"),
+                    self.tr(
+                        "<p>The repository fastexport process finished"
+                        " successfully.</p>"
+                    ),
+                )
+            elif exitCode == 255:
+                EricMessageBox.warning(
+                    self.ui,
+                    self.tr("Mercurial Fastexport"),
+                    self.tr("<p>The repository fastexport process was cancelled.</p>"),
+                )
+            else:
+                EricMessageBox.warning(
+                    self.ui,
+                    self.tr("Mercurial Fastexport"),
+                    self.tr(
+                        "<p>The repository fastexport process finished"
+                        " with exit code <b>{0}</b>.</p>"
+                    ).format(exitCode),
+                )
+        else:
+            EricMessageBox.critical(
+                self.ui,
+                self.tr("Mercurial Fastexport"),
+                self.tr("<p>The repository fastexport process crashed.</p>"),
+            )
+
+        self.__process.deleteLater()
+        self.__process = None
--- a/src/eric7/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py	Tue Dec 03 14:20:10 2024 +0100
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py	Tue Dec 03 14:21:36 2024 +0100
@@ -38,6 +38,7 @@
         @type str
         """
         from .CloseheadExtension.ProjectHelper import CloseheadProjectHelper
+        from .FastexportExtension.ProjectHelper import FastexportProjectHelper
         from .GpgExtension.ProjectHelper import GpgProjectHelper
         from .HisteditExtension.ProjectHelper import HisteditProjectHelper
         from .LargefilesExtension.ProjectHelper import LargefilesProjectHelper
@@ -60,6 +61,7 @@
         # instantiate the extensions
         self.__extensions = {
             "closehead": CloseheadProjectHelper(),
+            "fastexport": FastexportProjectHelper(),
             "gpg": GpgProjectHelper(),
             "histedit": HisteditProjectHelper(),
             "largefiles": LargefilesProjectHelper(),
--- a/src/eric7/Plugins/VcsPlugins/vcsMercurial/hg.py	Tue Dec 03 14:20:10 2024 +0100
+++ b/src/eric7/Plugins/VcsPlugins/vcsMercurial/hg.py	Tue Dec 03 14:21:36 2024 +0100
@@ -57,6 +57,7 @@
         @type str
         """
         from .CloseheadExtension.closehead import Closehead
+        from .FastexportExtension.fastexport import Fastexport
         from .GpgExtension.gpg import Gpg
         from .HgUtilities import getConfigPath
         from .HisteditExtension.histedit import Histedit
@@ -160,6 +161,7 @@
         # instantiate the extensions
         self.__extensions = {
             "closehead": Closehead(self, ui=self.__ui),
+            "fastexport": Fastexport(self, ui=self.__ui),
             "gpg": Gpg(self, ui=self.__ui),
             "histedit": Histedit(self, ui=self.__ui),
             "largefiles": Largefiles(self, ui=self.__ui),

eric ide

mercurial