--- a/src/eric7/Plugins/VcsPlugins/vcsGit/git.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/VcsPlugins/vcsGit/git.py Wed Jul 13 14:55:47 2022 +0200 @@ -34,51 +34,52 @@ class Git(VersionControl): """ Class implementing the version control systems interface to Git. - + @signal committed() emitted after the commit action has completed """ + committed = pyqtSignal() - + IgnoreFileName = ".gitignore" - + 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': [''] + "global": [""], + "commit": [""], + "checkout": [""], + "update": [""], + "add": [""], + "remove": [""], + "diff": [""], + "log": [""], + "history": [""], + "status": [""], + "tag": [""], + "export": [""], } - + self.__plugin = plugin self.__ui = parent - + self.options = self.defaultOptions - + self.tagTypeList = [ - 'tags', - 'branches', + "tags", + "branches", ] - + self.commandHistory = [] - - self.adminDir = '.git' - + + self.adminDir = ".git" + self.log = None self.logBrowser = None self.reflogBrowser = None @@ -96,27 +97,27 @@ self.bisectReplayEditor = None self.patchStatisticsDialog = None self.submoduleStatusDialog = None - + self.__lastBundlePath = None self.__lastReplayPath = None - + self.statusCache = {} - + self.__commitData = {} self.__commitDialog = None - + self.__patchCheckData = None - + self.__projectHelper = None - + def getPlugin(self): """ Public method to get a reference to the plugin object. - + @return reference to the plugin object (VcsGitPlugin) """ return self.__plugin - + def vcsShutdown(self): """ Public method used to shutdown the Git interface. @@ -155,44 +156,44 @@ self.patchStatisticsDialog.close() if self.submoduleStatusDialog is not None: self.submoduleStatusDialog.close() - + # shut down the project helpers if self.__projectHelper is not None: self.__projectHelper.shutdown() - + def initCommand(self, command): """ Public method to initialize a command arguments list. - + @param command command name (string) @return list of command options (list of string) """ args = [command] return args - + def vcsExists(self): """ Public method used to test for the presence of the git executable. - + @return flag indicating the existance (boolean) and an error message (string) """ - self.versionStr = '' + self.versionStr = "" errMsg = "" ioEncoding = Preferences.getSystem("IOEncoding") - + args = self.initCommand("version") process = QProcess() - process.start('git', args) + process.start("git", args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - ioEncoding, 'replace') + output = str(process.readAllStandardOutput(), ioEncoding, "replace") versionLine = output.splitlines()[0] - v = list(re.match(r'.*?(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?', - versionLine).groups()) + v = list( + re.match(r".*?(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?", versionLine).groups() + ) for i in range(4): try: v[i] = int(v[i]) @@ -201,7 +202,7 @@ except IndexError: v.append(0) self.version = tuple(v) - self.versionStr = '.'.join([str(v) for v in self.version]) + self.versionStr = ".".join([str(v) for v in self.version]) return True, errMsg else: if finished: @@ -209,32 +210,31 @@ "The git process finished with the exit code {0}" ).format(process.exitCode()) else: - errMsg = self.tr( - "The git process did not finish within 30s.") + errMsg = self.tr("The git process did not finish within 30s.") else: errMsg = self.tr("Could not start the git executable.") - + return False, errMsg - + def vcsInit(self, vcsDir, noDialog=False): """ Public method used to initialize the Git repository. - + The initialization is done, when a project is converted into a Git controlled project. 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, addAll=True): """ Public method to convert an uncontrolled project to a version controlled project. - + @param vcsDataDict dictionary of data required for the conversion @type dict @param project reference to the project object @@ -247,19 +247,19 @@ EricMessageBox.critical( self.__ui, self.tr("Create project repository"), - self.tr( - """The project repository could not be created.""")) + self.tr("""The project repository could not be created."""), + ) else: pfn = project.pfile if not os.path.isfile(pfn): pfn += "z" project.closeProject() project.openProject(pfn) - + def vcsImport(self, vcsDataDict, projectDir, noDialog=False, addAll=True): """ Public method used to import the project into the Git repository. - + @param vcsDataDict dictionary of data required for the import @type dict @param projectDir project directory (string) @@ -274,51 +274,47 @@ """ msg = vcsDataDict["message"] if not msg: - msg = '***' - + msg = "***" + args = self.initCommand("init") args.append(projectDir) - dia = GitDialog(self.tr('Creating Git repository'), self) + dia = GitDialog(self.tr("Creating Git repository"), self) res = dia.startProcess(args) if res: dia.exec() status = dia.normalExit() - + if status: ignoreName = os.path.join(projectDir, Git.IgnoreFileName) if not os.path.exists(ignoreName): status = self.gitCreateIgnoreFile(projectDir) - + if status and addAll: args = self.initCommand("add") args.append("-v") args.append(".") - dia = GitDialog( - self.tr('Adding files to Git repository'), - self) + dia = GitDialog(self.tr("Adding files to Git repository"), self) res = dia.startProcess(args, projectDir) if res: dia.exec() status = dia.normalExit() - + if status: args = self.initCommand("commit") - args.append('--message={0}'.format(msg)) - dia = GitDialog( - self.tr('Initial commit to Git repository'), - self) + args.append("--message={0}".format(msg)) + dia = GitDialog(self.tr("Initial commit to Git repository"), self) res = dia.startProcess(args, projectDir) if res: dia.exec() status = dia.normalExit() - + return status, False - + def vcsCheckout(self, vcsDataDict, projectDir, noDialog=False): """ Public method used to check the project out of a Git repository (clone). - + @param vcsDataDict dictionary of data required for the checkout @param projectDir project directory to create (string) @param noDialog flag indicating quiet operations @@ -326,26 +322,24 @@ """ noDialog = False vcsUrl = self.gitNormalizeURL(vcsDataDict["url"]) - + args = self.initCommand("clone") args.append(vcsUrl) args.append(projectDir) - + if noDialog: - return self.startSynchronizedProcess(QProcess(), 'git', args) + return self.startSynchronizedProcess(QProcess(), "git", args) else: - dia = GitDialog( - self.tr('Cloning project from a Git repository'), - self) + dia = GitDialog(self.tr("Cloning project from a Git repository"), self) 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 Git 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) @@ -355,13 +349,12 @@ if os.path.exists(os.path.join(projectDir, Git.IgnoreFileName)): os.remove(os.path.join(projectDir, Git.IgnoreFileName)) return status - - def vcsCommit(self, name, message="", noDialog=False, commitAll=True, - amend=False): + + def vcsCommit(self, name, message="", noDialog=False, commitAll=True, amend=False): """ Public method used to make the change of a file/directory permanent in the Git repository. - + @param name file/directory name to be committed (string or list of strings) @param message message for this operation (string) @@ -373,21 +366,23 @@ # call CommitDialog and get message from there if self.__commitDialog is None: from .GitCommitDialog import GitCommitDialog - self.__commitDialog = GitCommitDialog(self, message, amend, - commitAll, self.__ui) + + self.__commitDialog = GitCommitDialog( + self, message, amend, commitAll, 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"] = message self.__commitData["noDialog"] = noDialog self.__commitData["all"] = commitAll - + if noDialog: self.__vcsCommit_Step2() - + def __vcsCommit_Step2(self): """ Private slot performing the second step of the commit action. @@ -396,7 +391,7 @@ msg = self.__commitData["msg"] noDialog = self.__commitData["noDialog"] commitAll = self.__commitData["all"] - + if not noDialog: # check, if there are unsaved changes, that should be committed if isinstance(name, list): @@ -410,31 +405,30 @@ project = ericApp().getObject("Project") if nam == project.getProjectPath(): ok &= ( - project.checkAllScriptsDirty( - reportSyntaxErrors=True) and - project.checkDirty() + project.checkAllScriptsDirty(reportSyntaxErrors=True) + and project.checkDirty() ) continue elif os.path.isfile(nam): - editor = ( - ericApp().getObject("ViewManager").getOpenEditor(nam) - ) + editor = ericApp().getObject("ViewManager").getOpenEditor(nam) if editor: ok &= editor.checkDirty() if not ok: break - + if not ok: res = EricMessageBox.yesNo( self.__ui, self.tr("Commit Changes"), self.tr( """The commit affects files, that have unsaved""" - """ changes. Shall the commit be continued?"""), - icon=EricMessageBox.Warning) + """ changes. Shall the commit be continued?""" + ), + icon=EricMessageBox.Warning, + ) if not res: return - + if self.__commitDialog is not None: msg = self.__commitDialog.logMessage() amend = self.__commitDialog.amend() @@ -445,10 +439,10 @@ else: amend = False resetAuthor = False - + if not msg and not amend: - msg = '***' - + msg = "***" + args = self.initCommand("commit") if amend: args.append("--amend") @@ -457,19 +451,19 @@ if msg: args.append("--message") args.append(msg) - + if isinstance(name, list): dname, fnames = self.splitPathList(name) else: dname, fname = self.splitPath(name) - + # 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 - + if isinstance(name, list): args.append("--") self.addArguments(args, fnames) @@ -480,23 +474,21 @@ else: if commitAll: args.append("--all") - + if noDialog: self.startSynchronizedProcess(QProcess(), "git", args, dname) else: - dia = GitDialog( - self.tr('Committing changes to Git repository'), - self) + dia = GitDialog(self.tr("Committing changes to Git repository"), self) res = dia.startProcess(args, dname) if res: dia.exec() self.committed.emit() self.checkVCSStatus() - + def vcsCommitMessages(self): """ Public method to get the list of saved commit messages. - + @return list of saved commit messages @rtype list of str """ @@ -504,14 +496,14 @@ messages = self._vcsProjectCommitMessages() if not messages: # empty list returned, try the vcs specific one - messages = self.getPlugin().getPreferences('Commits') - + messages = self.getPlugin().getPreferences("Commits") + return messages - + def vcsAddCommitMessage(self, message): """ Public method to add a commit message to the list of saved messages. - + @param message message to be added @type str """ @@ -522,20 +514,20 @@ commitMessages.insert(0, message) no = Preferences.getVCS("CommitMessages") del commitMessages[no:] - self.getPlugin().setPreferences('Commits', commitMessages) - + self.getPlugin().setPreferences("Commits", commitMessages) + def vcsClearCommitMessages(self): """ Public method to clear the list of saved messages. """ if not self._vcsClearProjectCommitMessages(): - self.getPlugin().setPreferences('Commits', []) - + self.getPlugin().setPreferences("Commits", []) + def vcsUpdate(self, name, noDialog=False, revision=None): """ Public method used to update a file/directory with the Git repository. - + @param name file/directory name to be updated (string or list of strings) @param noDialog flag indicating quiet operations (boolean) @@ -548,13 +540,15 @@ res = EricMessageBox.yesNo( None, self.tr("Switch"), - self.tr("""<p>Do you really want to switch to <b>{0}</b>?""" - """</p>""").format(revision), - yesDefault=True) + self.tr( + """<p>Do you really want to switch to <b>{0}</b>?""" """</p>""" + ).format(revision), + yesDefault=True, + ) if not res: return False args.append(revision) - + if isinstance(name, list): args.append("--") dname, fnames = self.splitPathList(name) @@ -564,39 +558,37 @@ if fname != ".": args.append("--") args.append(fname) - + # 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 False - + if noDialog: - self.startSynchronizedProcess(QProcess(), 'git', args, repodir) + self.startSynchronizedProcess(QProcess(), "git", args, repodir) res = False else: - dia = GitDialog(self.tr( - 'Synchronizing with the Git repository'), - self) + dia = GitDialog(self.tr("Synchronizing with the Git repository"), self) res = dia.startProcess(args, repodir) 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 Git 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 = self.initCommand("add") args.append("-v") - + if isinstance(name, list): if isDir: dname, fname = os.path.split(name[0]) @@ -607,57 +599,56 @@ dname, fname = os.path.split(name) else: dname, fname = self.splitPath(name) - + # 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 - + if isinstance(name, list): self.addArguments(args, name) else: args.append(name) - + if noDialog: - self.startSynchronizedProcess(QProcess(), 'git', args, repodir) + self.startSynchronizedProcess(QProcess(), "git", args, repodir) else: dia = GitDialog( - self.tr( - 'Adding files/directories to the Git repository'), - self) + self.tr("Adding files/directories to the Git repository"), self + ) res = dia.startProcess(args, repodir) if res: dia.exec() - + def vcsAddBinary(self, name, isDir=False): """ Public method used to add a file/directory in binary mode to the Git 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 Git repository. - + @param path root directory of the tree to be added (string or list of strings)) """ self.vcsAdd(path, isDir=False) - + def vcsRemove(self, name, project=False, noDialog=False, stageOnly=False): """ Public method used to remove a file/directory from the Git 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) @@ -668,11 +659,11 @@ @return flag indicating successful operation (boolean) """ args = self.initCommand("rm") - if noDialog and '--force' not in args: - args.append('--force') + if noDialog and "--force" not in args: + args.append("--force") if stageOnly: - args.append('--cached') - + args.append("--cached") + if isinstance(name, list): if os.path.isdir(name[0]): args.append("-r") @@ -685,45 +676,42 @@ dname, fname = self.splitPath(name) args.append("--") args.append(name) - + # 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 False - + if noDialog: - res = self.startSynchronizedProcess( - QProcess(), 'git', args, repodir) + res = self.startSynchronizedProcess(QProcess(), "git", args, repodir) else: dia = GitDialog( - self.tr( - 'Removing files/directories from the Git' - ' repository'), - self) + self.tr("Removing files/directories from the Git" " repository"), self + ) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.normalExitWithoutErrors() - + return res - + def vcsForget(self, name): """ Public method used to remove a file from the Mercurial repository. - + This will not remove the file from the project directory. - + @param name file/directory name to be removed @type str or list of str """ self.vcsRemove(name, stageOnly=True) - + 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) @@ -731,7 +719,7 @@ @return flag indicating successful operation (boolean) """ isDir = os.path.isdir(name) - + res = False if noDialog: if target is None: @@ -740,19 +728,20 @@ accepted = True else: from .GitCopyDialog import GitCopyDialog + dlg = GitCopyDialog(name, None, True) accepted = dlg.exec() == QDialog.DialogCode.Accepted if accepted: target, force = dlg.getData() - + if accepted: args = self.initCommand("mv") args.append("-v") if force: - args.append('--force') + args.append("--force") args.append(name) args.append(target) - + dname, fname = self.splitPath(name) # find the root of the repo repodir = dname @@ -760,12 +749,11 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + if noDialog: - res = self.startSynchronizedProcess( - QProcess(), 'git', args, repodir) + res = self.startSynchronizedProcess(QProcess(), "git", args, repodir) else: - dia = GitDialog(self.tr('Renaming {0}').format(name), self) + dia = GitDialog(self.tr("Renaming {0}").format(name), self) res = dia.startProcess(args, repodir) if res: dia.exec() @@ -782,46 +770,48 @@ else: project.removeFile(name) return res - + def vcsLogBrowser(self, name, isFile=False): """ Public method used to browse the log of a file/directory from the Git 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 .GitLogBrowserDialog import GitLogBrowserDialog + self.logBrowser = GitLogBrowserDialog(self) self.logBrowser.show() self.logBrowser.raise_() self.logBrowser.start(name, isFile=isFile) - + def gitReflogBrowser(self, projectDir): """ Public method used to browse the reflog of the project. - + @param projectDir name of the project directory (string) """ if self.reflogBrowser is None: from .GitReflogBrowserDialog import GitReflogBrowserDialog + self.reflogBrowser = GitReflogBrowserDialog(self) self.reflogBrowser.show() self.reflogBrowser.raise_() self.reflogBrowser.start(projectDir) - + def vcsDiff(self, name): """ Public method used to view the difference of a file/directory to the Git 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) """ names = name[:] if isinstance(name, list) else [name] @@ -836,31 +826,33 @@ return if self.diff is None: from .GitDiffDialog import GitDiffDialog + self.diff = GitDiffDialog(self) self.diff.show() self.diff.raise_() QApplication.processEvents() self.diff.start(name, diffMode="work2stage2repo", refreshable=True) - + def vcsStatus(self, name): """ Public method used to view the status of files/directories in the Git repository. - + @param name file/directory name(s) to show the status of (string or list of strings) """ if self.status is None: from .GitStatusDialog import GitStatusDialog + self.status = GitStatusDialog(self) self.status.show() self.status.raise_() self.status.start(name) - + def gitUnstage(self, name): """ Public method used to unstage a file/directory. - + @param name file/directory name to be reverted (string) @return flag indicating, that the update contained an add or delete (boolean) @@ -869,14 +861,14 @@ dname, fnames = self.splitPathList(name) else: dname, fname = self.splitPath(name) - + # 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 False - + args = self.initCommand("reset") args.append("HEAD") args.append("--") @@ -884,22 +876,20 @@ self.addArguments(args, name) else: args.append(name) - - dia = GitDialog( - self.tr('Unstage files/directories'), - self) + + dia = GitDialog(self.tr("Unstage files/directories"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() - + return res - + def vcsRevert(self, name): """ Public method used to revert changes made to a file/directory. - + @param name file/directory name to be reverted @type str @return flag indicating, that the update contained an add @@ -916,36 +906,40 @@ dname, fname = self.splitPath(name) args.append(name) names = [name] - + # 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 False - + project = ericApp().getObject("Project") names = [project.getRelativePath(nam) for nam in names] if names[0]: - from UI.DeleteFilesConfirmationDialog import ( - DeleteFilesConfirmationDialog - ) + 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) + " or directories?" + ), + names, + ) yes = dlg.exec() == QDialog.DialogCode.Accepted else: yes = EricMessageBox.yesNo( None, self.tr("Revert changes"), - self.tr("""Do you really want to revert all changes of""" - """ the project?""")) + self.tr( + """Do you really want to revert all changes of""" + """ the project?""" + ), + ) if yes: - dia = GitDialog(self.tr('Reverting changes'), self) + dia = GitDialog(self.tr("Reverting changes"), self) res = dia.startProcess(args, repodir) if res: dia.exec() @@ -953,33 +947,34 @@ self.checkVCSStatus() else: res = False - + 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) - + # 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 - + from .GitMergeDialog import GitMergeDialog - dlg = GitMergeDialog(self.gitGetTagsList(repodir), - self.gitGetBranchesList(repodir, withMaster=True), - self.gitGetCurrentBranch(repodir), - self.gitGetBranchesList(repodir, remotes=True)) + + dlg = GitMergeDialog( + self.gitGetTagsList(repodir), + self.gitGetBranchesList(repodir, withMaster=True), + self.gitGetCurrentBranch(repodir), + self.gitGetBranchesList(repodir, remotes=True), + ) if dlg.exec() == QDialog.DialogCode.Accepted: - commit, doCommit, commitMessage, addLog, diffStat = ( - dlg.getParameters() - ) + commit, doCommit, commitMessage, addLog, diffStat = dlg.getParameters() args = self.initCommand("merge") if doCommit: args.append("--commit") @@ -997,48 +992,49 @@ args.append("--no-stat") if commit: args.append(commit) - - dia = GitDialog(self.tr('Merging').format(name), self) + + dia = GitDialog(self.tr("Merging").format(name), self) res = dia.startProcess(args, repodir) if res: dia.exec() self.checkVCSStatus() - + def vcsSwitch(self, name): """ Public method used to switch a working directory to a different revision. - + @param name directory name to be switched (string) @return flag indicating, that the switch contained an add or delete (boolean) """ dname, fname = self.splitPath(name) - + # 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 False - + from .GitRevisionSelectionDialog import GitRevisionSelectionDialog + dlg = GitRevisionSelectionDialog( self.gitGetTagsList(repodir), self.gitGetBranchesList(repodir), - trackingBranchesList=self.gitGetBranchesList( - repodir, remotes=True), - noneLabel=self.tr("Master branch head")) + trackingBranchesList=self.gitGetBranchesList(repodir, remotes=True), + noneLabel=self.tr("Master branch head"), + ) if dlg.exec() == QDialog.DialogCode.Accepted: rev = dlg.getRevision() return self.vcsUpdate(name, revision=rev) - + return False 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 """ @@ -1046,36 +1042,35 @@ 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)): + + if fname == "." and os.path.isdir(os.path.join(dname, self.adminDir)): return self.canBeCommitted - + if name in self.statusCache: return self.statusCache[name] - + # 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 0 - + args = self.initCommand("status") - args.append('--porcelain') + args.append("--porcelain") args.append(name) - + ioEncoding = Preferences.getSystem("IOEncoding") output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - ioEncoding, 'replace') - + output = str(process.readAllStandardOutput(), ioEncoding, "replace") + if output: for line in output.splitlines(): if line and line[0] in " MADRCU!?": @@ -1085,7 +1080,7 @@ if absname.endswith(("/", "\\")): absname = absname[:-1] if flag not in "?!": - if fname == '.': + if fname == ".": if absname.startswith(dname + os.path.sep): return self.canBeCommitted if absname == dname: @@ -1095,18 +1090,18 @@ return self.canBeCommitted else: return self.canBeCommitted - + 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) @@ -1116,17 +1111,17 @@ if dname.endswith(os.sep): dname = dname[:-1] dname = os.path.normcase(dname) - + # revert the logic because git status doesn't show unchanged files for name in names: names[name] = self.canBeCommitted - + found = False for name in self.statusCache: if name in names: found = True names[name] = self.statusCache[name] - + if not found: # find the root of the repo repodir = dname @@ -1134,22 +1129,21 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return names - + args = self.initCommand("status") - args.append('--porcelain') - + args.append("--porcelain") + ioEncoding = Preferences.getSystem("IOEncoding") output = "" process = QProcess() process.setWorkingDirectory(dname) - process.start('git', args) + process.start("git", args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - ioEncoding, 'replace') - + output = str(process.readAllStandardOutput(), ioEncoding, "replace") + if output: for line in output.splitlines(): if line and line[0] in " MADRCU!?": @@ -1175,91 +1169,91 @@ self.statusCache[name] = self.canBeAdded if dirName not in self.statusCache: self.statusCache[dirName] = self.canBeAdded - + return names - + def clearStatusCache(self): """ Public method to clear the status cache. """ self.statusCache = {} - + def vcsName(self): """ Public method returning the name of the vcs. - + @return always 'Git' (string) """ return "Git" - + 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) """ ppath = project.getProjectPath() if ppath: ignoreName = os.path.join(ppath, Git.IgnoreFileName) if not os.path.exists(ignoreName): - self.gitCreateIgnoreFile(project.getProjectPath(), - autoAdd=True) - + self.gitCreateIgnoreFile(project.getProjectPath(), autoAdd=True) + def vcsCleanup(self, name): """ Public method used to cleanup the working directory. - + @param name directory name to be cleaned up (string) """ patterns = self.__plugin.getPreferences("CleanupPatterns").split() - + entries = [] for pat in patterns: entries.extend(Utilities.direntries(name, True, pat)) - + for entry in entries: with contextlib.suppress(OSError): os.remove(entry) - + def vcsCommandLine(self, name): """ Public method used to execute arbitrary Git commands. - + @param name directory name of the working directory (string) """ from .GitCommandDialog import GitCommandDialog + dlg = GitCommandDialog(self.commandHistory, name) if dlg.exec() == QDialog.DialogCode.Accepted: command = 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) - + args = [] self.addArguments(args, commandList) - + # find the root of the repo repodir = name 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 - - dia = GitDialog(self.tr('Git Command'), self) + + dia = GitDialog(self.tr("Git Command"), self) res = dia.startProcess(args, repodir) 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 @@ -1268,66 +1262,80 @@ @return reference to the instantiated options dialog (GitOptionsDialog) """ from .GitOptionsDialog import GitOptionsDialog + return GitOptionsDialog(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 (GitNewProjectOptionsDialog) """ from .GitNewProjectOptionsDialog import GitNewProjectOptionsDialog + return GitNewProjectOptionsDialog(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) """ formatTemplate = ( - 'format:' - '%h%n' - '%p%n' - '%an%n' - '%ae%n' - '%ai%n' - '%cn%n' - '%ce%n' - '%ci%n' - '%d%n' - '%s') - + "format:" + "%h%n" + "%p%n" + "%an%n" + "%ae%n" + "%ai%n" + "%cn%n" + "%ce%n" + "%ci%n" + "%d%n" + "%s" + ) + args = self.initCommand("show") args.append("--abbrev-commit") - args.append("--abbrev={0}".format( - self.__plugin.getPreferences("CommitIdLength"))) + args.append( + "--abbrev={0}".format(self.__plugin.getPreferences("CommitIdLength")) + ) args.append("--format={0}".format(formatTemplate)) args.append("--no-patch") args.append("HEAD") - + output = "" process = QProcess() process.setWorkingDirectory(ppath) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + if output: info = [] - (commit, parents, author, authorMail, authorDate, - committer, committerMail, committerDate, refs, subject) = ( - output.splitlines() - ) + ( + commit, + parents, + author, + authorMail, + authorDate, + committer, + committerMail, + committerDate, + refs, + subject, + ) = output.splitlines() tags = [] branches = [] for name in refs.strip()[1:-1].split(","): @@ -1337,41 +1345,55 @@ tags.append(name.split()[1]) elif name != "HEAD": branches.append(name) - - info.append(self.tr( - "<tr><td><b>Commit</b></td><td>{0}</td></tr>").format( - commit)) + + info.append( + self.tr("<tr><td><b>Commit</b></td><td>{0}</td></tr>").format(commit) + ) if parents: - info.append(self.tr( - "<tr><td><b>Parents</b></td><td>{0}</td></tr>").format( - '<br/>'.join(parents.strip().split()))) + info.append( + self.tr("<tr><td><b>Parents</b></td><td>{0}</td></tr>").format( + "<br/>".join(parents.strip().split()) + ) + ) if tags: - info.append(self.tr( - "<tr><td><b>Tags</b></td><td>{0}</td></tr>").format( - '<br/>'.join(tags))) + info.append( + self.tr("<tr><td><b>Tags</b></td><td>{0}</td></tr>").format( + "<br/>".join(tags) + ) + ) if branches: - info.append(self.tr( - "<tr><td><b>Branches</b></td><td>{0}</td></tr>").format( - '<br/>'.join(branches))) - info.append(self.tr( - "<tr><td><b>Author</b></td><td>{0} <{1}></td></tr>") - .format(author, authorMail)) - info.append(self.tr( - "<tr><td><b>Date</b></td><td>{0}</td></tr>").format( - authorDate.rsplit(":", 1)[0])) - info.append(self.tr( - "<tr><td><b>Committer</b></td><td>{0} <{1}></td></tr>") - .format(committer, committerMail)) - info.append(self.tr( - "<tr><td><b>Committed Date</b></td><td>{0}</td></tr>").format( - committerDate.rsplit(":", 1)[0])) - info.append(self.tr( - "<tr><td><b>Subject</b></td><td>{0}</td></tr>").format( - subject)) + info.append( + self.tr("<tr><td><b>Branches</b></td><td>{0}</td></tr>").format( + "<br/>".join(branches) + ) + ) + info.append( + self.tr( + "<tr><td><b>Author</b></td><td>{0} <{1}></td></tr>" + ).format(author, authorMail) + ) + info.append( + self.tr("<tr><td><b>Date</b></td><td>{0}</td></tr>").format( + authorDate.rsplit(":", 1)[0] + ) + ) + info.append( + self.tr( + "<tr><td><b>Committer</b></td><td>{0} <{1}></td></tr>" + ).format(committer, committerMail) + ) + info.append( + self.tr("<tr><td><b>Committed Date</b></td><td>{0}</td></tr>").format( + committerDate.rsplit(":", 1)[0] + ) + ) + info.append( + self.tr("<tr><td><b>Subject</b></td><td>{0}</td></tr>").format(subject) + ) infoStr = "\n".join(info) else: infoStr = "" - + return self.tr( """<h3>Repository information</h3>\n""" """<p><table>\n""" @@ -1380,40 +1402,40 @@ """{1}""" """</table></p>\n""" ).format(self.versionStr, infoStr) - + def vcsSupportCommandOptions(self): """ Public method to signal the support of user settable command options. - + @return flag indicating the support of user settable command options (boolean) """ return False - + ########################################################################### ## Git specific methods are below. ########################################################################### - + def gitNormalizeURL(self, url): """ Public method to normalize a url for Git. - + @param url url string (string) @return properly normalized url for git (string) """ - url = url.replace('\\', '/') - if url.endswith('/'): + url = url.replace("\\", "/") + if url.endswith("/"): url = url[:-1] - urll = url.split('//') + urll = url.split("//") if len(urll) > 1: - url = "{0}//{1}".format(urll[0], '/'.join(urll[1:])) - + url = "{0}//{1}".format(urll[0], "/".join(urll[1:])) + return url - + def gitCreateIgnoreFile(self, name, autoAdd=False): """ Public method to create the ignore file. - + @param name directory name to create the ignore file in (string) @param autoAdd flag indicating to add it automatically (boolean) @return flag indicating success @@ -1436,17 +1458,20 @@ "__pycache__/", "*.DS_Store", ] - + ignoreName = os.path.join(name, Git.IgnoreFileName) res = ( EricMessageBox.yesNo( self.__ui, self.tr("Create {0} file").format(ignoreName), - self.tr("""<p>The file <b>{0}</b> exists already.""" - """ Overwrite it?</p>""").format(ignoreName), - icon=EricMessageBox.Warning) - if os.path.exists(ignoreName) else - True + self.tr( + """<p>The file <b>{0}</b> exists already.""" + """ Overwrite it?</p>""" + ).format(ignoreName), + icon=EricMessageBox.Warning, + ) + if os.path.exists(ignoreName) + else True ) if res: try: @@ -1457,27 +1482,28 @@ status = True except OSError: status = False - + if status and autoAdd: self.vcsAdd(ignoreName, noDialog=True) project = ericApp().getObject("Project") project.appendFile(ignoreName) - + return status - + def gitCopy(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 successful operation (boolean) """ from .GitCopyDialog import GitCopyDialog + dlg = GitCopyDialog(name) if dlg.exec() == QDialog.DialogCode.Accepted: target, force = dlg.getData() - + # step 1: copy the file/directory: if os.path.isdir(name): try: @@ -1486,9 +1512,11 @@ EricMessageBox.critical( self, self.tr("Git Copy"), - self.tr("""<p>Copying the directory <b>{0}</b>""" - """ failed.</p><p>Reason: {1}</p>""").format( - name, str(why))) + self.tr( + """<p>Copying the directory <b>{0}</b>""" + """ failed.</p><p>Reason: {1}</p>""" + ).format(name, str(why)), + ) return False self.vcsAdd(target, isDir=True) if target.startswith(project.getProjectPath()): @@ -1501,41 +1529,44 @@ EricMessageBox.critical( self, self.tr("Git Copy"), - self.tr("""<p>Copying the file <b>{0}</b>""" - """ failed.</p><p>Reason: {1}</p>""").format( - name, str(why))) + self.tr( + """<p>Copying the file <b>{0}</b>""" + """ failed.</p><p>Reason: {1}</p>""" + ).format(name, str(why)), + ) return False self.vcsAdd(target, isDir=False) if target.startswith(project.getProjectPath()): project.appendFile(target) self.checkVCSStatus() return True - + def gitBlame(self, name): """ Public method to show the output of the git blame command. - + @param name file name to show the annotations for (string) """ if self.blame is None: from .GitBlameDialog import GitBlameDialog + self.blame = GitBlameDialog(self) self.blame.show() self.blame.raise_() self.blame.start(name) - + def gitExtendedDiff(self, name): """ Public method used to view the difference of a file/directory to the Git 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): @@ -1553,31 +1584,34 @@ project = ericApp().getObject("Project") if nam == project.ppath and not project.saveAllScripts(): return - + # 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 - + from .GitRevisionsSelectionDialog import GitRevisionsSelectionDialog - dlg = GitRevisionsSelectionDialog(self.gitGetTagsList(repodir), - self.gitGetBranchesList(repodir)) + + dlg = GitRevisionsSelectionDialog( + self.gitGetTagsList(repodir), self.gitGetBranchesList(repodir) + ) if dlg.exec() == QDialog.DialogCode.Accepted: revisions = dlg.getRevisions() if self.diff is None: from .GitDiffDialog import GitDiffDialog + self.diff = GitDiffDialog(self) self.diff.show() self.diff.raise_() self.diff.start(name, revisions) - + def __gitGetFileForRevision(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) @param rev revision to retrieve (string) @return contents of the file (string) and an error message (string) @@ -1588,45 +1622,49 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + args = self.initCommand("cat-file") args.append("blob") args.append("{0}:{1}".format(rev, name.replace(repodir + os.sep, ""))) - + output = "" error = "" - + process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) else: - error = str(process.readAllStandardError(), - Preferences.getSystem("IOEncoding"), - 'replace') + error = str( + process.readAllStandardError(), + Preferences.getSystem("IOEncoding"), + "replace", + ) else: - error = self.tr( - "The git process did not finish within 30s.") + error = self.tr("The git 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('git') - + "The process {0} could not be started. " + "Ensure, that it is in the search path." + ).format("git") + # return file contents with 'universal newlines' - return output.replace('\r\n', '\n').replace('\r', '\n'), error - + return output.replace("\r\n", "\n").replace("\r", "\n"), error + def vcsSbsDiff(self, name, extended=False, revisions=None): """ Public method used to view the difference of a file to the Git repository side-by-side. - + @param name file name to be diffed (string) @param extended flag indicating the extended variant (boolean) @param revisions tuple of two revisions (tuple of strings) @@ -1634,7 +1672,7 @@ """ if isinstance(name, list): raise ValueError("Wrong parameter type") - + if extended: # find the root of the repo repodir = self.splitPath(name)[0] @@ -1642,37 +1680,35 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - - from .GitRevisionsSelectionDialog import ( - GitRevisionsSelectionDialog + + from .GitRevisionsSelectionDialog import GitRevisionsSelectionDialog + + dlg = GitRevisionsSelectionDialog( + self.gitGetTagsList(repodir), self.gitGetBranchesList(repodir) ) - dlg = GitRevisionsSelectionDialog(self.gitGetTagsList(repodir), - self.gitGetBranchesList(repodir)) if dlg.exec() == QDialog.DialogCode.Accepted: rev1, rev2 = dlg.getRevisions() elif revisions: rev1, rev2 = revisions[0], revisions[1] else: rev1, rev2 = "", "" - + output1, error = self.__gitGetFileForRevision(name, rev=rev1) if error: EricMessageBox.critical( - self.__ui, - self.tr("Git Side-by-Side Difference"), - error) + self.__ui, self.tr("Git Side-by-Side Difference"), error + ) return name1 = "{0} (rev. {1})".format(name, rev1 and rev1 or "Stage") - + if rev2: if rev2 == "Stage": rev2 = "" output2, error = self.__gitGetFileForRevision(name, rev=rev2) if error: EricMessageBox.critical( - self.__ui, - self.tr("Git Side-by-Side Difference"), - error) + self.__ui, self.tr("Git Side-by-Side Difference"), error + ) return name2 = "{0} (rev. {1})".format(name, rev2) else: @@ -1685,22 +1721,24 @@ EricMessageBox.critical( self.__ui, self.tr("Git Side-by-Side Difference"), - self.tr( - """<p>The file <b>{0}</b> could not be read.</p>""") - .format(name)) + 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 gitFetch(self, name): """ Public method to fetch changes from a remote repository. - + @param name directory name (string) """ # find the root of the repo @@ -1709,39 +1747,46 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitFetchDialog import GitFetchDialog + dlg = GitFetchDialog(self, repodir) if dlg.exec() == QDialog.DialogCode.Accepted: - (remote, url, remoteBranches, localBranch, fetchAll, prune, - includeTags) = dlg.getData() - + ( + remote, + url, + remoteBranches, + localBranch, + fetchAll, + prune, + includeTags, + ) = dlg.getData() + args = self.initCommand("fetch") - args.append('--verbose') + args.append("--verbose") if prune: - args.append('--prune') + args.append("--prune") if includeTags: args.append("--tags") if fetchAll: - args.append('--all') + args.append("--all") else: args.append(remote) if remote else args.append(url) if len(remoteBranches) == 1 and localBranch: args.append(remoteBranches[0] + ":" + localBranch) else: args.extend(remoteBranches) - - dia = GitDialog(self.tr('Fetching from a remote Git repository'), - self) + + dia = GitDialog(self.tr("Fetching from a remote Git repository"), self) res = dia.startProcess(args, repodir) if res: dia.exec() self.checkVCSStatus() - + def gitPull(self, name): """ Public method used to pull changes from a remote Git repository. - + @param name directory name of the project to be pulled to (string) @return flag indicating, that the update contained an add or delete (boolean) @@ -1752,25 +1797,25 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + from .GitPullDialog import GitPullDialog + dlg = GitPullDialog(self, repodir) if dlg.exec() == QDialog.DialogCode.Accepted: remote, url, branches, pullAll, prune = dlg.getData() - - args = self.initCommand('pull') - args.append('--no-commit') - args.append('--verbose') + + args = self.initCommand("pull") + args.append("--no-commit") + args.append("--verbose") if prune: - args.append('--prune') + args.append("--prune") if pullAll: - args.append('--all') + args.append("--all") else: args.append(remote) if remote else args.append(url) args.extend(branches) - - dia = GitDialog(self.tr('Pulling from a remote Git repository'), - self) + + dia = GitDialog(self.tr("Pulling from a remote Git repository"), self) res = dia.startProcess(args, repodir) if res: dia.exec() @@ -1779,11 +1824,11 @@ return res else: return False - + def gitPush(self, name): """ Public method used to push changes to a remote Git repository. - + @param name directory name of the project to be pushed from (string) """ # find the root of the repo @@ -1792,15 +1837,16 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitPushDialog import GitPushDialog + dlg = GitPushDialog(self, repodir) if dlg.exec() == QDialog.DialogCode.Accepted: remote, refspecs, tags, tracking, submodule = dlg.getData() - + args = self.initCommand("push") - args.append('--verbose') - args.append('--porcelain') + args.append("--verbose") + args.append("--porcelain") if tags: args.append("--tags") if tracking: @@ -1809,78 +1855,74 @@ args.append("--recurse-submodules={0}".format(submodule)) args.append(remote) args.extend(refspecs) - - dia = GitDialog( - self.tr('Pushing to a remote Git repository'), self) + + dia = GitDialog(self.tr("Pushing to a remote Git repository"), self) res = dia.startProcess(args, repodir) if res: dia.exec() self.checkVCSStatus() - + def gitCommitMerge(self, name): """ Public method to commit a failed merge. - + @param name file/directory name (string) """ dname, fname = self.splitPath(name) - + # 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 - + import sys + editor = sys.argv[0].replace(".py", "_editor.py") - env = {"GIT_EDITOR": "{0} {1}".format( - Globals.getPythonExecutable(), editor)} - + env = {"GIT_EDITOR": "{0} {1}".format(Globals.getPythonExecutable(), editor)} + args = self.initCommand("commit") - - dia = GitDialog(self.tr('Committing failed merge'), self) + + dia = GitDialog(self.tr("Committing failed merge"), self) res = dia.startProcess(args, repodir, environment=env) if res: dia.exec() self.committed.emit() self.checkVCSStatus() - + def gitCancelMerge(self, name): """ Public method to cancel an uncommitted or failed merge. - + @param name file/directory name (string) @return flag indicating, that the cancellation contained an add or delete (boolean) """ dname, fname = self.splitPath(name) - + # 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 False - + args = self.initCommand("merge") args.append("--abort") - - dia = GitDialog( - self.tr('Aborting uncommitted/failed merge'), - self) + + dia = GitDialog(self.tr("Aborting uncommitted/failed merge"), self) res = dia.startProcess(args, repodir, False) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - - def gitApply(self, repodir, patchFile, cached=False, reverse=False, - noDialog=False): + + def gitApply(self, repodir, patchFile, cached=False, reverse=False, noDialog=False): """ Public method to apply a patch stored in a given file. - + @param repodir directory name of the repository (string) @param patchFile name of the patch file (string) @param cached flag indicating to apply the patch to the staging area @@ -1895,22 +1937,20 @@ if reverse: args.append("--reverse") args.append(patchFile) - + if noDialog: self.startSynchronizedProcess(QProcess(), "git", args, repodir) else: - dia = GitDialog( - self.tr('Applying patch'), - self) + dia = GitDialog(self.tr("Applying patch"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitApplyCheckPatches(self, projectDir, check=False): """ Public method to apply a list of patch files or check, if they would apply cleanly. - + @param projectDir directory name of the project (string) @param check flag indicating to perform a check operation (boolean) """ @@ -1920,8 +1960,9 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitPatchFilesDialog import GitPatchFilesDialog + dlg = GitPatchFilesDialog(repodir, self.__patchCheckData) if dlg.exec() == QDialog.DialogCode.Accepted: patchFilesList, stripCount, inaccurateEof, recount = dlg.getData() @@ -1930,49 +1971,52 @@ if check: args.append("--check") self.__patchCheckData = ( - patchFilesList, stripCount, inaccurateEof, recount) - title = self.tr('Check patch files') + patchFilesList, + stripCount, + inaccurateEof, + recount, + ) + title = self.tr("Check patch files") else: self.__patchCheckData = None - title = self.tr('Apply patch files') + title = self.tr("Apply patch files") if inaccurateEof: args.append("--inaccurate-eof") if recount: args.append("--recount") args.append("-p{0}".format(stripCount)) args.extend(patchFilesList) - - dia = GitDialog( - title, - self) + + dia = GitDialog(title, self) res = dia.startProcess(args, repodir) if res: dia.exec() if not check: self.checkVCSStatus() - + def gitShowPatchesStatistics(self, projectDir): """ Public method to show statistics for a set of patch files. - + @param projectDir directory name of the project (string) """ if self.patchStatisticsDialog is None: from .GitPatchStatisticsDialog import GitPatchStatisticsDialog + self.patchStatisticsDialog = GitPatchStatisticsDialog(self) self.patchStatisticsDialog.show() self.patchStatisticsDialog.raise_() self.patchStatisticsDialog.start(projectDir, self.__patchCheckData) self.__patchCheckData = self.patchStatisticsDialog.getData() - + ########################################################################### ## Methods for tag handling. ########################################################################### - + def vcsTag(self, name, revision=None, tagName=None): """ Public method used to set/remove a tag in the Git repository. - + @param name file/directory name to determine the repo root from (string) @param revision revision to set tag for (string) @@ -1980,21 +2024,22 @@ @return flag indicating a performed tag action (boolean) """ dname, fname = self.splitPath(name) - + # 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 False - + from .GitTagDialog import GitTagDialog + dlg = GitTagDialog(self.gitGetTagsList(repodir), revision, tagName) if dlg.exec() == QDialog.DialogCode.Accepted: tag, revision, tagOp, tagType, force = dlg.getParameters() else: return False - + args = self.initCommand("tag") if tagOp == GitTagDialog.CreateTag: msg = "Created tag <{0}>.".format(tag) @@ -2015,19 +2060,18 @@ args.append(tag) if tagOp == GitTagDialog.CreateTag and revision: args.append(revision) - - dia = GitDialog(self.tr('Tagging in the Git repository'), - self) + + dia = GitDialog(self.tr("Tagging in the Git repository"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + return True - + def gitGetTagsList(self, repodir, withType=False): """ Public method to get the list of tags. - + @param repodir directory name of the repository (string) @param withType flag indicating to get the tag type as well (boolean) @return list of tags (list of string) or list of tuples of @@ -2035,32 +2079,34 @@ and boolean), if withType is True """ args = self.initCommand("tag") - args.append('--list') - + args.append("--list") + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + tagsList = [] if output: for line in output.splitlines(): name = line.strip() tagsList.append(name) - + return tagsList - + def gitListTagBranch(self, path, tags=True, listAll=True, merged=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) @@ -2070,6 +2116,7 @@ """ if self.tagbranchList is None: from .GitTagBranchListDialog import GitTagBranchListDialog + self.tagbranchList = GitTagBranchListDialog(self) self.tagbranchList.show() self.tagbranchList.raise_() @@ -2077,16 +2124,17 @@ self.tagbranchList.start(path, tags) else: self.tagbranchList.start(path, tags, listAll, merged) - + ########################################################################### ## Methods for branch handling. ########################################################################### - - def gitGetBranchesList(self, repodir, withMaster=False, allBranches=False, - remotes=False): + + def gitGetBranchesList( + self, repodir, withMaster=False, allBranches=False, remotes=False + ): """ Public method to get the list of branches. - + @param repodir directory name of the repository (string) @param withMaster flag indicating to get 'master' as well (boolean) @param allBranches flag indicating to return all branches (boolean) @@ -2094,61 +2142,65 @@ @return list of branches (list of string) """ args = self.initCommand("branch") - args.append('--list') + args.append("--list") if allBranches: args.append("--all") elif remotes: args.append("--remotes") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + branchesList = [] if output: for line in output.splitlines(): name = line[2:].strip() if ( - (name != "master" or withMaster) and - "->" not in name and - not name.startswith("(") and - not name.endswith(")") + (name != "master" or withMaster) + and "->" not in name + and not name.startswith("(") + and not name.endswith(")") ): branchesList.append(name) - + return branchesList - + def gitGetCurrentBranch(self, repodir): """ Public method used to show the current branch of the working directory. - + @param repodir directory name of the repository (string) @return name of the current branch (string) """ args = self.initCommand("branch") - args.append('--list') - + args.append("--list") + branchName = "" output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + if output: for line in output.splitlines(): if line.startswith("* "): @@ -2157,14 +2209,14 @@ # not a valid branch name, probably detached head branchName = "" break - + return branchName - + def gitBranch(self, name, revision=None, branchName=None, branchOp=None): """ Public method used to create, delete or move a branch in the Git repository. - + @param name file/directory name to be branched (string) @param revision revision to set tag for (string) @param branchName name of the branch (string) @@ -2174,30 +2226,41 @@ or delete (boolean) """ dname, fname = self.splitPath(name) - + # 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 False, False - + from .GitBranchDialog import GitBranchDialog + dlg = GitBranchDialog( self.gitGetBranchesList(repodir, allBranches=True), - revision, branchName, branchOp) + revision, + branchName, + branchOp, + ) if dlg.exec() == QDialog.DialogCode.Accepted: - branchOp, branch, revision, newBranch, remoteBranch, force = ( - dlg.getParameters() - ) + ( + branchOp, + branch, + revision, + newBranch, + remoteBranch, + force, + ) = dlg.getParameters() else: return False, False - - if branchOp in [GitBranchDialog.CreateBranch, - GitBranchDialog.DeleteBranch, - GitBranchDialog.RenameBranch, - GitBranchDialog.SetTrackingBranch, - GitBranchDialog.UnsetTrackingBranch]: + + if branchOp in [ + GitBranchDialog.CreateBranch, + GitBranchDialog.DeleteBranch, + GitBranchDialog.RenameBranch, + GitBranchDialog.SetTrackingBranch, + GitBranchDialog.UnsetTrackingBranch, + ]: args = self.initCommand("branch") if branchOp == GitBranchDialog.CreateBranch: if force: @@ -2226,8 +2289,10 @@ args.append("--unset-upstream") if branch: args.append(branch) - elif branchOp in [GitBranchDialog.CreateSwitchBranch, - GitBranchDialog.CreateTrackingBranch]: + elif branchOp in [ + GitBranchDialog.CreateSwitchBranch, + GitBranchDialog.CreateTrackingBranch, + ]: args = self.initCommand("checkout") if branchOp == GitBranchDialog.CreateSwitchBranch: if force: @@ -2242,82 +2307,88 @@ args.append(branch) else: return False, False - - dia = GitDialog(self.tr('Branching in the Git repository'), - self) + + dia = GitDialog(self.tr("Branching in the Git repository"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - if branchOp in [GitBranchDialog.CreateSwitchBranch, - GitBranchDialog.CreateTrackingBranch]: + if branchOp in [ + GitBranchDialog.CreateSwitchBranch, + GitBranchDialog.CreateTrackingBranch, + ]: update = dia.hasAddOrDelete() self.checkVCSStatus() else: update = False - + return True, update - + def gitDeleteRemoteBranch(self, name): """ Public method to delete a branch from a remote repository. - + @param name file/directory name (string) """ dname, fname = self.splitPath(name) - + # 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 - + from .GitBranchPushDialog import GitBranchPushDialog - dlg = GitBranchPushDialog(self.gitGetBranchesList(repodir), - self.gitGetRemotesList(repodir), - delete=True) + + dlg = GitBranchPushDialog( + self.gitGetBranchesList(repodir), + self.gitGetRemotesList(repodir), + delete=True, + ) if dlg.exec() == QDialog.DialogCode.Accepted: branchName, remoteName = dlg.getData()[:2] - + args = self.initCommand("push") args.append(remoteName) args.append(":{0}".format(branchName)) - - dia = GitDialog(self.tr('Delete Remote Branch'), self) + + dia = GitDialog(self.tr("Delete Remote Branch"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitShowBranch(self, name): """ Public method used to show the current branch of the working directory. - + @param name file/directory name (string) """ dname, fname = self.splitPath(name) - + # 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 - + branchName = self.gitGetCurrentBranch(repodir) EricMessageBox.information( None, self.tr("Current Branch"), - self.tr("""<p>The current branch is <b>{0}</b>.""" - """</p>""").format(branchName)) - + self.tr("""<p>The current branch is <b>{0}</b>.""" """</p>""").format( + branchName + ), + ) + ########################################################################### ## Methods for bundles handling. ########################################################################### - + def gitBundle(self, projectDir): """ Public method to create a bundle file. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2326,24 +2397,27 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitBundleDialog import GitBundleDialog - dlg = GitBundleDialog(self.gitGetTagsList(repodir), - self.gitGetBranchesList(repodir)) + + dlg = GitBundleDialog( + self.gitGetTagsList(repodir), self.gitGetBranchesList(repodir) + ) if dlg.exec() == QDialog.DialogCode.Accepted: revs = dlg.getData() - + fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( None, self.tr("Create Bundle"), self.__lastBundlePath or repodir, self.tr("Git Bundle Files (*.bundle)"), None, - EricFileDialog.DontConfirmOverwrite) - + EricFileDialog.DontConfirmOverwrite, + ) + if not fname: return # user aborted - + fpath = pathlib.Path(fname) if not fpath.suffix: ex = selectedFilter.split("(*")[1].split(")")[0] @@ -2353,30 +2427,32 @@ res = EricMessageBox.yesNo( self.__ui, self.tr("Create Bundle"), - self.tr("<p>The Git bundle file <b>{0}</b> " - "already exists. Overwrite it?</p>") - .format(fpath), - icon=EricMessageBox.Warning) + self.tr( + "<p>The Git bundle file <b>{0}</b> " + "already exists. Overwrite it?</p>" + ).format(fpath), + icon=EricMessageBox.Warning, + ) if not res: return - + self.__lastBundlePath = str(fpath.parent) - + args = self.initCommand("bundle") args.append("create") args.append(str(fpath)) for rev in revs: args.append(rev) - - dia = GitDialog(self.tr('Create Bundle'), self) + + dia = GitDialog(self.tr("Create Bundle"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitVerifyBundle(self, projectDir): """ Public method to verify a bundle file. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2385,28 +2461,29 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + fname = EricFileDialog.getOpenFileName( None, self.tr("Verify Bundle"), self.__lastBundlePath or repodir, - self.tr("Git Bundle Files (*.bundle);;All Files (*)")) + self.tr("Git Bundle Files (*.bundle);;All Files (*)"), + ) if fname: self.__lastBundlePath = os.path.dirname(fname) - + args = self.initCommand("bundle") args.append("verify") args.append(fname) - - dia = GitDialog(self.tr('Verify Bundle'), self) + + dia = GitDialog(self.tr("Verify Bundle"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitBundleListHeads(self, projectDir): """ Public method to list the heads contained in a bundle file. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2415,28 +2492,29 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + fname = EricFileDialog.getOpenFileName( None, self.tr("List Bundle Heads"), self.__lastBundlePath or repodir, - self.tr("Git Bundle Files (*.bundle);;All Files (*)")) + self.tr("Git Bundle Files (*.bundle);;All Files (*)"), + ) if fname: self.__lastBundlePath = os.path.dirname(fname) - + args = self.initCommand("bundle") args.append("list-heads") args.append(fname) - - dia = GitDialog(self.tr('List Bundle Heads'), self) + + dia = GitDialog(self.tr("List Bundle Heads"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitGetBundleHeads(self, repodir, bundleFile): """ Public method to get a list of heads contained in a bundle file. - + @param repodir directory name of the repository (string) @param bundleFile file name of a git bundle file (string) @return list of heads (list of strings) @@ -2444,32 +2522,34 @@ args = self.initCommand("bundle") args.append("list-heads") args.append(bundleFile) - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + heads = [] if output: for line in output.splitlines(): head = line.strip().split(None, 1)[1] # commit id, head heads.append(head.replace("refs/heads/", "")) - + return heads - + def gitBundleFetch(self, projectDir): """ Public method to fetch a head of a bundle file into the local repository. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2478,43 +2558,44 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + fname = EricFileDialog.getOpenFileName( None, self.tr("Apply Bundle"), self.__lastBundlePath or repodir, - self.tr("Git Bundle Files (*.bundle);;All Files (*)")) + self.tr("Git Bundle Files (*.bundle);;All Files (*)"), + ) if fname: self.__lastBundlePath = os.path.dirname(fname) - + from .GitApplyBundleDataDialog import GitApplyBundleDataDialog + dlg = GitApplyBundleDataDialog( - self.gitGetBundleHeads(repodir, fname), - self.gitGetBranchesList(repodir)) + self.gitGetBundleHeads(repodir, fname), self.gitGetBranchesList(repodir) + ) if dlg.exec() == QDialog.DialogCode.Accepted: bundleHead, branch = dlg.getData() - + args = self.initCommand("fetch") - args.append('--verbose') + args.append("--verbose") args.append(fname) if branch: args.append(bundleHead + ":" + branch) else: args.append(bundleHead) - - dia = GitDialog(self.tr('Applying a bundle file (fetch)'), - self) + + dia = GitDialog(self.tr("Applying a bundle file (fetch)"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() - + def gitBundlePull(self, projectDir): """ Public method to pull a head of a bundle file into the local repository and working area. - + @param projectDir name of the project directory (string) @return flag indicating, that the update contained an add or delete (boolean) @@ -2525,49 +2606,50 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + res = False fname = EricFileDialog.getOpenFileName( None, self.tr("Apply Bundle"), self.__lastBundlePath or repodir, - self.tr("Git Bundle Files (*.bundle);;All Files (*)")) + self.tr("Git Bundle Files (*.bundle);;All Files (*)"), + ) if fname: self.__lastBundlePath = os.path.dirname(fname) - + from .GitApplyBundleDataDialog import GitApplyBundleDataDialog + dlg = GitApplyBundleDataDialog( - self.gitGetBundleHeads(repodir, fname), - self.gitGetBranchesList(repodir)) + self.gitGetBundleHeads(repodir, fname), self.gitGetBranchesList(repodir) + ) if dlg.exec() == QDialog.DialogCode.Accepted: bundleHead, branch = dlg.getData() - + args = self.initCommand("pull") - args.append('--verbose') + args.append("--verbose") args.append(fname) if branch: args.append(bundleHead + ":" + branch) else: args.append(bundleHead) - - dia = GitDialog(self.tr('Applying a bundle file (fetch)'), - self) + + dia = GitDialog(self.tr("Applying a bundle file (fetch)"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() - + return res - + ########################################################################### ## Methods for bisect handling. ########################################################################### - + def gitBisect(self, projectDir, subcommand): """ Public method to perform bisect commands. - + @param projectDir name of the project directory (string) @param subcommand name of the subcommand (string, one of 'start', 'start_extended', 'good', 'bad', 'skip' or 'reset') @@ -2575,37 +2657,47 @@ or delete (boolean) @exception ValueError raised to indicate an invalid bisect subcommand """ - if subcommand not in ("start", "start_extended", "good", "bad", - "skip", "reset"): + if subcommand not in ( + "start", + "start_extended", + "good", + "bad", + "skip", + "reset", + ): raise ValueError( - self.tr("Bisect subcommand ({0}) invalid.") - .format(subcommand)) - + self.tr("Bisect subcommand ({0}) invalid.").format(subcommand) + ) + # find the root of the repo repodir = projectDir 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 False - + res = False rev = "" if subcommand in ("good", "bad", "skip", "reset"): showBranches = subcommand == "reset" showHead = subcommand == "reset" from .GitRevisionSelectionDialog import GitRevisionSelectionDialog - dlg = GitRevisionSelectionDialog(self.gitGetTagsList(repodir), - self.gitGetBranchesList(repodir), - showBranches=showBranches, - showHead=showHead) + + dlg = GitRevisionSelectionDialog( + self.gitGetTagsList(repodir), + self.gitGetBranchesList(repodir), + showBranches=showBranches, + showHead=showHead, + ) if dlg.exec() == QDialog.DialogCode.Accepted: rev = dlg.getRevision() else: return False - + args = self.initCommand("bisect") if subcommand == "start_extended": from .GitBisectStartDialog import GitBisectStartDialog + dlg = GitBisectStartDialog() if dlg.exec() == QDialog.DialogCode.Accepted: bad, good, noCheckout = dlg.getData() @@ -2622,34 +2714,34 @@ if rev: args.extend(rev.split()) # treat rev as a list separated by whitespace - - dia = GitDialog( - self.tr('Git Bisect ({0})').format(subcommand), self) + + dia = GitDialog(self.tr("Git Bisect ({0})").format(subcommand), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() - + return res - + def gitBisectLogBrowser(self, projectDir): """ Public method used to browse the bisect log of the project. - + @param projectDir name of the project directory (string) """ if self.bisectlogBrowser is None: from .GitBisectLogBrowserDialog import GitBisectLogBrowserDialog + self.bisectlogBrowser = GitBisectLogBrowserDialog(self) self.bisectlogBrowser.show() self.bisectlogBrowser.raise_() self.bisectlogBrowser.start(projectDir) - + def gitBisectCreateReplayFile(self, projectDir): """ Public method used to create a bisect replay file for the project. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2658,31 +2750,34 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("bisect") args.append("log") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) else: EricMessageBox.critical( self, - self.tr('Process Generation Error'), + self.tr("Process Generation Error"), self.tr( - 'The process {0} could not be started. ' - 'Ensure, that it is in the search path.' - ).format('git')) + "The process {0} could not be started. " + "Ensure, that it is in the search path." + ).format("git"), + ) return - + if output: fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( None, @@ -2690,11 +2785,12 @@ self.__lastBundlePath or repodir, self.tr("Git Bisect Replay Files (*.replay)"), None, - EricFileDialog.DontConfirmOverwrite) - + EricFileDialog.DontConfirmOverwrite, + ) + if not fname: return # user aborted - + fpath = pathlib.Path(fname) if not fpath.suffix: ex = selectedFilter.split("(*")[1].split(")")[0] @@ -2704,14 +2800,16 @@ res = EricMessageBox.yesNo( self.__ui, self.tr("Create Bisect Replay File"), - self.tr("<p>The Git bisect replay file <b>{0}</b> " - "already exists. Overwrite it?</p>") - .format(fpath), - icon=EricMessageBox.Warning) + self.tr( + "<p>The Git bisect replay file <b>{0}</b> " + "already exists. Overwrite it?</p>" + ).format(fpath), + icon=EricMessageBox.Warning, + ) if not res: return self.__lastReplayPath = str(fpath.parent) - + try: with fpath.open("w") as f: f.write(output) @@ -2721,13 +2819,14 @@ self.tr("Create Bisect Replay File"), self.tr( """<p>The file <b>{0}</b> could not be written.</p>""" - """<p>Reason: {1}</p>""") - .format(fpath, str(err))) - + """<p>Reason: {1}</p>""" + ).format(fpath, str(err)), + ) + def gitBisectEditReplayFile(self, projectDir): """ Public method used to edit a bisect replay file. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2736,22 +2835,23 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + fname = EricFileDialog.getOpenFileName( None, self.tr("Edit Bisect Replay File"), self.__lastReplayPath or repodir, - self.tr("Git Bisect Replay Files (*.replay);;All Files (*)")) + self.tr("Git Bisect Replay Files (*.replay);;All Files (*)"), + ) if fname: self.__lastReplayPath = os.path.dirname(fname) - + self.bisectReplayEditor = MiniEditor(fname) self.bisectReplayEditor.show() - + def gitBisectReplay(self, projectDir): """ Public method to replay a bisect session. - + @param projectDir name of the project directory (string) @return flag indicating, that the update contained an add or delete (boolean) @@ -2762,67 +2862,69 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + res = False fname = EricFileDialog.getOpenFileName( None, self.tr("Bisect Replay"), self.__lastReplayPath or repodir, - self.tr("Git Bisect Replay Files (*.replay);;All Files (*)")) + self.tr("Git Bisect Replay Files (*.replay);;All Files (*)"), + ) if fname: self.__lastReplayPath = os.path.dirname(fname) - + args = self.initCommand("bisect") args.append("replay") args.append(fname) - - dia = GitDialog( - self.tr('Git Bisect ({0})').format("replay"), self) + + dia = GitDialog(self.tr("Git Bisect ({0})").format("replay"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() - + return res - + ########################################################################### ## Methods for remotes handling. ########################################################################### - + def gitGetRemotesList(self, repodir): """ Public method to get the list of remote repos. - + @param repodir directory name of the repository (string) @return list of remote repos (list of string) """ args = self.initCommand("remote") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + remotesList = [] if output: for line in output.splitlines(): name = line.strip() remotesList.append(name) - + return remotesList - + def gitGetRemoteUrlsList(self, repodir, forFetch=True): """ Public method to get the list of remote repos and their URLs. - + @param repodir directory name of the repository (string) @param forFetch flag indicating to get Fetch info (string) @return list of tuples of remote repo name and repo URL (list of @@ -2830,36 +2932,37 @@ """ args = self.initCommand("remote") args.append("--verbose") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + remotesList = [] if output: for line in output.splitlines(): name, urlmode = line.strip().split(None, 1) url, mode = urlmode.rsplit(None, 1) - if ( - (forFetch and mode == "(fetch)") or - ((not forFetch) and mode == "(push)") + if (forFetch and mode == "(fetch)") or ( + (not forFetch) and mode == "(push)" ): remotesList.append((name, url)) - + return remotesList - + def gitGetRemoteUrl(self, repodir, remoteName): """ Public method to get the URL of a remote repository. - + @param repodir directory name of the repository @type str @param remoteName name of the remote repository @@ -2870,25 +2973,27 @@ args = self.initCommand("remote") args.append("get-url") args.append(remoteName) - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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').strip() - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ).strip() + return output - + def gitGetRemoteBranchesList(self, repodir, remote): """ Public method to get the list of a remote repository branches. - + @param repodir directory name of the repository (string) @param remote remote repository name (string) @return list of remote repository branches (list of string) @@ -2896,31 +3001,33 @@ args = self.initCommand("ls-remote") args.append("--heads") args.append(remote) - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + remoteBranches = [] if output: for line in output.splitlines(): branch = line.strip().split()[-1].split("/")[-1] remoteBranches.append(branch) - + return remoteBranches - + def gitShowRemote(self, projectDir, remoteName): """ Public method to show information about a remote repository. - + @param projectDir name of the project directory (string) @param remoteName name of the remote repository (string) """ @@ -2930,35 +3037,34 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("remote") args.append("show") args.append(remoteName) - - dia = GitDialog(self.tr('Show Remote Info'), self) + + dia = GitDialog(self.tr("Show Remote Info"), self) res = dia.startProcess(args, repodir, showArgs=False) if res: dia.exec() - + def gitShowRemotes(self, projectDir): """ Public method to show available remote repositories. - + @param projectDir name of the project directory (string) """ if self.remotesDialog is None: - from .GitRemoteRepositoriesDialog import ( - GitRemoteRepositoriesDialog - ) + from .GitRemoteRepositoriesDialog import GitRemoteRepositoriesDialog + self.remotesDialog = GitRemoteRepositoriesDialog(self) self.remotesDialog.show() self.remotesDialog.raise_() self.remotesDialog.start(projectDir) - + def gitAddRemote(self, projectDir): """ Public method to add a remote repository. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -2967,8 +3073,9 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitAddRemoteDialog import GitAddRemoteDialog + dlg = GitAddRemoteDialog() if dlg.exec() == QDialog.DialogCode.Accepted: name, url = dlg.getData() @@ -2976,14 +3083,13 @@ args.append("add") args.append(name) args.append(url) - - self.startSynchronizedProcess(QProcess(), "git", args, - workingDir=repodir) - + + self.startSynchronizedProcess(QProcess(), "git", args, workingDir=repodir) + def gitRenameRemote(self, projectDir, remoteName): """ Public method to rename a remote repository. - + @param projectDir name of the project directory (string) @param remoteName name of the remote repository (string) """ @@ -2993,25 +3099,25 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + newName, ok = QInputDialog.getText( None, self.tr("Rename Remote Repository"), self.tr("Enter new name for remote repository:"), - QLineEdit.EchoMode.Normal) + QLineEdit.EchoMode.Normal, + ) if ok and newName and newName != remoteName: args = self.initCommand("remote") args.append("rename") args.append(remoteName) args.append(newName) - - self.startSynchronizedProcess(QProcess(), "git", args, - workingDir=repodir) - + + self.startSynchronizedProcess(QProcess(), "git", args, workingDir=repodir) + def gitChangeRemoteUrl(self, projectDir, remoteName, remoteUrl=""): """ Public method to change the URL of a remote repository. - + @param projectDir name of the project directory @type str @param remoteName name of the remote repository @@ -3025,11 +3131,12 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + if not remoteUrl: remoteUrl = self.gitGetRemoteUrl(repodir, remoteName) - + from .GitChangeRemoteUrlDialog import GitChangeRemoteUrlDialog + dlg = GitChangeRemoteUrlDialog(remoteName, remoteUrl) if dlg.exec() == QDialog.DialogCode.Accepted: name, url = dlg.getData() @@ -3038,14 +3145,15 @@ args.append("set-url") args.append(name) args.append(url) - - self.startSynchronizedProcess(QProcess(), "git", args, - workingDir=repodir) - + + self.startSynchronizedProcess( + QProcess(), "git", args, workingDir=repodir + ) + def gitChangeRemoteCredentials(self, projectDir, remoteName, remoteUrl=""): """ Public method to change the user credentials of a remote repository. - + @param projectDir name of the project directory @type str @param remoteName name of the remote repository @@ -3059,11 +3167,12 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + if not remoteUrl: remoteUrl = self.gitGetRemoteUrl(repodir, remoteName) - + from .GitRemoteCredentialsDialog import GitRemoteCredentialsDialog + dlg = GitRemoteCredentialsDialog(remoteName, remoteUrl) if dlg.exec() == QDialog.DialogCode.Accepted: name, url = dlg.getData() @@ -3072,14 +3181,15 @@ args.append("set-url") args.append(name) args.append(url) - - self.startSynchronizedProcess(QProcess(), "git", args, - workingDir=repodir) - + + self.startSynchronizedProcess( + QProcess(), "git", args, workingDir=repodir + ) + def gitRemoveRemote(self, projectDir, remoteName): """ Public method to remove a remote repository. - + @param projectDir name of the project directory (string) @param remoteName name of the remote repository (string) """ @@ -3089,18 +3199,17 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("remote") args.append("remove") args.append(remoteName) - - self.startSynchronizedProcess(QProcess(), "git", args, - workingDir=repodir) - + + self.startSynchronizedProcess(QProcess(), "git", args, workingDir=repodir) + def gitPruneRemote(self, projectDir, remoteName): """ Public method to prune stale remote-tracking branches. - + @param projectDir name of the project directory (string) @param remoteName name of the remote repository (string) """ @@ -3110,21 +3219,21 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("remote") args.append("prune") args.append(remoteName) - - dia = GitDialog(self.tr('Show Remote Info'), self) + + dia = GitDialog(self.tr("Show Remote Info"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitShortlog(self, projectDir, commit): """ Public method to show a short log suitable for inclusion in release announcements. - + @param projectDir name of the project directory (string) @param commit commit to start the log at (strings) """ @@ -3134,40 +3243,41 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("shortlog") args.append("-w") args.append(commit) - - dia = GitDialog(self.tr('Show Shortlog'), self) + + dia = GitDialog(self.tr("Show Shortlog"), self) res = dia.startProcess(args, repodir, showArgs=False) if res: dia.exec() - + def gitDescribe(self, projectDir, commits): """ Public method to find the most recent tag reachable from each commit. - + @param projectDir name of the project directory (string) @param commits list of commits to start the search from (list of strings) """ if self.describeDialog is None: from .GitDescribeDialog import GitDescribeDialog + self.describeDialog = GitDescribeDialog(self) self.describeDialog.show() self.describeDialog.raise_() self.describeDialog.start(projectDir, commits) - + ########################################################################### ## Methods for cherry-pick handling. ########################################################################### - + def gitCherryPick(self, projectDir, commits=None): """ Public method to cherry pick commits and apply them to the current branch. - + @param projectDir name of the project directory (string) @param commits list of commits to be applied (list of strings) @return flag indicating that the project should be reread (boolean) @@ -3178,16 +3288,15 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + res = False - + from .GitCherryPickDialog import GitCherryPickDialog + dlg = GitCherryPickDialog(commits) if dlg.exec() == QDialog.DialogCode.Accepted: - commits, cherrypickInfo, signoff, nocommit = ( - dlg.getData() - ) - + commits, cherrypickInfo, signoff, nocommit = dlg.getData() + args = self.initCommand("cherry-pick") args.append("-Xpatience") if cherrypickInfo: @@ -3197,20 +3306,20 @@ if nocommit: args.append("--no-commit") args.extend(commits) - - dia = GitDialog(self.tr('Cherry-pick'), self) + + dia = GitDialog(self.tr("Cherry-pick"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitCherryPickContinue(self, projectDir): """ Public method to continue the last copying session after conflicts were resolved. - + @param projectDir name of the project directory (string) @return flag indicating that the project should be reread (boolean) """ @@ -3220,27 +3329,27 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + import sys + editor = sys.argv[0].replace(".py", "_editor.py") - env = {"GIT_EDITOR": "{0} {1}".format( - Globals.getPythonExecutable(), editor)} - + env = {"GIT_EDITOR": "{0} {1}".format(Globals.getPythonExecutable(), editor)} + args = self.initCommand("cherry-pick") args.append("--continue") - - dia = GitDialog(self.tr('Copy Changesets (Continue)'), self) + + dia = GitDialog(self.tr("Copy Changesets (Continue)"), self) res = dia.startProcess(args, repodir, environment=env) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitCherryPickQuit(self, projectDir): """ Public method to quit the current copying operation. - + @param projectDir name of the project directory (string) @return flag indicating that the project should be reread (boolean) """ @@ -3250,23 +3359,23 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + args = self.initCommand("cherry-pick") args.append("--quit") - - dia = GitDialog(self.tr('Copy Changesets (Quit)'), self) + + dia = GitDialog(self.tr("Copy Changesets (Quit)"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitCherryPickAbort(self, projectDir): """ Public method to cancel the last copying session and return to the previous state. - + @param projectDir name of the project directory (string) @return flag indicating that the project should be reread (boolean) """ @@ -3276,26 +3385,26 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + args = self.initCommand("cherry-pick") args.append("--abort") - - dia = GitDialog(self.tr('Copy Changesets (Cancel)'), self) + + dia = GitDialog(self.tr("Copy Changesets (Cancel)"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + ########################################################################### ## Methods for stash handling. ########################################################################### - + def __gitGetStashesList(self, projectDir): """ Private method to get a list of stash names. - + @param projectDir name of the project directory (string) @return list of available stashes (list of string) """ @@ -3305,33 +3414,35 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return [] - + args = self.initCommand("stash") args.append("list") args.append("--format=format:%gd") - + stashesList = [] output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + if output: stashesList = output.strip().splitlines() - + return stashesList - + def gitStashSave(self, projectDir): """ Public method to save the current changes to a new stash. - + @param projectDir name of the project directory (string) @return flag indicating, that the save contained an add or delete (boolean) @@ -3342,9 +3453,10 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + res = False from .GitStashDataDialog import GitStashDataDialog + dlg = GitStashDataDialog() if dlg.exec() == QDialog.DialogCode.Accepted: message, keepIndex, untracked = dlg.getData() @@ -3358,32 +3470,33 @@ args.append("--all") if message: args.append(message) - - dia = GitDialog(self.tr('Saving stash'), self) + + dia = GitDialog(self.tr("Saving stash"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitStashBrowser(self, projectDir): """ Public method used to browse the stashed changes. - + @param projectDir name of the project directory (string) """ if self.stashBrowser is None: from .GitStashBrowserDialog import GitStashBrowserDialog + self.stashBrowser = GitStashBrowserDialog(self) self.stashBrowser.show() self.stashBrowser.raise_() self.stashBrowser.start(projectDir) - + def gitStashShowPatch(self, projectDir, stashName=""): """ Public method to show the contents of a stash. - + @param projectDir name of the project directory (string) @param stashName name of a stash (string) """ @@ -3393,7 +3506,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + if not stashName: availableStashes = self.__gitGetStashesList(repodir) stashName, ok = QInputDialog.getItem( @@ -3401,21 +3514,24 @@ self.tr("Show Stash"), self.tr("Select a stash (empty for latest stash):"), [""] + availableStashes, - 0, False) + 0, + False, + ) if not ok: return - + if self.diff is None: from .GitDiffDialog import GitDiffDialog + self.diff = GitDiffDialog(self) self.diff.show() self.diff.raise_() self.diff.start(repodir, diffMode="stash", stashName=stashName) - + def gitStashApply(self, projectDir, stashName=""): """ Public method to apply a stash but keep it. - + @param projectDir name of the project directory (string) @param stashName name of a stash (string) @return flag indicating, that the restore contained an add @@ -3427,7 +3543,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + if not stashName: availableStashes = self.__gitGetStashesList(repodir) stashName, ok = QInputDialog.getItem( @@ -3435,27 +3551,29 @@ self.tr("Restore Stash"), self.tr("Select a stash (empty for latest stash):"), [""] + availableStashes, - 0, False) + 0, + False, + ) if not ok: return False - + args = self.initCommand("stash") args.append("apply") if stashName: args.append(stashName) - - dia = GitDialog(self.tr('Restoring stash'), self) + + dia = GitDialog(self.tr("Restoring stash"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitStashPop(self, projectDir, stashName=""): """ Public method to apply a stash and delete it. - + @param projectDir name of the project directory (string) @param stashName name of a stash (string) @return flag indicating, that the restore contained an add @@ -3467,7 +3585,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + if not stashName: availableStashes = self.__gitGetStashesList(repodir) stashName, ok = QInputDialog.getItem( @@ -3475,27 +3593,29 @@ self.tr("Restore Stash"), self.tr("Select a stash (empty for latest stash):"), [""] + availableStashes, - 0, False) + 0, + False, + ) if not ok: return False - + args = self.initCommand("stash") args.append("pop") if stashName: args.append(stashName) - - dia = GitDialog(self.tr('Restoring stash'), self) + + dia = GitDialog(self.tr("Restoring stash"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitStashBranch(self, projectDir, stashName=""): """ Public method to create a branch from a stash. - + @param projectDir name of the project directory (string) @param stashName name of a stash (string) @return flag indicating, that the restore contained an add @@ -3507,15 +3627,16 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + branchName, ok = QInputDialog.getText( None, self.tr("Create Branch"), self.tr("Enter a branch name to restore a stash to:"), - QLineEdit.EchoMode.Normal) + QLineEdit.EchoMode.Normal, + ) if not ok or branchName == "": return False - + if not stashName: availableStashes = self.__gitGetStashesList(repodir) stashName, ok = QInputDialog.getItem( @@ -3523,28 +3644,30 @@ self.tr("Create Branch"), self.tr("Select a stash (empty for latest stash):"), [""] + availableStashes, - 0, False) + 0, + False, + ) if not ok: return False - + args = self.initCommand("stash") args.append("branch") args.append(branchName) if stashName: args.append(stashName) - - dia = GitDialog(self.tr('Creating branch'), self) + + dia = GitDialog(self.tr("Creating branch"), self) res = dia.startProcess(args, repodir) if res: dia.exec() res = dia.hasAddOrDelete() self.checkVCSStatus() return res - + def gitStashDrop(self, projectDir, stashName=""): """ Public method to delete a stash. - + @param projectDir name of the project directory (string) @param stashName name of a stash (string) @return flag indicating a successful deletion (boolean) @@ -3555,7 +3678,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + if not stashName: availableStashes = self.__gitGetStashesList(repodir) stashName, ok = QInputDialog.getItem( @@ -3563,31 +3686,35 @@ self.tr("Show Stash"), self.tr("Select a stash (empty for latest stash):"), [""] + availableStashes, - 0, False) + 0, + False, + ) if not ok: return False - + res = EricMessageBox.yesNo( None, self.tr("Delete Stash"), - self.tr("""Do you really want to delete the stash <b>{0}</b>?""") - .format(stashName)) + self.tr("""Do you really want to delete the stash <b>{0}</b>?""").format( + stashName + ), + ) if res: args = self.initCommand("stash") args.append("drop") if stashName: args.append(stashName) - - dia = GitDialog(self.tr('Deleting stash'), self) + + dia = GitDialog(self.tr("Deleting stash"), self) res = dia.startProcess(args, repodir) if res: dia.exec() return res - + def gitStashClear(self, projectDir): """ Public method to delete all stashes. - + @param projectDir name of the project directory (string) @return flag indicating a successful deletion (boolean) """ @@ -3597,25 +3724,26 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return False - + res = EricMessageBox.yesNo( None, self.tr("Delete All Stashes"), - self.tr("""Do you really want to delete all stashes?""")) + self.tr("""Do you really want to delete all stashes?"""), + ) if res: args = self.initCommand("stash") args.append("clear") - - dia = GitDialog(self.tr('Deleting all stashes'), self) + + dia = GitDialog(self.tr("Deleting all stashes"), self) res = dia.startProcess(args, repodir) if res: dia.exec() return res - + def gitEditConfig(self, projectDir): """ Public method used to edit the repository configuration file. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3624,7 +3752,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + cfgFile = os.path.join(repodir, self.adminDir, "config") if not os.path.exists(cfgFile): # create an empty one @@ -3632,32 +3760,33 @@ pass self.repoEditor = MiniEditor(cfgFile, "Properties") self.repoEditor.show() - + def gitEditUserConfig(self): """ Public method used to edit the user configuration file. """ from .GitUtilities import getConfigPath + cfgFile = getConfigPath() if not os.path.exists(cfgFile): from .GitUserConfigDataDialog import GitUserConfigDataDialog + dlg = GitUserConfigDataDialog() if dlg.exec() == QDialog.DialogCode.Accepted: firstName, lastName, email = dlg.getData() else: - firstName, lastName, email = ( - "Firstname", "Lastname", "email_address") + firstName, lastName, email = ("Firstname", "Lastname", "email_address") with contextlib.suppress(OSError), open(cfgFile, "w") as f: f.write("[user]\n") f.write(" name = {0} {1}\n".format(firstName, lastName)) f.write(" email = {0}\n".format(email)) self.userEditor = MiniEditor(cfgFile, "Properties") self.userEditor.show() - + def gitShowConfig(self, projectDir): """ Public method to show the combined configuration. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3666,22 +3795,20 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("config") args.append("--list") - - dia = GitDialog( - self.tr('Showing the combined configuration settings'), - self) + + dia = GitDialog(self.tr("Showing the combined configuration settings"), self) res = dia.startProcess(args, repodir, False) if res: dia.exec() - + def gitVerify(self, projectDir): """ Public method to verify the connectivity and validity of objects of the database. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3690,23 +3817,21 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("fsck") args.append("--strict") args.append("--full") args.append("--cache") - - dia = GitDialog( - self.tr('Verifying the integrity of the Git repository'), - self) + + dia = GitDialog(self.tr("Verifying the integrity of the Git repository"), self) res = dia.startProcess(args, repodir, False) if res: dia.exec() - + def gitHouseKeeping(self, projectDir): """ Public method to cleanup and optimize the local repository. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3715,23 +3840,21 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("gc") args.append("--prune") if self.__plugin.getPreferences("AggressiveGC"): args.append("--aggressive") - - dia = GitDialog( - self.tr('Performing Repository Housekeeping'), - self) + + dia = GitDialog(self.tr("Performing Repository Housekeeping"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitStatistics(self, projectDir): """ Public method to show some statistics of the local repository. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3740,103 +3863,120 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + args = self.initCommand("count-objects") args.append("-v") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + info = [] if output: statistics = {} for line in output.splitlines(): key, value = line.strip().split(": ", 1) statistics[key] = value - + info.append("""<p><table>""") info.append(self.tr("""<tr><td><b>Statistics</b></td></tr>""")) info.append( - self.tr("""<tr><td>Number of loose objects: </td>""" - """<td>{0}</td></tr>""") - .format(statistics["count"])) + self.tr( + """<tr><td>Number of loose objects: </td>""" """<td>{0}</td></tr>""" + ).format(statistics["count"]) + ) info.append( - self.tr("""<tr><td>Disk space used by loose objects: </td>""" - """<td>{0} KiB</td></tr>""") - .format(statistics["size"])) + self.tr( + """<tr><td>Disk space used by loose objects: </td>""" + """<td>{0} KiB</td></tr>""" + ).format(statistics["size"]) + ) info.append( - self.tr("""<tr><td>Number of packed objects: </td>""" - """<td>{0}</td></tr>""") - .format(statistics["in-pack"])) + self.tr( + """<tr><td>Number of packed objects: </td>""" + """<td>{0}</td></tr>""" + ).format(statistics["in-pack"]) + ) info.append( - self.tr("""<tr><td>Number of packs: </td>""" - """<td>{0}</td></tr>""") - .format(statistics["packs"])) + self.tr( + """<tr><td>Number of packs: </td>""" """<td>{0}</td></tr>""" + ).format(statistics["packs"]) + ) info.append( - self.tr("""<tr><td>Disk space used by packed objects: </td>""" - """<td>{0} KiB</td></tr>""") - .format(statistics["size-pack"])) - info.append( - self.tr("""<tr><td>Packed objects waiting for pruning: </td>""" - """<td>{0}</td></tr>""") - .format(statistics["prune-packable"])) + self.tr( + """<tr><td>Disk space used by packed objects: </td>""" + """<td>{0} KiB</td></tr>""" + ).format(statistics["size-pack"]) + ) info.append( - self.tr("""<tr><td>Garbage files: </td>""" - """<td>{0}</td></tr>""") - .format(statistics["garbage"])) + self.tr( + """<tr><td>Packed objects waiting for pruning: </td>""" + """<td>{0}</td></tr>""" + ).format(statistics["prune-packable"]) + ) info.append( - self.tr("""<tr><td>Disk space used by garbage files: </td>""" - """<td>{0} KiB</td></tr>""") - .format(statistics["size-garbage"])) + self.tr( + """<tr><td>Garbage files: </td>""" """<td>{0}</td></tr>""" + ).format(statistics["garbage"]) + ) + info.append( + self.tr( + """<tr><td>Disk space used by garbage files: </td>""" + """<td>{0} KiB</td></tr>""" + ).format(statistics["size-garbage"]) + ) info.append("""</table></p>""") else: info.append(self.tr("<p><b>No statistics available.</b></p>")) dlg = VcsRepositoryInfoDialog(None, "\n".join(info)) dlg.exec() - + def gitGetArchiveFormats(self, repodir): """ Public method to get a list of supported archive formats. - + @param repodir directory name of the repository (string) @return list of supported archive formats (list of strings) """ args = self.initCommand("archive") args.append("--list") - + output = "" process = QProcess() process.setWorkingDirectory(repodir) - process.start('git', args) + process.start("git", 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') - + output = str( + process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + archiveFormats = [] if output: for line in output.splitlines(): archiveFormat = line.strip() archiveFormats.append(archiveFormat) - + return archiveFormats - + def gitCreateArchive(self, projectDir): """ Public method to show some statistics of the local repository. - + @param projectDir name of the project directory (string) """ # find the root of the repo @@ -3845,12 +3985,13 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitArchiveDataDialog import GitArchiveDataDialog + dlg = GitArchiveDataDialog( self.gitGetTagsList(repodir), self.gitGetBranchesList(repodir, withMaster=True), - self.gitGetArchiveFormats(repodir) + self.gitGetArchiveFormats(repodir), ) if dlg.exec() == QDialog.DialogCode.Accepted: commit, archiveFormat, fileName, prefix = dlg.getData() @@ -3863,22 +4004,20 @@ prefix += "/" args.append("--prefix={0}".format(prefix)) args.append(commit) - - dia = GitDialog( - self.tr('Creating Archive'), - self) + + dia = GitDialog(self.tr("Creating Archive"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + ########################################################################### ## Methods related to submodules. ########################################################################### - + def gitSubmoduleAdd(self, projectDir): """ Public method to add a submodule to the project. - + @param projectDir name of the project directory @type str """ @@ -3888,8 +4027,9 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + from .GitSubmoduleAddDialog import GitSubmoduleAddDialog + dlg = GitSubmoduleAddDialog(self, repodir) if dlg.exec() == QDialog.DialogCode.Accepted: repo, branch, name, path, force = dlg.getData() @@ -3906,18 +4046,16 @@ args.append(repo) if path: args.append(path) - - dia = GitDialog( - self.tr("Add Submodule"), - self) + + dia = GitDialog(self.tr("Add Submodule"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def __gitSubmodulesList(self, repodir): """ Private method to get the data of defined submodules. - + @param repodir name of the directory containing the repo subdirectory @type str @return list of dictionaries with submodule name, path, URL and branch @@ -3926,14 +4064,14 @@ submodulesFile = os.path.join(repodir, ".gitmodules") if not os.path.exists(submodulesFile): return [] - + try: with open(submodulesFile, "r") as modulesFile: contents = modulesFile.readlines() except OSError: # silently ignore them return [] - + submodules = [] submoduleDict = None for line in contents: @@ -3951,13 +4089,13 @@ if "branch" not in submoduleDict: submoduleDict["branch"] = "" submodules.append(submoduleDict) - + return submodules - + def gitSubmoduleList(self, projectDir): """ Public method to show a list of all submodules of the project. - + @param projectDir name of the project directory @type str """ @@ -3967,22 +4105,24 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + submodulesList = self.__gitSubmodulesList(repodir) if submodulesList: from .GitSubmodulesListDialog import GitSubmodulesListDialog + dlg = GitSubmodulesListDialog(submodulesList) dlg.exec() else: EricMessageBox.information( None, self.tr("List Submodules"), - self.tr("""No submodules defined for the project.""")) - + self.tr("""No submodules defined for the project."""), + ) + def __selectSubmodulePath(self, repodir): """ Private method to select a submodule path. - + @param repodir name of the directory containing the repo subdirectory @type str @return tuple of selected submodule path and flag indicating @@ -3990,44 +4130,45 @@ @rtype tuple of (str, bool) """ allEntry = self.tr("All") - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] submodulePath, ok = QInputDialog.getItem( None, self.tr("Submodule Path"), self.tr("Select a submodule path:"), [allEntry] + sorted(paths), - 0, False) + 0, + False, + ) if submodulePath == allEntry: submodulePath = "" - + return submodulePath, ok - + def __selectSubmodulePaths(self, repodir): """ Private method to select a list of submodule paths. - + @param repodir name of the directory containing the repo subdirectory @type str @return tuple of selected submodule paths and flag indicating a cancellation @rtype tuple of (list of str, bool) """ - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] - + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] + from .GitListDialog import GitListDialog + dlg = GitListDialog(sorted(paths)) if dlg.exec() == QDialog.DialogCode.Accepted: selectedPaths = dlg.getSelection() return selectedPaths, True else: return [], False - + def gitSubmoduleInit(self, projectDir): """ Public method to initialize one or all submodules. - + @param projectDir name of the project directory @type str """ @@ -4037,24 +4178,22 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + submodulePaths, ok = self.__selectSubmodulePaths(repodir) if ok: args = self.initCommand("submodule") args.append("init") args.extend(submodulePaths) - - dia = GitDialog( - self.tr("Initialize Submodules"), - self) + + dia = GitDialog(self.tr("Initialize Submodules"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitSubmoduleDeinit(self, projectDir): """ Public method to unregister submodules. - + @param projectDir name of the project directory @type str """ @@ -4064,11 +4203,11 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] - + + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] + from .GitSubmodulesDeinitDialog import GitSubmodulesDeinitDialog + dlg = GitSubmodulesDeinitDialog(paths) if dlg.exec() == QDialog.DialogCode.Accepted: deinitAll, submodulePaths, force = dlg.getData() @@ -4080,18 +4219,16 @@ args.extend(submodulePaths) if force: args.append("--force") - - dia = GitDialog( - self.tr("Unregister Submodules"), - self) + + dia = GitDialog(self.tr("Unregister Submodules"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitSubmoduleUpdate(self, projectDir, initialize=False, remote=False): """ Public method to update submodules. - + @param projectDir name of the project directory @type str @param initialize flag indicating an initialize and update operation @@ -4105,7 +4242,7 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - + submodulePaths, ok = self.__selectSubmodulePaths(repodir) if ok: args = self.initCommand("submodule") @@ -4115,19 +4252,17 @@ if remote: args.append("--remote") args.extend(submodulePaths) - - dia = GitDialog( - self.tr("Update Submodules"), - self) + + dia = GitDialog(self.tr("Update Submodules"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitSubmoduleUpdateWithOptions(self, projectDir): """ Public method to update submodules offering a dialog to select the update options. - + @param projectDir name of the project directory @type str """ @@ -4137,19 +4272,15 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] - - from .GitSubmodulesUpdateOptionsDialog import ( - GitSubmodulesUpdateOptionsDialog - ) + + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] + + from .GitSubmodulesUpdateOptionsDialog import GitSubmodulesUpdateOptionsDialog + dlg = GitSubmodulesUpdateOptionsDialog(paths) if dlg.exec() == QDialog.DialogCode.Accepted: - procedure, init, remote, noFetch, force, submodulePaths = ( - dlg.getData() - ) - + procedure, init, remote, noFetch, force, submodulePaths = dlg.getData() + args = self.initCommand("submodule") args.append("update") args.append(procedure) @@ -4162,18 +4293,16 @@ if force: args.append("--force") args.extend(submodulePaths) - - dia = GitDialog( - self.tr("Update Submodules"), - self) + + dia = GitDialog(self.tr("Update Submodules"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitSubmoduleSync(self, projectDir): """ Public method to synchronize submodules. - + @param projectDir name of the project directory @type str """ @@ -4183,11 +4312,11 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] - + + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] + from .GitSubmodulesSyncDialog import GitSubmodulesSyncDialog + dlg = GitSubmodulesSyncDialog(paths) if dlg.exec() == QDialog.DialogCode.Accepted: submodulePaths, recursive = dlg.getData() @@ -4196,32 +4325,31 @@ if recursive: args.append("--recursive") args.extend(submodulePaths) - - dia = GitDialog( - self.tr("Synchronize Submodules"), - self) + + dia = GitDialog(self.tr("Synchronize Submodules"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + def gitSubmoduleStatus(self, projectDir): """ Public method to show the status of the submodules. - + @param projectDir name of the project directory @type str """ if self.submoduleStatusDialog is None: from .GitSubmodulesStatusDialog import GitSubmodulesStatusDialog + self.submoduleStatusDialog = GitSubmodulesStatusDialog(self) self.submoduleStatusDialog.show() self.submoduleStatusDialog.raise_() self.submoduleStatusDialog.start(projectDir) - + def gitSubmoduleSummary(self, projectDir): """ Public method to show the status of the submodules. - + @param projectDir name of the project directory @type str """ @@ -4231,13 +4359,11 @@ repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return - - paths = [submodule["path"] - for submodule in self.__gitSubmodulesList(repodir)] - - from .GitSubmodulesSummaryOptionsDialog import ( - GitSubmodulesSummaryOptionsDialog - ) + + paths = [submodule["path"] for submodule in self.__gitSubmodulesList(repodir)] + + from .GitSubmodulesSummaryOptionsDialog import GitSubmodulesSummaryOptionsDialog + dlg = GitSubmodulesSummaryOptionsDialog(paths) if dlg.exec() == QDialog.DialogCode.Accepted: submodulePaths, superProject, index, commit, limit = dlg.getData() @@ -4255,24 +4381,21 @@ if submodulePaths: args.append("--") args.extend(submodulePaths) - - dia = GitDialog( - self.tr("Submodules Summary"), - self) + + dia = GitDialog(self.tr("Submodules Summary"), self) res = dia.startProcess(args, repodir) if res: dia.exec() - + ########################################################################### ## Methods to get the helper objects are below. ########################################################################### - - def vcsGetProjectBrowserHelper(self, browser, project, - isTranslationsBrowser=False): + + def vcsGetProjectBrowserHelper(self, browser, project, isTranslationsBrowser=False): """ Public method to instantiate 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 @@ -4280,13 +4403,13 @@ @return the project browser helper object """ from .ProjectBrowserHelper import GitProjectBrowserHelper - return GitProjectBrowserHelper(self, browser, project, - isTranslationsBrowser) - + + return GitProjectBrowserHelper(self, browser, project, isTranslationsBrowser) + def vcsGetProjectHelper(self, project): """ Public method to instantiate a helper object for the project. - + @param project reference to the project object @return the project helper object """ @@ -4302,11 +4425,12 @@ """ 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 (Project) @return reference to the monitor thread (QThread) """ from .GitStatusMonitorThread import GitStatusMonitorThread + return GitStatusMonitorThread(interval, project, self)