eric7/VCS/VersionControl.py

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

eric ide

mercurial