VCS/VersionControl.py

changeset 0
de9c2efb9d02
child 13
1af94a91f439
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an abstract base class to be subclassed by all specific
8 VCS interfaces.
9 """
10
11 import os
12
13 from PyQt4.QtCore import QObject, QThread, QMutex, QProcess, \
14 SIGNAL, Qt
15 from PyQt4.QtGui import QApplication, QMessageBox
16
17 import Preferences
18
19 class VersionControl(QObject):
20 """
21 Class implementing an abstract base class to be subclassed by all specific
22 VCS interfaces.
23
24 It defines the vcs interface to be implemented by subclasses
25 and the common methods.
26
27 @signal vcsStatusMonitorData(QStringList) emitted to update the VCS status
28 @signal vcsStatusMonitorStatus(QString, QString) emitted to signal the status of the
29 monitoring thread (ok, nok, op, off) and a status message
30 """
31
32 canBeCommitted = 1 # Indicates that a file/directory is in the vcs.
33 canBeAdded = 2 # Indicates that a file/directory is not in vcs.
34
35 def __init__(self, parent=None, name=None):
36 """
37 Constructor
38
39 @param parent parent widget (QWidget)
40 @param name name of this object (string)
41 """
42 QObject.__init__(self, parent)
43 if name:
44 self.setObjectName(name)
45 self.defaultOptions = {
46 'global' : [''],
47 'commit' : [''],
48 'checkout' : [''],
49 'update' : [''],
50 'add' : [''],
51 'remove' : [''],
52 'diff' : [''],
53 'log' : [''],
54 'history' : [''],
55 'status' : [''],
56 'tag' : [''],
57 'export' : ['']
58 }
59 self.interestingDataKeys = []
60 self.options = {}
61 self.otherData = {}
62 self.canDetectBinaries = True
63 self.autoCommit = False
64
65 self.statusMonitorThread = None
66 self.vcsExecutionMutex = QMutex()
67
68 def vcsShutdown(self):
69 """
70 Public method used to shutdown the vcs interface.
71 """
72 raise RuntimeError('Not implemented')
73
74 def vcsExists(self):
75 """
76 Public method used to test for the presence of the vcs.
77
78 It must return a bool to indicate the existance and a QString giving
79 an error message in case of failure.
80
81 @exception RuntimeError not implemented
82 """
83 raise RuntimeError('Not implemented')
84
85 def vcsInit(self, vcsDir, noDialog = False):
86 """
87 Public method used to initialize the vcs.
88
89 It must return a boolean to indicate an execution without errors.
90
91 @param vcsDir name of the VCS directory (string)
92 @param noDialog flag indicating quiet operations (boolean)
93 @exception RuntimeError not implemented
94 """
95 raise RuntimeError('Not implemented')
96
97 def vcsConvertProject(self, vcsDataDict, project):
98 """
99 Public method to convert an uncontrolled project to a version controlled project.
100
101 @param vcsDataDict dictionary of data required for the conversion
102 @param project reference to the project object
103 @exception RuntimeError not implemented
104 """
105 raise RuntimeError('Not implemented')
106
107 def vcsImport(self, vcsDataDict, projectDir, noDialog = False):
108 """
109 Public method used to import the project into the vcs.
110
111 @param vcsDataDict dictionary of data required for the import
112 @param projectDir project directory (string)
113 @param noDialog flag indicating quiet operations
114 @return flag indicating an execution without errors (boolean)
115 and a flag indicating the version controll status (boolean)
116 @exception RuntimeError not implemented
117 """
118 raise RuntimeError('Not implemented')
119
120 def vcsCheckout(self, vcsDataDict, projectDir, noDialog = False):
121 """
122 Public method used to check the project out of the vcs.
123
124 @param vcsDataDict dictionary of data required for the checkout
125 @param projectDir project directory to create (string)
126 @param noDialog flag indicating quiet operations
127 @return flag indicating an execution without errors (boolean)
128 @exception RuntimeError not implemented
129 """
130 raise RuntimeError('Not implemented')
131
132 def vcsExport(self, vcsDataDict, projectDir):
133 """
134 Public method used to export a directory from the vcs.
135
136 It must return a boolean to indicate an execution without errors.
137
138 @param vcsDataDict dictionary of data required for the export
139 @param projectDir project directory to create (string)
140 @return flag indicating an execution without errors (boolean)
141 @exception RuntimeError not implemented
142 """
143 raise RuntimeError('Not implemented')
144
145 def vcsCommit(self, name, message, noDialog = False):
146 """
147 Public method used to make the change of a file/directory permanent in the vcs.
148
149 It must return a boolean to indicate an execution without errors.
150
151 @param name file/directory name to be committed (string)
152 @param message message for this operation (string)
153 @param noDialog flag indicating quiet operations
154 @exception RuntimeError not implemented
155 """
156 raise RuntimeError('Not implemented')
157
158 def vcsUpdate(self, name, noDialog = False):
159 """
160 Public method used to update a file/directory in the vcs.
161
162 It must not return anything.
163
164 @param name file/directory name to be updated (string)
165 @param noDialog flag indicating quiet operations (boolean)
166 @return flag indicating, that the update contained an add
167 or delete (boolean)
168 @exception RuntimeError not implemented
169 """
170 raise RuntimeError('Not implemented')
171
172 def vcsAdd(self, name, isDir = False, noDialog = False):
173 """
174 Public method used to add a file/directory in the vcs.
175
176 It must not return anything.
177
178 @param name file/directory name to be added (string)
179 @param isDir flag indicating name is a directory (boolean)
180 @param noDialog flag indicating quiet operations (boolean)
181 @exception RuntimeError not implemented
182 """
183 raise RuntimeError('Not implemented')
184
185 def vcsAddBinary(self, name, isDir = False):
186 """
187 Public method used to add a file/directory in binary mode in the vcs.
188
189 It must not return anything.
190
191 @param name file/directory name to be added (string)
192 @param isDir flag indicating name is a directory (boolean)
193 @exception RuntimeError not implemented
194 """
195 raise RuntimeError('Not implemented')
196
197 def vcsAddTree(self, path):
198 """
199 Public method to add a directory tree rooted at path in the vcs.
200
201 It must not return anything.
202
203 @param path root directory of the tree to be added (string)
204 @exception RuntimeError not implemented
205 """
206 raise RuntimeError('Not implemented')
207
208 def vcsRemove(self, name, project = False, noDialog = False):
209 """
210 Public method used to add a file/directory in the vcs.
211
212 It must return a flag indicating successfull operation
213
214 @param name file/directory name to be removed (string)
215 @param project flag indicating deletion of a project tree (boolean)
216 @param noDialog flag indicating quiet operations
217 @exception RuntimeError not implemented
218 """
219 raise RuntimeError('Not implemented')
220
221 def vcsMove(self, name, project, target = None, noDialog = False):
222 """
223 Public method used to move a file/directory.
224
225 @param name file/directory name to be moved (string)
226 @param project reference to the project object
227 @param target new name of the file/directory (string)
228 @param noDialog flag indicating quiet operations
229 @return flag indicating successfull operation (boolean)
230 @exception RuntimeError not implemented
231 """
232 raise RuntimeError('Not implemented')
233
234 def vcsLog(self, name):
235 """
236 Public method used to view the log of a file/directory in the vcs.
237
238 It must not return anything.
239
240 @param name file/directory name to show the log for (string)
241 @exception RuntimeError not implemented
242 """
243 raise RuntimeError('Not implemented')
244
245 def vcsDiff(self, name):
246 """
247 Public method used to view the diff of a file/directory in the vcs.
248
249 It must not return anything.
250
251 @param name file/directory name to be diffed (string)
252 @exception RuntimeError not implemented
253 """
254 raise RuntimeError('Not implemented')
255
256 def vcsHistory(self, name):
257 """
258 Public method used to view the history of a file/directory in the vcs.
259
260 It must not return anything.
261
262 @param name file/directory name to show the history for (string)
263 @exception RuntimeError not implemented
264 """
265 raise RuntimeError('Not implemented')
266
267 def vcsStatus(self, name):
268 """
269 Public method used to view the status of a file/directory in the vcs.
270
271 It must not return anything.
272
273 @param name file/directory name to show the status for (string)
274 @exception RuntimeError not implemented
275 """
276 raise RuntimeError('Not implemented')
277
278 def vcsTag(self, name):
279 """
280 Public method used to set the tag of a file/directory in the vcs.
281
282 It must not return anything.
283
284 @param name file/directory name to be tagged (string)
285 @exception RuntimeError not implemented
286 """
287 raise RuntimeError('Not implemented')
288
289 def vcsRevert(self, name):
290 """
291 Public method used to revert changes made to a file/directory.
292
293 It must not return anything.
294
295 @param name file/directory name to be reverted (string)
296 @exception RuntimeError not implemented
297 """
298 raise RuntimeError('Not implemented')
299
300 def vcsSwitch(self, name):
301 """
302 Public method used to switch a directory to a different tag/branch.
303
304 It must not return anything.
305
306 @param name directory name to be switched (string)
307 @exception RuntimeError not implemented
308 """
309 raise RuntimeError('Not implemented')
310
311 def vcsMerge(self, name):
312 """
313 Public method used to merge a tag/branch into the local project.
314
315 It must not return anything.
316
317 @param name file/directory name to be merged (string)
318 @exception RuntimeError not implemented
319 """
320 raise RuntimeError('Not implemented')
321
322 def vcsRegisteredState(self, name):
323 """
324 Public method used to get the registered state of a file in the vcs.
325
326 @param name filename to check (string)
327 @return a combination of canBeCommited and canBeAdded or
328 0 in order to signal an error
329 @exception RuntimeError not implemented
330 """
331 raise RuntimeError('Not implemented')
332
333 def vcsAllRegisteredStates(self, names, dname):
334 """
335 Public method used to get the registered states of a number of files in the vcs.
336
337 @param names dictionary with all filenames to be checked as keys
338 @param dname directory to check in (string)
339 @return the received dictionary completed with a combination of
340 canBeCommited and canBeAdded or None in order to signal an error
341 @exception RuntimeError not implemented
342 """
343 raise RuntimeError('Not implemented')
344
345 def vcsName(self):
346 """
347 Public method returning the name of the vcs.
348
349 @return name of the vcs (string)
350 @exception RuntimeError not implemented
351 """
352 raise RuntimeError('Not implemented')
353
354 def vcsCleanup(self, name):
355 """
356 Public method used to cleanup the local copy.
357
358 @param name directory name to be cleaned up (string)
359 @exception RuntimeError not implemented
360 """
361 raise RuntimeError('Not implemented')
362
363 def vcsCommandLine(self, name):
364 """
365 Public method used to execute arbitrary vcs commands.
366
367 @param name directory name of the working directory (string)
368 @exception RuntimeError not implemented
369 """
370 raise RuntimeError('Not implemented')
371
372 def vcsOptionsDialog(self, project, archive, editable = False, parent = None):
373 """
374 Public method to get a dialog to enter repository info.
375
376 @param project reference to the project object
377 @param archive name of the project in the repository (string)
378 @param editable flag indicating that the project name is editable (boolean)
379 @param parent parent widget (QWidget)
380 """
381 raise RuntimeError('Not implemented')
382
383 def vcsNewProjectOptionsDialog(self, parent = None):
384 """
385 Public method to get a dialog to enter repository info for getting a new project.
386
387 @param parent parent widget (QWidget)
388 """
389 raise RuntimeError('Not implemented')
390
391 def vcsRepositoryInfos(self, ppath):
392 """
393 Public method to retrieve information about the repository.
394
395 @param ppath local path to get the repository infos (string)
396 @return string with ready formated info for display (string)
397 """
398 raise RuntimeError('Not implemented')
399
400 def vcsGetProjectBrowserHelper(self, browser, project, isTranslationsBrowser = False):
401 """
402 Public method to instanciate a helper object for the different project browsers.
403
404 @param browser reference to the project browser object
405 @param project reference to the project object
406 @param isTranslationsBrowser flag indicating, the helper is requested for the
407 translations browser (this needs some special treatment)
408 @return the project browser helper object
409 """
410 raise RuntimeError('Not implemented')
411
412 def vcsGetProjectHelper(self, project):
413 """
414 Public method to instanciate a helper object for the project.
415
416 @param project reference to the project object
417 @return the project helper object
418 """
419 raise RuntimeError('Not implemented')
420
421 #####################################################################
422 ## methods above need to be implemented by a subclass
423 #####################################################################
424
425 def clearStatusCache(self):
426 """
427 Public method to clear the status cache.
428 """
429 pass
430
431 def vcsDefaultOptions(self):
432 """
433 Public method used to retrieve the default options for the vcs.
434
435 @return a dictionary with the vcs operations as key and
436 the respective options as values. The key 'global' must contain
437 the global options. The other keys must be 'commit', 'update',
438 'add', 'remove', 'diff', 'log', 'history', 'tag', 'status' and 'export'.
439 """
440 return self.defaultOptions
441
442 def vcsSetOptions(self, options):
443 """
444 Public method used to set the options for the vcs.
445
446 @param options a dictionary of option strings with keys as
447 defined by the default options
448 """
449 for key in options:
450 try:
451 self.options[key] = options[key]
452 except KeyError:
453 pass
454
455 def vcsGetOptions(self):
456 """
457 Public method used to retrieve the options of the vcs.
458
459 @return a dictionary of option strings that can be passed to
460 vcsSetOptions.
461 """
462 return self.options
463
464 def vcsSetOtherData(self, data):
465 """
466 Public method used to set vcs specific data.
467
468 @param data a dictionary of vcs specific data
469 """
470 for key in data:
471 try:
472 self.otherData[key] = data[key]
473 except KeyError:
474 pass
475
476 def vcsGetOtherData(self):
477 """
478 Public method used to retrieve vcs specific data.
479
480 @return a dictionary of vcs specific data
481 """
482 return self.otherData
483
484 def vcsSetData(self, key, value):
485 """
486 Public method used to set an entry in the otherData dictionary.
487
488 @param key the key of the data (string)
489 @param value the value of the data
490 """
491 if key in self.interestingDataKeys:
492 self.otherData[key] = value
493
494 def vcsSetDataFromDict(self, dict):
495 """
496 Public method used to set entries in the otherData dictionary.
497
498 @param dict dictionary to pick entries from
499 """
500 for key in self.interestingDataKeys:
501 if key in dict:
502 self.otherData[key] = dict[key]
503
504 #####################################################################
505 ## below are some utility methods
506 #####################################################################
507
508 def startSynchronizedProcess(self, proc, program, arguments, workingDir = None):
509 """
510 Public method to start a synchroneous process
511
512 This method starts a process and waits
513 for its end while still serving the Qt event loop.
514
515 @param proc process to start (QProcess)
516 @param program path of the executable to start (string)
517 @param arguments list of arguments for the process (list of strings)
518 @param workingDir working directory for the process (string)
519 @return flag indicating normal exit (boolean)
520 """
521 if proc is None:
522 return
523
524 if workingDir:
525 proc.setWorkingDirectory(workingDir)
526 proc.start(program, arguments)
527 procStarted = proc.waitForStarted()
528 if not procStarted:
529 QMessageBox.critical(None,
530 self.trUtf8('Process Generation Error'),
531 self.trUtf8(
532 'The process {0} could not be started. '
533 'Ensure, that it is in the search path.'
534 ).format(program))
535 return False
536 else:
537 while proc.state() == QProcess.Running:
538 QApplication.processEvents()
539 QThread.msleep(300)
540 QApplication.processEvents()
541 return (proc.exitStatus() == QProcess.NormalExit) and (proc.exitCode() == 0)
542
543 def splitPath(self, name):
544 """
545 Public method splitting name into a directory part and a file part.
546
547 @param name path name (string)
548 @return a tuple of 2 strings (dirname, filename).
549 """
550 if os.path.isdir(name):
551 dn = os.path.abspath(name)
552 fn = "."
553 else:
554 dn, fn = os.path.split(name)
555 return (dn, fn)
556
557 def splitPathList(self, names):
558 """
559 Public method splitting the list of names into a common directory part and
560 a file list.
561
562 @param names list of paths (list of strings)
563 @return a tuple of string and list of strings (dirname, filenamelist)
564 """
565 dname = os.path.commonprefix(names)
566 if dname:
567 if not dname.endswith(os.sep):
568 dname = os.path.dirname(dname) + os.sep
569 fnames = [n.replace(dname, '') for n in names]
570 dname = os.path.dirname(dname)
571 return (dname, fnames)
572 else:
573 return ("/", names)
574
575 def addArguments(self, args, argslist):
576 """
577 Protected method to add an argument list to the already present arguments.
578
579 @param args current arguments list (list of strings)
580 @param argslist list of arguments (list of strings)
581 """
582 for arg in argslist:
583 if arg != '':
584 args.append(arg)
585
586 ############################################################################
587 ## VCS status monitor thread related methods
588 ############################################################################
589
590 def __statusMonitorStatus(self, status, statusMsg):
591 """
592 Private method to receive the status monitor status.
593
594 It simply reemits the received status.
595
596 @param status status of the monitoring thread (QString, ok, nok or off)
597 @param statusMsg explanotory text for the signaled status (string)
598 """
599 self.emit(SIGNAL("vcsStatusMonitorStatus(QString, QString)"), status, statusMsg)
600 QApplication.flush()
601
602 def __statusMonitorData(self, statusList):
603 """
604 Private method to receive the status monitor status.
605
606 It simply reemits the received status list.
607
608 @param statusList list of status records (list of strings)
609 """
610 self.emit(SIGNAL("vcsStatusMonitorData(QStringList)"), statusList)
611 QApplication.flush()
612
613 def startStatusMonitor(self, project):
614 """
615 Public method to start the VCS status monitor thread.
616
617 @param project reference to the project object
618 @return reference to the monitor thread (QThread)
619 """
620 if project.pudata["VCSSTATUSMONITORINTERVAL"]:
621 vcsStatusMonitorInterval = project.pudata["VCSSTATUSMONITORINTERVAL"][0]
622 else:
623 vcsStatusMonitorInterval = Preferences.getVCS("StatusMonitorInterval")
624 if vcsStatusMonitorInterval > 0:
625 self.statusMonitorThread = \
626 self._createStatusMonitorThread(vcsStatusMonitorInterval, project)
627 if self.statusMonitorThread is not None:
628 self.connect(self.statusMonitorThread,
629 SIGNAL("vcsStatusMonitorData(QStringList)"),
630 self.__statusMonitorData, Qt.QueuedConnection)
631 self.connect(self.statusMonitorThread,
632 SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
633 self.__statusMonitorStatus, Qt.QueuedConnection)
634 self.statusMonitorThread.setAutoUpdate(
635 Preferences.getVCS("AutoUpdate"))
636 self.statusMonitorThread.start()
637 else:
638 self.statusMonitorThread = None
639 return self.statusMonitorThread
640
641 def stopStatusMonitor(self):
642 """
643 Public method to stop the VCS status monitor thread.
644 """
645 if self.statusMonitorThread is not None:
646 self.__statusMonitorData(["--RESET--"])
647 self.disconnect(self.statusMonitorThread,
648 SIGNAL("vcsStatusMonitorData(QStringList)"),
649 self.__statusMonitorData)
650 self.disconnect(self.statusMonitorThread,
651 SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
652 self.__statusMonitorStatus)
653 self.statusMonitorThread.stop()
654 self.statusMonitorThread.wait(10000)
655 if not self.statusMonitorThread.isFinished():
656 self.statusMonitorThread.terminate()
657 self.statusMonitorThread.wait(10000)
658 self.statusMonitorThread = None
659 self.__statusMonitorStatus("off",
660 self.trUtf8("Repository status checking is switched off"))
661
662 def setStatusMonitorInterval(self, interval, project):
663 """
664 Public method to change the monitor interval.
665
666 @param interval new interval in seconds (integer)
667 @param project reference to the project object
668 """
669 if self.statusMonitorThread is not None:
670 if interval == 0:
671 self.stopStatusMonitor()
672 else:
673 self.statusMonitorThread.setInterval(interval)
674 else:
675 self.startStatusMonitor(project)
676
677 def getStatusMonitorInterval(self):
678 """
679 Public method to get the monitor interval.
680
681 @return interval in seconds (integer)
682 """
683 if self.statusMonitorThread is not None:
684 return self.statusMonitorThread.getInterval()
685 else:
686 return 0
687
688 def setStatusMonitorAutoUpdate(self, auto):
689 """
690 Public method to enable the auto update function.
691
692 @param auto status of the auto update function (boolean)
693 """
694 if self.statusMonitorThread is not None:
695 self.statusMonitorThread.setAutoUpdate(auto)
696
697 def getStatusMonitorAutoUpdate(self):
698 """
699 Public method to retrieve the status of the auto update function.
700
701 @return status of the auto update function (boolean)
702 """
703 if self.statusMonitorThread is not None:
704 return self.statusMonitorThread.getAutoUpdate()
705 else:
706 return False
707
708 def checkVCSStatus(self):
709 """
710 Public method to wake up the VCS status monitor thread.
711 """
712 if self.statusMonitorThread is not None:
713 self.statusMonitorThread.checkStatus()
714
715 def clearStatusMonitorCachedState(self, name):
716 """
717 Public method to clear the cached VCS state of a file/directory.
718
719 @param name name of the entry to be cleared (string)
720 """
721 if self.statusMonitorThread is not None:
722 self.statusMonitorThread.clearCachedState(name)
723
724 def _createStatusMonitorThread(self, interval, project):
725 """
726 Protected method to create an instance of the VCS status monitor thread.
727
728 Note: This method should be overwritten in subclasses in order to support
729 VCS status monitoring.
730
731 @param interval check interval for the monitor thread in seconds (integer)
732 @param project reference to the project object
733 @return reference to the monitor thread (QThread)
734 """
735 return None

eric ide

mercurial