34 |
34 |
35 |
35 |
36 class Subversion(VersionControl): |
36 class Subversion(VersionControl): |
37 """ |
37 """ |
38 Class implementing the version control systems interface to Subversion. |
38 Class implementing the version control systems interface to Subversion. |
39 |
39 |
40 @signal committed() emitted after the commit action has completed |
40 @signal committed() emitted after the commit action has completed |
41 """ |
41 """ |
|
42 |
42 committed = pyqtSignal() |
43 committed = pyqtSignal() |
43 |
44 |
44 def __init__(self, plugin, parent=None, name=None): |
45 def __init__(self, plugin, parent=None, name=None): |
45 """ |
46 """ |
46 Constructor |
47 Constructor |
47 |
48 |
48 @param plugin reference to the plugin object |
49 @param plugin reference to the plugin object |
49 @param parent parent widget (QWidget) |
50 @param parent parent widget (QWidget) |
50 @param name name of this object (string) |
51 @param name name of this object (string) |
51 """ |
52 """ |
52 VersionControl.__init__(self, parent, name) |
53 VersionControl.__init__(self, parent, name) |
53 self.defaultOptions = { |
54 self.defaultOptions = { |
54 'global': [''], |
55 "global": [""], |
55 'commit': [''], |
56 "commit": [""], |
56 'checkout': [''], |
57 "checkout": [""], |
57 'update': [''], |
58 "update": [""], |
58 'add': [''], |
59 "add": [""], |
59 'remove': [''], |
60 "remove": [""], |
60 'diff': [''], |
61 "diff": [""], |
61 'log': [''], |
62 "log": [""], |
62 'history': [''], |
63 "history": [""], |
63 'status': [''], |
64 "status": [""], |
64 'tag': [''], |
65 "tag": [""], |
65 'export': [''] |
66 "export": [""], |
66 } |
67 } |
67 self.interestingDataKeys = [ |
68 self.interestingDataKeys = [ |
68 "standardLayout", |
69 "standardLayout", |
69 ] |
70 ] |
70 |
71 |
71 self.__plugin = plugin |
72 self.__plugin = plugin |
72 self.__ui = parent |
73 self.__ui = parent |
73 |
74 |
74 self.options = self.defaultOptions |
75 self.options = self.defaultOptions |
75 self.otherData["standardLayout"] = True |
76 self.otherData["standardLayout"] = True |
76 self.tagsList = [] |
77 self.tagsList = [] |
77 self.branchesList = [] |
78 self.branchesList = [] |
78 self.allTagsBranchesList = [] |
79 self.allTagsBranchesList = [] |
79 self.mergeList = [[], [], []] |
80 self.mergeList = [[], [], []] |
80 self.showedTags = False |
81 self.showedTags = False |
81 self.showedBranches = False |
82 self.showedBranches = False |
82 |
83 |
83 self.tagTypeList = [ |
84 self.tagTypeList = ["tags", "branches"] |
84 'tags', |
85 |
85 'branches' |
|
86 ] |
|
87 |
|
88 self.commandHistory = [] |
86 self.commandHistory = [] |
89 self.wdHistory = [] |
87 self.wdHistory = [] |
90 |
88 |
91 if ( |
89 if pysvn.version >= (1, 4, 3, 0) and "SVN_ASP_DOT_NET_HACK" in os.environ: |
92 pysvn.version >= (1, 4, 3, 0) and |
90 self.adminDir = "_svn" |
93 "SVN_ASP_DOT_NET_HACK" in os.environ |
91 else: |
94 ): |
92 self.adminDir = ".svn" |
95 self.adminDir = '_svn' |
93 |
96 else: |
|
97 self.adminDir = '.svn' |
|
98 |
|
99 self.log = None |
94 self.log = None |
100 self.diff = None |
95 self.diff = None |
101 self.sbsDiff = None |
96 self.sbsDiff = None |
102 self.status = None |
97 self.status = None |
103 self.propList = None |
98 self.propList = None |
104 self.tagbranchList = None |
99 self.tagbranchList = None |
105 self.blame = None |
100 self.blame = None |
106 self.repoBrowser = None |
101 self.repoBrowser = None |
107 self.logBrowser = None |
102 self.logBrowser = None |
108 |
103 |
109 self.statusCache = {} |
104 self.statusCache = {} |
110 |
105 |
111 self.__commitData = {} |
106 self.__commitData = {} |
112 self.__commitDialog = None |
107 self.__commitDialog = None |
113 |
108 |
114 self.__wcng = True |
109 self.__wcng = True |
115 # assume new generation working copy metadata format |
110 # assume new generation working copy metadata format |
116 |
111 |
117 def getPlugin(self): |
112 def getPlugin(self): |
118 """ |
113 """ |
119 Public method to get a reference to the plugin object. |
114 Public method to get a reference to the plugin object. |
120 |
115 |
121 @return reference to the plugin object (VcsPySvnPlugin) |
116 @return reference to the plugin object (VcsPySvnPlugin) |
122 """ |
117 """ |
123 return self.__plugin |
118 return self.__plugin |
124 |
119 |
125 def getClient(self): |
120 def getClient(self): |
126 """ |
121 """ |
127 Public method to create and initialize the pysvn client object. |
122 Public method to create and initialize the pysvn client object. |
128 |
123 |
129 @return the pysvn client object (pysvn.Client) |
124 @return the pysvn client object (pysvn.Client) |
130 """ |
125 """ |
131 configDir = "" |
126 configDir = "" |
132 authCache = True |
127 authCache = True |
133 for arg in self.options['global']: |
128 for arg in self.options["global"]: |
134 if arg.startswith("--config-dir"): |
129 if arg.startswith("--config-dir"): |
135 configDir = arg.split("=", 1)[1] |
130 configDir = arg.split("=", 1)[1] |
136 if arg.startswith("--no-auth-cache"): |
131 if arg.startswith("--no-auth-cache"): |
137 authCache = False |
132 authCache = False |
138 |
133 |
139 client = pysvn.Client(configDir) |
134 client = pysvn.Client(configDir) |
140 client.exception_style = 1 |
135 client.exception_style = 1 |
141 client.set_auth_cache(authCache) |
136 client.set_auth_cache(authCache) |
142 |
137 |
143 return client |
138 return client |
144 |
139 |
145 ########################################################################### |
140 ########################################################################### |
146 ## Methods of the VCS interface |
141 ## Methods of the VCS interface |
147 ########################################################################### |
142 ########################################################################### |
148 |
143 |
149 def vcsShutdown(self): |
144 def vcsShutdown(self): |
150 """ |
145 """ |
151 Public method used to shutdown the Subversion interface. |
146 Public method used to shutdown the Subversion interface. |
152 """ |
147 """ |
153 if self.log is not None: |
148 if self.log is not None: |
274 @rtype tuple of (bool, bool) |
273 @rtype tuple of (bool, bool) |
275 """ |
274 """ |
276 noDialog = False |
275 noDialog = False |
277 msg = vcsDataDict["message"] |
276 msg = vcsDataDict["message"] |
278 if not msg: |
277 if not msg: |
279 msg = '***' |
278 msg = "***" |
280 |
279 |
281 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
280 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
282 if vcsDir.startswith('/'): |
281 if vcsDir.startswith("/"): |
283 vcsDir = 'file://{0}'.format(vcsDir) |
282 vcsDir = "file://{0}".format(vcsDir) |
284 elif vcsDir[1] in ['|', ':']: |
283 elif vcsDir[1] in ["|", ":"]: |
285 vcsDir = 'file:///{0}'.format(vcsDir) |
284 vcsDir = "file:///{0}".format(vcsDir) |
286 |
285 |
287 project = vcsDir[vcsDir.rfind('/') + 1:] |
286 project = vcsDir[vcsDir.rfind("/") + 1 :] |
288 |
287 |
289 # create the dir structure to be imported into the repository |
288 # create the dir structure to be imported into the repository |
290 tmpDir = '{0}_tmp'.format(projectDir) |
289 tmpDir = "{0}_tmp".format(projectDir) |
291 try: |
290 try: |
292 os.makedirs(tmpDir) |
291 os.makedirs(tmpDir) |
293 if self.otherData["standardLayout"]: |
292 if self.otherData["standardLayout"]: |
294 os.mkdir(os.path.join(tmpDir, project)) |
293 os.mkdir(os.path.join(tmpDir, project)) |
295 os.mkdir(os.path.join(tmpDir, project, 'branches')) |
294 os.mkdir(os.path.join(tmpDir, project, "branches")) |
296 os.mkdir(os.path.join(tmpDir, project, 'tags')) |
295 os.mkdir(os.path.join(tmpDir, project, "tags")) |
297 shutil.copytree( |
296 shutil.copytree(projectDir, os.path.join(tmpDir, project, "trunk")) |
298 projectDir, os.path.join(tmpDir, project, 'trunk')) |
|
299 else: |
297 else: |
300 shutil.copytree(projectDir, os.path.join(tmpDir, project)) |
298 shutil.copytree(projectDir, os.path.join(tmpDir, project)) |
301 except OSError: |
299 except OSError: |
302 if os.path.isdir(tmpDir): |
300 if os.path.isdir(tmpDir): |
303 shutil.rmtree(tmpDir, True) |
301 shutil.rmtree(tmpDir, True) |
304 return False, False |
302 return False, False |
305 |
303 |
306 cwd = os.getcwd() |
304 cwd = os.getcwd() |
307 os.chdir(os.path.join(tmpDir, project)) |
305 os.chdir(os.path.join(tmpDir, project)) |
308 opts = self.options['global'] |
306 opts = self.options["global"] |
309 recurse = "--non-recursive" not in opts |
307 recurse = "--non-recursive" not in opts |
310 url = self.__svnURL(vcsDir) |
308 url = self.__svnURL(vcsDir) |
311 client = self.getClient() |
309 client = self.getClient() |
312 if not noDialog: |
310 if not noDialog: |
313 dlg = SvnDialog( |
311 dlg = SvnDialog( |
314 self.tr('Importing project into Subversion repository'), |
312 self.tr("Importing project into Subversion repository"), |
315 "import{0} --message {1} .".format( |
313 "import{0} --message {1} .".format( |
316 (not recurse) and " --non-recursive" or "", msg), |
314 (not recurse) and " --non-recursive" or "", msg |
317 client) |
315 ), |
|
316 client, |
|
317 ) |
318 QApplication.processEvents() |
318 QApplication.processEvents() |
319 try: |
319 try: |
320 with EricMutexLocker(self.vcsExecutionMutex): |
320 with EricMutexLocker(self.vcsExecutionMutex): |
321 rev = client.import_(".", url, msg, recurse, ignore=True) |
321 rev = client.import_(".", url, msg, recurse, ignore=True) |
322 status = True |
322 status = True |
349 try: |
350 try: |
350 tag = vcsDataDict["tag"] |
351 tag = vcsDataDict["tag"] |
351 except KeyError: |
352 except KeyError: |
352 tag = None |
353 tag = None |
353 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
354 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
354 if vcsDir.startswith('/'): |
355 if vcsDir.startswith("/"): |
355 vcsDir = 'file://{0}'.format(vcsDir) |
356 vcsDir = "file://{0}".format(vcsDir) |
356 elif vcsDir[1] in ['|', ':']: |
357 elif vcsDir[1] in ["|", ":"]: |
357 vcsDir = 'file:///{0}'.format(vcsDir) |
358 vcsDir = "file:///{0}".format(vcsDir) |
358 |
359 |
359 if self.otherData["standardLayout"]: |
360 if self.otherData["standardLayout"]: |
360 if tag is None or tag == '': |
361 if tag is None or tag == "": |
361 svnUrl = '{0}/trunk'.format(vcsDir) |
362 svnUrl = "{0}/trunk".format(vcsDir) |
362 else: |
363 else: |
363 if ( |
364 if not tag.startswith("tags") and not tag.startswith("branches"): |
364 not tag.startswith('tags') and |
|
365 not tag.startswith('branches') |
|
366 ): |
|
367 type_, ok = QInputDialog.getItem( |
365 type_, ok = QInputDialog.getItem( |
368 None, |
366 None, |
369 self.tr("Subversion Checkout"), |
367 self.tr("Subversion Checkout"), |
370 self.tr( |
368 self.tr( |
371 "The tag must be a normal tag (tags) or" |
369 "The tag must be a normal tag (tags) or" |
372 " a branch tag (branches)." |
370 " a branch tag (branches)." |
373 " Please select from the list."), |
371 " Please select from the list." |
|
372 ), |
374 self.tagTypeList, |
373 self.tagTypeList, |
375 0, False) |
374 0, |
|
375 False, |
|
376 ) |
376 if not ok: |
377 if not ok: |
377 return False |
378 return False |
378 tag = '{0}/{1}'.format(type_, tag) |
379 tag = "{0}/{1}".format(type_, tag) |
379 svnUrl = '{0}/{1}'.format(vcsDir, tag) |
380 svnUrl = "{0}/{1}".format(vcsDir, tag) |
380 else: |
381 else: |
381 svnUrl = vcsDir |
382 svnUrl = vcsDir |
382 |
383 |
383 opts = self.options['global'] + self.options['checkout'] |
384 opts = self.options["global"] + self.options["checkout"] |
384 recurse = "--non-recursive" not in opts |
385 recurse = "--non-recursive" not in opts |
385 url = self.__svnURL(svnUrl) |
386 url = self.__svnURL(svnUrl) |
386 client = self.getClient() |
387 client = self.getClient() |
387 if not noDialog: |
388 if not noDialog: |
388 dlg = SvnDialog( |
389 dlg = SvnDialog( |
389 self.tr('Checking project out of Subversion repository'), |
390 self.tr("Checking project out of Subversion repository"), |
390 "checkout{0} {1} {2}".format( |
391 "checkout{0} {1} {2}".format( |
391 (not recurse) and " --non-recursive" or "", |
392 (not recurse) and " --non-recursive" or "", url, projectDir |
392 url, projectDir), |
393 ), |
393 client) |
394 client, |
|
395 ) |
394 QApplication.processEvents() |
396 QApplication.processEvents() |
395 try: |
397 try: |
396 with EricMutexLocker(self.vcsExecutionMutex): |
398 with EricMutexLocker(self.vcsExecutionMutex): |
397 client.checkout(url, projectDir, recurse) |
399 client.checkout(url, projectDir, recurse) |
398 status = True |
400 status = True |
402 dlg.showError(e.args[0]) |
404 dlg.showError(e.args[0]) |
403 if not noDialog: |
405 if not noDialog: |
404 dlg.finish() |
406 dlg.finish() |
405 dlg.exec() |
407 dlg.exec() |
406 return status |
408 return status |
407 |
409 |
408 def vcsExport(self, vcsDataDict, projectDir): |
410 def vcsExport(self, vcsDataDict, projectDir): |
409 """ |
411 """ |
410 Public method used to export a directory from the Subversion |
412 Public method used to export a directory from the Subversion |
411 repository. |
413 repository. |
412 |
414 |
413 @param vcsDataDict dictionary of data required for the checkout |
415 @param vcsDataDict dictionary of data required for the checkout |
414 @param projectDir project directory to create (string) |
416 @param projectDir project directory to create (string) |
415 @return flag indicating an execution without errors (boolean) |
417 @return flag indicating an execution without errors (boolean) |
416 """ |
418 """ |
417 try: |
419 try: |
418 tag = vcsDataDict["tag"] |
420 tag = vcsDataDict["tag"] |
419 except KeyError: |
421 except KeyError: |
420 tag = None |
422 tag = None |
421 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
423 vcsDir = self.svnNormalizeURL(vcsDataDict["url"]) |
422 if vcsDir.startswith('/') or vcsDir[1] == '|': |
424 if vcsDir.startswith("/") or vcsDir[1] == "|": |
423 vcsDir = 'file://{0}'.format(vcsDir) |
425 vcsDir = "file://{0}".format(vcsDir) |
424 |
426 |
425 if self.otherData["standardLayout"]: |
427 if self.otherData["standardLayout"]: |
426 if tag is None or tag == '': |
428 if tag is None or tag == "": |
427 svnUrl = '{0}/trunk'.format(vcsDir) |
429 svnUrl = "{0}/trunk".format(vcsDir) |
428 else: |
430 else: |
429 if ( |
431 if not tag.startswith("tags") and not tag.startswith("branches"): |
430 not tag.startswith('tags') and |
|
431 not tag.startswith('branches') |
|
432 ): |
|
433 type_, ok = QInputDialog.getItem( |
432 type_, ok = QInputDialog.getItem( |
434 None, |
433 None, |
435 self.tr("Subversion Export"), |
434 self.tr("Subversion Export"), |
436 self.tr( |
435 self.tr( |
437 "The tag must be a normal tag (tags) or" |
436 "The tag must be a normal tag (tags) or" |
438 " a branch tag (branches)." |
437 " a branch tag (branches)." |
439 " Please select from the list."), |
438 " Please select from the list." |
|
439 ), |
440 self.tagTypeList, |
440 self.tagTypeList, |
441 0, False) |
441 0, |
|
442 False, |
|
443 ) |
442 if not ok: |
444 if not ok: |
443 return False |
445 return False |
444 tag = '{0}/{1}'.format(type_, tag) |
446 tag = "{0}/{1}".format(type_, tag) |
445 svnUrl = '{0}/{1}'.format(vcsDir, tag) |
447 svnUrl = "{0}/{1}".format(vcsDir, tag) |
446 else: |
448 else: |
447 svnUrl = vcsDir |
449 svnUrl = vcsDir |
448 |
450 |
449 opts = self.options['global'] |
451 opts = self.options["global"] |
450 recurse = "--non-recursive" not in opts |
452 recurse = "--non-recursive" not in opts |
451 url = self.__svnURL(svnUrl) |
453 url = self.__svnURL(svnUrl) |
452 client = self.getClient() |
454 client = self.getClient() |
453 dlg = SvnDialog( |
455 dlg = SvnDialog( |
454 self.tr('Exporting project from Subversion repository'), |
456 self.tr("Exporting project from Subversion repository"), |
455 "export --force{0} {1} {2}".format( |
457 "export --force{0} {1} {2}".format( |
456 (not recurse) and " --non-recursive" or "", |
458 (not recurse) and " --non-recursive" or "", url, projectDir |
457 url, projectDir), |
459 ), |
458 client) |
460 client, |
|
461 ) |
459 QApplication.processEvents() |
462 QApplication.processEvents() |
460 try: |
463 try: |
461 with EricMutexLocker(self.vcsExecutionMutex): |
464 with EricMutexLocker(self.vcsExecutionMutex): |
462 client.export(url, projectDir, force=True, recurse=recurse) |
465 client.export(url, projectDir, force=True, recurse=recurse) |
463 status = True |
466 status = True |
465 status = False |
468 status = False |
466 dlg.showError(e.args[0]) |
469 dlg.showError(e.args[0]) |
467 dlg.finish() |
470 dlg.finish() |
468 dlg.exec() |
471 dlg.exec() |
469 return status |
472 return status |
470 |
473 |
471 def vcsCommit(self, name, message, noDialog=False): |
474 def vcsCommit(self, name, message, noDialog=False): |
472 """ |
475 """ |
473 Public method used to make the change of a file/directory permanent |
476 Public method used to make the change of a file/directory permanent |
474 in the Subversion repository. |
477 in the Subversion repository. |
475 |
478 |
476 @param name file/directory name to be committed (string or |
479 @param name file/directory name to be committed (string or |
477 list of strings) |
480 list of strings) |
478 @param message message for this operation (string) |
481 @param message message for this operation (string) |
479 @param noDialog flag indicating quiet operations |
482 @param noDialog flag indicating quiet operations |
480 """ |
483 """ |
481 if not noDialog and not message: |
484 if not noDialog and not message: |
482 # call CommitDialog and get message from there |
485 # call CommitDialog and get message from there |
483 if self.__commitDialog is None: |
486 if self.__commitDialog is None: |
484 from .SvnCommitDialog import SvnCommitDialog |
487 from .SvnCommitDialog import SvnCommitDialog |
|
488 |
485 self.__commitDialog = SvnCommitDialog(self, self.__ui) |
489 self.__commitDialog = SvnCommitDialog(self, self.__ui) |
486 self.__commitDialog.accepted.connect(self.__vcsCommit_Step2) |
490 self.__commitDialog.accepted.connect(self.__vcsCommit_Step2) |
487 self.__commitDialog.show() |
491 self.__commitDialog.show() |
488 self.__commitDialog.raise_() |
492 self.__commitDialog.raise_() |
489 self.__commitDialog.activateWindow() |
493 self.__commitDialog.activateWindow() |
490 |
494 |
491 self.__commitData["name"] = name |
495 self.__commitData["name"] = name |
492 self.__commitData["msg"] = message |
496 self.__commitData["msg"] = message |
493 self.__commitData["noDialog"] = noDialog |
497 self.__commitData["noDialog"] = noDialog |
494 |
498 |
495 if noDialog: |
499 if noDialog: |
496 self.__vcsCommit_Step2() |
500 self.__vcsCommit_Step2() |
497 |
501 |
498 def __vcsCommit_Step2(self): |
502 def __vcsCommit_Step2(self): |
499 """ |
503 """ |
500 Private slot performing the second step of the commit action. |
504 Private slot performing the second step of the commit action. |
501 """ |
505 """ |
502 name = self.__commitData["name"] |
506 name = self.__commitData["name"] |
503 msg = self.__commitData["msg"] |
507 msg = self.__commitData["msg"] |
504 noDialog = self.__commitData["noDialog"] |
508 noDialog = self.__commitData["noDialog"] |
505 |
509 |
506 if not noDialog: |
510 if not noDialog: |
507 # check, if there are unsaved changes, that should be committed |
511 # check, if there are unsaved changes, that should be committed |
508 if isinstance(name, list): |
512 if isinstance(name, list): |
509 nameList = name |
513 nameList = name |
510 else: |
514 else: |
513 for nam in nameList: |
517 for nam in nameList: |
514 # check for commit of the project |
518 # check for commit of the project |
515 if os.path.isdir(nam): |
519 if os.path.isdir(nam): |
516 project = ericApp().getObject("Project") |
520 project = ericApp().getObject("Project") |
517 if nam == project.getProjectPath(): |
521 if nam == project.getProjectPath(): |
518 ok &= project.checkAllScriptsDirty( |
522 ok &= ( |
519 reportSyntaxErrors=True) and project.checkDirty() |
523 project.checkAllScriptsDirty(reportSyntaxErrors=True) |
|
524 and project.checkDirty() |
|
525 ) |
520 continue |
526 continue |
521 elif os.path.isfile(nam): |
527 elif os.path.isfile(nam): |
522 editor = ericApp().getObject("ViewManager").getOpenEditor( |
528 editor = ericApp().getObject("ViewManager").getOpenEditor(nam) |
523 nam) |
|
524 if editor: |
529 if editor: |
525 ok &= editor.checkDirty() |
530 ok &= editor.checkDirty() |
526 if not ok: |
531 if not ok: |
527 break |
532 break |
528 |
533 |
529 if not ok: |
534 if not ok: |
530 res = EricMessageBox.yesNo( |
535 res = EricMessageBox.yesNo( |
531 self.__ui, |
536 self.__ui, |
532 self.tr("Commit Changes"), |
537 self.tr("Commit Changes"), |
533 self.tr( |
538 self.tr( |
534 """The commit affects files, that have unsaved""" |
539 """The commit affects files, that have unsaved""" |
535 """ changes. Shall the commit be continued?"""), |
540 """ changes. Shall the commit be continued?""" |
536 icon=EricMessageBox.Warning) |
541 ), |
|
542 icon=EricMessageBox.Warning, |
|
543 ) |
537 if not res: |
544 if not res: |
538 return |
545 return |
539 |
546 |
540 if self.__commitDialog is not None: |
547 if self.__commitDialog is not None: |
541 msg = self.__commitDialog.logMessage() |
548 msg = self.__commitDialog.logMessage() |
542 if self.__commitDialog.hasChangelists(): |
549 if self.__commitDialog.hasChangelists(): |
543 changelists, keepChangelists = ( |
550 changelists, keepChangelists = self.__commitDialog.changelistsData() |
544 self.__commitDialog.changelistsData() |
|
545 ) |
|
546 else: |
551 else: |
547 changelists, keepChangelists = [], False |
552 changelists, keepChangelists = [], False |
548 self.__commitDialog.deleteLater() |
553 self.__commitDialog.deleteLater() |
549 self.__commitDialog = None |
554 self.__commitDialog = None |
550 else: |
555 else: |
551 changelists, keepChangelists = [], False |
556 changelists, keepChangelists = [], False |
552 |
557 |
553 if not msg: |
558 if not msg: |
554 msg = '***' |
559 msg = "***" |
555 |
560 |
556 if isinstance(name, list): |
561 if isinstance(name, list): |
557 dname, fnames = self.splitPathList(name) |
562 dname, fnames = self.splitPathList(name) |
558 else: |
563 else: |
559 dname, fname = self.splitPath(name) |
564 dname, fname = self.splitPath(name) |
560 fnames = [fname] |
565 fnames = [fname] |
561 |
566 |
562 if ( |
567 if self.svnGetReposName(dname).startswith("http") or self.svnGetReposName( |
563 self.svnGetReposName(dname).startswith('http') or |
568 dname |
564 self.svnGetReposName(dname).startswith('svn') |
569 ).startswith("svn"): |
565 ): |
|
566 noDialog = False |
570 noDialog = False |
567 |
571 |
568 cwd = os.getcwd() |
572 cwd = os.getcwd() |
569 os.chdir(dname) |
573 os.chdir(dname) |
570 opts = self.options['global'] + self.options['commit'] |
574 opts = self.options["global"] + self.options["commit"] |
571 recurse = "--non-recursive" not in opts |
575 recurse = "--non-recursive" not in opts |
572 keeplocks = "--keep-locks" in opts |
576 keeplocks = "--keep-locks" in opts |
573 client = self.getClient() |
577 client = self.getClient() |
574 if not noDialog: |
578 if not noDialog: |
575 dlg = SvnDialog( |
579 dlg = SvnDialog( |
576 self.tr('Commiting changes to Subversion repository'), |
580 self.tr("Commiting changes to Subversion repository"), |
577 "commit{0}{1}{2}{3} --message {4} {5}".format( |
581 "commit{0}{1}{2}{3} --message {4} {5}".format( |
578 (not recurse) and " --non-recursive" or "", |
582 (not recurse) and " --non-recursive" or "", |
579 keeplocks and " --keep-locks" or "", |
583 keeplocks and " --keep-locks" or "", |
580 keepChangelists and " --keep-changelists" or "", |
584 keepChangelists and " --keep-changelists" or "", |
581 changelists and |
585 changelists and " --changelist ".join([""] + changelists) or "", |
582 " --changelist ".join([""] + changelists) or "", |
586 msg, |
583 msg, " ".join(fnames)), |
587 " ".join(fnames), |
584 client) |
588 ), |
|
589 client, |
|
590 ) |
585 QApplication.processEvents() |
591 QApplication.processEvents() |
586 try: |
592 try: |
587 with EricMutexLocker(self.vcsExecutionMutex): |
593 with EricMutexLocker(self.vcsExecutionMutex): |
588 rev = ( |
594 rev = ( |
589 client.checkin( |
595 client.checkin( |
590 fnames, msg, recurse=recurse, keep_locks=keeplocks, |
596 fnames, |
|
597 msg, |
|
598 recurse=recurse, |
|
599 keep_locks=keeplocks, |
591 keep_changelist=keepChangelists, |
600 keep_changelist=keepChangelists, |
592 changelists=changelists) |
601 changelists=changelists, |
593 if changelists else |
602 ) |
594 client.checkin( |
603 if changelists |
595 fnames, msg, recurse=recurse, keep_locks=keeplocks) |
604 else client.checkin( |
|
605 fnames, msg, recurse=recurse, keep_locks=keeplocks |
|
606 ) |
596 ) |
607 ) |
597 except pysvn.ClientError as e: |
608 except pysvn.ClientError as e: |
598 rev = None |
609 rev = None |
599 if not noDialog: |
610 if not noDialog: |
600 dlg.showError(e.args[0]) |
611 dlg.showError(e.args[0]) |
601 if not noDialog: |
612 if not noDialog: |
602 rev and dlg.showMessage(self.tr("Committed revision {0}.") |
613 rev and dlg.showMessage( |
603 .format(rev.number)) |
614 self.tr("Committed revision {0}.").format(rev.number) |
|
615 ) |
604 dlg.finish() |
616 dlg.finish() |
605 dlg.exec() |
617 dlg.exec() |
606 os.chdir(cwd) |
618 os.chdir(cwd) |
607 self.committed.emit() |
619 self.committed.emit() |
608 self.checkVCSStatus() |
620 self.checkVCSStatus() |
609 |
621 |
610 def vcsCommitMessages(self): |
622 def vcsCommitMessages(self): |
611 """ |
623 """ |
612 Public method to get the list of saved commit messages. |
624 Public method to get the list of saved commit messages. |
613 |
625 |
614 @return list of saved commit messages |
626 @return list of saved commit messages |
615 @rtype list of str |
627 @rtype list of str |
616 """ |
628 """ |
617 # try per project commit history first |
629 # try per project commit history first |
618 messages = self._vcsProjectCommitMessages() |
630 messages = self._vcsProjectCommitMessages() |
619 if not messages: |
631 if not messages: |
620 # empty list returned, try the vcs specific one |
632 # empty list returned, try the vcs specific one |
621 messages = self.getPlugin().getPreferences("Commits") |
633 messages = self.getPlugin().getPreferences("Commits") |
622 |
634 |
623 return messages |
635 return messages |
624 |
636 |
625 def vcsAddCommitMessage(self, message): |
637 def vcsAddCommitMessage(self, message): |
626 """ |
638 """ |
627 Public method to add a commit message to the list of saved messages. |
639 Public method to add a commit message to the list of saved messages. |
628 |
640 |
629 @param message message to be added |
641 @param message message to be added |
630 @type str |
642 @type str |
631 """ |
643 """ |
632 if not self._vcsAddProjectCommitMessage(message): |
644 if not self._vcsAddProjectCommitMessage(message): |
633 commitMessages = self.vcsCommitMessages() |
645 commitMessages = self.vcsCommitMessages() |
767 tree2.reverse() |
778 tree2.reverse() |
768 names.extend(tree2) |
779 names.extend(tree2) |
769 names.extend(name) |
780 names.extend(name) |
770 else: |
781 else: |
771 names.append(name) |
782 names.append(name) |
772 |
783 |
773 cwd = os.getcwd() |
784 cwd = os.getcwd() |
774 os.chdir(wdir) |
785 os.chdir(wdir) |
775 opts = self.options['global'] + self.options['add'] |
786 opts = self.options["global"] + self.options["add"] |
776 recurse = False |
787 recurse = False |
777 force = "--force" in opts or noDialog |
788 force = "--force" in opts or noDialog |
778 noignore = "--no-ignore" in opts |
789 noignore = "--no-ignore" in opts |
779 client = self.getClient() |
790 client = self.getClient() |
780 if not noDialog: |
791 if not noDialog: |
781 dlg = SvnDialog( |
792 dlg = SvnDialog( |
782 self.tr('Adding files/directories to the Subversion' |
793 self.tr("Adding files/directories to the Subversion" " repository"), |
783 ' repository'), |
|
784 "add --non-recursive{0}{1} {2}".format( |
794 "add --non-recursive{0}{1} {2}".format( |
785 force and " --force" or "", |
795 force and " --force" or "", |
786 noignore and " --no-ignore" or "", |
796 noignore and " --no-ignore" or "", |
787 " ".join(names)), |
797 " ".join(names), |
788 client) |
798 ), |
|
799 client, |
|
800 ) |
789 QApplication.processEvents() |
801 QApplication.processEvents() |
790 try: |
802 try: |
791 with EricMutexLocker(self.vcsExecutionMutex): |
803 with EricMutexLocker(self.vcsExecutionMutex): |
792 client.add(names, recurse=recurse, force=force, |
804 client.add(names, recurse=recurse, force=force, ignore=not noignore) |
793 ignore=not noignore) |
|
794 except pysvn.ClientError as e: |
805 except pysvn.ClientError as e: |
795 if not noDialog: |
806 if not noDialog: |
796 dlg.showError(e.args[0]) |
807 dlg.showError(e.args[0]) |
797 if not noDialog: |
808 if not noDialog: |
798 dlg.finish() |
809 dlg.finish() |
799 dlg.exec() |
810 dlg.exec() |
800 os.chdir(cwd) |
811 os.chdir(cwd) |
801 |
812 |
802 def vcsAddBinary(self, name, isDir=False): |
813 def vcsAddBinary(self, name, isDir=False): |
803 """ |
814 """ |
804 Public method used to add a file/directory in binary mode to the |
815 Public method used to add a file/directory in binary mode to the |
805 Subversion repository. |
816 Subversion repository. |
806 |
817 |
807 @param name file/directory name to be added (string) |
818 @param name file/directory name to be added (string) |
808 @param isDir flag indicating name is a directory (boolean) |
819 @param isDir flag indicating name is a directory (boolean) |
809 """ |
820 """ |
810 self.vcsAdd(name, isDir) |
821 self.vcsAdd(name, isDir) |
811 |
822 |
812 def vcsAddTree(self, path): |
823 def vcsAddTree(self, path): |
813 """ |
824 """ |
814 Public method to add a directory tree rooted at path to the Subversion |
825 Public method to add a directory tree rooted at path to the Subversion |
815 repository. |
826 repository. |
816 |
827 |
817 @param path root directory of the tree to be added (string or list of |
828 @param path root directory of the tree to be added (string or list of |
818 strings)) |
829 strings)) |
819 """ |
830 """ |
820 tree = [] |
831 tree = [] |
821 if isinstance(path, list): |
832 if isinstance(path, list): |
822 dname, fnames = self.splitPathList(path) |
833 dname, fnames = self.splitPathList(path) |
823 for n in path: |
834 for n in path: |
824 d = os.path.dirname(n) |
835 d = os.path.dirname(n) |
825 if self.__wcng: |
836 if self.__wcng: |
826 repodir = d |
837 repodir = d |
827 while not os.path.isdir( |
838 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
828 os.path.join(repodir, self.adminDir)): |
|
829 repodir = os.path.dirname(repodir) |
839 repodir = os.path.dirname(repodir) |
830 if os.path.splitdrive(repodir)[1] == os.sep: |
840 if os.path.splitdrive(repodir)[1] == os.sep: |
831 return # oops, project is not version controlled |
841 return # oops, project is not version controlled |
832 while ( |
842 while ( |
833 (os.path.normcase(d) != os.path.normcase(repodir)) and |
843 (os.path.normcase(d) != os.path.normcase(repodir)) |
834 (d not in tree) and |
844 and (d not in tree) |
835 (os.path.normcase(d) not in self.statusCache or |
845 and ( |
836 self.statusCache[os.path.normcase(d)] == |
846 os.path.normcase(d) not in self.statusCache |
837 self.canBeAdded) |
847 or self.statusCache[os.path.normcase(d)] == self.canBeAdded |
|
848 ) |
838 ): |
849 ): |
839 tree.append(d) |
850 tree.append(d) |
840 d = os.path.dirname(d) |
851 d = os.path.dirname(d) |
841 else: |
852 else: |
842 while not os.path.exists(os.path.join(d, self.adminDir)): |
853 while not os.path.exists(os.path.join(d, self.adminDir)): |
871 # if they aren't in the repository already |
880 # if they aren't in the repository already |
872 tree.insert(-1, dname) |
881 tree.insert(-1, dname) |
873 dname = os.path.dirname(dname) |
882 dname = os.path.dirname(dname) |
874 if tree: |
883 if tree: |
875 self.vcsAdd(tree, True) |
884 self.vcsAdd(tree, True) |
876 |
885 |
877 names = [] |
886 names = [] |
878 if isinstance(path, list): |
887 if isinstance(path, list): |
879 names.extend(path) |
888 names.extend(path) |
880 else: |
889 else: |
881 names.append(path) |
890 names.append(path) |
882 |
891 |
883 cwd = os.getcwd() |
892 cwd = os.getcwd() |
884 os.chdir(dname) |
893 os.chdir(dname) |
885 opts = self.options['global'] + self.options['add'] |
894 opts = self.options["global"] + self.options["add"] |
886 recurse = True |
895 recurse = True |
887 force = "--force" in opts |
896 force = "--force" in opts |
888 ignore = "--ignore" in opts |
897 ignore = "--ignore" in opts |
889 client = self.getClient() |
898 client = self.getClient() |
890 dlg = SvnDialog( |
899 dlg = SvnDialog( |
891 self.tr('Adding directory trees to the Subversion repository'), |
900 self.tr("Adding directory trees to the Subversion repository"), |
892 "add{0}{1} {2}".format( |
901 "add{0}{1} {2}".format( |
893 force and " --force" or "", |
902 force and " --force" or "", |
894 ignore and " --ignore" or "", |
903 ignore and " --ignore" or "", |
895 " ".join(names)), |
904 " ".join(names), |
896 client) |
905 ), |
|
906 client, |
|
907 ) |
897 QApplication.processEvents() |
908 QApplication.processEvents() |
898 try: |
909 try: |
899 with EricMutexLocker(self.vcsExecutionMutex): |
910 with EricMutexLocker(self.vcsExecutionMutex): |
900 client.add(names, recurse=recurse, force=force, ignore=ignore) |
911 client.add(names, recurse=recurse, force=force, ignore=ignore) |
901 except pysvn.ClientError as e: |
912 except pysvn.ClientError as e: |
902 dlg.showError(e.args[0]) |
913 dlg.showError(e.args[0]) |
903 dlg.finish() |
914 dlg.finish() |
904 dlg.exec() |
915 dlg.exec() |
905 os.chdir(cwd) |
916 os.chdir(cwd) |
906 |
917 |
907 def vcsRemove(self, name, project=False, noDialog=False): |
918 def vcsRemove(self, name, project=False, noDialog=False): |
908 """ |
919 """ |
909 Public method used to remove a file/directory from the Subversion |
920 Public method used to remove a file/directory from the Subversion |
910 repository. |
921 repository. |
911 |
922 |
912 The default operation is to remove the local copy as well. |
923 The default operation is to remove the local copy as well. |
913 |
924 |
914 @param name file/directory name to be removed (string or list of |
925 @param name file/directory name to be removed (string or list of |
915 strings)) |
926 strings)) |
916 @param project flag indicating deletion of a project tree (boolean) |
927 @param project flag indicating deletion of a project tree (boolean) |
917 (not needed) |
928 (not needed) |
918 @param noDialog flag indicating quiet operations |
929 @param noDialog flag indicating quiet operations |
919 @return flag indicating successfull operation (boolean) |
930 @return flag indicating successfull operation (boolean) |
920 """ |
931 """ |
921 if not isinstance(name, list): |
932 if not isinstance(name, list): |
922 name = [name] |
933 name = [name] |
923 opts = self.options['global'] + self.options['remove'] |
934 opts = self.options["global"] + self.options["remove"] |
924 force = "--force" in opts or noDialog |
935 force = "--force" in opts or noDialog |
925 client = self.getClient() |
936 client = self.getClient() |
926 if not noDialog: |
937 if not noDialog: |
927 dlg = SvnDialog( |
938 dlg = SvnDialog( |
928 self.tr('Removing files/directories from the Subversion' |
939 self.tr("Removing files/directories from the Subversion" " repository"), |
929 ' repository'), |
940 "remove{0} {1}".format(force and " --force" or "", " ".join(name)), |
930 "remove{0} {1}".format( |
941 client, |
931 force and " --force" or "", |
942 ) |
932 " ".join(name)), |
|
933 client) |
|
934 QApplication.processEvents() |
943 QApplication.processEvents() |
935 try: |
944 try: |
936 with EricMutexLocker(self.vcsExecutionMutex): |
945 with EricMutexLocker(self.vcsExecutionMutex): |
937 client.remove(name, force=force) |
946 client.remove(name, force=force) |
938 res = True |
947 res = True |
941 if not noDialog: |
950 if not noDialog: |
942 dlg.showError(e.args[0]) |
951 dlg.showError(e.args[0]) |
943 if not noDialog: |
952 if not noDialog: |
944 dlg.finish() |
953 dlg.finish() |
945 dlg.exec() |
954 dlg.exec() |
946 |
955 |
947 return res |
956 return res |
948 |
957 |
949 def vcsMove(self, name, project, target=None, noDialog=False): |
958 def vcsMove(self, name, project, target=None, noDialog=False): |
950 """ |
959 """ |
951 Public method used to move a file/directory. |
960 Public method used to move a file/directory. |
952 |
961 |
953 @param name file/directory name to be moved (string) |
962 @param name file/directory name to be moved (string) |
954 @param project reference to the project object |
963 @param project reference to the project object |
955 @param target new name of the file/directory (string) |
964 @param target new name of the file/directory (string) |
956 @param noDialog flag indicating quiet operations |
965 @param noDialog flag indicating quiet operations |
957 @return flag indicating successfull operation (boolean) |
966 @return flag indicating successfull operation (boolean) |
958 """ |
967 """ |
959 rx_prot = re.compile('(file:|svn:|svn+ssh:|http:|https:).+') |
968 rx_prot = re.compile("(file:|svn:|svn+ssh:|http:|https:).+") |
960 opts = self.options['global'] |
969 opts = self.options["global"] |
961 res = False |
970 res = False |
962 |
971 |
963 if noDialog: |
972 if noDialog: |
964 if target is None: |
973 if target is None: |
965 return False |
974 return False |
966 force = True |
975 force = True |
967 accepted = True |
976 accepted = True |
968 else: |
977 else: |
969 from .SvnCopyDialog import SvnCopyDialog |
978 from .SvnCopyDialog import SvnCopyDialog |
|
979 |
970 dlg = SvnCopyDialog(name, None, True, "--force" in opts) |
980 dlg = SvnCopyDialog(name, None, True, "--force" in opts) |
971 accepted = (dlg.exec() == QDialog.DialogCode.Accepted) |
981 accepted = dlg.exec() == QDialog.DialogCode.Accepted |
972 if accepted: |
982 if accepted: |
973 target, force = dlg.getData() |
983 target, force = dlg.getData() |
974 if not target: |
984 if not target: |
975 return False |
985 return False |
976 |
986 |
977 isDir = (os.path.isdir(name) if rx_prot.fullmatch(target) is None |
987 isDir = os.path.isdir(name) if rx_prot.fullmatch(target) is None else False |
978 else False) |
988 |
979 |
|
980 if accepted: |
989 if accepted: |
981 client = self.getClient() |
990 client = self.getClient() |
982 if rx_prot.fullmatch(target) is not None: |
991 if rx_prot.fullmatch(target) is not None: |
983 target = self.__svnURL(target) |
992 target = self.__svnURL(target) |
984 log = "Moving {0} to {1}".format(name, target) |
993 log = "Moving {0} to {1}".format(name, target) |
985 else: |
994 else: |
986 log = "" |
995 log = "" |
987 target = target |
996 target = target |
988 if not noDialog: |
997 if not noDialog: |
989 dlg = SvnDialog( |
998 dlg = SvnDialog( |
990 self.tr('Moving {0}').format(name), |
999 self.tr("Moving {0}").format(name), |
991 "move{0}{1} {2} {3}".format( |
1000 "move{0}{1} {2} {3}".format( |
992 force and " --force" or "", |
1001 force and " --force" or "", |
993 log and (" --message {0}".format(log)) or "", |
1002 log and (" --message {0}".format(log)) or "", |
994 name, target), |
1003 name, |
995 client, log=log) |
1004 target, |
|
1005 ), |
|
1006 client, |
|
1007 log=log, |
|
1008 ) |
996 QApplication.processEvents() |
1009 QApplication.processEvents() |
997 try: |
1010 try: |
998 with EricMutexLocker(self.vcsExecutionMutex): |
1011 with EricMutexLocker(self.vcsExecutionMutex): |
999 client.move(name, target, force=force) |
1012 client.move(name, target, force=force) |
1000 res = True |
1013 res = True |
1040 project = ericApp().getObject("Project") |
1053 project = ericApp().getObject("Project") |
1041 if nam == project.ppath and not project.saveAllScripts(): |
1054 if nam == project.ppath and not project.saveAllScripts(): |
1042 return |
1055 return |
1043 if self.diff is None: |
1056 if self.diff is None: |
1044 from .SvnDiffDialog import SvnDiffDialog |
1057 from .SvnDiffDialog import SvnDiffDialog |
|
1058 |
1045 self.diff = SvnDiffDialog(self) |
1059 self.diff = SvnDiffDialog(self) |
1046 self.diff.show() |
1060 self.diff.show() |
1047 self.diff.raise_() |
1061 self.diff.raise_() |
1048 QApplication.processEvents() |
1062 QApplication.processEvents() |
1049 self.diff.start(name, refreshable=True) |
1063 self.diff.start(name, refreshable=True) |
1050 |
1064 |
1051 def vcsStatus(self, name): |
1065 def vcsStatus(self, name): |
1052 """ |
1066 """ |
1053 Public method used to view the status of files/directories in the |
1067 Public method used to view the status of files/directories in the |
1054 Subversion repository. |
1068 Subversion repository. |
1055 |
1069 |
1056 @param name file/directory name(s) to show the status of |
1070 @param name file/directory name(s) to show the status of |
1057 (string or list of strings) |
1071 (string or list of strings) |
1058 """ |
1072 """ |
1059 if self.status is None: |
1073 if self.status is None: |
1060 from .SvnStatusDialog import SvnStatusDialog |
1074 from .SvnStatusDialog import SvnStatusDialog |
|
1075 |
1061 self.status = SvnStatusDialog(self) |
1076 self.status = SvnStatusDialog(self) |
1062 self.status.show() |
1077 self.status.show() |
1063 self.status.raise_() |
1078 self.status.raise_() |
1064 QApplication.processEvents() |
1079 QApplication.processEvents() |
1065 self.status.start(name) |
1080 self.status.start(name) |
1066 |
1081 |
1067 def vcsTag(self, name): |
1082 def vcsTag(self, name): |
1068 """ |
1083 """ |
1069 Public method used to set the tag of a file/directory in the |
1084 Public method used to set the tag of a file/directory in the |
1070 Subversion repository. |
1085 Subversion repository. |
1071 |
1086 |
1072 @param name file/directory name to be tagged (string) |
1087 @param name file/directory name to be tagged (string) |
1073 """ |
1088 """ |
1074 dname, fname = self.splitPath(name) |
1089 dname, fname = self.splitPath(name) |
1075 |
1090 |
1076 reposURL = self.svnGetReposName(dname) |
1091 reposURL = self.svnGetReposName(dname) |
1077 if reposURL is None: |
1092 if reposURL is None: |
1078 EricMessageBox.critical( |
1093 EricMessageBox.critical( |
1079 self.__ui, |
1094 self.__ui, |
1080 self.tr("Subversion Error"), |
1095 self.tr("Subversion Error"), |
1081 self.tr( |
1096 self.tr( |
1082 """The URL of the project repository could not be""" |
1097 """The URL of the project repository could not be""" |
1083 """ retrieved from the working copy. The tag operation""" |
1098 """ retrieved from the working copy. The tag operation""" |
1084 """ will be aborted""")) |
1099 """ will be aborted""" |
|
1100 ), |
|
1101 ) |
1085 return |
1102 return |
1086 |
1103 |
1087 url = ( |
1104 url = ( |
1088 None |
1105 None if self.otherData["standardLayout"] else self.svnNormalizeURL(reposURL) |
1089 if self.otherData["standardLayout"] else |
|
1090 self.svnNormalizeURL(reposURL) |
|
1091 ) |
1106 ) |
1092 from .SvnTagDialog import SvnTagDialog |
1107 from .SvnTagDialog import SvnTagDialog |
1093 dlg = SvnTagDialog(self.allTagsBranchesList, url, |
1108 |
1094 self.otherData["standardLayout"]) |
1109 dlg = SvnTagDialog( |
|
1110 self.allTagsBranchesList, url, self.otherData["standardLayout"] |
|
1111 ) |
1095 if dlg.exec() == QDialog.DialogCode.Accepted: |
1112 if dlg.exec() == QDialog.DialogCode.Accepted: |
1096 tag, tagOp = dlg.getParameters() |
1113 tag, tagOp = dlg.getParameters() |
1097 if tag in self.allTagsBranchesList: |
1114 if tag in self.allTagsBranchesList: |
1098 self.allTagsBranchesList.remove(tag) |
1115 self.allTagsBranchesList.remove(tag) |
1099 self.allTagsBranchesList.insert(0, tag) |
1116 self.allTagsBranchesList.insert(0, tag) |
1100 else: |
1117 else: |
1101 return |
1118 return |
1102 |
1119 |
1103 if self.otherData["standardLayout"]: |
1120 if self.otherData["standardLayout"]: |
1104 rx_base = re.compile('(.+)/(trunk|tags|branches).*') |
1121 rx_base = re.compile("(.+)/(trunk|tags|branches).*") |
1105 |
1122 |
1106 match = rx_base.fullmatch(reposURL) |
1123 match = rx_base.fullmatch(reposURL) |
1107 if match is None: |
1124 if match is None: |
1108 EricMessageBox.critical( |
1125 EricMessageBox.critical( |
1109 self.__ui, |
1126 self.__ui, |
1110 self.tr("Subversion Error"), |
1127 self.tr("Subversion Error"), |
1111 self.tr( |
1128 self.tr( |
1112 """The URL of the project repository has an""" |
1129 """The URL of the project repository has an""" |
1113 """ invalid format. The tag operation will""" |
1130 """ invalid format. The tag operation will""" |
1114 """ be aborted""")) |
1131 """ be aborted""" |
|
1132 ), |
|
1133 ) |
1115 return |
1134 return |
1116 |
1135 |
1117 reposRoot = match.group(1) |
1136 reposRoot = match.group(1) |
1118 if tagOp in [1, 4]: |
1137 if tagOp in [1, 4]: |
1119 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1138 url = "{0}/tags/{1}".format(reposRoot, quote(tag)) |
1120 elif tagOp in [2, 8]: |
1139 elif tagOp in [2, 8]: |
1121 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1140 url = "{0}/branches/{1}".format(reposRoot, quote(tag)) |
1122 else: |
1141 else: |
1123 url = self.__svnURL(tag) |
1142 url = self.__svnURL(tag) |
1124 |
1143 |
1125 self.tagName = tag |
1144 self.tagName = tag |
1126 client = self.getClient() |
1145 client = self.getClient() |
1127 rev = None |
1146 rev = None |
1128 if tagOp in [1, 2]: |
1147 if tagOp in [1, 2]: |
1129 log = 'Created tag <{0}>'.format(self.tagName) |
1148 log = "Created tag <{0}>".format(self.tagName) |
1130 dlg = SvnDialog( |
1149 dlg = SvnDialog( |
1131 self.tr('Tagging {0} in the Subversion repository') |
1150 self.tr("Tagging {0} in the Subversion repository").format(name), |
1132 .format(name), |
|
1133 "copy --message {0} {1} {2}".format(log, reposURL, url), |
1151 "copy --message {0} {1} {2}".format(log, reposURL, url), |
1134 client, log=log) |
1152 client, |
|
1153 log=log, |
|
1154 ) |
1135 QApplication.processEvents() |
1155 QApplication.processEvents() |
1136 try: |
1156 try: |
1137 with EricMutexLocker(self.vcsExecutionMutex): |
1157 with EricMutexLocker(self.vcsExecutionMutex): |
1138 rev = client.copy(reposURL, url) |
1158 rev = client.copy(reposURL, url) |
1139 except pysvn.ClientError as e: |
1159 except pysvn.ClientError as e: |
1140 dlg.showError(e.args[0]) |
1160 dlg.showError(e.args[0]) |
1141 else: |
1161 else: |
1142 log = 'Deleted tag <{0}>'.format(self.tagName) |
1162 log = "Deleted tag <{0}>".format(self.tagName) |
1143 dlg = SvnDialog( |
1163 dlg = SvnDialog( |
1144 self.tr('Tagging {0} in the Subversion repository') |
1164 self.tr("Tagging {0} in the Subversion repository").format(name), |
1145 .format(name), |
|
1146 "remove --message {0} {1}".format(log, url), |
1165 "remove --message {0} {1}".format(log, url), |
1147 client, log=log) |
1166 client, |
|
1167 log=log, |
|
1168 ) |
1148 QApplication.processEvents() |
1169 QApplication.processEvents() |
1149 try: |
1170 try: |
1150 with EricMutexLocker(self.vcsExecutionMutex): |
1171 with EricMutexLocker(self.vcsExecutionMutex): |
1151 rev = client.remove(url) |
1172 rev = client.remove(url) |
1152 except pysvn.ClientError as e: |
1173 except pysvn.ClientError as e: |
1153 dlg.showError(e.args[0]) |
1174 dlg.showError(e.args[0]) |
1154 rev and dlg.showMessage( |
1175 rev and dlg.showMessage(self.tr("Revision {0}.\n").format(rev.number)) |
1155 self.tr("Revision {0}.\n").format(rev.number)) |
|
1156 dlg.finish() |
1176 dlg.finish() |
1157 dlg.exec() |
1177 dlg.exec() |
1158 |
1178 |
1159 def vcsRevert(self, name): |
1179 def vcsRevert(self, name): |
1160 """ |
1180 """ |
1161 Public method used to revert changes made to a file/directory. |
1181 Public method used to revert changes made to a file/directory. |
1162 |
1182 |
1163 @param name file/directory name to be reverted |
1183 @param name file/directory name to be reverted |
1164 @type str |
1184 @type str |
1165 @return flag indicating, that the update contained an add |
1185 @return flag indicating, that the update contained an add |
1166 or delete |
1186 or delete |
1167 @rtype bool |
1187 @rtype bool |
1169 recurse = False |
1189 recurse = False |
1170 if not isinstance(name, list): |
1190 if not isinstance(name, list): |
1171 name = [name] |
1191 name = [name] |
1172 if os.path.isdir(name[0]): |
1192 if os.path.isdir(name[0]): |
1173 recurse = True |
1193 recurse = True |
1174 |
1194 |
1175 project = ericApp().getObject("Project") |
1195 project = ericApp().getObject("Project") |
1176 names = [project.getRelativePath(nam) for nam in name] |
1196 names = [project.getRelativePath(nam) for nam in name] |
1177 if names[0]: |
1197 if names[0]: |
1178 from UI.DeleteFilesConfirmationDialog import ( |
1198 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog |
1179 DeleteFilesConfirmationDialog |
1199 |
1180 ) |
|
1181 dia = DeleteFilesConfirmationDialog( |
1200 dia = DeleteFilesConfirmationDialog( |
1182 self.parent(), |
1201 self.parent(), |
1183 self.tr("Revert changes"), |
1202 self.tr("Revert changes"), |
1184 self.tr( |
1203 self.tr( |
1185 "Do you really want to revert all changes to these files" |
1204 "Do you really want to revert all changes to these files" |
1186 " or directories?"), |
1205 " or directories?" |
1187 name) |
1206 ), |
|
1207 name, |
|
1208 ) |
1188 yes = dia.exec() == QDialog.DialogCode.Accepted |
1209 yes = dia.exec() == QDialog.DialogCode.Accepted |
1189 else: |
1210 else: |
1190 yes = EricMessageBox.yesNo( |
1211 yes = EricMessageBox.yesNo( |
1191 None, |
1212 None, |
1192 self.tr("Revert changes"), |
1213 self.tr("Revert changes"), |
1193 self.tr("""Do you really want to revert all changes of""" |
1214 self.tr( |
1194 """ the project?""")) |
1215 """Do you really want to revert all changes of""" |
|
1216 """ the project?""" |
|
1217 ), |
|
1218 ) |
1195 if yes: |
1219 if yes: |
1196 client = self.getClient() |
1220 client = self.getClient() |
1197 dlg = SvnDialog( |
1221 dlg = SvnDialog( |
1198 self.tr('Reverting changes'), |
1222 self.tr("Reverting changes"), |
1199 "revert {0} {1}".format( |
1223 "revert {0} {1}".format( |
1200 (not recurse) and " --non-recursive" or "", |
1224 (not recurse) and " --non-recursive" or "", " ".join(name) |
1201 " ".join(name)), |
1225 ), |
1202 client) |
1226 client, |
|
1227 ) |
1203 QApplication.processEvents() |
1228 QApplication.processEvents() |
1204 try: |
1229 try: |
1205 with EricMutexLocker(self.vcsExecutionMutex): |
1230 with EricMutexLocker(self.vcsExecutionMutex): |
1206 client.revert(name, recurse) |
1231 client.revert(name, recurse) |
1207 except pysvn.ClientError as e: |
1232 except pysvn.ClientError as e: |
1208 dlg.showError(e.args[0]) |
1233 dlg.showError(e.args[0]) |
1209 dlg.finish() |
1234 dlg.finish() |
1210 dlg.exec() |
1235 dlg.exec() |
1211 self.checkVCSStatus() |
1236 self.checkVCSStatus() |
1212 |
1237 |
1213 return False |
1238 return False |
1214 |
1239 |
1215 def vcsForget(self, name): |
1240 def vcsForget(self, name): |
1216 """ |
1241 """ |
1217 Public method used to remove a file from the repository. |
1242 Public method used to remove a file from the repository. |
1218 |
1243 |
1219 Note: svn does not support this operation. The method is implemented |
1244 Note: svn does not support this operation. The method is implemented |
1220 as a NoOp. |
1245 as a NoOp. |
1221 |
1246 |
1222 @param name file/directory name to be removed |
1247 @param name file/directory name to be removed |
1223 @type str or list of str |
1248 @type str or list of str |
1224 """ |
1249 """ |
1225 pass |
1250 pass |
1226 |
1251 |
1227 def vcsSwitch(self, name): |
1252 def vcsSwitch(self, name): |
1228 """ |
1253 """ |
1229 Public method used to switch a directory to a different tag/branch. |
1254 Public method used to switch a directory to a different tag/branch. |
1230 |
1255 |
1231 @param name directory name to be switched (string) |
1256 @param name directory name to be switched (string) |
1232 @return flag indicating, that the switch contained an add |
1257 @return flag indicating, that the switch contained an add |
1233 or delete (boolean) |
1258 or delete (boolean) |
1234 """ |
1259 """ |
1235 dname, fname = self.splitPath(name) |
1260 dname, fname = self.splitPath(name) |
1236 |
1261 |
1237 reposURL = self.svnGetReposName(dname) |
1262 reposURL = self.svnGetReposName(dname) |
1238 if reposURL is None: |
1263 if reposURL is None: |
1239 EricMessageBox.critical( |
1264 EricMessageBox.critical( |
1240 self.__ui, |
1265 self.__ui, |
1241 self.tr("Subversion Error"), |
1266 self.tr("Subversion Error"), |
1242 self.tr( |
1267 self.tr( |
1243 """The URL of the project repository could not be""" |
1268 """The URL of the project repository could not be""" |
1244 """ retrieved from the working copy. The switch""" |
1269 """ retrieved from the working copy. The switch""" |
1245 """ operation will be aborted""")) |
1270 """ operation will be aborted""" |
|
1271 ), |
|
1272 ) |
1246 return False |
1273 return False |
1247 |
1274 |
1248 url = ( |
1275 url = ( |
1249 None |
1276 None if self.otherData["standardLayout"] else self.svnNormalizeURL(reposURL) |
1250 if self.otherData["standardLayout"] else |
|
1251 self.svnNormalizeURL(reposURL) |
|
1252 ) |
1277 ) |
1253 from .SvnSwitchDialog import SvnSwitchDialog |
1278 from .SvnSwitchDialog import SvnSwitchDialog |
1254 dlg = SvnSwitchDialog(self.allTagsBranchesList, url, |
1279 |
1255 self.otherData["standardLayout"]) |
1280 dlg = SvnSwitchDialog( |
|
1281 self.allTagsBranchesList, url, self.otherData["standardLayout"] |
|
1282 ) |
1256 if dlg.exec() == QDialog.DialogCode.Accepted: |
1283 if dlg.exec() == QDialog.DialogCode.Accepted: |
1257 tag, tagType = dlg.getParameters() |
1284 tag, tagType = dlg.getParameters() |
1258 if tag in self.allTagsBranchesList: |
1285 if tag in self.allTagsBranchesList: |
1259 self.allTagsBranchesList.remove(tag) |
1286 self.allTagsBranchesList.remove(tag) |
1260 self.allTagsBranchesList.insert(0, tag) |
1287 self.allTagsBranchesList.insert(0, tag) |
1261 else: |
1288 else: |
1262 return False |
1289 return False |
1263 |
1290 |
1264 if self.otherData["standardLayout"]: |
1291 if self.otherData["standardLayout"]: |
1265 rx_base = re.compile('(.+)/(trunk|tags|branches).*') |
1292 rx_base = re.compile("(.+)/(trunk|tags|branches).*") |
1266 match = rx_base.fullmatch(reposURL) |
1293 match = rx_base.fullmatch(reposURL) |
1267 if match is None: |
1294 if match is None: |
1268 EricMessageBox.critical( |
1295 EricMessageBox.critical( |
1269 self.__ui, |
1296 self.__ui, |
1270 self.tr("Subversion Error"), |
1297 self.tr("Subversion Error"), |
1271 self.tr( |
1298 self.tr( |
1272 """The URL of the project repository has an""" |
1299 """The URL of the project repository has an""" |
1273 """ invalid format. The switch operation will""" |
1300 """ invalid format. The switch operation will""" |
1274 """ be aborted""")) |
1301 """ be aborted""" |
|
1302 ), |
|
1303 ) |
1275 return False |
1304 return False |
1276 |
1305 |
1277 reposRoot = match.group(1) |
1306 reposRoot = match.group(1) |
1278 tn = tag |
1307 tn = tag |
1279 if tagType == 1: |
1308 if tagType == 1: |
1280 url = '{0}/tags/{1}'.format(reposRoot, quote(tag)) |
1309 url = "{0}/tags/{1}".format(reposRoot, quote(tag)) |
1281 elif tagType == 2: |
1310 elif tagType == 2: |
1282 url = '{0}/branches/{1}'.format(reposRoot, quote(tag)) |
1311 url = "{0}/branches/{1}".format(reposRoot, quote(tag)) |
1283 elif tagType == 4: |
1312 elif tagType == 4: |
1284 url = '{0}/trunk'.format(reposRoot) |
1313 url = "{0}/trunk".format(reposRoot) |
1285 tn = 'HEAD' |
1314 tn = "HEAD" |
1286 else: |
1315 else: |
1287 url = self.__svnURL(tag) |
1316 url = self.__svnURL(tag) |
1288 tn = url |
1317 tn = url |
1289 |
1318 |
1290 client = self.getClient() |
1319 client = self.getClient() |
1291 dlg = SvnDialog(self.tr('Switching to {0}').format(tn), |
1320 dlg = SvnDialog( |
1292 "switch {0} {1}".format(url, name), |
1321 self.tr("Switching to {0}").format(tn), |
1293 client) |
1322 "switch {0} {1}".format(url, name), |
|
1323 client, |
|
1324 ) |
1294 QApplication.processEvents() |
1325 QApplication.processEvents() |
1295 try: |
1326 try: |
1296 with EricMutexLocker(self.vcsExecutionMutex): |
1327 with EricMutexLocker(self.vcsExecutionMutex): |
1297 rev = client.switch(name, url) |
1328 rev = client.switch(name, url) |
1298 dlg.showMessage(self.tr("Revision {0}.\n").format(rev.number)) |
1329 dlg.showMessage(self.tr("Revision {0}.\n").format(rev.number)) |
1301 dlg.finish() |
1332 dlg.finish() |
1302 dlg.exec() |
1333 dlg.exec() |
1303 res = dlg.hasAddOrDelete() |
1334 res = dlg.hasAddOrDelete() |
1304 self.checkVCSStatus() |
1335 self.checkVCSStatus() |
1305 return res |
1336 return res |
1306 |
1337 |
1307 def vcsMerge(self, name): |
1338 def vcsMerge(self, name): |
1308 """ |
1339 """ |
1309 Public method used to merge a URL/revision into the local project. |
1340 Public method used to merge a URL/revision into the local project. |
1310 |
1341 |
1311 @param name file/directory name to be merged (string) |
1342 @param name file/directory name to be merged (string) |
1312 """ |
1343 """ |
1313 dname, fname = self.splitPath(name) |
1344 dname, fname = self.splitPath(name) |
1314 |
1345 |
1315 opts = self.options['global'] |
1346 opts = self.options["global"] |
1316 from .SvnMergeDialog import SvnMergeDialog |
1347 from .SvnMergeDialog import SvnMergeDialog |
1317 dlg = SvnMergeDialog(self.mergeList[0], self.mergeList[1], |
1348 |
1318 self.mergeList[2], "--force" in opts) |
1349 dlg = SvnMergeDialog( |
|
1350 self.mergeList[0], self.mergeList[1], self.mergeList[2], "--force" in opts |
|
1351 ) |
1319 if dlg.exec() == QDialog.DialogCode.Accepted: |
1352 if dlg.exec() == QDialog.DialogCode.Accepted: |
1320 urlrev1, urlrev2, target, force = dlg.getParameters() |
1353 urlrev1, urlrev2, target, force = dlg.getParameters() |
1321 else: |
1354 else: |
1322 return |
1355 return |
1323 |
1356 |
1324 # remember URL or revision |
1357 # remember URL or revision |
1325 if urlrev1 in self.mergeList[0]: |
1358 if urlrev1 in self.mergeList[0]: |
1326 self.mergeList[0].remove(urlrev1) |
1359 self.mergeList[0].remove(urlrev1) |
1327 self.mergeList[0].insert(0, urlrev1) |
1360 self.mergeList[0].insert(0, urlrev1) |
1328 if urlrev2 in self.mergeList[1]: |
1361 if urlrev2 in self.mergeList[1]: |
1329 self.mergeList[1].remove(urlrev2) |
1362 self.mergeList[1].remove(urlrev2) |
1330 self.mergeList[1].insert(0, urlrev2) |
1363 self.mergeList[1].insert(0, urlrev2) |
1331 |
1364 |
1332 rx_rev = re.compile('\\d+|HEAD|head') |
1365 rx_rev = re.compile("\\d+|HEAD|head") |
1333 |
1366 |
1334 cwd = os.getcwd() |
1367 cwd = os.getcwd() |
1335 os.chdir(dname) |
1368 os.chdir(dname) |
1336 recurse = "--non-recursive" not in opts |
1369 recurse = "--non-recursive" not in opts |
1337 if bool(rx_rev.fullmatch(urlrev1)): |
1370 if bool(rx_rev.fullmatch(urlrev1)): |
1338 if urlrev1 in ["HEAD", "head"]: |
1371 if urlrev1 in ["HEAD", "head"]: |
1339 revision1 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1372 revision1 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1340 rev1 = "HEAD" |
1373 rev1 = "HEAD" |
1341 else: |
1374 else: |
1342 revision1 = pysvn.Revision( |
1375 revision1 = pysvn.Revision(pysvn.opt_revision_kind.number, int(urlrev1)) |
1343 pysvn.opt_revision_kind.number, int(urlrev1)) |
|
1344 rev1 = urlrev1 |
1376 rev1 = urlrev1 |
1345 if urlrev2 in ["HEAD", "head"]: |
1377 if urlrev2 in ["HEAD", "head"]: |
1346 revision2 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1378 revision2 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1347 rev2 = "HEAD" |
1379 rev2 = "HEAD" |
1348 else: |
1380 else: |
1349 revision2 = pysvn.Revision( |
1381 revision2 = pysvn.Revision(pysvn.opt_revision_kind.number, int(urlrev2)) |
1350 pysvn.opt_revision_kind.number, int(urlrev2)) |
|
1351 rev2 = urlrev2 |
1382 rev2 = urlrev2 |
1352 if not target: |
1383 if not target: |
1353 url1 = name |
1384 url1 = name |
1354 url2 = name |
1385 url2 = name |
1355 else: |
1386 else: |
1356 url1 = target |
1387 url1 = target |
1357 url2 = target |
1388 url2 = target |
1358 |
1389 |
1359 # remember target |
1390 # remember target |
1360 if target in self.mergeList[2]: |
1391 if target in self.mergeList[2]: |
1361 self.mergeList[2].remove(target) |
1392 self.mergeList[2].remove(target) |
1362 self.mergeList[2].insert(0, target) |
1393 self.mergeList[2].insert(0, target) |
1363 else: |
1394 else: |
1378 url2, rev = urlrev2.split("@") |
1408 url2, rev = urlrev2.split("@") |
1379 if rev in ["HEAD", "head"]: |
1409 if rev in ["HEAD", "head"]: |
1380 revision2 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1410 revision2 = pysvn.Revision(pysvn.opt_revision_kind.head) |
1381 rev2 = "HEAD" |
1411 rev2 = "HEAD" |
1382 else: |
1412 else: |
1383 revision2 = pysvn.Revision( |
1413 revision2 = pysvn.Revision(pysvn.opt_revision_kind.number, int(rev)) |
1384 pysvn.opt_revision_kind.number, int(rev)) |
|
1385 rev2 = rev |
1414 rev2 = rev |
1386 else: |
1415 else: |
1387 url2 = urlrev2 |
1416 url2 = urlrev2 |
1388 revision2 = pysvn.Revision(pysvn.opt_revision_kind.unspecified) |
1417 revision2 = pysvn.Revision(pysvn.opt_revision_kind.unspecified) |
1389 rev2 = "" |
1418 rev2 = "" |
1390 client = self.getClient() |
1419 client = self.getClient() |
1391 dlg = SvnDialog( |
1420 dlg = SvnDialog( |
1392 self.tr('Merging {0}').format(name), |
1421 self.tr("Merging {0}").format(name), |
1393 "merge{0}{1} {2} {3} {4}".format( |
1422 "merge{0}{1} {2} {3} {4}".format( |
1394 (not recurse) and " --non-recursive" or "", |
1423 (not recurse) and " --non-recursive" or "", |
1395 force and " --force" or "", |
1424 force and " --force" or "", |
1396 "{0}{1}".format(url1, rev1 and ("@" + rev1) or ""), |
1425 "{0}{1}".format(url1, rev1 and ("@" + rev1) or ""), |
1397 "{0}{1}".format(url2, rev2 and ("@" + rev2) or ""), |
1426 "{0}{1}".format(url2, rev2 and ("@" + rev2) or ""), |
1398 fname), |
1427 fname, |
1399 client) |
1428 ), |
|
1429 client, |
|
1430 ) |
1400 QApplication.processEvents() |
1431 QApplication.processEvents() |
1401 try: |
1432 try: |
1402 with EricMutexLocker(self.vcsExecutionMutex): |
1433 with EricMutexLocker(self.vcsExecutionMutex): |
1403 client.merge(url1, revision1, url2, revision2, fname, |
1434 client.merge( |
1404 recurse=recurse, force=force) |
1435 url1, |
|
1436 revision1, |
|
1437 url2, |
|
1438 revision2, |
|
1439 fname, |
|
1440 recurse=recurse, |
|
1441 force=force, |
|
1442 ) |
1405 except pysvn.ClientError as e: |
1443 except pysvn.ClientError as e: |
1406 dlg.showError(e.args[0]) |
1444 dlg.showError(e.args[0]) |
1407 dlg.finish() |
1445 dlg.finish() |
1408 dlg.exec() |
1446 dlg.exec() |
1409 os.chdir(cwd) |
1447 os.chdir(cwd) |
1410 |
1448 |
1411 def vcsRegisteredState(self, name): |
1449 def vcsRegisteredState(self, name): |
1412 """ |
1450 """ |
1413 Public method used to get the registered state of a file in the vcs. |
1451 Public method used to get the registered state of a file in the vcs. |
1414 |
1452 |
1415 @param name filename to check (string) |
1453 @param name filename to check (string) |
1416 @return a combination of canBeCommited and canBeAdded |
1454 @return a combination of canBeCommited and canBeAdded |
1417 """ |
1455 """ |
1418 if self.__wcng: |
1456 if self.__wcng: |
1419 return self.__vcsRegisteredState_wcng(name) |
1457 return self.__vcsRegisteredState_wcng(name) |
1420 else: |
1458 else: |
1421 return self.__vcsRegisteredState_wc(name) |
1459 return self.__vcsRegisteredState_wc(name) |
1422 |
1460 |
1423 def __vcsRegisteredState_wcng(self, name): |
1461 def __vcsRegisteredState_wcng(self, name): |
1424 """ |
1462 """ |
1425 Private method used to get the registered state of a file in the vcs. |
1463 Private method used to get the registered state of a file in the vcs. |
1426 |
1464 |
1427 This is the variant for subversion installations using the new |
1465 This is the variant for subversion installations using the new |
1428 working copy meta-data format. |
1466 working copy meta-data format. |
1429 |
1467 |
1430 @param name filename to check (string) |
1468 @param name filename to check (string) |
1431 @return a combination of canBeCommited and canBeAdded |
1469 @return a combination of canBeCommited and canBeAdded |
1432 """ |
1470 """ |
1433 if name.endswith(os.sep): |
1471 if name.endswith(os.sep): |
1434 name = name[:-1] |
1472 name = name[:-1] |
1435 name = os.path.normcase(name) |
1473 name = os.path.normcase(name) |
1436 dname, fname = self.splitPath(name) |
1474 dname, fname = self.splitPath(name) |
1437 |
1475 |
1438 if fname == '.' and os.path.isdir(os.path.join(dname, self.adminDir)): |
1476 if fname == "." and os.path.isdir(os.path.join(dname, self.adminDir)): |
1439 return self.canBeCommitted |
1477 return self.canBeCommitted |
1440 |
1478 |
1441 if name in self.statusCache: |
1479 if name in self.statusCache: |
1442 return self.statusCache[name] |
1480 return self.statusCache[name] |
1443 |
1481 |
1444 name = os.path.normcase(name) |
1482 name = os.path.normcase(name) |
1445 states = {name: 0} |
1483 states = {name: 0} |
1446 states = self.vcsAllRegisteredStates(states, dname, False) |
1484 states = self.vcsAllRegisteredStates(states, dname, False) |
1447 if states[name] == self.canBeCommitted: |
1485 if states[name] == self.canBeCommitted: |
1448 return self.canBeCommitted |
1486 return self.canBeCommitted |
1449 else: |
1487 else: |
1450 return self.canBeAdded |
1488 return self.canBeAdded |
1451 |
1489 |
1452 def __vcsRegisteredState_wc(self, name): |
1490 def __vcsRegisteredState_wc(self, name): |
1453 """ |
1491 """ |
1454 Private method used to get the registered state of a file in the vcs. |
1492 Private method used to get the registered state of a file in the vcs. |
1455 |
1493 |
1456 This is the variant for subversion installations using the old working |
1494 This is the variant for subversion installations using the old working |
1457 copy meta-data format. |
1495 copy meta-data format. |
1458 |
1496 |
1459 @param name filename to check (string) |
1497 @param name filename to check (string) |
1460 @return a combination of canBeCommited and canBeAdded |
1498 @return a combination of canBeCommited and canBeAdded |
1461 """ |
1499 """ |
1462 dname, fname = self.splitPath(name) |
1500 dname, fname = self.splitPath(name) |
1463 |
1501 |
1464 if fname == '.': |
1502 if fname == ".": |
1465 if os.path.isdir(os.path.join(dname, self.adminDir)): |
1503 if os.path.isdir(os.path.join(dname, self.adminDir)): |
1466 return self.canBeCommitted |
1504 return self.canBeCommitted |
1467 else: |
1505 else: |
1468 return self.canBeAdded |
1506 return self.canBeAdded |
1469 |
1507 |
1470 name = os.path.normcase(name) |
1508 name = os.path.normcase(name) |
1471 states = {name: 0} |
1509 states = {name: 0} |
1472 states = self.vcsAllRegisteredStates(states, dname, False) |
1510 states = self.vcsAllRegisteredStates(states, dname, False) |
1473 if states[name] == self.canBeCommitted: |
1511 if states[name] == self.canBeCommitted: |
1474 return self.canBeCommitted |
1512 return self.canBeCommitted |
1475 else: |
1513 else: |
1476 return self.canBeAdded |
1514 return self.canBeAdded |
1477 |
1515 |
1478 def vcsAllRegisteredStates(self, names, dname, shortcut=True): |
1516 def vcsAllRegisteredStates(self, names, dname, shortcut=True): |
1479 """ |
1517 """ |
1480 Public method used to get the registered states of a number of files |
1518 Public method used to get the registered states of a number of files |
1481 in the vcs. |
1519 in the vcs. |
1482 |
1520 |
1483 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1521 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1484 if the named directory has been scanned already. If so, it is assumed, |
1522 if the named directory has been scanned already. If so, it is assumed, |
1485 that the states for all files has been populated by the previous run. |
1523 that the states for all files has been populated by the previous run. |
1486 |
1524 |
1487 @param names dictionary with all filenames to be checked as keys |
1525 @param names dictionary with all filenames to be checked as keys |
1488 @param dname directory to check in (string) |
1526 @param dname directory to check in (string) |
1489 @param shortcut flag indicating a shortcut should be taken (boolean) |
1527 @param shortcut flag indicating a shortcut should be taken (boolean) |
1490 @return the received dictionary completed with a combination of |
1528 @return the received dictionary completed with a combination of |
1491 canBeCommited and canBeAdded or None in order to signal an error |
1529 canBeCommited and canBeAdded or None in order to signal an error |
1492 """ |
1530 """ |
1493 if self.__wcng: |
1531 if self.__wcng: |
1494 return self.__vcsAllRegisteredStates_wcng(names, dname, shortcut) |
1532 return self.__vcsAllRegisteredStates_wcng(names, dname, shortcut) |
1495 else: |
1533 else: |
1496 return self.__vcsAllRegisteredStates_wc(names, dname, shortcut) |
1534 return self.__vcsAllRegisteredStates_wc(names, dname, shortcut) |
1497 |
1535 |
1498 def __vcsAllRegisteredStates_wcng(self, names, dname, shortcut=True): |
1536 def __vcsAllRegisteredStates_wcng(self, names, dname, shortcut=True): |
1499 """ |
1537 """ |
1500 Private method used to get the registered states of a number of files |
1538 Private method used to get the registered states of a number of files |
1501 in the vcs. |
1539 in the vcs. |
1502 |
1540 |
1503 This is the variant for subversion installations using the new working |
1541 This is the variant for subversion installations using the new working |
1504 copy meta-data format. |
1542 copy meta-data format. |
1505 |
1543 |
1506 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1544 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1507 if the named directory has been scanned already. If so, it is assumed, |
1545 if the named directory has been scanned already. If so, it is assumed, |
1508 that the states for all files has been populated by the previous run. |
1546 that the states for all files has been populated by the previous run. |
1509 |
1547 |
1510 @param names dictionary with all filenames to be checked as keys |
1548 @param names dictionary with all filenames to be checked as keys |
1511 @param dname directory to check in (string) |
1549 @param dname directory to check in (string) |
1512 @param shortcut flag indicating a shortcut should be taken (boolean) |
1550 @param shortcut flag indicating a shortcut should be taken (boolean) |
1513 @return the received dictionary completed with a combination of |
1551 @return the received dictionary completed with a combination of |
1514 canBeCommited and canBeAdded or None in order to signal an error |
1552 canBeCommited and canBeAdded or None in order to signal an error |
1515 """ |
1553 """ |
1516 if dname.endswith(os.sep): |
1554 if dname.endswith(os.sep): |
1517 dname = dname[:-1] |
1555 dname = dname[:-1] |
1518 dname = os.path.normcase(dname) |
1556 dname = os.path.normcase(dname) |
1519 |
1557 |
1520 found = False |
1558 found = False |
1521 for name in self.statusCache: |
1559 for name in self.statusCache: |
1522 if name in names: |
1560 if name in names: |
1523 found = True |
1561 found = True |
1524 names[name] = self.statusCache[name] |
1562 names[name] = self.statusCache[name] |
1525 |
1563 |
1526 if not found: |
1564 if not found: |
1527 # find the root of the repo |
1565 # find the root of the repo |
1528 repodir = dname |
1566 repodir = dname |
1529 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
1567 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
1530 repodir = os.path.dirname(repodir) |
1568 repodir = os.path.dirname(repodir) |
1531 if os.path.splitdrive(repodir)[1] == os.sep: |
1569 if os.path.splitdrive(repodir)[1] == os.sep: |
1532 return names |
1570 return names |
1533 |
1571 |
1534 from .SvnDialogMixin import SvnDialogMixin |
1572 from .SvnDialogMixin import SvnDialogMixin |
|
1573 |
1535 mixin = SvnDialogMixin() |
1574 mixin = SvnDialogMixin() |
1536 client = self.getClient() |
1575 client = self.getClient() |
1537 client.callback_get_login = mixin._clientLoginCallback |
1576 client.callback_get_login = mixin._clientLoginCallback |
1538 client.callback_ssl_server_trust_prompt = ( |
1577 client.callback_ssl_server_trust_prompt = ( |
1539 mixin._clientSslServerTrustPromptCallback |
1578 mixin._clientSslServerTrustPromptCallback |
1540 ) |
1579 ) |
1541 |
1580 |
1542 with contextlib.suppress(pysvn.ClientError): |
1581 with contextlib.suppress(pysvn.ClientError): |
1543 with EricMutexLocker(self.vcsExecutionMutex): |
1582 with EricMutexLocker(self.vcsExecutionMutex): |
1544 allFiles = client.status(dname, recurse=True, get_all=True, |
1583 allFiles = client.status( |
1545 ignore=True, update=False) |
1584 dname, recurse=True, get_all=True, ignore=True, update=False |
|
1585 ) |
1546 dirs = [x for x in names.keys() if os.path.isdir(x)] |
1586 dirs = [x for x in names.keys() if os.path.isdir(x)] |
1547 for file in allFiles: |
1587 for file in allFiles: |
1548 name = os.path.normcase(file.path) |
1588 name = os.path.normcase(file.path) |
1549 if self.__isVersioned(file): |
1589 if self.__isVersioned(file): |
1550 if name in names: |
1590 if name in names: |
1551 names[name] = self.canBeCommitted |
1591 names[name] = self.canBeCommitted |
1552 dn = name |
1592 dn = name |
1553 while ( |
1593 while os.path.splitdrive(dn)[1] != os.sep and dn != repodir: |
1554 os.path.splitdrive(dn)[1] != os.sep and |
|
1555 dn != repodir |
|
1556 ): |
|
1557 dn = os.path.dirname(dn) |
1594 dn = os.path.dirname(dn) |
1558 if ( |
1595 if ( |
1559 dn in self.statusCache and |
1596 dn in self.statusCache |
1560 self.statusCache[dn] == |
1597 and self.statusCache[dn] == self.canBeCommitted |
1561 self.canBeCommitted |
|
1562 ): |
1598 ): |
1563 break |
1599 break |
1564 self.statusCache[dn] = self.canBeCommitted |
1600 self.statusCache[dn] = self.canBeCommitted |
1565 self.statusCache[name] = self.canBeCommitted |
1601 self.statusCache[name] = self.canBeCommitted |
1566 if dirs: |
1602 if dirs: |
1570 self.statusCache[d] = self.canBeCommitted |
1606 self.statusCache[d] = self.canBeCommitted |
1571 dirs.remove(d) |
1607 dirs.remove(d) |
1572 break |
1608 break |
1573 else: |
1609 else: |
1574 self.statusCache[name] = self.canBeAdded |
1610 self.statusCache[name] = self.canBeAdded |
1575 |
1611 |
1576 return names |
1612 return names |
1577 |
1613 |
1578 def __vcsAllRegisteredStates_wc(self, names, dname, shortcut=True): |
1614 def __vcsAllRegisteredStates_wc(self, names, dname, shortcut=True): |
1579 """ |
1615 """ |
1580 Private method used to get the registered states of a number of files |
1616 Private method used to get the registered states of a number of files |
1581 in the VCS. |
1617 in the VCS. |
1582 |
1618 |
1583 This is the variant for subversion installations using the old working |
1619 This is the variant for subversion installations using the old working |
1584 copy meta-data format. |
1620 copy meta-data format. |
1585 |
1621 |
1586 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1622 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
1587 if the named directory has been scanned already. If so, it is assumed, |
1623 if the named directory has been scanned already. If so, it is assumed, |
1588 that the states for all files has been populated by the previous run. |
1624 that the states for all files has been populated by the previous run. |
1589 |
1625 |
1590 @param names dictionary with all filenames to be checked as keys |
1626 @param names dictionary with all filenames to be checked as keys |
1591 @param dname directory to check in (string) |
1627 @param dname directory to check in (string) |
1592 @param shortcut flag indicating a shortcut should be taken (boolean) |
1628 @param shortcut flag indicating a shortcut should be taken (boolean) |
1593 @return the received dictionary completed with a combination of |
1629 @return the received dictionary completed with a combination of |
1594 canBeCommited and canBeAdded or None in order to signal an error |
1630 canBeCommited and canBeAdded or None in order to signal an error |
1595 """ |
1631 """ |
1596 if not os.path.isdir(os.path.join(dname, self.adminDir)): |
1632 if not os.path.isdir(os.path.join(dname, self.adminDir)): |
1597 # not under version control -> do nothing |
1633 # not under version control -> do nothing |
1598 return names |
1634 return names |
1599 |
1635 |
1600 found = False |
1636 found = False |
1601 for name in self.statusCache: |
1637 for name in self.statusCache: |
1602 if os.path.dirname(name) == dname: |
1638 if os.path.dirname(name) == dname: |
1603 if shortcut: |
1639 if shortcut: |
1604 found = True |
1640 found = True |
1605 break |
1641 break |
1606 if name in names: |
1642 if name in names: |
1607 found = True |
1643 found = True |
1608 names[name] = self.statusCache[name] |
1644 names[name] = self.statusCache[name] |
1609 |
1645 |
1610 if not found: |
1646 if not found: |
1611 from .SvnDialogMixin import SvnDialogMixin |
1647 from .SvnDialogMixin import SvnDialogMixin |
|
1648 |
1612 mixin = SvnDialogMixin() |
1649 mixin = SvnDialogMixin() |
1613 client = self.getClient() |
1650 client = self.getClient() |
1614 client.callback_get_login = mixin._clientLoginCallback |
1651 client.callback_get_login = mixin._clientLoginCallback |
1615 client.callback_ssl_server_trust_prompt = ( |
1652 client.callback_ssl_server_trust_prompt = ( |
1616 mixin._clientSslServerTrustPromptCallback |
1653 mixin._clientSslServerTrustPromptCallback |
1617 ) |
1654 ) |
1618 |
1655 |
1619 with contextlib.suppress(pysvn.ClientError): |
1656 with contextlib.suppress(pysvn.ClientError): |
1620 with EricMutexLocker(self.vcsExecutionMutex): |
1657 with EricMutexLocker(self.vcsExecutionMutex): |
1621 allFiles = client.status(dname, recurse=True, get_all=True, |
1658 allFiles = client.status( |
1622 ignore=True, update=False) |
1659 dname, recurse=True, get_all=True, ignore=True, update=False |
|
1660 ) |
1623 for file in allFiles: |
1661 for file in allFiles: |
1624 name = os.path.normcase(file.path) |
1662 name = os.path.normcase(file.path) |
1625 if self.__isVersioned(file): |
1663 if self.__isVersioned(file): |
1626 if name in names: |
1664 if name in names: |
1627 names[name] = self.canBeCommitted |
1665 names[name] = self.canBeCommitted |
1628 self.statusCache[name] = self.canBeCommitted |
1666 self.statusCache[name] = self.canBeCommitted |
1629 else: |
1667 else: |
1630 self.statusCache[name] = self.canBeAdded |
1668 self.statusCache[name] = self.canBeAdded |
1631 |
1669 |
1632 return names |
1670 return names |
1633 |
1671 |
1634 def __isVersioned(self, status): |
1672 def __isVersioned(self, status): |
1635 """ |
1673 """ |
1636 Private method to check, if the given status indicates a |
1674 Private method to check, if the given status indicates a |
1637 versioned state. |
1675 versioned state. |
1638 |
1676 |
1639 @param status status object to check (pysvn.PysvnStatus) |
1677 @param status status object to check (pysvn.PysvnStatus) |
1640 @return flag indicating a versioned state (boolean) |
1678 @return flag indicating a versioned state (boolean) |
1641 """ |
1679 """ |
1642 return status["text_status"] in [ |
1680 return status["text_status"] in [ |
1643 pysvn.wc_status_kind.normal, |
1681 pysvn.wc_status_kind.normal, |
1647 pysvn.wc_status_kind.replaced, |
1685 pysvn.wc_status_kind.replaced, |
1648 pysvn.wc_status_kind.modified, |
1686 pysvn.wc_status_kind.modified, |
1649 pysvn.wc_status_kind.merged, |
1687 pysvn.wc_status_kind.merged, |
1650 pysvn.wc_status_kind.conflicted, |
1688 pysvn.wc_status_kind.conflicted, |
1651 ] |
1689 ] |
1652 |
1690 |
1653 def clearStatusCache(self): |
1691 def clearStatusCache(self): |
1654 """ |
1692 """ |
1655 Public method to clear the status cache. |
1693 Public method to clear the status cache. |
1656 """ |
1694 """ |
1657 self.statusCache = {} |
1695 self.statusCache = {} |
1658 |
1696 |
1659 def vcsInitConfig(self, project): |
1697 def vcsInitConfig(self, project): |
1660 """ |
1698 """ |
1661 Public method to initialize the VCS configuration. |
1699 Public method to initialize the VCS configuration. |
1662 |
1700 |
1663 This method ensures, that eric specific files and directories are |
1701 This method ensures, that eric specific files and directories are |
1664 ignored. |
1702 ignored. |
1665 |
1703 |
1666 @param project reference to the project (Project) |
1704 @param project reference to the project (Project) |
1667 """ |
1705 """ |
1668 configPath = getConfigPath() |
1706 configPath = getConfigPath() |
1669 if os.path.exists(configPath): |
1707 if os.path.exists(configPath): |
1670 amendConfig() |
1708 amendConfig() |
1671 else: |
1709 else: |
1672 createDefaultConfig() |
1710 createDefaultConfig() |
1673 |
1711 |
1674 def vcsName(self): |
1712 def vcsName(self): |
1675 """ |
1713 """ |
1676 Public method returning the name of the vcs. |
1714 Public method returning the name of the vcs. |
1677 |
1715 |
1678 @return always 'Subversion' (string) |
1716 @return always 'Subversion' (string) |
1679 """ |
1717 """ |
1680 return "Subversion" |
1718 return "Subversion" |
1681 |
1719 |
1682 def vcsCleanup(self, name): |
1720 def vcsCleanup(self, name): |
1683 """ |
1721 """ |
1684 Public method used to cleanup the working copy. |
1722 Public method used to cleanup the working copy. |
1685 |
1723 |
1686 @param name directory name to be cleaned up (string) |
1724 @param name directory name to be cleaned up (string) |
1687 """ |
1725 """ |
1688 client = self.getClient() |
1726 client = self.getClient() |
1689 dlg = SvnDialog(self.tr('Cleaning up {0}').format(name), |
1727 dlg = SvnDialog( |
1690 "cleanup {0}".format(name), |
1728 self.tr("Cleaning up {0}").format(name), "cleanup {0}".format(name), client |
1691 client) |
1729 ) |
1692 QApplication.processEvents() |
1730 QApplication.processEvents() |
1693 try: |
1731 try: |
1694 with EricMutexLocker(self.vcsExecutionMutex): |
1732 with EricMutexLocker(self.vcsExecutionMutex): |
1695 client.cleanup(name) |
1733 client.cleanup(name) |
1696 except pysvn.ClientError as e: |
1734 except pysvn.ClientError as e: |
1697 dlg.showError(e.args[0]) |
1735 dlg.showError(e.args[0]) |
1698 dlg.finish() |
1736 dlg.finish() |
1699 dlg.exec() |
1737 dlg.exec() |
1700 |
1738 |
1701 def vcsCommandLine(self, name): |
1739 def vcsCommandLine(self, name): |
1702 """ |
1740 """ |
1703 Public method used to execute arbitrary subversion commands. |
1741 Public method used to execute arbitrary subversion commands. |
1704 |
1742 |
1705 @param name directory name of the working directory (string) |
1743 @param name directory name of the working directory (string) |
1706 """ |
1744 """ |
1707 from .SvnCommandDialog import SvnCommandDialog |
1745 from .SvnCommandDialog import SvnCommandDialog |
|
1746 |
1708 dlg = SvnCommandDialog(self.commandHistory, self.wdHistory, name) |
1747 dlg = SvnCommandDialog(self.commandHistory, self.wdHistory, name) |
1709 if dlg.exec() == QDialog.DialogCode.Accepted: |
1748 if dlg.exec() == QDialog.DialogCode.Accepted: |
1710 command, wd = dlg.getData() |
1749 command, wd = dlg.getData() |
1711 commandList = Utilities.parseOptionString(command) |
1750 commandList = Utilities.parseOptionString(command) |
1712 |
1751 |
1713 # This moves any previous occurrence of these arguments to the head |
1752 # This moves any previous occurrence of these arguments to the head |
1714 # of the list. |
1753 # of the list. |
1715 if command in self.commandHistory: |
1754 if command in self.commandHistory: |
1716 self.commandHistory.remove(command) |
1755 self.commandHistory.remove(command) |
1717 self.commandHistory.insert(0, command) |
1756 self.commandHistory.insert(0, command) |
1718 if wd in self.wdHistory: |
1757 if wd in self.wdHistory: |
1719 self.wdHistory.remove(wd) |
1758 self.wdHistory.remove(wd) |
1720 self.wdHistory.insert(0, wd) |
1759 self.wdHistory.insert(0, wd) |
1721 |
1760 |
1722 args = [] |
1761 args = [] |
1723 self.addArguments(args, commandList) |
1762 self.addArguments(args, commandList) |
1724 |
1763 |
1725 from Plugins.VcsPlugins.vcsSubversion.SvnDialog import ( |
1764 from Plugins.VcsPlugins.vcsSubversion.SvnDialog import ( |
1726 SvnDialog as SvnProcessDialog |
1765 SvnDialog as SvnProcessDialog, |
1727 ) |
1766 ) |
1728 dia = SvnProcessDialog(self.tr('Subversion command')) |
1767 |
|
1768 dia = SvnProcessDialog(self.tr("Subversion command")) |
1729 res = dia.startProcess(args, wd) |
1769 res = dia.startProcess(args, wd) |
1730 if res: |
1770 if res: |
1731 dia.exec() |
1771 dia.exec() |
1732 |
1772 |
1733 def vcsOptionsDialog(self, project, archive, editable=False, parent=None): |
1773 def vcsOptionsDialog(self, project, archive, editable=False, parent=None): |
1734 """ |
1774 """ |
1735 Public method to get a dialog to enter repository info. |
1775 Public method to get a dialog to enter repository info. |
1736 |
1776 |
1737 @param project reference to the project object |
1777 @param project reference to the project object |
1738 @param archive name of the project in the repository (string) |
1778 @param archive name of the project in the repository (string) |
1739 @param editable flag indicating that the project name is editable |
1779 @param editable flag indicating that the project name is editable |
1740 (boolean) |
1780 (boolean) |
1741 @param parent parent widget (QWidget) |
1781 @param parent parent widget (QWidget) |
1742 @return reference to the instantiated options dialog (SvnOptionsDialog) |
1782 @return reference to the instantiated options dialog (SvnOptionsDialog) |
1743 """ |
1783 """ |
1744 from .SvnOptionsDialog import SvnOptionsDialog |
1784 from .SvnOptionsDialog import SvnOptionsDialog |
|
1785 |
1745 return SvnOptionsDialog(self, project, parent) |
1786 return SvnOptionsDialog(self, project, parent) |
1746 |
1787 |
1747 def vcsNewProjectOptionsDialog(self, parent=None): |
1788 def vcsNewProjectOptionsDialog(self, parent=None): |
1748 """ |
1789 """ |
1749 Public method to get a dialog to enter repository info for getting a |
1790 Public method to get a dialog to enter repository info for getting a |
1750 new project. |
1791 new project. |
1751 |
1792 |
1752 @param parent parent widget (QWidget) |
1793 @param parent parent widget (QWidget) |
1753 @return reference to the instantiated options dialog |
1794 @return reference to the instantiated options dialog |
1754 (SvnNewProjectOptionsDialog) |
1795 (SvnNewProjectOptionsDialog) |
1755 """ |
1796 """ |
1756 from .SvnNewProjectOptionsDialog import SvnNewProjectOptionsDialog |
1797 from .SvnNewProjectOptionsDialog import SvnNewProjectOptionsDialog |
|
1798 |
1757 return SvnNewProjectOptionsDialog(self, parent) |
1799 return SvnNewProjectOptionsDialog(self, parent) |
1758 |
1800 |
1759 def vcsRepositoryInfos(self, ppath): |
1801 def vcsRepositoryInfos(self, ppath): |
1760 """ |
1802 """ |
1761 Public method to retrieve information about the repository. |
1803 Public method to retrieve information about the repository. |
1762 |
1804 |
1763 @param ppath local path to get the repository infos (string) |
1805 @param ppath local path to get the repository infos (string) |
1764 @return string with ready formated info for display (string) |
1806 @return string with ready formated info for display (string) |
1765 """ |
1807 """ |
1766 try: |
1808 try: |
1767 entry = self.getClient().info(ppath) |
1809 entry = self.getClient().info(ppath) |
1768 except pysvn.ClientError as e: |
1810 except pysvn.ClientError as e: |
1769 return e.args[0] |
1811 return e.args[0] |
1770 |
1812 |
1771 apiVersion = ( |
1813 apiVersion = ( |
1772 "{0} {1}".format( |
1814 "{0} {1}".format( |
1773 ".".join([str(v) for v in pysvn.svn_api_version[:3]]), |
1815 ".".join([str(v) for v in pysvn.svn_api_version[:3]]), |
1774 pysvn.svn_api_version[3]) |
1816 pysvn.svn_api_version[3], |
1775 if hasattr(pysvn, 'svn_api_version') else |
1817 ) |
1776 QCoreApplication.translate('subversion', "unknown") |
1818 if hasattr(pysvn, "svn_api_version") |
|
1819 else QCoreApplication.translate("subversion", "unknown") |
1777 ) |
1820 ) |
1778 |
1821 |
1779 hmsz = time.strftime("%H:%M:%S %Z", time.localtime(entry.commit_time)) |
1822 hmsz = time.strftime("%H:%M:%S %Z", time.localtime(entry.commit_time)) |
1780 return QCoreApplication.translate( |
1823 return QCoreApplication.translate( |
1781 'subversion', |
1824 "subversion", |
1782 """<h3>Repository information</h3>""" |
1825 """<h3>Repository information</h3>""" |
1783 """<table>""" |
1826 """<table>""" |
1784 """<tr><td><b>PySvn V.</b></td><td>{0}</td></tr>""" |
1827 """<tr><td><b>PySvn V.</b></td><td>{0}</td></tr>""" |
1785 """<tr><td><b>Subversion V.</b></td><td>{1}</td></tr>""" |
1828 """<tr><td><b>Subversion V.</b></td><td>{1}</td></tr>""" |
1786 """<tr><td><b>Subversion API V.</b></td><td>{2}</td></tr>""" |
1829 """<tr><td><b>Subversion API V.</b></td><td>{2}</td></tr>""" |
1896 res = False |
1943 res = False |
1897 dlg.showError(e.args[0]) |
1944 dlg.showError(e.args[0]) |
1898 dlg.finish() |
1945 dlg.finish() |
1899 dlg.exec() |
1946 dlg.exec() |
1900 if ( |
1947 if ( |
1901 res and |
1948 res |
1902 not bool(rx_prot.fullmatch(target)) and |
1949 and not bool(rx_prot.fullmatch(target)) |
1903 target.startswith(project.getProjectPath()) |
1950 and target.startswith(project.getProjectPath()) |
1904 ): |
1951 ): |
1905 if os.path.isdir(name): |
1952 if os.path.isdir(name): |
1906 project.copyDirectory(name, target) |
1953 project.copyDirectory(name, target) |
1907 else: |
1954 else: |
1908 project.appendFile(target) |
1955 project.appendFile(target) |
1909 return res |
1956 return res |
1910 |
1957 |
1911 def svnListProps(self, name, recursive=False): |
1958 def svnListProps(self, name, recursive=False): |
1912 """ |
1959 """ |
1913 Public method used to list the properties of a file/directory. |
1960 Public method used to list the properties of a file/directory. |
1914 |
1961 |
1915 @param name file/directory name (string or list of strings) |
1962 @param name file/directory name (string or list of strings) |
1916 @param recursive flag indicating a recursive list is requested |
1963 @param recursive flag indicating a recursive list is requested |
1917 """ |
1964 """ |
1918 if self.propList is None: |
1965 if self.propList is None: |
1919 from .SvnPropListDialog import SvnPropListDialog |
1966 from .SvnPropListDialog import SvnPropListDialog |
|
1967 |
1920 self.propList = SvnPropListDialog(self) |
1968 self.propList = SvnPropListDialog(self) |
1921 self.propList.show() |
1969 self.propList.show() |
1922 self.propList.raise_() |
1970 self.propList.raise_() |
1923 QApplication.processEvents() |
1971 QApplication.processEvents() |
1924 self.propList.start(name, recursive) |
1972 self.propList.start(name, recursive) |
1925 |
1973 |
1926 def svnSetProp(self, name, recursive=False): |
1974 def svnSetProp(self, name, recursive=False): |
1927 """ |
1975 """ |
1928 Public method used to add a property to a file/directory. |
1976 Public method used to add a property to a file/directory. |
1929 |
1977 |
1930 @param name file/directory name (string or list of strings) |
1978 @param name file/directory name (string or list of strings) |
1931 @param recursive flag indicating a recursive set is requested |
1979 @param recursive flag indicating a recursive set is requested |
1932 """ |
1980 """ |
1933 from .SvnPropSetDialog import SvnPropSetDialog |
1981 from .SvnPropSetDialog import SvnPropSetDialog |
|
1982 |
1934 dlg = SvnPropSetDialog(recursive) |
1983 dlg = SvnPropSetDialog(recursive) |
1935 if dlg.exec() == QDialog.DialogCode.Accepted: |
1984 if dlg.exec() == QDialog.DialogCode.Accepted: |
1936 propName, propValue, recurse = dlg.getData() |
1985 propName, propValue, recurse = dlg.getData() |
1937 if not propName: |
1986 if not propName: |
1938 EricMessageBox.critical( |
1987 EricMessageBox.critical( |
1939 self.__ui, |
1988 self.__ui, |
1940 self.tr("Subversion Set Property"), |
1989 self.tr("Subversion Set Property"), |
1941 self.tr( |
1990 self.tr("""You have to supply a property name. Aborting."""), |
1942 """You have to supply a property name. Aborting.""")) |
1991 ) |
1943 return |
1992 return |
1944 |
1993 |
1945 if isinstance(name, list): |
1994 if isinstance(name, list): |
1946 dname, fnames = self.splitPathList(name) |
1995 dname, fnames = self.splitPathList(name) |
1947 else: |
1996 else: |
1948 dname, fname = self.splitPath(name) |
1997 dname, fname = self.splitPath(name) |
1949 fnames = [fname] |
1998 fnames = [fname] |
1950 |
1999 |
1951 cwd = os.getcwd() |
2000 cwd = os.getcwd() |
1952 os.chdir(dname) |
2001 os.chdir(dname) |
1953 opts = self.options['global'] |
2002 opts = self.options["global"] |
1954 skipchecks = "--skip-checks" in opts |
2003 skipchecks = "--skip-checks" in opts |
1955 client = self.getClient() |
2004 client = self.getClient() |
1956 dlg = SvnDialog( |
2005 dlg = SvnDialog( |
1957 self.tr('Subversion Set Property'), |
2006 self.tr("Subversion Set Property"), |
1958 "propset{0}{1} {2} {3} {4}".format( |
2007 "propset{0}{1} {2} {3} {4}".format( |
1959 recurse and " --recurse" or "", |
2008 recurse and " --recurse" or "", |
1960 skipchecks and " --skip-checks" or "", |
2009 skipchecks and " --skip-checks" or "", |
1961 propName, propValue, |
2010 propName, |
1962 " ".join(fnames)), |
2011 propValue, |
1963 client) |
2012 " ".join(fnames), |
|
2013 ), |
|
2014 client, |
|
2015 ) |
1964 QApplication.processEvents() |
2016 QApplication.processEvents() |
1965 try: |
2017 try: |
1966 with EricMutexLocker(self.vcsExecutionMutex): |
2018 with EricMutexLocker(self.vcsExecutionMutex): |
1967 for name in fnames: |
2019 for name in fnames: |
1968 client.propset(propName, propValue, name, |
2020 client.propset( |
1969 recurse=recurse, skip_checks=skipchecks) |
2021 propName, |
|
2022 propValue, |
|
2023 name, |
|
2024 recurse=recurse, |
|
2025 skip_checks=skipchecks, |
|
2026 ) |
1970 except pysvn.ClientError as e: |
2027 except pysvn.ClientError as e: |
1971 dlg.showError(e.args[0]) |
2028 dlg.showError(e.args[0]) |
1972 dlg.showMessage(self.tr("Property set.")) |
2029 dlg.showMessage(self.tr("Property set.")) |
1973 dlg.finish() |
2030 dlg.finish() |
1974 dlg.exec() |
2031 dlg.exec() |
1975 os.chdir(cwd) |
2032 os.chdir(cwd) |
1976 |
2033 |
1977 def svnDelProp(self, name, recursive=False): |
2034 def svnDelProp(self, name, recursive=False): |
1978 """ |
2035 """ |
1979 Public method used to delete a property of a file/directory. |
2036 Public method used to delete a property of a file/directory. |
1980 |
2037 |
1981 @param name file/directory name (string or list of strings) |
2038 @param name file/directory name (string or list of strings) |
1982 @param recursive flag indicating a recursive list is requested |
2039 @param recursive flag indicating a recursive list is requested |
1983 """ |
2040 """ |
1984 from .SvnPropDelDialog import SvnPropDelDialog |
2041 from .SvnPropDelDialog import SvnPropDelDialog |
|
2042 |
1985 dlg = SvnPropDelDialog(recursive) |
2043 dlg = SvnPropDelDialog(recursive) |
1986 if dlg.exec() == QDialog.DialogCode.Accepted: |
2044 if dlg.exec() == QDialog.DialogCode.Accepted: |
1987 propName, recurse = dlg.getData() |
2045 propName, recurse = dlg.getData() |
1988 |
2046 |
1989 if not propName: |
2047 if not propName: |
1990 EricMessageBox.critical( |
2048 EricMessageBox.critical( |
1991 self.__ui, |
2049 self.__ui, |
1992 self.tr("Subversion Delete Property"), |
2050 self.tr("Subversion Delete Property"), |
1993 self.tr( |
2051 self.tr("""You have to supply a property name. Aborting."""), |
1994 """You have to supply a property name. Aborting.""")) |
2052 ) |
1995 return |
2053 return |
1996 |
2054 |
1997 if isinstance(name, list): |
2055 if isinstance(name, list): |
1998 dname, fnames = self.splitPathList(name) |
2056 dname, fnames = self.splitPathList(name) |
1999 else: |
2057 else: |
2000 dname, fname = self.splitPath(name) |
2058 dname, fname = self.splitPath(name) |
2001 fnames = [fname] |
2059 fnames = [fname] |
2002 |
2060 |
2003 cwd = os.getcwd() |
2061 cwd = os.getcwd() |
2004 os.chdir(dname) |
2062 os.chdir(dname) |
2005 opts = self.options['global'] |
2063 opts = self.options["global"] |
2006 skipchecks = "--skip-checks" in opts |
2064 skipchecks = "--skip-checks" in opts |
2007 client = self.getClient() |
2065 client = self.getClient() |
2008 dlg = SvnDialog( |
2066 dlg = SvnDialog( |
2009 self.tr('Subversion Delete Property'), |
2067 self.tr("Subversion Delete Property"), |
2010 "propdel{0}{1} {2} {3}".format( |
2068 "propdel{0}{1} {2} {3}".format( |
2011 recurse and " --recurse" or "", |
2069 recurse and " --recurse" or "", |
2012 skipchecks and " --skip-checks" or "", |
2070 skipchecks and " --skip-checks" or "", |
2013 propName, " ".join(fnames)), |
2071 propName, |
2014 client) |
2072 " ".join(fnames), |
|
2073 ), |
|
2074 client, |
|
2075 ) |
2015 QApplication.processEvents() |
2076 QApplication.processEvents() |
2016 try: |
2077 try: |
2017 with EricMutexLocker(self.vcsExecutionMutex): |
2078 with EricMutexLocker(self.vcsExecutionMutex): |
2018 for name in fnames: |
2079 for name in fnames: |
2019 client.propdel(propName, name, |
2080 client.propdel( |
2020 recurse=recurse, skip_checks=skipchecks) |
2081 propName, name, recurse=recurse, skip_checks=skipchecks |
|
2082 ) |
2021 except pysvn.ClientError as e: |
2083 except pysvn.ClientError as e: |
2022 dlg.showError(e.args[0]) |
2084 dlg.showError(e.args[0]) |
2023 dlg.showMessage(self.tr("Property deleted.")) |
2085 dlg.showMessage(self.tr("Property deleted.")) |
2024 dlg.finish() |
2086 dlg.finish() |
2025 dlg.exec() |
2087 dlg.exec() |
2026 os.chdir(cwd) |
2088 os.chdir(cwd) |
2027 |
2089 |
2028 def svnListTagBranch(self, path, tags=True): |
2090 def svnListTagBranch(self, path, tags=True): |
2029 """ |
2091 """ |
2030 Public method used to list the available tags or branches. |
2092 Public method used to list the available tags or branches. |
2031 |
2093 |
2032 @param path directory name of the project (string) |
2094 @param path directory name of the project (string) |
2033 @param tags flag indicating listing of branches or tags |
2095 @param tags flag indicating listing of branches or tags |
2034 (False = branches, True = tags) |
2096 (False = branches, True = tags) |
2035 """ |
2097 """ |
2036 if self.tagbranchList is None: |
2098 if self.tagbranchList is None: |
2037 from .SvnTagBranchListDialog import SvnTagBranchListDialog |
2099 from .SvnTagBranchListDialog import SvnTagBranchListDialog |
|
2100 |
2038 self.tagbranchList = SvnTagBranchListDialog(self) |
2101 self.tagbranchList = SvnTagBranchListDialog(self) |
2039 self.tagbranchList.show() |
2102 self.tagbranchList.show() |
2040 self.tagbranchList.raise_() |
2103 self.tagbranchList.raise_() |
2041 QApplication.processEvents() |
2104 QApplication.processEvents() |
2042 res = self.tagbranchList.start(path, tags) |
2105 res = self.tagbranchList.start(path, tags) |
2043 if res: |
2106 if res: |
2044 if tags: |
2107 if tags: |
2045 self.tagsList = self.tagbranchList.getTagList() |
2108 self.tagsList = self.tagbranchList.getTagList() |
2046 if not self.showedTags: |
2109 if not self.showedTags: |
2047 self.allTagsBranchesList = ( |
2110 self.allTagsBranchesList = self.allTagsBranchesList + self.tagsList |
2048 self.allTagsBranchesList + |
|
2049 self.tagsList |
|
2050 ) |
|
2051 self.showedTags = True |
2111 self.showedTags = True |
2052 elif not tags: |
2112 elif not tags: |
2053 self.branchesList = self.tagbranchList.getTagList() |
2113 self.branchesList = self.tagbranchList.getTagList() |
2054 if not self.showedBranches: |
2114 if not self.showedBranches: |
2055 self.allTagsBranchesList = ( |
2115 self.allTagsBranchesList = ( |
2056 self.allTagsBranchesList + |
2116 self.allTagsBranchesList + self.branchesList |
2057 self.branchesList |
|
2058 ) |
2117 ) |
2059 self.showedBranches = True |
2118 self.showedBranches = True |
2060 |
2119 |
2061 def svnBlame(self, name): |
2120 def svnBlame(self, name): |
2062 """ |
2121 """ |
2063 Public method to show the output of the svn blame command. |
2122 Public method to show the output of the svn blame command. |
2064 |
2123 |
2065 @param name file name to show the blame for (string) |
2124 @param name file name to show the blame for (string) |
2066 """ |
2125 """ |
2067 if self.blame is None: |
2126 if self.blame is None: |
2068 from .SvnBlameDialog import SvnBlameDialog |
2127 from .SvnBlameDialog import SvnBlameDialog |
|
2128 |
2069 self.blame = SvnBlameDialog(self) |
2129 self.blame = SvnBlameDialog(self) |
2070 self.blame.show() |
2130 self.blame.show() |
2071 self.blame.raise_() |
2131 self.blame.raise_() |
2072 QApplication.processEvents() |
2132 QApplication.processEvents() |
2073 self.blame.start(name) |
2133 self.blame.start(name) |
2074 |
2134 |
2075 def svnExtendedDiff(self, name): |
2135 def svnExtendedDiff(self, name): |
2076 """ |
2136 """ |
2077 Public method used to view the difference of a file/directory to the |
2137 Public method used to view the difference of a file/directory to the |
2078 Subversion repository. |
2138 Subversion repository. |
2079 |
2139 |
2080 If name is a directory and is the project directory, all project files |
2140 If name is a directory and is the project directory, all project files |
2081 are saved first. If name is a file (or list of files), which is/are |
2141 are saved first. If name is a file (or list of files), which is/are |
2082 being edited and has unsaved modification, they can be saved or the |
2142 being edited and has unsaved modification, they can be saved or the |
2083 operation may be aborted. |
2143 operation may be aborted. |
2084 |
2144 |
2085 This method gives the chance to enter the revisions to be compared. |
2145 This method gives the chance to enter the revisions to be compared. |
2086 |
2146 |
2087 @param name file/directory name to be diffed (string) |
2147 @param name file/directory name to be diffed (string) |
2088 """ |
2148 """ |
2089 names = name[:] if isinstance(name, list) else [name] |
2149 names = name[:] if isinstance(name, list) else [name] |
2090 for nam in names: |
2150 for nam in names: |
2091 if os.path.isfile(nam): |
2151 if os.path.isfile(nam): |
2130 return |
2192 return |
2131 else: |
2193 else: |
2132 project = ericApp().getObject("Project") |
2194 project = ericApp().getObject("Project") |
2133 if nam == project.ppath and not project.saveAllScripts(): |
2195 if nam == project.ppath and not project.saveAllScripts(): |
2134 return |
2196 return |
2135 |
2197 |
2136 dname = self.splitPath(names[0])[0] |
2198 dname = self.splitPath(names[0])[0] |
2137 |
2199 |
2138 from .SvnUrlSelectionDialog import SvnUrlSelectionDialog |
2200 from .SvnUrlSelectionDialog import SvnUrlSelectionDialog |
2139 dlg = SvnUrlSelectionDialog(self, self.tagsList, self.branchesList, |
2201 |
2140 dname) |
2202 dlg = SvnUrlSelectionDialog(self, self.tagsList, self.branchesList, dname) |
2141 if dlg.exec() == QDialog.DialogCode.Accepted: |
2203 if dlg.exec() == QDialog.DialogCode.Accepted: |
2142 urls, summary = dlg.getURLs() |
2204 urls, summary = dlg.getURLs() |
2143 if self.diff is None: |
2205 if self.diff is None: |
2144 from .SvnDiffDialog import SvnDiffDialog |
2206 from .SvnDiffDialog import SvnDiffDialog |
|
2207 |
2145 self.diff = SvnDiffDialog(self) |
2208 self.diff = SvnDiffDialog(self) |
2146 self.diff.show() |
2209 self.diff.show() |
2147 self.diff.raise_() |
2210 self.diff.raise_() |
2148 QApplication.processEvents() |
2211 QApplication.processEvents() |
2149 self.diff.start(name, urls=urls, summary=summary) |
2212 self.diff.start(name, urls=urls, summary=summary) |
2150 |
2213 |
2151 def __svnGetFileForRevision(self, name, rev=""): |
2214 def __svnGetFileForRevision(self, name, rev=""): |
2152 """ |
2215 """ |
2153 Private method to get a file for a specific revision from the |
2216 Private method to get a file for a specific revision from the |
2154 repository. |
2217 repository. |
2155 |
2218 |
2156 @param name file name to get from the repository (string) |
2219 @param name file name to get from the repository (string) |
2157 @param rev revision to retrieve (integer or string) |
2220 @param rev revision to retrieve (integer or string) |
2158 @return contents of the file (string) and an error message (string) |
2221 @return contents of the file (string) and an error message (string) |
2159 """ |
2222 """ |
2160 output = "" |
2223 output = "" |
2161 error = "" |
2224 error = "" |
2162 |
2225 |
2163 client = self.getClient() |
2226 client = self.getClient() |
2164 try: |
2227 try: |
2165 if rev: |
2228 if rev: |
2166 if isinstance(rev, int) or rev.isdecimal(): |
2229 if isinstance(rev, int) or rev.isdecimal(): |
2167 rev = pysvn.Revision( |
2230 rev = pysvn.Revision(pysvn.opt_revision_kind.number, int(rev)) |
2168 pysvn.opt_revision_kind.number, int(rev)) |
|
2169 elif rev.startswith("{"): |
2231 elif rev.startswith("{"): |
2170 dateStr = rev[1:-1] |
2232 dateStr = rev[1:-1] |
2171 secs = QDateTime.fromString( |
2233 secs = QDateTime.fromString( |
2172 dateStr, Qt.DateFormat.ISODate).toTime_t() |
2234 dateStr, Qt.DateFormat.ISODate |
|
2235 ).toTime_t() |
2173 rev = pysvn.Revision(pysvn.opt_revision_kind.date, secs) |
2236 rev = pysvn.Revision(pysvn.opt_revision_kind.date, secs) |
2174 elif rev == "HEAD": |
2237 elif rev == "HEAD": |
2175 rev = pysvn.Revision(pysvn.opt_revision_kind.head) |
2238 rev = pysvn.Revision(pysvn.opt_revision_kind.head) |
2176 elif rev == "COMMITTED": |
2239 elif rev == "COMMITTED": |
2177 rev = pysvn.Revision(pysvn.opt_revision_kind.committed) |
2240 rev = pysvn.Revision(pysvn.opt_revision_kind.committed) |
2246 name2 = name |
2308 name2 = name |
2247 except OSError: |
2309 except OSError: |
2248 EricMessageBox.critical( |
2310 EricMessageBox.critical( |
2249 self.__ui, |
2311 self.__ui, |
2250 self.tr("Subversion Side-by-Side Difference"), |
2312 self.tr("Subversion Side-by-Side Difference"), |
2251 self.tr( |
2313 self.tr("""<p>The file <b>{0}</b> could not be read.</p>""").format( |
2252 """<p>The file <b>{0}</b> could not be read.</p>""") |
2314 name |
2253 .format(name)) |
2315 ), |
|
2316 ) |
2254 return |
2317 return |
2255 |
2318 |
2256 if self.sbsDiff is None: |
2319 if self.sbsDiff is None: |
2257 from UI.CompareDialog import CompareDialog |
2320 from UI.CompareDialog import CompareDialog |
|
2321 |
2258 self.sbsDiff = CompareDialog() |
2322 self.sbsDiff = CompareDialog() |
2259 self.sbsDiff.show() |
2323 self.sbsDiff.show() |
2260 self.sbsDiff.raise_() |
2324 self.sbsDiff.raise_() |
2261 self.sbsDiff.compare(output1, output2, name1, name2) |
2325 self.sbsDiff.compare(output1, output2, name1, name2) |
2262 |
2326 |
2263 def vcsLogBrowser(self, name, isFile=False): |
2327 def vcsLogBrowser(self, name, isFile=False): |
2264 """ |
2328 """ |
2265 Public method used to browse the log of a file/directory from the |
2329 Public method used to browse the log of a file/directory from the |
2266 Subversion repository. |
2330 Subversion repository. |
2267 |
2331 |
2268 @param name file/directory name to show the log of (string) |
2332 @param name file/directory name to show the log of (string) |
2269 @param isFile flag indicating log for a file is to be shown (boolean) |
2333 @param isFile flag indicating log for a file is to be shown (boolean) |
2270 """ |
2334 """ |
2271 if self.logBrowser is None: |
2335 if self.logBrowser is None: |
2272 from .SvnLogBrowserDialog import SvnLogBrowserDialog |
2336 from .SvnLogBrowserDialog import SvnLogBrowserDialog |
|
2337 |
2273 self.logBrowser = SvnLogBrowserDialog(self) |
2338 self.logBrowser = SvnLogBrowserDialog(self) |
2274 self.logBrowser.show() |
2339 self.logBrowser.show() |
2275 self.logBrowser.raise_() |
2340 self.logBrowser.raise_() |
2276 QApplication.processEvents() |
2341 QApplication.processEvents() |
2277 self.logBrowser.start(name, isFile=isFile) |
2342 self.logBrowser.start(name, isFile=isFile) |
2278 |
2343 |
2279 def svnLock(self, name, stealIt=False, parent=None): |
2344 def svnLock(self, name, stealIt=False, parent=None): |
2280 """ |
2345 """ |
2281 Public method used to lock a file in the Subversion repository. |
2346 Public method used to lock a file in the Subversion repository. |
2282 |
2347 |
2283 @param name file/directory name to be locked (string or list of |
2348 @param name file/directory name to be locked (string or list of |
2284 strings) |
2349 strings) |
2285 @param stealIt flag indicating a forced operation (boolean) |
2350 @param stealIt flag indicating a forced operation (boolean) |
2286 @param parent reference to the parent object of the subversion dialog |
2351 @param parent reference to the parent object of the subversion dialog |
2287 (QWidget) |
2352 (QWidget) |
2288 """ |
2353 """ |
2289 comment, ok = QInputDialog.getText( |
2354 comment, ok = QInputDialog.getText( |
2290 None, |
2355 None, |
2291 self.tr("Subversion Lock"), |
2356 self.tr("Subversion Lock"), |
2292 self.tr("Enter lock comment"), |
2357 self.tr("Enter lock comment"), |
2293 QLineEdit.EchoMode.Normal) |
2358 QLineEdit.EchoMode.Normal, |
2294 |
2359 ) |
|
2360 |
2295 if not ok: |
2361 if not ok: |
2296 return |
2362 return |
2297 |
2363 |
2298 if isinstance(name, list): |
2364 if isinstance(name, list): |
2299 dname, fnames = self.splitPathList(name) |
2365 dname, fnames = self.splitPathList(name) |
2300 else: |
2366 else: |
2301 dname, fname = self.splitPath(name) |
2367 dname, fname = self.splitPath(name) |
2302 fnames = [fname] |
2368 fnames = [fname] |
2303 |
2369 |
2304 cwd = os.getcwd() |
2370 cwd = os.getcwd() |
2305 os.chdir(dname) |
2371 os.chdir(dname) |
2306 client = self.getClient() |
2372 client = self.getClient() |
2307 dlg = SvnDialog( |
2373 dlg = SvnDialog( |
2308 self.tr('Locking in the Subversion repository'), |
2374 self.tr("Locking in the Subversion repository"), |
2309 "lock{0}{1} {2}".format( |
2375 "lock{0}{1} {2}".format( |
2310 stealIt and " --force" or "", |
2376 stealIt and " --force" or "", |
2311 comment and (" --message {0}".format(comment)) or "", |
2377 comment and (" --message {0}".format(comment)) or "", |
2312 " ".join(fnames)), |
2378 " ".join(fnames), |
2313 client, parent=parent) |
2379 ), |
|
2380 client, |
|
2381 parent=parent, |
|
2382 ) |
2314 QApplication.processEvents() |
2383 QApplication.processEvents() |
2315 try: |
2384 try: |
2316 with EricMutexLocker(self.vcsExecutionMutex): |
2385 with EricMutexLocker(self.vcsExecutionMutex): |
2317 client.lock(fnames, comment, force=stealIt) |
2386 client.lock(fnames, comment, force=stealIt) |
2318 except pysvn.ClientError as e: |
2387 except pysvn.ClientError as e: |
2356 except AttributeError as e: |
2426 except AttributeError as e: |
2357 dlg.showError(str(e)) |
2427 dlg.showError(str(e)) |
2358 dlg.finish() |
2428 dlg.finish() |
2359 dlg.exec() |
2429 dlg.exec() |
2360 os.chdir(cwd) |
2430 os.chdir(cwd) |
2361 |
2431 |
2362 def svnInfo(self, projectPath, name): |
2432 def svnInfo(self, projectPath, name): |
2363 """ |
2433 """ |
2364 Public method to show repository information about a file or directory. |
2434 Public method to show repository information about a file or directory. |
2365 |
2435 |
2366 @param projectPath path name of the project (string) |
2436 @param projectPath path name of the project (string) |
2367 @param name file/directory name relative to the project (string) |
2437 @param name file/directory name relative to the project (string) |
2368 """ |
2438 """ |
2369 from .SvnInfoDialog import SvnInfoDialog |
2439 from .SvnInfoDialog import SvnInfoDialog |
|
2440 |
2370 dlg = SvnInfoDialog(self) |
2441 dlg = SvnInfoDialog(self) |
2371 dlg.start(projectPath, name) |
2442 dlg.start(projectPath, name) |
2372 dlg.exec() |
2443 dlg.exec() |
2373 |
2444 |
2374 def svnRelocate(self, projectPath): |
2445 def svnRelocate(self, projectPath): |
2375 """ |
2446 """ |
2376 Public method to relocate the working copy to a new repository URL. |
2447 Public method to relocate the working copy to a new repository URL. |
2377 |
2448 |
2378 @param projectPath path name of the project (string) |
2449 @param projectPath path name of the project (string) |
2379 """ |
2450 """ |
2380 from .SvnRelocateDialog import SvnRelocateDialog |
2451 from .SvnRelocateDialog import SvnRelocateDialog |
|
2452 |
2381 currUrl = self.svnGetReposName(projectPath) |
2453 currUrl = self.svnGetReposName(projectPath) |
2382 dlg = SvnRelocateDialog(currUrl) |
2454 dlg = SvnRelocateDialog(currUrl) |
2383 if dlg.exec() == QDialog.DialogCode.Accepted: |
2455 if dlg.exec() == QDialog.DialogCode.Accepted: |
2384 newUrl, inside = dlg.getData() |
2456 newUrl, inside = dlg.getData() |
2385 if inside: |
2457 if inside: |
2386 msg = "switch {0} {1}".format(newUrl, projectPath) |
2458 msg = "switch {0} {1}".format(newUrl, projectPath) |
2387 else: |
2459 else: |
2388 msg = "relocate {0} {1} {2}".format(currUrl, newUrl, |
2460 msg = "relocate {0} {1} {2}".format(currUrl, newUrl, projectPath) |
2389 projectPath) |
|
2390 client = self.getClient() |
2461 client = self.getClient() |
2391 dlg = SvnDialog(self.tr('Relocating'), msg, client) |
2462 dlg = SvnDialog(self.tr("Relocating"), msg, client) |
2392 QApplication.processEvents() |
2463 QApplication.processEvents() |
2393 try: |
2464 try: |
2394 with EricMutexLocker(self.vcsExecutionMutex): |
2465 with EricMutexLocker(self.vcsExecutionMutex): |
2395 if inside: |
2466 if inside: |
2396 client.switch(projectPath, newUrl) |
2467 client.switch(projectPath, newUrl) |
2397 else: |
2468 else: |
2398 client.relocate(currUrl, newUrl, projectPath, |
2469 client.relocate(currUrl, newUrl, projectPath, recurse=True) |
2399 recurse=True) |
|
2400 except pysvn.ClientError as e: |
2470 except pysvn.ClientError as e: |
2401 dlg.showError(e.args[0]) |
2471 dlg.showError(e.args[0]) |
2402 dlg.finish() |
2472 dlg.finish() |
2403 dlg.exec() |
2473 dlg.exec() |
2404 |
2474 |
2405 def svnRepoBrowser(self, projectPath=None): |
2475 def svnRepoBrowser(self, projectPath=None): |
2406 """ |
2476 """ |
2407 Public method to open the repository browser. |
2477 Public method to open the repository browser. |
2408 |
2478 |
2409 @param projectPath path name of the project (string) |
2479 @param projectPath path name of the project (string) |
2410 """ |
2480 """ |
2411 url = self.svnGetReposName(projectPath) if projectPath else None |
2481 url = self.svnGetReposName(projectPath) if projectPath else None |
2412 if url is None: |
2482 if url is None: |
2413 url, ok = QInputDialog.getText( |
2483 url, ok = QInputDialog.getText( |
2414 None, |
2484 None, |
2415 self.tr("Repository Browser"), |
2485 self.tr("Repository Browser"), |
2416 self.tr("Enter the repository URL."), |
2486 self.tr("Enter the repository URL."), |
2417 QLineEdit.EchoMode.Normal) |
2487 QLineEdit.EchoMode.Normal, |
|
2488 ) |
2418 if not ok or not url: |
2489 if not ok or not url: |
2419 return |
2490 return |
2420 |
2491 |
2421 if self.repoBrowser is None: |
2492 if self.repoBrowser is None: |
2422 from .SvnRepoBrowserDialog import SvnRepoBrowserDialog |
2493 from .SvnRepoBrowserDialog import SvnRepoBrowserDialog |
|
2494 |
2423 self.repoBrowser = SvnRepoBrowserDialog(self) |
2495 self.repoBrowser = SvnRepoBrowserDialog(self) |
2424 self.repoBrowser.start(url) |
2496 self.repoBrowser.start(url) |
2425 self.repoBrowser.show() |
2497 self.repoBrowser.show() |
2426 self.repoBrowser.raise_() |
2498 self.repoBrowser.raise_() |
2427 |
2499 |
2428 def svnRemoveFromChangelist(self, names): |
2500 def svnRemoveFromChangelist(self, names): |
2429 """ |
2501 """ |
2430 Public method to remove a file or directory from its changelist. |
2502 Public method to remove a file or directory from its changelist. |
2431 |
2503 |
2432 Note: Directories will be removed recursively. |
2504 Note: Directories will be removed recursively. |
2433 |
2505 |
2434 @param names name or list of names of file or directory to remove |
2506 @param names name or list of names of file or directory to remove |
2435 (string) |
2507 (string) |
2436 """ |
2508 """ |
2437 if not isinstance(names, list): |
2509 if not isinstance(names, list): |
2438 names = [names] |
2510 names = [names] |
2439 client = self.getClient() |
2511 client = self.getClient() |
2440 dlg = SvnDialog( |
2512 dlg = SvnDialog( |
2441 self.tr('Remove from changelist'), |
2513 self.tr("Remove from changelist"), |
2442 "changelist --remove {0}".format(" ".join(names)), |
2514 "changelist --remove {0}".format(" ".join(names)), |
2443 client) |
2515 client, |
|
2516 ) |
2444 QApplication.processEvents() |
2517 QApplication.processEvents() |
2445 try: |
2518 try: |
2446 with EricMutexLocker(self.vcsExecutionMutex): |
2519 with EricMutexLocker(self.vcsExecutionMutex): |
2447 for name in names: |
2520 for name in names: |
2448 client.remove_from_changelists(name) |
2521 client.remove_from_changelists(name) |
2449 except pysvn.ClientError as e: |
2522 except pysvn.ClientError as e: |
2450 dlg.showError(e.args[0]) |
2523 dlg.showError(e.args[0]) |
2451 dlg.finish() |
2524 dlg.finish() |
2452 dlg.exec() |
2525 dlg.exec() |
2453 |
2526 |
2454 def svnAddToChangelist(self, names): |
2527 def svnAddToChangelist(self, names): |
2455 """ |
2528 """ |
2456 Public method to add a file or directory to a changelist. |
2529 Public method to add a file or directory to a changelist. |
2457 |
2530 |
2458 Note: Directories will be added recursively. |
2531 Note: Directories will be added recursively. |
2459 |
2532 |
2460 @param names name or list of names of file or directory to add |
2533 @param names name or list of names of file or directory to add |
2461 (string) |
2534 (string) |
2462 """ |
2535 """ |
2463 if not isinstance(names, list): |
2536 if not isinstance(names, list): |
2464 names = [names] |
2537 names = [names] |
2465 |
2538 |
2466 clname, ok = QInputDialog.getItem( |
2539 clname, ok = QInputDialog.getItem( |
2467 None, |
2540 None, |
2468 self.tr("Add to changelist"), |
2541 self.tr("Add to changelist"), |
2469 self.tr("Enter name of the changelist:"), |
2542 self.tr("Enter name of the changelist:"), |
2470 sorted(self.svnGetChangelists()), |
2543 sorted(self.svnGetChangelists()), |
2471 0, True) |
2544 0, |
|
2545 True, |
|
2546 ) |
2472 if not ok or not clname: |
2547 if not ok or not clname: |
2473 return |
2548 return |
2474 |
2549 |
2475 client = self.getClient() |
2550 client = self.getClient() |
2476 dlg = SvnDialog( |
2551 dlg = SvnDialog( |
2477 self.tr('Add to changelist'), |
2552 self.tr("Add to changelist"), |
2478 "changelist {0}".format(" ".join(names)), |
2553 "changelist {0}".format(" ".join(names)), |
2479 client) |
2554 client, |
|
2555 ) |
2480 QApplication.processEvents() |
2556 QApplication.processEvents() |
2481 try: |
2557 try: |
2482 with EricMutexLocker(self.vcsExecutionMutex): |
2558 with EricMutexLocker(self.vcsExecutionMutex): |
2483 for name in names: |
2559 for name in names: |
2484 client.add_to_changelist( |
2560 client.add_to_changelist(name, clname, depth=pysvn.depth.infinity) |
2485 name, clname, depth=pysvn.depth.infinity) |
|
2486 except pysvn.ClientError as e: |
2561 except pysvn.ClientError as e: |
2487 dlg.showError(e.args[0]) |
2562 dlg.showError(e.args[0]) |
2488 dlg.finish() |
2563 dlg.finish() |
2489 dlg.exec() |
2564 dlg.exec() |
2490 |
2565 |
2491 def svnShowChangelists(self, path): |
2566 def svnShowChangelists(self, path): |
2492 """ |
2567 """ |
2493 Public method used to inspect the change lists defined for the project. |
2568 Public method used to inspect the change lists defined for the project. |
2494 |
2569 |
2495 @param path directory name to show change lists for (string) |
2570 @param path directory name to show change lists for (string) |
2496 """ |
2571 """ |
2497 from .SvnChangeListsDialog import SvnChangeListsDialog |
2572 from .SvnChangeListsDialog import SvnChangeListsDialog |
|
2573 |
2498 self.changeLists = SvnChangeListsDialog(self) |
2574 self.changeLists = SvnChangeListsDialog(self) |
2499 self.changeLists.show() |
2575 self.changeLists.show() |
2500 QApplication.processEvents() |
2576 QApplication.processEvents() |
2501 self.changeLists.start(path) |
2577 self.changeLists.start(path) |
2502 |
2578 |
2503 def svnGetChangelists(self): |
2579 def svnGetChangelists(self): |
2504 """ |
2580 """ |
2505 Public method to get a list of all defined change lists. |
2581 Public method to get a list of all defined change lists. |
2506 |
2582 |
2507 @return list of defined change list names (list of strings) |
2583 @return list of defined change list names (list of strings) |
2508 """ |
2584 """ |
2509 changelists = [] |
2585 changelists = [] |
2510 client = self.getClient() |
2586 client = self.getClient() |
2511 if hasattr(client, 'get_changelist'): |
2587 if hasattr(client, "get_changelist"): |
2512 ppath = ericApp().getObject("Project").getProjectPath() |
2588 ppath = ericApp().getObject("Project").getProjectPath() |
2513 with contextlib.suppress(pysvn.ClientError): |
2589 with contextlib.suppress(pysvn.ClientError): |
2514 with EricMutexLocker(self.vcsExecutionMutex): |
2590 with EricMutexLocker(self.vcsExecutionMutex): |
2515 entries = client.get_changelist( |
2591 entries = client.get_changelist(ppath, depth=pysvn.depth.infinity) |
2516 ppath, depth=pysvn.depth.infinity) |
|
2517 for entry in entries: |
2592 for entry in entries: |
2518 changelist = entry[1] |
2593 changelist = entry[1] |
2519 if changelist not in changelists: |
2594 if changelist not in changelists: |
2520 changelists.append(changelist) |
2595 changelists.append(changelist) |
2521 |
2596 |
2522 return changelists |
2597 return changelists |
2523 |
2598 |
2524 def svnUpgrade(self, path): |
2599 def svnUpgrade(self, path): |
2525 """ |
2600 """ |
2526 Public method to upgrade the working copy format. |
2601 Public method to upgrade the working copy format. |
2527 |
2602 |
2528 @param path directory name to show change lists for (string) |
2603 @param path directory name to show change lists for (string) |
2529 """ |
2604 """ |
2530 client = self.getClient() |
2605 client = self.getClient() |
2531 dlg = SvnDialog( |
2606 dlg = SvnDialog(self.tr("Upgrade"), "upgrade {0}".format(path), client) |
2532 self.tr('Upgrade'), |
|
2533 "upgrade {0}".format(path), |
|
2534 client) |
|
2535 QApplication.processEvents() |
2607 QApplication.processEvents() |
2536 try: |
2608 try: |
2537 with EricMutexLocker(self.vcsExecutionMutex): |
2609 with EricMutexLocker(self.vcsExecutionMutex): |
2538 client.upgrade(path) |
2610 client.upgrade(path) |
2539 except pysvn.ClientError as e: |
2611 except pysvn.ClientError as e: |
2572 return "{0}://{1}/{2}".format(scheme, host, quote(path)) |
2644 return "{0}://{1}/{2}".format(scheme, host, quote(path)) |
2573 |
2645 |
2574 def svnNormalizeURL(self, url): |
2646 def svnNormalizeURL(self, url): |
2575 """ |
2647 """ |
2576 Public method to normalize a url for subversion. |
2648 Public method to normalize a url for subversion. |
2577 |
2649 |
2578 @param url url string (string) |
2650 @param url url string (string) |
2579 @return properly normalized url for subversion (string) |
2651 @return properly normalized url for subversion (string) |
2580 """ |
2652 """ |
2581 protocol, url = url.split("://", 1) |
2653 protocol, url = url.split("://", 1) |
2582 if url.startswith("\\\\"): |
2654 if url.startswith("\\\\"): |
2583 url = url[2:] |
2655 url = url[2:] |
2584 if protocol == "file": |
2656 if protocol == "file": |
2585 url = os.path.normcase(url) |
2657 url = os.path.normcase(url) |
2586 if url[1] == ":": |
2658 if url[1] == ":": |
2587 url = url.replace(":", "|", 1) |
2659 url = url.replace(":", "|", 1) |
2588 url = url.replace('\\', '/') |
2660 url = url.replace("\\", "/") |
2589 if url.endswith('/'): |
2661 if url.endswith("/"): |
2590 url = url[:-1] |
2662 url = url[:-1] |
2591 if not url.startswith("/") and url[1] in [":", "|"]: |
2663 if not url.startswith("/") and url[1] in [":", "|"]: |
2592 url = "/{0}".format(url) |
2664 url = "/{0}".format(url) |
2593 return "{0}://{1}".format(protocol, url) |
2665 return "{0}://{1}".format(protocol, url) |
2594 |
2666 |
2595 ########################################################################### |
2667 ########################################################################### |
2596 ## Methods to get the helper objects are below. |
2668 ## Methods to get the helper objects are below. |
2597 ########################################################################### |
2669 ########################################################################### |
2598 |
2670 |
2599 def vcsGetProjectBrowserHelper(self, browser, project, |
2671 def vcsGetProjectBrowserHelper(self, browser, project, isTranslationsBrowser=False): |
2600 isTranslationsBrowser=False): |
|
2601 """ |
2672 """ |
2602 Public method to instanciate a helper object for the different |
2673 Public method to instanciate a helper object for the different |
2603 project browsers. |
2674 project browsers. |
2604 |
2675 |
2605 @param browser reference to the project browser object |
2676 @param browser reference to the project browser object |
2606 @param project reference to the project object |
2677 @param project reference to the project object |
2607 @param isTranslationsBrowser flag indicating, the helper is requested |
2678 @param isTranslationsBrowser flag indicating, the helper is requested |
2608 for the translations browser (this needs some special treatment) |
2679 for the translations browser (this needs some special treatment) |
2609 @return the project browser helper object |
2680 @return the project browser helper object |
2610 """ |
2681 """ |
2611 from .ProjectBrowserHelper import SvnProjectBrowserHelper |
2682 from .ProjectBrowserHelper import SvnProjectBrowserHelper |
2612 return SvnProjectBrowserHelper(self, browser, project, |
2683 |
2613 isTranslationsBrowser) |
2684 return SvnProjectBrowserHelper(self, browser, project, isTranslationsBrowser) |
2614 |
2685 |
2615 def vcsGetProjectHelper(self, project): |
2686 def vcsGetProjectHelper(self, project): |
2616 """ |
2687 """ |
2617 Public method to instanciate a helper object for the project. |
2688 Public method to instanciate a helper object for the project. |
2618 |
2689 |
2619 @param project reference to the project object |
2690 @param project reference to the project object |
2620 @return the project helper object |
2691 @return the project helper object |
2621 """ |
2692 """ |
2622 helper = self.__plugin.getProjectHelper() |
2693 helper = self.__plugin.getProjectHelper() |
2623 helper.setObjects(self, project) |
2694 helper.setObjects(self, project) |
2624 self.__wcng = ( |
2695 self.__wcng = ( |
2625 os.path.exists( |
2696 os.path.exists(os.path.join(project.getProjectPath(), ".svn", "format")) |
2626 os.path.join(project.getProjectPath(), ".svn", "format")) or |
2697 or os.path.exists(os.path.join(project.getProjectPath(), "_svn", "format")) |
2627 os.path.exists( |
2698 or os.path.exists(os.path.join(project.getProjectPath(), ".svn", "wc.db")) |
2628 os.path.join(project.getProjectPath(), "_svn", "format")) or |
2699 or os.path.exists(os.path.join(project.getProjectPath(), "_svn", "wc.db")) |
2629 os.path.exists( |
|
2630 os.path.join(project.getProjectPath(), ".svn", "wc.db")) or |
|
2631 os.path.exists( |
|
2632 os.path.join(project.getProjectPath(), "_svn", "wc.db")) |
|
2633 ) |
2700 ) |
2634 return helper |
2701 return helper |
2635 |
2702 |
2636 ########################################################################### |
2703 ########################################################################### |
2637 ## Status Monitor Thread methods |
2704 ## Status Monitor Thread methods |