From d4ea028edfd28af3383afea6470f034b2374fe25 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Thu, 6 Feb 2025 11:03:00 -0600 Subject: [PATCH] Addon Manager: improve git branch changing --- .../gui/test_change_branch.py | 0 .../addonmanager_freecad_interface.py | 4 +- .../AddonManager/addonmanager_utilities.py | 15 ++++ src/Mod/AddonManager/change_branch.py | 84 ++++++++++++++----- 4 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 src/Mod/AddonManager/AddonManagerTest/gui/test_change_branch.py diff --git a/src/Mod/AddonManager/AddonManagerTest/gui/test_change_branch.py b/src/Mod/AddonManager/AddonManagerTest/gui/test_change_branch.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Mod/AddonManager/addonmanager_freecad_interface.py b/src/Mod/AddonManager/addonmanager_freecad_interface.py index ac66a100ca..42839f6802 100644 --- a/src/Mod/AddonManager/addonmanager_freecad_interface.py +++ b/src/Mod/AddonManager/addonmanager_freecad_interface.py @@ -49,6 +49,8 @@ try: if FreeCAD.GuiUp: import FreeCADGui + + loadUi = FreeCADGui.PySideUic.loadUi else: FreeCADGui = None @@ -63,7 +65,7 @@ except ImportError: return string def Version(): - return 0, 22, 0, "dev" + return 1, 1, 0, "dev" class ConsoleReplacement: """If FreeCAD's Console is not available, create a replacement by redirecting FreeCAD diff --git a/src/Mod/AddonManager/addonmanager_utilities.py b/src/Mod/AddonManager/addonmanager_utilities.py index 5a95d60229..2d878e1676 100644 --- a/src/Mod/AddonManager/addonmanager_utilities.py +++ b/src/Mod/AddonManager/addonmanager_utilities.py @@ -69,6 +69,21 @@ else: import urllib.request import ssl +if fci.FreeCADGui: + loadUi = fci.loadUi +else: + try: + from PySide6.QtUiTools import QUiLoader + except ImportError: + from PySide2.QtUiTools import QUiLoader + + def loadUi(ui_file: str) -> QtWidgets.QWidget: + q_ui_file = QtCore.QFile(ui_file) + q_ui_file.open(QtCore.QFile.OpenModeFlag.ReadOnly) + loader = QUiLoader() + return loader.load(ui_file) + + # @package AddonManager_utilities # \ingroup ADDONMANAGER # \brief Utilities to work across different platforms, providers and python versions diff --git a/src/Mod/AddonManager/change_branch.py b/src/Mod/AddonManager/change_branch.py index 3fb73358de..478a012378 100644 --- a/src/Mod/AddonManager/change_branch.py +++ b/src/Mod/AddonManager/change_branch.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * * -# * Copyright (c) 2022-2023 FreeCAD Project Association * +# * Copyright (c) 2022-2025 The FreeCAD Project Association AISBL * # * * # * This file is part of FreeCAD. * # * * @@ -22,14 +22,22 @@ # *************************************************************************** import os +from typing import Dict -import FreeCAD -import FreeCADGui -from addonmanager_git import initialize_git +import addonmanager_freecad_interface as fci +import addonmanager_utilities as utils -from PySide import QtWidgets, QtCore +from addonmanager_git import initialize_git, GitFailed -translate = FreeCAD.Qt.translate +try: + from PySide import QtWidgets, QtCore +except ImportError: + try: + from PySide6 import QtWidgets, QtCore + except ImportError: + from PySide2 import QtWidgets, QtCore + +translate = fci.translate class ChangeBranchDialog(QtWidgets.QWidget): @@ -39,9 +47,7 @@ class ChangeBranchDialog(QtWidgets.QWidget): def __init__(self, path: str, parent=None): super().__init__(parent) - self.ui = FreeCADGui.PySideUic.loadUi( - os.path.join(os.path.dirname(__file__), "change_branch.ui") - ) + self.ui = utils.loadUi(os.path.join(os.path.dirname(__file__), "change_branch.ui")) self.item_filter = ChangeBranchDialogFilter() self.ui.tableView.setModel(self.item_filter) @@ -54,6 +60,9 @@ class ChangeBranchDialog(QtWidgets.QWidget): # Figure out what row gets selected: git_manager = initialize_git() + if git_manager is None: + return + row = 0 self.current_ref = git_manager.current_branch(path) selection_model = self.ui.tableView.selectionModel() @@ -111,14 +120,51 @@ class ChangeBranchDialog(QtWidgets.QWidget): if result == QtWidgets.QMessageBox.Cancel: return - gm = initialize_git() - remote_name = ref["ref_name"] - _, _, local_name = ref["ref_name"].rpartition("/") + self._change_branch(self.item_model.path, ref) + + def _change_branch(self, path: str, ref: Dict[str, str]) -> None: + """Change the git clone in `path` to git ref `ref`. Emits the branch_changed signal + on success.""" + remote_name = ref["ref_name"] + _, _, local_name = ref["ref_name"].rpartition("/") + gm = initialize_git() + if gm is None: + self._show_no_git_dialog() + return + + try: if ref["upstream"]: - gm.checkout(self.item_model.path, remote_name) + gm.checkout(path, remote_name) else: - gm.checkout(self.item_model.path, remote_name, args=["-b", local_name]) + gm.checkout(path, remote_name, args=["-b", local_name]) self.branch_changed.emit(self.current_ref, local_name) + except GitFailed: + self._show_git_failed_dialog() + + def _show_no_git_dialog(self): + QtWidgets.QMessageBox.critical( + self, + translate("AddonsInstaller", "Cannot find git"), + translate( + "AddonsInstaller", + "Could not find git executable: cannot change branch", + ), + QtWidgets.QMessageBox.Ok, + QtWidgets.QMessageBox.Ok, + ) + + def _show_git_failed_dialog(self): + QtWidgets.QMessageBox.critical( + self, + translate("AddonsInstaller", "git operation failed"), + translate( + "AddonsInstaller", + "Git returned an error code when attempting to change branch. There may be " + "more details in the Report View.", + ), + QtWidgets.QMessageBox.Ok, + QtWidgets.QMessageBox.Ok, + ) class ChangeBranchDialogModel(QtCore.QAbstractTableModel): @@ -235,11 +281,11 @@ class ChangeBranchDialogModel(QtCore.QAbstractTableModel): class ChangeBranchDialogFilter(QtCore.QSortFilterProxyModel): def lessThan(self, left: QtCore.QModelIndex, right: QtCore.QModelIndex): - leftData = self.sourceModel().data(left, ChangeBranchDialogModel.DataSortRole) - rightData = self.sourceModel().data(right, ChangeBranchDialogModel.DataSortRole) - if leftData is None or rightData is None: - if rightData is not None: + left_data = self.sourceModel().data(left, ChangeBranchDialogModel.DataSortRole) + right_data = self.sourceModel().data(right, ChangeBranchDialogModel.DataSortRole) + if left_data is None or right_data is None: + if right_data is not None: return True else: return False - return leftData < rightData + return left_data < right_data