--- /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