eric6/VCS/ProjectHelper.py

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

eric ide

mercurial