eric7/VCS/ProjectHelper.py

branch
eric7
changeset 8312
800c432b34c8
parent 8263
af06dde4f4ef
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the base class of the VCS project helper.
8 """
9
10 import os
11 import shutil
12 import copy
13
14 from PyQt5.QtCore import pyqtSlot, QDir, QFileInfo, QObject, QCoreApplication
15 from PyQt5.QtWidgets import QDialog, QInputDialog, QToolBar
16
17 from E5Gui.E5Action import E5Action
18 from E5Gui import E5MessageBox
19 from E5Gui.E5Application import e5App
20
21 import Preferences
22 import UI.PixmapCache
23 import UI.Config
24
25
26 class VcsProjectHelper(QObject):
27 """
28 Class implementing the base class of the VCS project helper.
29 """
30 def __init__(self, vcsObject, projectObject, parent=None, name=None):
31 """
32 Constructor
33
34 @param vcsObject reference to the vcs object
35 @param projectObject reference to the project object
36 @param parent parent widget (QWidget)
37 @param name name of this object (string)
38 """
39 super().__init__(parent)
40 if name:
41 self.setObjectName(name)
42
43 self.vcs = vcsObject
44 self.project = projectObject
45
46 self.actions = []
47
48 self.vcsAddAct = None
49
50 self.initActions()
51
52 def setObjects(self, vcsObject, projectObject):
53 """
54 Public method to set references to the vcs and project objects.
55
56 @param vcsObject reference to the vcs object
57 @param projectObject reference to the project object
58 """
59 self.vcs = vcsObject
60 self.project = projectObject
61
62 def initActions(self):
63 """
64 Public method to generate the action objects.
65 """
66 self.vcsNewAct = E5Action(
67 QCoreApplication.translate(
68 "VcsProjectHelper", 'New from repository'),
69 UI.PixmapCache.getIcon("vcsCheckout"),
70 QCoreApplication.translate(
71 "VcsProjectHelper", '&New from repository...'),
72 0, 0, self, 'vcs_new')
73 self.vcsNewAct.setStatusTip(QCoreApplication.translate(
74 "VcsProjectHelper",
75 'Create a new project from the VCS repository'
76 ))
77 self.vcsNewAct.setWhatsThis(QCoreApplication.translate(
78 "VcsProjectHelper",
79 """<b>New from repository</b>"""
80 """<p>This creates a new local project from the VCS"""
81 """ repository.</p>"""
82 ))
83 self.vcsNewAct.triggered.connect(self._vcsCheckout)
84 self.actions.append(self.vcsNewAct)
85
86 self.vcsExportAct = E5Action(
87 QCoreApplication.translate(
88 "VcsProjectHelper", 'Export from repository'),
89 UI.PixmapCache.getIcon("vcsExport"),
90 QCoreApplication.translate(
91 "VcsProjectHelper", '&Export from repository...'),
92 0, 0, self, 'vcs_export')
93 self.vcsExportAct.setStatusTip(QCoreApplication.translate(
94 "VcsProjectHelper",
95 'Export a project from the repository'
96 ))
97 self.vcsExportAct.setWhatsThis(QCoreApplication.translate(
98 "VcsProjectHelper",
99 """<b>Export from repository</b>"""
100 """<p>This exports a project from the repository.</p>"""
101 ))
102 self.vcsExportAct.triggered.connect(self._vcsExport)
103 self.actions.append(self.vcsExportAct)
104
105 self.vcsAddAct = E5Action(
106 QCoreApplication.translate(
107 "VcsProjectHelper", 'Add to repository'),
108 UI.PixmapCache.getIcon("vcsCommit"),
109 QCoreApplication.translate(
110 "VcsProjectHelper", '&Add to repository...'),
111 0, 0, self, 'vcs_add')
112 self.vcsAddAct.setStatusTip(QCoreApplication.translate(
113 "VcsProjectHelper",
114 'Add the local project to the VCS repository'
115 ))
116 self.vcsAddAct.setWhatsThis(QCoreApplication.translate(
117 "VcsProjectHelper",
118 """<b>Add to repository</b>"""
119 """<p>This adds (imports) the local project to the VCS"""
120 """ repository.</p>"""
121 ))
122 self.vcsAddAct.triggered.connect(self._vcsImport)
123 self.actions.append(self.vcsAddAct)
124
125 def initMenu(self, menu):
126 """
127 Public method to generate the VCS menu.
128
129 @param menu reference to the menu to be populated (QMenu)
130 """
131 menu.clear()
132
133 menu.addAction(self.vcsNewAct)
134 menu.addAction(self.vcsExportAct)
135 menu.addSeparator()
136 menu.addAction(self.vcsAddAct)
137 menu.addSeparator()
138
139 def initToolbar(self, ui, toolbarManager):
140 """
141 Public slot to initialize the VCS toolbar.
142
143 @param ui reference to the main window (UserInterface)
144 @param toolbarManager reference to a toolbar manager object
145 (E5ToolBarManager)
146 @return the toolbar generated (QToolBar)
147 """
148 return None # __IGNORE_WARNING_M831__
149
150 def initBasicToolbar(self, ui, toolbarManager):
151 """
152 Public slot to initialize the basic VCS toolbar.
153
154 @param ui reference to the main window (UserInterface)
155 @param toolbarManager reference to a toolbar manager object
156 (E5ToolBarManager)
157 @return the toolbar generated (QToolBar)
158 """
159 tb = QToolBar(QCoreApplication.translate("VcsProjectHelper", "VCS"),
160 ui)
161 tb.setIconSize(UI.Config.ToolBarIconSize)
162 tb.setObjectName("VersionControlToolbar")
163 tb.setToolTip(QCoreApplication.translate("VcsProjectHelper", 'VCS'))
164
165 tb.addAction(self.vcsNewAct)
166 tb.addAction(self.vcsExportAct)
167 tb.addSeparator()
168 tb.addAction(self.vcsAddAct)
169
170 toolbarManager.addToolBar(tb, tb.windowTitle())
171
172 return tb
173
174 def showMenu(self):
175 """
176 Public slot called before the vcs menu is shown.
177 """
178 if self.vcsAddAct:
179 self.vcsAddAct.setEnabled(self.project and self.project.isOpen())
180
181 @pyqtSlot()
182 def _vcsCheckout(self, export=False):
183 """
184 Protected slot used to create a local project from the repository.
185
186 @param export flag indicating whether an export or a checkout
187 should be performed
188 """
189 if not self.project or not self.project.checkDirty():
190 return
191
192 vcsSystemsDict = e5App().getObject(
193 "PluginManager").getPluginDisplayStrings("version_control")
194 if not vcsSystemsDict:
195 # no version control system found
196 return
197
198 vcsSystemsDisplay = []
199 keys = sorted(vcsSystemsDict.keys())
200 for key in keys:
201 vcsSystemsDisplay.append(vcsSystemsDict[key])
202 vcsSelected, ok = QInputDialog.getItem(
203 None,
204 QCoreApplication.translate("VcsProjectHelper", "New Project"),
205 QCoreApplication.translate(
206 "VcsProjectHelper",
207 "Select version control system for the project"),
208 vcsSystemsDisplay,
209 0, False)
210 if not ok:
211 return
212
213 selectedVcsSystem = None
214 for vcsSystem, vcsSystemDisplay in list(vcsSystemsDict.items()):
215 if vcsSystemDisplay == vcsSelected:
216 selectedVcsSystem = vcsSystem
217 break
218
219 if not self.project.closeProject():
220 return
221
222 vcs = self.project.initVCS(selectedVcsSystem)
223 if vcs is not None:
224 vcsdlg = vcs.vcsNewProjectOptionsDialog()
225 if vcsdlg.exec() == QDialog.DialogCode.Accepted:
226 projectdir, vcsDataDict = vcsdlg.getData()
227 # edit VCS command options
228 if vcs.vcsSupportCommandOptions():
229 vcores = E5MessageBox.yesNo(
230 self.parent(),
231 QCoreApplication.translate(
232 "VcsProjectHelper", "New Project"),
233 QCoreApplication.translate(
234 "VcsProjectHelper",
235 """Would you like to edit the VCS command"""
236 """ options?"""))
237 else:
238 vcores = False
239 if vcores:
240 from .CommandOptionsDialog import VcsCommandOptionsDialog
241 codlg = VcsCommandOptionsDialog(vcs)
242 if codlg.exec() == QDialog.DialogCode.Accepted:
243 vcs.vcsSetOptions(codlg.getOptions())
244
245 # create the project directory if it doesn't exist already
246 if not os.path.isdir(projectdir):
247 try:
248 os.makedirs(projectdir)
249 except OSError:
250 E5MessageBox.critical(
251 self.parent(),
252 QCoreApplication.translate(
253 "VcsProjectHelper",
254 "Create project directory"),
255 QCoreApplication.translate(
256 "VcsProjectHelper",
257 "<p>The project directory <b>{0}</b> could not"
258 " be created.</p>").format(projectdir))
259 self.project.resetVCS()
260 return
261
262 # create the project from the VCS
263 vcs.vcsSetDataFromDict(vcsDataDict)
264 if export:
265 ok = vcs.vcsExport(vcsDataDict, projectdir)
266 else:
267 ok = vcs.vcsCheckout(vcsDataDict, projectdir, False)
268 if ok:
269 projectdir = os.path.normpath(projectdir)
270 # look for JSON style project file first
271 filters = ["*.epj"]
272 d = QDir(projectdir)
273 plist = d.entryInfoList(filters)
274 if not plist:
275 # look for XML style project file second
276 filters = ["*.e4p"]
277 d = QDir(projectdir)
278 plist = d.entryInfoList(filters)
279 if plist:
280 if len(plist) == 1:
281 self.project.openProject(
282 plist[0].absoluteFilePath())
283 else:
284 pfilenamelist = d.entryList(filters)
285 pfilename, ok = QInputDialog.getItem(
286 None,
287 QCoreApplication.translate(
288 "VcsProjectHelper",
289 "New project from repository"),
290 QCoreApplication.translate(
291 "VcsProjectHelper",
292 "Select a project file to open."),
293 pfilenamelist, 0, False)
294 if ok:
295 self.project.openProject(
296 QFileInfo(d, pfilename).absoluteFilePath())
297 if export:
298 self.project.pdata["VCS"] = 'None'
299 self.project.vcs = self.project.initVCS()
300 self.project.setDirty(True)
301 self.project.saveProject()
302 else:
303 res = E5MessageBox.yesNo(
304 self.parent(),
305 QCoreApplication.translate(
306 "VcsProjectHelper",
307 "New project from repository"),
308 QCoreApplication.translate(
309 "VcsProjectHelper",
310 "The project retrieved from the repository"
311 " does not contain an eric project file"
312 " (*.epj). Create it?"),
313 yesDefault=True)
314 if res:
315 self.project.ppath = projectdir
316 self.project.opened = True
317
318 from Project.PropertiesDialog import (
319 PropertiesDialog
320 )
321 dlg = PropertiesDialog(self.project, False)
322 if dlg.exec() == QDialog.DialogCode.Accepted:
323 dlg.storeData()
324 self.project.initFileTypes()
325 self.project.pdata["VCS"] = selectedVcsSystem
326 self.project.setDirty(True)
327 if self.project.pdata["MAINSCRIPT"]:
328 ms = os.path.join(
329 self.project.ppath,
330 self.project.pdata["MAINSCRIPT"])
331 if os.path.exists(ms):
332 self.project.appendFile(ms)
333 else:
334 ms = ""
335 self.project.newProjectAddFiles(ms)
336 self.project.createProjectManagementDir()
337 self.project.saveProject()
338 self.project.openProject(self.project.pfile)
339 if not export:
340 res = E5MessageBox.yesNo(
341 self.parent(),
342 QCoreApplication.translate(
343 "VcsProjectHelper",
344 "New project from repository"),
345 QCoreApplication.translate(
346 "VcsProjectHelper",
347 "Shall the project file be added"
348 " to the repository?"),
349 yesDefault=True)
350 if res:
351 self.project.vcs.vcsAdd(
352 self.project.pfile)
353 else:
354 E5MessageBox.critical(
355 self.parent(),
356 QCoreApplication.translate(
357 "VcsProjectHelper", "New project from repository"),
358 QCoreApplication.translate(
359 "VcsProjectHelper",
360 """The project could not be retrieved from"""
361 """ the repository."""))
362 self.project.resetVCS()
363
364 def _vcsExport(self):
365 """
366 Protected slot used to export a project from the repository.
367 """
368 self._vcsCheckout(True)
369
370 def _vcsImport(self):
371 """
372 Protected slot used to import the local project into the repository.
373
374 <b>NOTE</b>:
375 This does not necessarily make the local project a vcs controlled
376 project. You may have to checkout the project from the repository
377 in order to accomplish that.
378 """
379 def revertChanges():
380 """
381 Local function to revert the changes made to the project object.
382 """
383 self.project.pdata["VCS"] = pdata_vcs
384 self.project.pdata["VCSOPTIONS"] = copy.deepcopy(pdata_vcsoptions)
385 self.project.pdata["VCSOTHERDATA"] = copy.deepcopy(pdata_vcsother)
386 self.project.vcs = vcs
387 self.project.vcsProjectHelper = vcsHelper
388 self.project.vcsBasicHelper = vcs is None
389 self.initMenu(self.project.vcsMenu)
390 self.project.setDirty(True)
391 self.project.saveProject()
392
393 pdata_vcs = self.project.pdata["VCS"]
394 pdata_vcsoptions = copy.deepcopy(self.project.pdata["VCSOPTIONS"])
395 pdata_vcsother = copy.deepcopy(self.project.pdata["VCSOTHERDATA"])
396 vcs = self.project.vcs
397 vcsHelper = self.project.vcsProjectHelper
398 vcsSystemsDict = e5App().getObject(
399 "PluginManager").getPluginDisplayStrings("version_control")
400 if not vcsSystemsDict:
401 # no version control system found
402 return
403
404 vcsSystemsDisplay = []
405 keys = sorted(list(vcsSystemsDict.keys()))
406 for key in keys:
407 vcsSystemsDisplay.append(vcsSystemsDict[key])
408 vcsSelected, ok = QInputDialog.getItem(
409 None,
410 QCoreApplication.translate("VcsProjectHelper", "Import Project"),
411 QCoreApplication.translate(
412 "VcsProjectHelper",
413 "Select version control system for the project"),
414 vcsSystemsDisplay,
415 0, False)
416 if not ok:
417 return
418
419 selectedVcsSystem = None
420 for vcsSystem, vcsSystemDisplay in list(vcsSystemsDict.items()):
421 if vcsSystemDisplay == vcsSelected:
422 selectedVcsSystem = vcsSystem
423 break
424
425 if selectedVcsSystem is not None:
426 self.project.pdata["VCS"] = selectedVcsSystem
427 self.project.vcs = self.project.initVCS(selectedVcsSystem)
428 if self.project.vcs is not None:
429 vcsdlg = self.project.vcs.vcsOptionsDialog(
430 self.project, self.project.name, 1)
431 if vcsdlg.exec() == QDialog.DialogCode.Accepted:
432 vcsDataDict = vcsdlg.getData()
433 # edit VCS command options
434 if self.project.vcs.vcsSupportCommandOptions():
435 vcores = E5MessageBox.yesNo(
436 self.parent(),
437 QCoreApplication.translate(
438 "VcsProjectHelper", "Import Project"),
439 QCoreApplication.translate(
440 "VcsProjectHelper",
441 """Would you like to edit the VCS command"""
442 """ options?"""))
443 else:
444 vcores = False
445 if vcores:
446 from .CommandOptionsDialog import (
447 VcsCommandOptionsDialog
448 )
449 codlg = VcsCommandOptionsDialog(self.project.vcs)
450 if codlg.exec() == QDialog.DialogCode.Accepted:
451 self.project.vcs.vcsSetOptions(codlg.getOptions())
452 self.project.setDirty(True)
453 self.project.vcs.vcsSetDataFromDict(vcsDataDict)
454 self.project.saveProject()
455 isVcsControlled = self.project.vcs.vcsImport(
456 vcsDataDict, self.project.ppath)[0]
457 if isVcsControlled:
458 # reopen the project
459 self.project.openProject(self.project.pfile)
460 else:
461 # revert the changes to the local project
462 # because the project dir is not a VCS directory
463 revertChanges()
464 else:
465 # revert the changes because user cancelled
466 revertChanges()
467
468 def _vcsUpdate(self):
469 """
470 Protected slot used to update the local project from the repository.
471 """
472 if self.vcs is None:
473 # just in case
474 return
475
476 shouldReopen = self.vcs.vcsUpdate(self.project.ppath)
477 if shouldReopen:
478 res = E5MessageBox.yesNo(
479 self.parent(),
480 QCoreApplication.translate("VcsProjectHelper", "Update"),
481 QCoreApplication.translate(
482 "VcsProjectHelper",
483 """The project should be reread. Do this now?"""),
484 yesDefault=True)
485 if res:
486 self.project.reopenProject()
487
488 def _vcsCommit(self):
489 """
490 Protected slot used to commit changes to the local project to the
491 repository.
492 """
493 if self.vcs is None:
494 # just in case
495 return
496
497 if Preferences.getVCS("AutoSaveProject"):
498 self.project.saveProject()
499 if Preferences.getVCS("AutoSaveFiles"):
500 self.project.saveAllScripts()
501 self.vcs.vcsCommit(self.project.ppath, '')
502
503 def _vcsRemove(self):
504 """
505 Protected slot used to remove the local project from the repository.
506
507 Depending on the parameters set in the vcs object the project
508 may be removed from the local disk as well.
509 """
510 if self.vcs is None:
511 # just in case
512 return
513
514 res = E5MessageBox.yesNo(
515 self.parent(),
516 QCoreApplication.translate(
517 "VcsProjectHelper",
518 "Remove project from repository"),
519 QCoreApplication.translate(
520 "VcsProjectHelper",
521 "Dou you really want to remove this project from"
522 " the repository (and disk)?"))
523 if res:
524 self.vcs.vcsRemove(self.project.ppath, True)
525 self._vcsCommit()
526 if not os.path.exists(self.project.pfile):
527 ppath = self.project.ppath
528 self.setDirty(False)
529 self.project.closeProject()
530 shutil.rmtree(ppath, True)
531
532 def _vcsCommandOptions(self):
533 """
534 Protected slot to edit the VCS command options.
535 """
536 if self.vcs is None:
537 # just in case
538 return
539
540 if self.vcs.vcsSupportCommandOptions():
541 from .CommandOptionsDialog import VcsCommandOptionsDialog
542 codlg = VcsCommandOptionsDialog(self.vcs)
543 if codlg.exec() == QDialog.DialogCode.Accepted:
544 self.vcs.vcsSetOptions(codlg.getOptions())
545 self.project.setDirty(True)
546
547 def _vcsLog(self):
548 """
549 Protected slot used to show the log of the local project.
550 """
551 # kept for backward compatibility for plug-ins
552 self._vcsLogBrowser()
553
554 def _vcsLogBrowser(self):
555 """
556 Protected slot used to show the log of the local project with a
557 log browser dialog.
558 """
559 if self.vcs is None:
560 # just in case
561 return
562
563 self.vcs.vcsLogBrowser(self.project.ppath)
564
565 def _vcsDiff(self):
566 """
567 Protected slot used to show the difference of the local project to
568 the repository.
569 """
570 if self.vcs is None:
571 # just in case
572 return
573
574 self.vcs.vcsDiff(self.project.ppath)
575
576 def _vcsStatus(self):
577 """
578 Protected slot used to show the status of the local project.
579 """
580 if self.vcs is None:
581 # just in case
582 return
583
584 self.vcs.vcsStatus(self.project.ppath)
585
586 def _vcsTag(self):
587 """
588 Protected slot used to tag the local project in the repository.
589 """
590 if self.vcs is None:
591 # just in case
592 return
593
594 self.vcs.vcsTag(self.project.ppath)
595
596 def _vcsRevert(self):
597 """
598 Protected slot used to revert changes made to the local project.
599 """
600 if self.vcs is None:
601 # just in case
602 return
603
604 self.vcs.vcsRevert(self.project.ppath)
605
606 def _vcsSwitch(self):
607 """
608 Protected slot used to switch the local project to another tag/branch.
609 """
610 if self.vcs is None:
611 # just in case
612 return
613
614 shouldReopen = self.vcs.vcsSwitch(self.project.ppath)
615 if shouldReopen:
616 res = E5MessageBox.yesNo(
617 self.parent(),
618 QCoreApplication.translate("VcsProjectHelper", "Switch"),
619 QCoreApplication.translate(
620 "VcsProjectHelper",
621 """The project should be reread. Do this now?"""),
622 yesDefault=True)
623 if res:
624 self.project.reopenProject()
625
626 def _vcsMerge(self):
627 """
628 Protected slot used to merge changes of a tag/revision into the local
629 project.
630 """
631 if self.vcs is None:
632 # just in case
633 return
634
635 self.vcs.vcsMerge(self.project.ppath)
636
637 def _vcsCleanup(self):
638 """
639 Protected slot used to cleanup the local project.
640 """
641 if self.vcs is None:
642 # just in case
643 return
644
645 self.vcs.vcsCleanup(self.project.ppath)
646
647 def _vcsCommand(self):
648 """
649 Protected slot used to execute an arbitrary vcs command.
650 """
651 if self.vcs is None:
652 # just in case
653 return
654
655 self.vcs.vcsCommandLine(self.project.ppath)
656
657 def _vcsInfoDisplay(self):
658 """
659 Protected slot called to show some vcs information.
660 """
661 if self.vcs is None:
662 # just in case
663 return
664
665 from .RepositoryInfoDialog import VcsRepositoryInfoDialog
666 info = self.vcs.vcsRepositoryInfos(self.project.ppath)
667 dlg = VcsRepositoryInfoDialog(None, info)
668 dlg.exec()

eric ide

mercurial