|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the version control systems interface to Mercurial. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 try: |
|
12 str = unicode |
|
13 except NameError: |
|
14 pass |
|
15 |
|
16 import os |
|
17 import shutil |
|
18 |
|
19 from PyQt5.QtCore import QProcess, pyqtSignal, QFileInfo, QFileSystemWatcher, \ |
|
20 QCoreApplication |
|
21 from PyQt5.QtWidgets import QApplication, QDialog, QInputDialog |
|
22 |
|
23 from E5Gui.E5Application import e5App |
|
24 from E5Gui import E5MessageBox, E5FileDialog |
|
25 |
|
26 from QScintilla.MiniEditor import MiniEditor |
|
27 |
|
28 from VCS.VersionControl import VersionControl |
|
29 from VCS.RepositoryInfoDialog import VcsRepositoryInfoDialog |
|
30 |
|
31 from .HgDialog import HgDialog |
|
32 |
|
33 import Utilities |
|
34 |
|
35 |
|
36 class Hg(VersionControl): |
|
37 """ |
|
38 Class implementing the version control systems interface to Mercurial. |
|
39 |
|
40 @signal committed() emitted after the commit action has completed |
|
41 @signal activeExtensionsChanged() emitted when the list of active |
|
42 extensions has changed |
|
43 @signal iniFileChanged() emitted when a Mercurial/repo configuration file |
|
44 has changed |
|
45 """ |
|
46 committed = pyqtSignal() |
|
47 activeExtensionsChanged = pyqtSignal() |
|
48 iniFileChanged = pyqtSignal() |
|
49 |
|
50 IgnoreFileName = ".hgignore" |
|
51 |
|
52 def __init__(self, plugin, parent=None, name=None): |
|
53 """ |
|
54 Constructor |
|
55 |
|
56 @param plugin reference to the plugin object |
|
57 @param parent parent widget (QWidget) |
|
58 @param name name of this object (string) |
|
59 """ |
|
60 VersionControl.__init__(self, parent, name) |
|
61 self.defaultOptions = { |
|
62 'global': [''], |
|
63 'commit': [''], |
|
64 'checkout': [''], |
|
65 'update': [''], |
|
66 'add': [''], |
|
67 'remove': [''], |
|
68 'diff': [''], |
|
69 'log': [''], |
|
70 'history': [''], |
|
71 'status': [''], |
|
72 'tag': [''], |
|
73 'export': [''] |
|
74 } |
|
75 |
|
76 self.__plugin = plugin |
|
77 self.__ui = parent |
|
78 |
|
79 self.options = self.defaultOptions |
|
80 self.tagsList = [] |
|
81 self.branchesList = [] |
|
82 self.allTagsBranchesList = [] |
|
83 self.bookmarksList = [] |
|
84 self.showedTags = False |
|
85 self.showedBranches = False |
|
86 |
|
87 self.tagTypeList = [ |
|
88 'tags', |
|
89 'branches', |
|
90 ] |
|
91 |
|
92 self.commandHistory = [] |
|
93 |
|
94 if "HG_ASP_DOT_NET_HACK" in os.environ: |
|
95 self.adminDir = '_hg' |
|
96 else: |
|
97 self.adminDir = '.hg' |
|
98 |
|
99 self.logBrowser = None |
|
100 self.logBrowserIncoming = None |
|
101 self.logBrowserOutgoing = None |
|
102 self.diff = None |
|
103 self.sbsDiff = None |
|
104 self.status = None |
|
105 self.summary = None |
|
106 self.tagbranchList = None |
|
107 self.annotate = None |
|
108 self.repoEditor = None |
|
109 self.serveDlg = None |
|
110 self.bookmarksListDlg = None |
|
111 self.bookmarksInOutDlg = None |
|
112 self.conflictsDlg = None |
|
113 |
|
114 self.bundleFile = None |
|
115 self.__lastChangeGroupPath = None |
|
116 |
|
117 self.statusCache = {} |
|
118 |
|
119 self.__commitData = {} |
|
120 self.__commitDialog = None |
|
121 |
|
122 self.__forgotNames = [] |
|
123 |
|
124 self.__activeExtensions = [] |
|
125 |
|
126 from .HgUtilities import getConfigPath |
|
127 self.__iniWatcher = QFileSystemWatcher(self) |
|
128 self.__iniWatcher.fileChanged.connect(self.__iniFileChanged) |
|
129 cfgFile = getConfigPath() |
|
130 if os.path.exists(cfgFile): |
|
131 self.__iniWatcher.addPath(cfgFile) |
|
132 |
|
133 self.__client = None |
|
134 |
|
135 self.__repoDir = "" |
|
136 self.__repoIniFile = "" |
|
137 self.__defaultConfigured = False |
|
138 self.__defaultPushConfigured = False |
|
139 |
|
140 # instantiate the extensions |
|
141 from .QueuesExtension.queues import Queues |
|
142 from .FetchExtension.fetch import Fetch |
|
143 from .PurgeExtension.purge import Purge |
|
144 from .GpgExtension.gpg import Gpg |
|
145 from .RebaseExtension.rebase import Rebase |
|
146 from .ShelveExtension.shelve import Shelve |
|
147 from .LargefilesExtension.largefiles import Largefiles |
|
148 from .StripExtension.strip import Strip |
|
149 from .HisteditExtension.histedit import Histedit |
|
150 self.__extensions = { |
|
151 "mq": Queues(self), |
|
152 "fetch": Fetch(self), |
|
153 "purge": Purge(self), |
|
154 "gpg": Gpg(self), |
|
155 "rebase": Rebase(self), |
|
156 "shelve": Shelve(self), |
|
157 "largefiles": Largefiles(self), |
|
158 "strip": Strip(self), |
|
159 "histedit": Histedit(self), |
|
160 } |
|
161 |
|
162 def getPlugin(self): |
|
163 """ |
|
164 Public method to get a reference to the plugin object. |
|
165 |
|
166 @return reference to the plugin object (VcsMercurialPlugin) |
|
167 """ |
|
168 return self.__plugin |
|
169 |
|
170 def getEncoding(self): |
|
171 """ |
|
172 Public method to get the encoding to be used by Mercurial. |
|
173 |
|
174 @return encoding (string) |
|
175 """ |
|
176 return self.__plugin.getPreferences("Encoding") |
|
177 |
|
178 def vcsShutdown(self): |
|
179 """ |
|
180 Public method used to shutdown the Mercurial interface. |
|
181 """ |
|
182 if self.logBrowser is not None: |
|
183 self.logBrowser.close() |
|
184 if self.logBrowserIncoming is not None: |
|
185 self.logBrowserIncoming.close() |
|
186 if self.logBrowserOutgoing is not None: |
|
187 self.logBrowserOutgoing.close() |
|
188 if self.diff is not None: |
|
189 self.diff.close() |
|
190 if self.sbsDiff is not None: |
|
191 self.sbsDiff.close() |
|
192 if self.status is not None: |
|
193 self.status.close() |
|
194 if self.summary is not None: |
|
195 self.summary.close() |
|
196 if self.tagbranchList is not None: |
|
197 self.tagbranchList.close() |
|
198 if self.annotate is not None: |
|
199 self.annotate.close() |
|
200 if self.serveDlg is not None: |
|
201 self.serveDlg.close() |
|
202 |
|
203 if self.bookmarksListDlg is not None: |
|
204 self.bookmarksListDlg.close() |
|
205 if self.bookmarksInOutDlg is not None: |
|
206 self.bookmarksInOutDlg.close() |
|
207 |
|
208 if self.conflictsDlg is not None: |
|
209 self.conflictsDlg.close() |
|
210 |
|
211 if self.bundleFile and os.path.exists(self.bundleFile): |
|
212 os.remove(self.bundleFile) |
|
213 |
|
214 # shut down the project helpers |
|
215 self.__projectHelper.shutdown() |
|
216 |
|
217 # shut down the extensions |
|
218 for extension in self.__extensions.values(): |
|
219 extension.shutdown() |
|
220 |
|
221 # shut down the client |
|
222 self.__client and self.__client.stopServer() |
|
223 |
|
224 def getClient(self): |
|
225 """ |
|
226 Public method to get a reference to the command server interface. |
|
227 |
|
228 @return reference to the client (HgClient) |
|
229 """ |
|
230 return self.__client |
|
231 |
|
232 def initCommand(self, command): |
|
233 """ |
|
234 Public method to initialize a command arguments list. |
|
235 |
|
236 @param command command name (string) |
|
237 @return list of command options (list of string) |
|
238 """ |
|
239 args = [command] |
|
240 self.addArguments(args, self.__plugin.getGlobalOptions()) |
|
241 return args |
|
242 |
|
243 def vcsExists(self): |
|
244 """ |
|
245 Public method used to test for the presence of the hg executable. |
|
246 |
|
247 @return flag indicating the existence (boolean) and an error message |
|
248 (string) |
|
249 """ |
|
250 from .HgUtilities import hgVersion |
|
251 |
|
252 self.versionStr, self.version, errMsg = hgVersion(self.__plugin) |
|
253 hgExists = errMsg == "" |
|
254 if hgExists: |
|
255 self.__getExtensionsInfo() |
|
256 return hgExists, errMsg |
|
257 |
|
258 def vcsInit(self, vcsDir, noDialog=False): |
|
259 """ |
|
260 Public method used to initialize the mercurial repository. |
|
261 |
|
262 The initialization is done, when a project is converted into a |
|
263 Mercurial controlled project. Therefore we always return TRUE without |
|
264 doing anything. |
|
265 |
|
266 @param vcsDir name of the VCS directory (string) |
|
267 @param noDialog flag indicating quiet operations (boolean) |
|
268 @return always TRUE |
|
269 """ |
|
270 return True |
|
271 |
|
272 def vcsConvertProject(self, vcsDataDict, project): |
|
273 """ |
|
274 Public method to convert an uncontrolled project to a version |
|
275 controlled project. |
|
276 |
|
277 @param vcsDataDict dictionary of data required for the conversion |
|
278 @param project reference to the project object |
|
279 """ |
|
280 success = self.vcsImport(vcsDataDict, project.ppath)[0] |
|
281 if not success: |
|
282 E5MessageBox.critical( |
|
283 self.__ui, |
|
284 self.tr("Create project repository"), |
|
285 self.tr( |
|
286 """The project repository could not be created.""")) |
|
287 else: |
|
288 pfn = project.pfile |
|
289 if not os.path.isfile(pfn): |
|
290 pfn += "z" |
|
291 project.closeProject() |
|
292 project.openProject(pfn) |
|
293 |
|
294 def vcsImport(self, vcsDataDict, projectDir, noDialog=False): |
|
295 """ |
|
296 Public method used to import the project into the Mercurial repository. |
|
297 |
|
298 @param vcsDataDict dictionary of data required for the import |
|
299 @param projectDir project directory (string) |
|
300 @param noDialog flag indicating quiet operations |
|
301 @return flag indicating an execution without errors (boolean) |
|
302 and a flag indicating the version controll status (boolean) |
|
303 """ |
|
304 msg = vcsDataDict["message"] |
|
305 if not msg: |
|
306 msg = '***' |
|
307 |
|
308 args = self.initCommand("init") |
|
309 args.append(projectDir) |
|
310 # init is not possible with the command server |
|
311 dia = HgDialog(self.tr('Creating Mercurial repository'), self) |
|
312 res = dia.startProcess(args) |
|
313 if res: |
|
314 dia.exec_() |
|
315 status = dia.normalExit() |
|
316 |
|
317 if status: |
|
318 ignoreName = os.path.join(projectDir, Hg.IgnoreFileName) |
|
319 if not os.path.exists(ignoreName): |
|
320 status = self.hgCreateIgnoreFile(projectDir) |
|
321 |
|
322 if status: |
|
323 args = self.initCommand("commit") |
|
324 args.append('--addremove') |
|
325 args.append('--message') |
|
326 args.append(msg) |
|
327 dia = HgDialog( |
|
328 self.tr('Initial commit to Mercurial repository'), |
|
329 self) |
|
330 res = dia.startProcess(args, projectDir) |
|
331 if res: |
|
332 dia.exec_() |
|
333 status = dia.normalExit() |
|
334 |
|
335 return status, False |
|
336 |
|
337 def vcsCheckout(self, vcsDataDict, projectDir, noDialog=False): |
|
338 """ |
|
339 Public method used to check the project out of a Mercurial repository |
|
340 (clone). |
|
341 |
|
342 @param vcsDataDict dictionary of data required for the checkout |
|
343 @param projectDir project directory to create (string) |
|
344 @param noDialog flag indicating quiet operations |
|
345 @return flag indicating an execution without errors (boolean) |
|
346 """ |
|
347 noDialog = False |
|
348 try: |
|
349 rev = vcsDataDict["revision"] |
|
350 except KeyError: |
|
351 rev = None |
|
352 vcsUrl = self.hgNormalizeURL(vcsDataDict["url"]) |
|
353 |
|
354 args = self.initCommand("clone") |
|
355 if rev: |
|
356 args.append("--rev") |
|
357 args.append(rev) |
|
358 if vcsDataDict["largefiles"]: |
|
359 args.append("--all-largefiles") |
|
360 args.append(vcsUrl) |
|
361 args.append(projectDir) |
|
362 |
|
363 if noDialog: |
|
364 if self.__client is None: |
|
365 return self.startSynchronizedProcess(QProcess(), 'hg', args) |
|
366 else: |
|
367 out, err = self.__client.runcommand(args) |
|
368 return err == "" |
|
369 else: |
|
370 dia = HgDialog( |
|
371 self.tr('Cloning project from a Mercurial repository'), |
|
372 self) |
|
373 res = dia.startProcess(args) |
|
374 if res: |
|
375 dia.exec_() |
|
376 return dia.normalExit() |
|
377 |
|
378 def vcsExport(self, vcsDataDict, projectDir): |
|
379 """ |
|
380 Public method used to export a directory from the Mercurial repository. |
|
381 |
|
382 @param vcsDataDict dictionary of data required for the checkout |
|
383 @param projectDir project directory to create (string) |
|
384 @return flag indicating an execution without errors (boolean) |
|
385 """ |
|
386 status = self.vcsCheckout(vcsDataDict, projectDir) |
|
387 shutil.rmtree(os.path.join(projectDir, self.adminDir), True) |
|
388 if os.path.exists(os.path.join(projectDir, Hg.IgnoreFileName)): |
|
389 os.remove(os.path.join(projectDir, Hg.IgnoreFileName)) |
|
390 return status |
|
391 |
|
392 def vcsCommit(self, name, message, noDialog=False, closeBranch=False, |
|
393 mq=False): |
|
394 """ |
|
395 Public method used to make the change of a file/directory permanent |
|
396 in the Mercurial repository. |
|
397 |
|
398 @param name file/directory name to be committed (string or list of |
|
399 strings) |
|
400 @param message message for this operation (string) |
|
401 @param noDialog flag indicating quiet operations |
|
402 @keyparam closeBranch flag indicating a close branch commit (boolean) |
|
403 @keyparam mq flag indicating a queue commit (boolean) |
|
404 """ |
|
405 msg = message |
|
406 |
|
407 if mq: |
|
408 # ensure dialog is shown for a queue commit |
|
409 noDialog = False |
|
410 |
|
411 if not noDialog: |
|
412 # call CommitDialog and get message from there |
|
413 if self.__commitDialog is None: |
|
414 from .HgCommitDialog import HgCommitDialog |
|
415 self.__commitDialog = HgCommitDialog(self, msg, mq, self.__ui) |
|
416 self.__commitDialog.accepted.connect(self.__vcsCommit_Step2) |
|
417 self.__commitDialog.show() |
|
418 self.__commitDialog.raise_() |
|
419 self.__commitDialog.activateWindow() |
|
420 |
|
421 self.__commitData["name"] = name |
|
422 self.__commitData["msg"] = msg |
|
423 self.__commitData["noDialog"] = noDialog |
|
424 self.__commitData["closeBranch"] = closeBranch |
|
425 self.__commitData["mq"] = mq |
|
426 |
|
427 if noDialog: |
|
428 self.__vcsCommit_Step2() |
|
429 |
|
430 def __vcsCommit_Step2(self): |
|
431 """ |
|
432 Private slot performing the second step of the commit action. |
|
433 """ |
|
434 name = self.__commitData["name"] |
|
435 msg = self.__commitData["msg"] |
|
436 noDialog = self.__commitData["noDialog"] |
|
437 closeBranch = self.__commitData["closeBranch"] |
|
438 mq = self.__commitData["mq"] |
|
439 |
|
440 if not noDialog: |
|
441 # check, if there are unsaved changes, that should be committed |
|
442 if isinstance(name, list): |
|
443 nameList = name |
|
444 else: |
|
445 nameList = [name] |
|
446 ok = True |
|
447 for nam in nameList: |
|
448 # check for commit of the project |
|
449 if os.path.isdir(nam): |
|
450 project = e5App().getObject("Project") |
|
451 if nam == project.getProjectPath(): |
|
452 ok &= \ |
|
453 project.checkAllScriptsDirty( |
|
454 reportSyntaxErrors=True) and \ |
|
455 project.checkDirty() |
|
456 continue |
|
457 elif os.path.isfile(nam): |
|
458 editor = \ |
|
459 e5App().getObject("ViewManager").getOpenEditor(nam) |
|
460 if editor: |
|
461 ok &= editor.checkDirty() |
|
462 if not ok: |
|
463 break |
|
464 |
|
465 if not ok: |
|
466 res = E5MessageBox.yesNo( |
|
467 self.__ui, |
|
468 self.tr("Commit Changes"), |
|
469 self.tr( |
|
470 """The commit affects files, that have unsaved""" |
|
471 """ changes. Shall the commit be continued?"""), |
|
472 icon=E5MessageBox.Warning) |
|
473 if not res: |
|
474 return |
|
475 |
|
476 if isinstance(name, list): |
|
477 dname, fnames = self.splitPathList(name) |
|
478 else: |
|
479 dname, fname = self.splitPath(name) |
|
480 |
|
481 # find the root of the repo |
|
482 repodir = dname |
|
483 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
484 repodir = os.path.dirname(repodir) |
|
485 if os.path.splitdrive(repodir)[1] == os.sep: |
|
486 return |
|
487 |
|
488 if self.__commitDialog is not None: |
|
489 msg, amend, commitSubrepositories, author, dateTime = \ |
|
490 self.__commitDialog.getCommitData() |
|
491 self.__commitDialog.deleteLater() |
|
492 self.__commitDialog = None |
|
493 if amend and not msg: |
|
494 msg = self.__getMostRecentCommitMessage(repodir) |
|
495 else: |
|
496 amend = False |
|
497 commitSubrepositories = False |
|
498 author = "" |
|
499 dateTime = "" |
|
500 |
|
501 if not msg and not amend: |
|
502 msg = '***' |
|
503 |
|
504 args = self.initCommand("commit") |
|
505 args.append("-v") |
|
506 if mq: |
|
507 args.append("--mq") |
|
508 else: |
|
509 if closeBranch: |
|
510 args.append("--close-branch") |
|
511 if amend: |
|
512 args.append("--amend") |
|
513 if commitSubrepositories: |
|
514 args.append("--subrepos") |
|
515 if author: |
|
516 args.append("--user") |
|
517 args.append(author) |
|
518 if dateTime: |
|
519 args.append("--date") |
|
520 args.append(dateTime) |
|
521 if msg: |
|
522 args.append("--message") |
|
523 args.append(msg) |
|
524 if self.__client: |
|
525 if isinstance(name, list): |
|
526 self.addArguments(args, name) |
|
527 else: |
|
528 if dname != repodir or fname != ".": |
|
529 args.append(name) |
|
530 else: |
|
531 if isinstance(name, list): |
|
532 self.addArguments(args, fnames) |
|
533 else: |
|
534 if dname != repodir or fname != ".": |
|
535 args.append(fname) |
|
536 |
|
537 if noDialog: |
|
538 self.startSynchronizedProcess(QProcess(), "hg", args, dname) |
|
539 else: |
|
540 dia = HgDialog( |
|
541 self.tr('Committing changes to Mercurial repository'), |
|
542 self) |
|
543 res = dia.startProcess(args, dname) |
|
544 if res: |
|
545 dia.exec_() |
|
546 self.committed.emit() |
|
547 if self.__forgotNames: |
|
548 model = e5App().getObject("Project").getModel() |
|
549 for name in self.__forgotNames: |
|
550 model.updateVCSStatus(name) |
|
551 self.__forgotNames = [] |
|
552 self.checkVCSStatus() |
|
553 |
|
554 def __getMostRecentCommitMessage(self, repodir): |
|
555 """ |
|
556 Private method to get the most recent commit message. |
|
557 |
|
558 Note: This message is extracted from the parent commit of the |
|
559 working directory. |
|
560 |
|
561 @param repodir path containing the repository |
|
562 @type str |
|
563 @return most recent commit message |
|
564 @rtype str |
|
565 """ |
|
566 args = self.initCommand("log") |
|
567 args.append("--rev") |
|
568 args.append(".") |
|
569 args.append('--template') |
|
570 args.append('{desc}') |
|
571 |
|
572 output = "" |
|
573 if self.__client is None: |
|
574 process = QProcess() |
|
575 process.setWorkingDirectory(repodir) |
|
576 process.start('hg', args) |
|
577 procStarted = process.waitForStarted(5000) |
|
578 if procStarted: |
|
579 finished = process.waitForFinished(30000) |
|
580 if finished and process.exitCode() == 0: |
|
581 output = str(process.readAllStandardOutput(), |
|
582 self.getEncoding(), 'replace') |
|
583 else: |
|
584 output, error = self.__client.runcommand(args) |
|
585 |
|
586 return output |
|
587 |
|
588 def vcsUpdate(self, name, noDialog=False, revision=None): |
|
589 """ |
|
590 Public method used to update a file/directory with the Mercurial |
|
591 repository. |
|
592 |
|
593 @param name file/directory name to be updated (string or list of |
|
594 strings) |
|
595 @param noDialog flag indicating quiet operations (boolean) |
|
596 @keyparam revision revision to update to (string) |
|
597 @return flag indicating, that the update contained an add |
|
598 or delete (boolean) |
|
599 """ |
|
600 args = self.initCommand("update") |
|
601 if "-v" not in args and "--verbose" not in args: |
|
602 args.append("-v") |
|
603 if revision: |
|
604 args.append("-r") |
|
605 args.append(revision) |
|
606 |
|
607 if isinstance(name, list): |
|
608 dname, fnames = self.splitPathList(name) |
|
609 else: |
|
610 dname, fname = self.splitPath(name) |
|
611 |
|
612 # find the root of the repo |
|
613 repodir = dname |
|
614 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
615 repodir = os.path.dirname(repodir) |
|
616 if os.path.splitdrive(repodir)[1] == os.sep: |
|
617 return False |
|
618 |
|
619 if noDialog: |
|
620 if self.__client is None: |
|
621 self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) |
|
622 else: |
|
623 out, err = self.__client.runcommand(args) |
|
624 res = False |
|
625 else: |
|
626 dia = HgDialog(self.tr( |
|
627 'Synchronizing with the Mercurial repository'), |
|
628 self) |
|
629 res = dia.startProcess(args, repodir) |
|
630 if res: |
|
631 dia.exec_() |
|
632 res = dia.hasAddOrDelete() |
|
633 self.checkVCSStatus() |
|
634 return res |
|
635 |
|
636 def vcsAdd(self, name, isDir=False, noDialog=False): |
|
637 """ |
|
638 Public method used to add a file/directory to the Mercurial repository. |
|
639 |
|
640 @param name file/directory name to be added (string) |
|
641 @param isDir flag indicating name is a directory (boolean) |
|
642 @param noDialog flag indicating quiet operations |
|
643 """ |
|
644 args = self.initCommand("add") |
|
645 args.append("-v") |
|
646 |
|
647 if isinstance(name, list): |
|
648 if isDir: |
|
649 dname, fname = os.path.split(name[0]) |
|
650 else: |
|
651 dname, fnames = self.splitPathList(name) |
|
652 else: |
|
653 if isDir: |
|
654 dname, fname = os.path.split(name) |
|
655 else: |
|
656 dname, fname = self.splitPath(name) |
|
657 |
|
658 # find the root of the repo |
|
659 repodir = dname |
|
660 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
661 repodir = os.path.dirname(repodir) |
|
662 if os.path.splitdrive(repodir)[1] == os.sep: |
|
663 return |
|
664 |
|
665 if isinstance(name, list): |
|
666 self.addArguments(args, name) |
|
667 else: |
|
668 args.append(name) |
|
669 |
|
670 if noDialog: |
|
671 if self.__client is None: |
|
672 self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) |
|
673 else: |
|
674 out, err = self.__client.runcommand(args) |
|
675 else: |
|
676 dia = HgDialog( |
|
677 self.tr( |
|
678 'Adding files/directories to the Mercurial repository'), |
|
679 self) |
|
680 res = dia.startProcess(args, repodir) |
|
681 if res: |
|
682 dia.exec_() |
|
683 |
|
684 def vcsAddBinary(self, name, isDir=False): |
|
685 """ |
|
686 Public method used to add a file/directory in binary mode to the |
|
687 Mercurial repository. |
|
688 |
|
689 @param name file/directory name to be added (string) |
|
690 @param isDir flag indicating name is a directory (boolean) |
|
691 """ |
|
692 self.vcsAdd(name, isDir) |
|
693 |
|
694 def vcsAddTree(self, path): |
|
695 """ |
|
696 Public method to add a directory tree rooted at path to the Mercurial |
|
697 repository. |
|
698 |
|
699 @param path root directory of the tree to be added (string or list of |
|
700 strings)) |
|
701 """ |
|
702 self.vcsAdd(path, isDir=False) |
|
703 |
|
704 def vcsRemove(self, name, project=False, noDialog=False): |
|
705 """ |
|
706 Public method used to remove a file/directory from the Mercurial |
|
707 repository. |
|
708 |
|
709 The default operation is to remove the local copy as well. |
|
710 |
|
711 @param name file/directory name to be removed (string or list of |
|
712 strings)) |
|
713 @param project flag indicating deletion of a project tree (boolean) |
|
714 (not needed) |
|
715 @param noDialog flag indicating quiet operations |
|
716 @return flag indicating successfull operation (boolean) |
|
717 """ |
|
718 args = self.initCommand("remove") |
|
719 args.append("-v") |
|
720 if noDialog and '--force' not in args: |
|
721 args.append('--force') |
|
722 |
|
723 if isinstance(name, list): |
|
724 dname, fnames = self.splitPathList(name) |
|
725 self.addArguments(args, name) |
|
726 else: |
|
727 dname, fname = self.splitPath(name) |
|
728 args.append(name) |
|
729 |
|
730 # find the root of the repo |
|
731 repodir = dname |
|
732 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
733 repodir = os.path.dirname(repodir) |
|
734 if os.path.splitdrive(repodir)[1] == os.sep: |
|
735 return False |
|
736 |
|
737 if noDialog: |
|
738 if self.__client is None: |
|
739 res = self.startSynchronizedProcess( |
|
740 QProcess(), 'hg', args, repodir) |
|
741 else: |
|
742 out, err = self.__client.runcommand(args) |
|
743 res = err == "" |
|
744 else: |
|
745 dia = HgDialog( |
|
746 self.tr( |
|
747 'Removing files/directories from the Mercurial' |
|
748 ' repository'), |
|
749 self) |
|
750 res = dia.startProcess(args, repodir) |
|
751 if res: |
|
752 dia.exec_() |
|
753 res = dia.normalExitWithoutErrors() |
|
754 |
|
755 return res |
|
756 |
|
757 def vcsMove(self, name, project, target=None, noDialog=False): |
|
758 """ |
|
759 Public method used to move a file/directory. |
|
760 |
|
761 @param name file/directory name to be moved (string) |
|
762 @param project reference to the project object |
|
763 @param target new name of the file/directory (string) |
|
764 @param noDialog flag indicating quiet operations |
|
765 @return flag indicating successfull operation (boolean) |
|
766 """ |
|
767 isDir = os.path.isdir(name) |
|
768 |
|
769 res = False |
|
770 if noDialog: |
|
771 if target is None: |
|
772 return False |
|
773 force = True |
|
774 accepted = True |
|
775 else: |
|
776 from .HgCopyDialog import HgCopyDialog |
|
777 dlg = HgCopyDialog(name, None, True) |
|
778 accepted = dlg.exec_() == QDialog.Accepted |
|
779 if accepted: |
|
780 target, force = dlg.getData() |
|
781 |
|
782 if accepted: |
|
783 args = self.initCommand("rename") |
|
784 args.append("-v") |
|
785 if force: |
|
786 args.append('--force') |
|
787 args.append(name) |
|
788 args.append(target) |
|
789 |
|
790 dname, fname = self.splitPath(name) |
|
791 # find the root of the repo |
|
792 repodir = dname |
|
793 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
794 repodir = os.path.dirname(repodir) |
|
795 if os.path.splitdrive(repodir)[1] == os.sep: |
|
796 return False |
|
797 |
|
798 if noDialog: |
|
799 if self.__client is None: |
|
800 res = self.startSynchronizedProcess( |
|
801 QProcess(), 'hg', args, repodir) |
|
802 else: |
|
803 out, err = self.__client.runcommand(args) |
|
804 res = err == "" |
|
805 else: |
|
806 dia = HgDialog(self.tr('Renaming {0}').format(name), self) |
|
807 res = dia.startProcess(args, repodir) |
|
808 if res: |
|
809 dia.exec_() |
|
810 res = dia.normalExit() |
|
811 if res: |
|
812 if target.startswith(project.getProjectPath()): |
|
813 if isDir: |
|
814 project.moveDirectory(name, target) |
|
815 else: |
|
816 project.renameFileInPdata(name, target) |
|
817 else: |
|
818 if isDir: |
|
819 project.removeDirectory(name) |
|
820 else: |
|
821 project.removeFile(name) |
|
822 return res |
|
823 |
|
824 def vcsDiff(self, name): |
|
825 """ |
|
826 Public method used to view the difference of a file/directory to the |
|
827 Mercurial repository. |
|
828 |
|
829 If name is a directory and is the project directory, all project files |
|
830 are saved first. If name is a file (or list of files), which is/are |
|
831 being edited and has unsaved modification, they can be saved or the |
|
832 operation may be aborted. |
|
833 |
|
834 @param name file/directory name to be diffed (string) |
|
835 """ |
|
836 if isinstance(name, list): |
|
837 names = name[:] |
|
838 else: |
|
839 names = [name] |
|
840 for nam in names: |
|
841 if os.path.isfile(nam): |
|
842 editor = e5App().getObject("ViewManager").getOpenEditor(nam) |
|
843 if editor and not editor.checkDirty(): |
|
844 return |
|
845 else: |
|
846 project = e5App().getObject("Project") |
|
847 if nam == project.ppath and not project.saveAllScripts(): |
|
848 return |
|
849 if self.diff is None: |
|
850 from .HgDiffDialog import HgDiffDialog |
|
851 self.diff = HgDiffDialog(self) |
|
852 self.diff.show() |
|
853 self.diff.raise_() |
|
854 QApplication.processEvents() |
|
855 self.diff.start(name, refreshable=True) |
|
856 |
|
857 def vcsStatus(self, name): |
|
858 """ |
|
859 Public method used to view the status of files/directories in the |
|
860 Mercurial repository. |
|
861 |
|
862 @param name file/directory name(s) to show the status of |
|
863 (string or list of strings) |
|
864 """ |
|
865 if self.status is None: |
|
866 from .HgStatusDialog import HgStatusDialog |
|
867 self.status = HgStatusDialog(self) |
|
868 self.status.show() |
|
869 self.status.raise_() |
|
870 self.status.start(name) |
|
871 |
|
872 def hgSummary(self, mq=False, largefiles=False): |
|
873 """ |
|
874 Public method used to show some summary information of the |
|
875 working directory state. |
|
876 |
|
877 @param mq flag indicating to show the queue status as well (boolean) |
|
878 @param largefiles flag indicating to show the largefiles status as |
|
879 well (boolean) |
|
880 """ |
|
881 if self.summary is None: |
|
882 from .HgSummaryDialog import HgSummaryDialog |
|
883 self.summary = HgSummaryDialog(self) |
|
884 self.summary.show() |
|
885 self.summary.raise_() |
|
886 self.summary.start(self.__projectHelper.getProject().getProjectPath(), |
|
887 mq=mq, largefiles=largefiles) |
|
888 |
|
889 def vcsTag(self, name, revision=None, tagName=None): |
|
890 """ |
|
891 Public method used to set/remove a tag in the Mercurial repository. |
|
892 |
|
893 @param name file/directory name to determine the repo root from |
|
894 (string) |
|
895 @param revision revision to set tag for (string) |
|
896 @param tagName name of the tag (string) |
|
897 @return flag indicating a performed tag action (boolean) |
|
898 """ |
|
899 dname, fname = self.splitPath(name) |
|
900 |
|
901 # find the root of the repo |
|
902 repodir = dname |
|
903 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
904 repodir = os.path.dirname(repodir) |
|
905 if os.path.splitdrive(repodir)[1] == os.sep: |
|
906 return False |
|
907 |
|
908 from .HgTagDialog import HgTagDialog |
|
909 dlg = HgTagDialog(self.hgGetTagsList(repodir, withType=True), |
|
910 revision, tagName) |
|
911 if dlg.exec_() == QDialog.Accepted: |
|
912 tag, revision, tagOp, force = dlg.getParameters() |
|
913 else: |
|
914 return False |
|
915 |
|
916 args = self.initCommand("tag") |
|
917 msgPart = "" |
|
918 if tagOp in [HgTagDialog.CreateLocalTag, HgTagDialog.DeleteLocalTag]: |
|
919 args.append('--local') |
|
920 msgPart = "local " |
|
921 else: |
|
922 msgPart = "global " |
|
923 if tagOp in [HgTagDialog.DeleteGlobalTag, HgTagDialog.DeleteLocalTag]: |
|
924 args.append('--remove') |
|
925 if tagOp in [HgTagDialog.CreateGlobalTag, HgTagDialog.CreateLocalTag]: |
|
926 if revision: |
|
927 args.append("--rev") |
|
928 args.append(revision) |
|
929 if force: |
|
930 args.append("--force") |
|
931 args.append('--message') |
|
932 if tagOp in [HgTagDialog.CreateGlobalTag, HgTagDialog.CreateLocalTag]: |
|
933 tag = tag.strip().replace(" ", "_") |
|
934 args.append("Created {1}tag <{0}>.".format(tag, msgPart)) |
|
935 else: |
|
936 args.append("Removed {1}tag <{0}>.".format(tag, msgPart)) |
|
937 args.append(tag) |
|
938 |
|
939 dia = HgDialog(self.tr('Tagging in the Mercurial repository'), |
|
940 self) |
|
941 res = dia.startProcess(args, repodir) |
|
942 if res: |
|
943 dia.exec_() |
|
944 |
|
945 return True |
|
946 |
|
947 def hgRevert(self, name): |
|
948 """ |
|
949 Public method used to revert changes made to a file/directory. |
|
950 |
|
951 @param name file/directory name to be reverted (string) |
|
952 @return flag indicating, that the update contained an add |
|
953 or delete (boolean) |
|
954 """ |
|
955 args = self.initCommand("revert") |
|
956 if not self.getPlugin().getPreferences("CreateBackup"): |
|
957 args.append("--no-backup") |
|
958 args.append("-v") |
|
959 if isinstance(name, list): |
|
960 dname, fnames = self.splitPathList(name) |
|
961 self.addArguments(args, name) |
|
962 names = name[:] |
|
963 else: |
|
964 dname, fname = self.splitPath(name) |
|
965 args.append(name) |
|
966 names = [name] |
|
967 |
|
968 # find the root of the repo |
|
969 repodir = dname |
|
970 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
971 repodir = os.path.dirname(repodir) |
|
972 if os.path.splitdrive(repodir)[1] == os.sep: |
|
973 return False |
|
974 |
|
975 project = e5App().getObject("Project") |
|
976 names = [project.getRelativePath(nam) for nam in names] |
|
977 if names[0]: |
|
978 from UI.DeleteFilesConfirmationDialog import \ |
|
979 DeleteFilesConfirmationDialog |
|
980 dlg = DeleteFilesConfirmationDialog( |
|
981 self.parent(), |
|
982 self.tr("Revert changes"), |
|
983 self.tr( |
|
984 "Do you really want to revert all changes to these files" |
|
985 " or directories?"), |
|
986 names) |
|
987 yes = dlg.exec_() == QDialog.Accepted |
|
988 else: |
|
989 yes = E5MessageBox.yesNo( |
|
990 None, |
|
991 self.tr("Revert changes"), |
|
992 self.tr("""Do you really want to revert all changes of""" |
|
993 """ the project?""")) |
|
994 if yes: |
|
995 dia = HgDialog(self.tr('Reverting changes'), self) |
|
996 res = dia.startProcess(args, repodir) |
|
997 if res: |
|
998 dia.exec_() |
|
999 res = dia.hasAddOrDelete() |
|
1000 self.checkVCSStatus() |
|
1001 else: |
|
1002 res = False |
|
1003 |
|
1004 return res |
|
1005 |
|
1006 def vcsMerge(self, name, rev=""): |
|
1007 """ |
|
1008 Public method used to merge a URL/revision into the local project. |
|
1009 |
|
1010 @param name file/directory name to be merged |
|
1011 @type str |
|
1012 @keyparam rev revision to merge with |
|
1013 @type str |
|
1014 """ |
|
1015 dname, fname = self.splitPath(name) |
|
1016 |
|
1017 # find the root of the repo |
|
1018 repodir = dname |
|
1019 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1020 repodir = os.path.dirname(repodir) |
|
1021 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1022 return |
|
1023 |
|
1024 if not rev: |
|
1025 from .HgMergeDialog import HgMergeDialog |
|
1026 dlg = HgMergeDialog(self.hgGetTagsList(repodir), |
|
1027 self.hgGetBranchesList(repodir), |
|
1028 self.hgGetBookmarksList(repodir)) |
|
1029 if dlg.exec_() == QDialog.Accepted: |
|
1030 rev, force = dlg.getParameters() |
|
1031 else: |
|
1032 return |
|
1033 else: |
|
1034 force = False |
|
1035 |
|
1036 args = self.initCommand("merge") |
|
1037 if force: |
|
1038 args.append("--force") |
|
1039 if self.getPlugin().getPreferences("InternalMerge"): |
|
1040 args.append("--tool") |
|
1041 args.append("internal:merge") |
|
1042 if rev: |
|
1043 args.append("--rev") |
|
1044 args.append(rev) |
|
1045 |
|
1046 dia = HgDialog(self.tr('Merging').format(name), self) |
|
1047 res = dia.startProcess(args, repodir) |
|
1048 if res: |
|
1049 dia.exec_() |
|
1050 self.checkVCSStatus() |
|
1051 |
|
1052 def hgReMerge(self, name): |
|
1053 """ |
|
1054 Public method used to merge a URL/revision into the local project. |
|
1055 |
|
1056 @param name file/directory name to be merged (string) |
|
1057 """ |
|
1058 args = self.initCommand("resolve") |
|
1059 if self.getPlugin().getPreferences("InternalMerge"): |
|
1060 args.append("--tool") |
|
1061 args.append("internal:merge") |
|
1062 if isinstance(name, list): |
|
1063 dname, fnames = self.splitPathList(name) |
|
1064 self.addArguments(args, name) |
|
1065 names = name[:] |
|
1066 else: |
|
1067 dname, fname = self.splitPath(name) |
|
1068 args.append(name) |
|
1069 names = [name] |
|
1070 |
|
1071 # find the root of the repo |
|
1072 repodir = dname |
|
1073 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1074 repodir = os.path.dirname(repodir) |
|
1075 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1076 return |
|
1077 |
|
1078 project = e5App().getObject("Project") |
|
1079 names = [project.getRelativePath(nam) for nam in names] |
|
1080 if names[0]: |
|
1081 from UI.DeleteFilesConfirmationDialog import \ |
|
1082 DeleteFilesConfirmationDialog |
|
1083 dlg = DeleteFilesConfirmationDialog( |
|
1084 self.parent(), |
|
1085 self.tr("Re-Merge"), |
|
1086 self.tr( |
|
1087 "Do you really want to re-merge these files" |
|
1088 " or directories?"), |
|
1089 names) |
|
1090 yes = dlg.exec_() == QDialog.Accepted |
|
1091 else: |
|
1092 yes = E5MessageBox.yesNo( |
|
1093 None, |
|
1094 self.tr("Re-Merge"), |
|
1095 self.tr("""Do you really want to re-merge the project?""")) |
|
1096 if yes: |
|
1097 dia = HgDialog(self.tr('Re-Merging').format(name), self) |
|
1098 res = dia.startProcess(args, repodir) |
|
1099 if res: |
|
1100 dia.exec_() |
|
1101 self.checkVCSStatus() |
|
1102 |
|
1103 def vcsSwitch(self, name): |
|
1104 """ |
|
1105 Public method used to switch a working directory to a different |
|
1106 revision. |
|
1107 |
|
1108 @param name directory name to be switched (string) |
|
1109 @return flag indicating, that the switch contained an add |
|
1110 or delete (boolean) |
|
1111 """ |
|
1112 dname, fname = self.splitPath(name) |
|
1113 |
|
1114 # find the root of the repo |
|
1115 repodir = dname |
|
1116 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1117 repodir = os.path.dirname(repodir) |
|
1118 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1119 return False |
|
1120 |
|
1121 from .HgRevisionSelectionDialog import HgRevisionSelectionDialog |
|
1122 dlg = HgRevisionSelectionDialog(self.hgGetTagsList(repodir), |
|
1123 self.hgGetBranchesList(repodir), |
|
1124 self.hgGetBookmarksList(repodir), |
|
1125 self.tr("Current branch tip")) |
|
1126 if dlg.exec_() == QDialog.Accepted: |
|
1127 rev = dlg.getRevision() |
|
1128 return self.vcsUpdate(name, revision=rev) |
|
1129 |
|
1130 return False |
|
1131 |
|
1132 def vcsRegisteredState(self, name): |
|
1133 """ |
|
1134 Public method used to get the registered state of a file in the vcs. |
|
1135 |
|
1136 @param name filename to check (string) |
|
1137 @return a combination of canBeCommited and canBeAdded |
|
1138 """ |
|
1139 if name.endswith(os.sep): |
|
1140 name = name[:-1] |
|
1141 name = os.path.normcase(name) |
|
1142 dname, fname = self.splitPath(name) |
|
1143 |
|
1144 if fname == '.' and os.path.isdir(os.path.join(dname, self.adminDir)): |
|
1145 return self.canBeCommitted |
|
1146 |
|
1147 if name in self.statusCache: |
|
1148 return self.statusCache[name] |
|
1149 |
|
1150 # find the root of the repo |
|
1151 repodir = dname |
|
1152 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1153 repodir = os.path.dirname(repodir) |
|
1154 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1155 return 0 |
|
1156 |
|
1157 args = self.initCommand("status") |
|
1158 args.append('--all') |
|
1159 args.append('--noninteractive') |
|
1160 |
|
1161 output = "" |
|
1162 if self.__client is None: |
|
1163 process = QProcess() |
|
1164 process.setWorkingDirectory(repodir) |
|
1165 process.start('hg', args) |
|
1166 procStarted = process.waitForStarted(5000) |
|
1167 if procStarted: |
|
1168 finished = process.waitForFinished(30000) |
|
1169 if finished and process.exitCode() == 0: |
|
1170 output = str(process.readAllStandardOutput(), |
|
1171 self.getEncoding(), 'replace') |
|
1172 else: |
|
1173 output, error = self.__client.runcommand(args) |
|
1174 |
|
1175 if output: |
|
1176 for line in output.splitlines(): |
|
1177 if len(line) > 2 and line[0] in "MARC!?I" and line[1] == " ": |
|
1178 flag, path = line.split(" ", 1) |
|
1179 absname = os.path.join(repodir, os.path.normcase(path)) |
|
1180 if flag not in "?I": |
|
1181 if fname == '.': |
|
1182 if absname.startswith(dname + os.path.sep): |
|
1183 return self.canBeCommitted |
|
1184 if absname == dname: |
|
1185 return self.canBeCommitted |
|
1186 else: |
|
1187 if absname == name: |
|
1188 return self.canBeCommitted |
|
1189 |
|
1190 return self.canBeAdded |
|
1191 |
|
1192 def vcsAllRegisteredStates(self, names, dname, shortcut=True): |
|
1193 """ |
|
1194 Public method used to get the registered states of a number of files |
|
1195 in the vcs. |
|
1196 |
|
1197 <b>Note:</b> If a shortcut is to be taken, the code will only check, |
|
1198 if the named directory has been scanned already. If so, it is assumed, |
|
1199 that the states for all files have been populated by the previous run. |
|
1200 |
|
1201 @param names dictionary with all filenames to be checked as keys |
|
1202 @param dname directory to check in (string) |
|
1203 @param shortcut flag indicating a shortcut should be taken (boolean) |
|
1204 @return the received dictionary completed with a combination of |
|
1205 canBeCommited and canBeAdded or None in order to signal an error |
|
1206 """ |
|
1207 if dname.endswith(os.sep): |
|
1208 dname = dname[:-1] |
|
1209 dname = os.path.normcase(dname) |
|
1210 |
|
1211 found = False |
|
1212 for name in list(self.statusCache.keys()): |
|
1213 if name in names: |
|
1214 found = True |
|
1215 names[name] = self.statusCache[name] |
|
1216 |
|
1217 if not found: |
|
1218 # find the root of the repo |
|
1219 repodir = dname |
|
1220 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1221 repodir = os.path.dirname(repodir) |
|
1222 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1223 return names |
|
1224 |
|
1225 args = self.initCommand("status") |
|
1226 args.append('--all') |
|
1227 args.append('--noninteractive') |
|
1228 |
|
1229 output = "" |
|
1230 if self.__client is None: |
|
1231 process = QProcess() |
|
1232 process.setWorkingDirectory(dname) |
|
1233 process.start('hg', args) |
|
1234 procStarted = process.waitForStarted(5000) |
|
1235 if procStarted: |
|
1236 finished = process.waitForFinished(30000) |
|
1237 if finished and process.exitCode() == 0: |
|
1238 output = str(process.readAllStandardOutput(), |
|
1239 self.getEncoding(), 'replace') |
|
1240 else: |
|
1241 output, error = self.__client.runcommand(args) |
|
1242 |
|
1243 if output: |
|
1244 dirs = [x for x in names.keys() if os.path.isdir(x)] |
|
1245 for line in output.splitlines(): |
|
1246 if line and line[0] in "MARC!?I": |
|
1247 flag, path = line.split(" ", 1) |
|
1248 name = os.path.normcase(os.path.join(repodir, path)) |
|
1249 dirName = os.path.dirname(name) |
|
1250 if name.startswith(dname): |
|
1251 if flag not in "?I": |
|
1252 if name in names: |
|
1253 names[name] = self.canBeCommitted |
|
1254 if dirName in names: |
|
1255 names[dirName] = self.canBeCommitted |
|
1256 if dirs: |
|
1257 for d in dirs: |
|
1258 if name.startswith(d): |
|
1259 names[d] = self.canBeCommitted |
|
1260 dirs.remove(d) |
|
1261 break |
|
1262 if flag not in "?I": |
|
1263 self.statusCache[name] = self.canBeCommitted |
|
1264 self.statusCache[dirName] = self.canBeCommitted |
|
1265 else: |
|
1266 self.statusCache[name] = self.canBeAdded |
|
1267 if dirName not in self.statusCache: |
|
1268 self.statusCache[dirName] = self.canBeAdded |
|
1269 |
|
1270 return names |
|
1271 |
|
1272 def clearStatusCache(self): |
|
1273 """ |
|
1274 Public method to clear the status cache. |
|
1275 """ |
|
1276 self.statusCache = {} |
|
1277 |
|
1278 def vcsName(self): |
|
1279 """ |
|
1280 Public method returning the name of the vcs. |
|
1281 |
|
1282 @return always 'Mercurial' (string) |
|
1283 """ |
|
1284 return "Mercurial" |
|
1285 |
|
1286 def vcsInitConfig(self, project): |
|
1287 """ |
|
1288 Public method to initialize the VCS configuration. |
|
1289 |
|
1290 This method ensures, that an ignore file exists. |
|
1291 |
|
1292 @param project reference to the project (Project) |
|
1293 """ |
|
1294 ppath = project.getProjectPath() |
|
1295 if ppath: |
|
1296 ignoreName = os.path.join(ppath, Hg.IgnoreFileName) |
|
1297 if not os.path.exists(ignoreName): |
|
1298 self.hgCreateIgnoreFile(project.getProjectPath(), autoAdd=True) |
|
1299 |
|
1300 def vcsCleanup(self, name): |
|
1301 """ |
|
1302 Public method used to cleanup the working directory. |
|
1303 |
|
1304 @param name directory name to be cleaned up (string) |
|
1305 """ |
|
1306 patterns = self.getPlugin().getPreferences("CleanupPatterns").split() |
|
1307 |
|
1308 entries = [] |
|
1309 for pat in patterns: |
|
1310 entries.extend(Utilities.direntries(name, True, pat)) |
|
1311 |
|
1312 for entry in entries: |
|
1313 try: |
|
1314 os.remove(entry) |
|
1315 except OSError: |
|
1316 pass |
|
1317 |
|
1318 def vcsCommandLine(self, name): |
|
1319 """ |
|
1320 Public method used to execute arbitrary mercurial commands. |
|
1321 |
|
1322 @param name directory name of the working directory (string) |
|
1323 """ |
|
1324 from .HgCommandDialog import HgCommandDialog |
|
1325 dlg = HgCommandDialog(self.commandHistory, name) |
|
1326 if dlg.exec_() == QDialog.Accepted: |
|
1327 command = dlg.getData() |
|
1328 commandList = Utilities.parseOptionString(command) |
|
1329 |
|
1330 # This moves any previous occurrence of these arguments to the head |
|
1331 # of the list. |
|
1332 if command in self.commandHistory: |
|
1333 self.commandHistory.remove(command) |
|
1334 self.commandHistory.insert(0, command) |
|
1335 |
|
1336 args = [] |
|
1337 self.addArguments(args, commandList) |
|
1338 |
|
1339 # find the root of the repo |
|
1340 repodir = name |
|
1341 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1342 repodir = os.path.dirname(repodir) |
|
1343 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1344 return |
|
1345 |
|
1346 dia = HgDialog(self.tr('Mercurial command'), self) |
|
1347 res = dia.startProcess(args, repodir) |
|
1348 if res: |
|
1349 dia.exec_() |
|
1350 |
|
1351 def vcsOptionsDialog(self, project, archive, editable=False, parent=None): |
|
1352 """ |
|
1353 Public method to get a dialog to enter repository info. |
|
1354 |
|
1355 @param project reference to the project object |
|
1356 @param archive name of the project in the repository (string) |
|
1357 @param editable flag indicating that the project name is editable |
|
1358 (boolean) |
|
1359 @param parent parent widget (QWidget) |
|
1360 @return reference to the instantiated options dialog (HgOptionsDialog) |
|
1361 """ |
|
1362 from .HgOptionsDialog import HgOptionsDialog |
|
1363 return HgOptionsDialog(self, project, parent) |
|
1364 |
|
1365 def vcsNewProjectOptionsDialog(self, parent=None): |
|
1366 """ |
|
1367 Public method to get a dialog to enter repository info for getting a |
|
1368 new project. |
|
1369 |
|
1370 @param parent parent widget (QWidget) |
|
1371 @return reference to the instantiated options dialog |
|
1372 (HgNewProjectOptionsDialog) |
|
1373 """ |
|
1374 from .HgNewProjectOptionsDialog import HgNewProjectOptionsDialog |
|
1375 return HgNewProjectOptionsDialog(self, parent) |
|
1376 |
|
1377 def vcsRepositoryInfos(self, ppath): |
|
1378 """ |
|
1379 Public method to retrieve information about the repository. |
|
1380 |
|
1381 @param ppath local path to get the repository infos (string) |
|
1382 @return string with ready formated info for display (string) |
|
1383 """ |
|
1384 args = self.initCommand("parents") |
|
1385 args.append('--template') |
|
1386 args.append('{rev}:{node|short}@@@{tags}@@@{author|xmlescape}@@@' |
|
1387 '{date|isodate}@@@{branches}@@@{bookmarks}\n') |
|
1388 |
|
1389 output = "" |
|
1390 if self.__client is None: |
|
1391 process = QProcess() |
|
1392 process.setWorkingDirectory(ppath) |
|
1393 process.start('hg', args) |
|
1394 procStarted = process.waitForStarted(5000) |
|
1395 if procStarted: |
|
1396 finished = process.waitForFinished(30000) |
|
1397 if finished and process.exitCode() == 0: |
|
1398 output = str(process.readAllStandardOutput(), |
|
1399 self.getEncoding(), 'replace') |
|
1400 else: |
|
1401 output, error = self.__client.runcommand(args) |
|
1402 |
|
1403 infoBlock = [] |
|
1404 if output: |
|
1405 index = 0 |
|
1406 for line in output.splitlines(): |
|
1407 index += 1 |
|
1408 changeset, tags, author, date, branches, bookmarks = \ |
|
1409 line.split("@@@") |
|
1410 cdate, ctime = date.split()[:2] |
|
1411 info = [] |
|
1412 info.append(QCoreApplication.translate( |
|
1413 "mercurial", |
|
1414 """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""" |
|
1415 """<tr><td><b>Changeset</b></td><td>{1}</td></tr>""") |
|
1416 .format(index, changeset)) |
|
1417 if tags: |
|
1418 info.append(QCoreApplication.translate( |
|
1419 "mercurial", |
|
1420 """<tr><td><b>Tags</b></td><td>{0}</td></tr>""") |
|
1421 .format('<br/>'.join(tags.split()))) |
|
1422 if bookmarks: |
|
1423 info.append(QCoreApplication.translate( |
|
1424 "mercurial", |
|
1425 """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""") |
|
1426 .format('<br/>'.join(bookmarks.split()))) |
|
1427 if branches: |
|
1428 info.append(QCoreApplication.translate( |
|
1429 "mercurial", |
|
1430 """<tr><td><b>Branches</b></td><td>{0}</td></tr>""") |
|
1431 .format('<br/>'.join(branches.split()))) |
|
1432 info.append(QCoreApplication.translate( |
|
1433 "mercurial", |
|
1434 """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" |
|
1435 """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" |
|
1436 """<tr><td><b>Committed time</b></td><td>{2}</td></tr>""") |
|
1437 .format(author, cdate, ctime)) |
|
1438 infoBlock.append("\n".join(info)) |
|
1439 if infoBlock: |
|
1440 infoStr = """<tr></tr>{0}""".format("<tr></tr>".join(infoBlock)) |
|
1441 else: |
|
1442 infoStr = "" |
|
1443 |
|
1444 url = "" |
|
1445 args = self.initCommand("showconfig") |
|
1446 args.append('paths.default') |
|
1447 |
|
1448 output = "" |
|
1449 if self.__client is None: |
|
1450 process.setWorkingDirectory(ppath) |
|
1451 process.start('hg', args) |
|
1452 procStarted = process.waitForStarted(5000) |
|
1453 if procStarted: |
|
1454 finished = process.waitForFinished(30000) |
|
1455 if finished and process.exitCode() == 0: |
|
1456 output = str(process.readAllStandardOutput(), |
|
1457 self.getEncoding(), 'replace') |
|
1458 else: |
|
1459 output, error = self.__client.runcommand(args) |
|
1460 |
|
1461 if output: |
|
1462 url = output.splitlines()[0].strip() |
|
1463 else: |
|
1464 url = "" |
|
1465 |
|
1466 return QCoreApplication.translate( |
|
1467 'mercurial', |
|
1468 """<h3>Repository information</h3>\n""" |
|
1469 """<p><table>\n""" |
|
1470 """<tr><td><b>Mercurial V.</b></td><td>{0}</td></tr>\n""" |
|
1471 """<tr></tr>\n""" |
|
1472 """<tr><td><b>URL</b></td><td>{1}</td></tr>\n""" |
|
1473 """{2}""" |
|
1474 """</table></p>\n""" |
|
1475 ).format(self.versionStr, url, infoStr) |
|
1476 |
|
1477 def vcsSupportCommandOptions(self): |
|
1478 """ |
|
1479 Public method to signal the support of user settable command options. |
|
1480 |
|
1481 @return flag indicating the support of user settable command options |
|
1482 (boolean) |
|
1483 """ |
|
1484 return False |
|
1485 |
|
1486 ########################################################################### |
|
1487 ## Private Mercurial specific methods are below. |
|
1488 ########################################################################### |
|
1489 |
|
1490 def hgNormalizeURL(self, url): |
|
1491 """ |
|
1492 Public method to normalize a url for Mercurial. |
|
1493 |
|
1494 @param url url string (string) |
|
1495 @return properly normalized url for mercurial (string) |
|
1496 """ |
|
1497 url = url.replace('\\', '/') |
|
1498 if url.endswith('/'): |
|
1499 url = url[:-1] |
|
1500 urll = url.split('//') |
|
1501 return "{0}//{1}".format(urll[0], '/'.join(urll[1:])) |
|
1502 |
|
1503 def hgCopy(self, name, project): |
|
1504 """ |
|
1505 Public method used to copy a file/directory. |
|
1506 |
|
1507 @param name file/directory name to be copied (string) |
|
1508 @param project reference to the project object |
|
1509 @return flag indicating successful operation (boolean) |
|
1510 """ |
|
1511 from .HgCopyDialog import HgCopyDialog |
|
1512 dlg = HgCopyDialog(name) |
|
1513 res = False |
|
1514 if dlg.exec_() == QDialog.Accepted: |
|
1515 target, force = dlg.getData() |
|
1516 |
|
1517 args = self.initCommand("copy") |
|
1518 args.append("-v") |
|
1519 args.append(name) |
|
1520 args.append(target) |
|
1521 |
|
1522 dname, fname = self.splitPath(name) |
|
1523 # find the root of the repo |
|
1524 repodir = dname |
|
1525 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1526 repodir = os.path.dirname(repodir) |
|
1527 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1528 return False |
|
1529 |
|
1530 dia = HgDialog( |
|
1531 self.tr('Copying {0}').format(name), self) |
|
1532 res = dia.startProcess(args, repodir) |
|
1533 if res: |
|
1534 dia.exec_() |
|
1535 res = dia.normalExit() |
|
1536 if res and \ |
|
1537 target.startswith(project.getProjectPath()): |
|
1538 if os.path.isdir(name): |
|
1539 project.copyDirectory(name, target) |
|
1540 else: |
|
1541 project.appendFile(target) |
|
1542 return res |
|
1543 |
|
1544 def hgGetTagsList(self, repodir, withType=False): |
|
1545 """ |
|
1546 Public method to get the list of tags. |
|
1547 |
|
1548 @param repodir directory name of the repository (string) |
|
1549 @param withType flag indicating to get the tag type as well (boolean) |
|
1550 @return list of tags (list of string) or list of tuples of |
|
1551 tag name and flag indicating a local tag (list of tuple of string |
|
1552 and boolean), if withType is True |
|
1553 """ |
|
1554 args = self.initCommand("tags") |
|
1555 args.append('--verbose') |
|
1556 |
|
1557 output = "" |
|
1558 if self.__client is None: |
|
1559 process = QProcess() |
|
1560 process.setWorkingDirectory(repodir) |
|
1561 process.start('hg', args) |
|
1562 procStarted = process.waitForStarted(5000) |
|
1563 if procStarted: |
|
1564 finished = process.waitForFinished(30000) |
|
1565 if finished and process.exitCode() == 0: |
|
1566 output = str(process.readAllStandardOutput(), |
|
1567 self.getEncoding(), 'replace') |
|
1568 else: |
|
1569 output, error = self.__client.runcommand(args) |
|
1570 |
|
1571 tagsList = [] |
|
1572 if output: |
|
1573 for line in output.splitlines(): |
|
1574 li = line.strip().split() |
|
1575 if li[-1][0] in "1234567890": |
|
1576 # last element is a rev:changeset |
|
1577 del li[-1] |
|
1578 isLocal = False |
|
1579 else: |
|
1580 del li[-2:] |
|
1581 isLocal = True |
|
1582 name = " ".join(li) |
|
1583 if name not in ["tip", "default"]: |
|
1584 if withType: |
|
1585 tagsList.append((name, isLocal)) |
|
1586 else: |
|
1587 tagsList.append(name) |
|
1588 |
|
1589 if withType: |
|
1590 return tagsList |
|
1591 else: |
|
1592 if tagsList: |
|
1593 self.tagsList = tagsList |
|
1594 return self.tagsList[:] |
|
1595 |
|
1596 def hgGetBranchesList(self, repodir): |
|
1597 """ |
|
1598 Public method to get the list of branches. |
|
1599 |
|
1600 @param repodir directory name of the repository (string) |
|
1601 @return list of branches (list of string) |
|
1602 """ |
|
1603 args = self.initCommand("branches") |
|
1604 args.append('--closed') |
|
1605 |
|
1606 output = "" |
|
1607 if self.__client is None: |
|
1608 process = QProcess() |
|
1609 process.setWorkingDirectory(repodir) |
|
1610 process.start('hg', args) |
|
1611 procStarted = process.waitForStarted(5000) |
|
1612 if procStarted: |
|
1613 finished = process.waitForFinished(30000) |
|
1614 if finished and process.exitCode() == 0: |
|
1615 output = str(process.readAllStandardOutput(), |
|
1616 self.getEncoding(), 'replace') |
|
1617 else: |
|
1618 output, error = self.__client.runcommand(args) |
|
1619 |
|
1620 if output: |
|
1621 self.branchesList = [] |
|
1622 for line in output.splitlines(): |
|
1623 li = line.strip().split() |
|
1624 if li[-1][0] in "1234567890": |
|
1625 # last element is a rev:changeset |
|
1626 del li[-1] |
|
1627 else: |
|
1628 del li[-2:] |
|
1629 name = " ".join(li) |
|
1630 if name not in ["tip", "default"]: |
|
1631 self.branchesList.append(name) |
|
1632 |
|
1633 return self.branchesList[:] |
|
1634 |
|
1635 def hgListTagBranch(self, path, tags=True): |
|
1636 """ |
|
1637 Public method used to list the available tags or branches. |
|
1638 |
|
1639 @param path directory name of the project (string) |
|
1640 @param tags flag indicating listing of branches or tags |
|
1641 (False = branches, True = tags) |
|
1642 """ |
|
1643 from .HgTagBranchListDialog import HgTagBranchListDialog |
|
1644 self.tagbranchList = HgTagBranchListDialog(self) |
|
1645 self.tagbranchList.show() |
|
1646 if tags: |
|
1647 if not self.showedTags: |
|
1648 self.showedTags = True |
|
1649 allTagsBranchesList = self.allTagsBranchesList |
|
1650 else: |
|
1651 self.tagsList = [] |
|
1652 allTagsBranchesList = None |
|
1653 self.tagbranchList.start(path, tags, |
|
1654 self.tagsList, allTagsBranchesList) |
|
1655 else: |
|
1656 if not self.showedBranches: |
|
1657 self.showedBranches = True |
|
1658 allTagsBranchesList = self.allTagsBranchesList |
|
1659 else: |
|
1660 self.branchesList = [] |
|
1661 allTagsBranchesList = None |
|
1662 self.tagbranchList.start(path, tags, |
|
1663 self.branchesList, |
|
1664 self.allTagsBranchesList) |
|
1665 |
|
1666 def hgAnnotate(self, name): |
|
1667 """ |
|
1668 Public method to show the output of the hg annotate command. |
|
1669 |
|
1670 @param name file name to show the annotations for (string) |
|
1671 """ |
|
1672 if self.annotate is None: |
|
1673 from .HgAnnotateDialog import HgAnnotateDialog |
|
1674 self.annotate = HgAnnotateDialog(self) |
|
1675 self.annotate.show() |
|
1676 self.annotate.raise_() |
|
1677 self.annotate.start(name) |
|
1678 |
|
1679 def hgExtendedDiff(self, name): |
|
1680 """ |
|
1681 Public method used to view the difference of a file/directory to the |
|
1682 Mercurial repository. |
|
1683 |
|
1684 If name is a directory and is the project directory, all project files |
|
1685 are saved first. If name is a file (or list of files), which is/are |
|
1686 being edited and has unsaved modification, they can be saved or the |
|
1687 operation may be aborted. |
|
1688 |
|
1689 This method gives the chance to enter the revisions to be compared. |
|
1690 |
|
1691 @param name file/directory name to be diffed (string) |
|
1692 """ |
|
1693 if isinstance(name, list): |
|
1694 dname, fnames = self.splitPathList(name) |
|
1695 names = name[:] |
|
1696 else: |
|
1697 dname, fname = self.splitPath(name) |
|
1698 names = [name] |
|
1699 for nam in names: |
|
1700 if os.path.isfile(nam): |
|
1701 editor = e5App().getObject("ViewManager").getOpenEditor(nam) |
|
1702 if editor and not editor.checkDirty(): |
|
1703 return |
|
1704 else: |
|
1705 project = e5App().getObject("Project") |
|
1706 if nam == project.ppath and not project.saveAllScripts(): |
|
1707 return |
|
1708 |
|
1709 # find the root of the repo |
|
1710 repodir = dname |
|
1711 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1712 repodir = os.path.dirname(repodir) |
|
1713 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1714 return |
|
1715 |
|
1716 from .HgRevisionsSelectionDialog import HgRevisionsSelectionDialog |
|
1717 dlg = HgRevisionsSelectionDialog(self.hgGetTagsList(repodir), |
|
1718 self.hgGetBranchesList(repodir), |
|
1719 self.hgGetBookmarksList(repodir)) |
|
1720 if dlg.exec_() == QDialog.Accepted: |
|
1721 revisions = dlg.getRevisions() |
|
1722 if self.diff is None: |
|
1723 from .HgDiffDialog import HgDiffDialog |
|
1724 self.diff = HgDiffDialog(self) |
|
1725 self.diff.show() |
|
1726 self.diff.raise_() |
|
1727 self.diff.start(name, revisions) |
|
1728 |
|
1729 def __hgGetFileForRevision(self, name, rev=""): |
|
1730 """ |
|
1731 Private method to get a file for a specific revision from the |
|
1732 repository. |
|
1733 |
|
1734 @param name file name to get from the repository (string) |
|
1735 @keyparam rev revision to retrieve (string) |
|
1736 @return contents of the file (string) and an error message (string) |
|
1737 """ |
|
1738 args = self.initCommand("cat") |
|
1739 if rev: |
|
1740 args.append("--rev") |
|
1741 args.append(rev) |
|
1742 args.append(name) |
|
1743 |
|
1744 if self.__client is None: |
|
1745 output = "" |
|
1746 error = "" |
|
1747 |
|
1748 # find the root of the repo |
|
1749 repodir = self.splitPath(name)[0] |
|
1750 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1751 repodir = os.path.dirname(repodir) |
|
1752 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1753 return "", "" |
|
1754 |
|
1755 process = QProcess() |
|
1756 process.setWorkingDirectory(repodir) |
|
1757 process.start('hg', args) |
|
1758 procStarted = process.waitForStarted(5000) |
|
1759 if procStarted: |
|
1760 finished = process.waitForFinished(30000) |
|
1761 if finished: |
|
1762 if process.exitCode() == 0: |
|
1763 output = str(process.readAllStandardOutput(), |
|
1764 self.getEncoding(), 'replace') |
|
1765 else: |
|
1766 error = str(process.readAllStandardError(), |
|
1767 self.getEncoding(), 'replace') |
|
1768 else: |
|
1769 error = self.tr( |
|
1770 "The hg process did not finish within 30s.") |
|
1771 else: |
|
1772 error = self.tr( |
|
1773 'The process {0} could not be started. ' |
|
1774 'Ensure, that it is in the search path.').format('hg') |
|
1775 else: |
|
1776 output, error = self.__client.runcommand(args) |
|
1777 |
|
1778 # return file contents with 'universal newlines' |
|
1779 return output.replace('\r\n', '\n').replace('\r', '\n'), error |
|
1780 |
|
1781 def hgSbsDiff(self, name, extended=False, revisions=None): |
|
1782 """ |
|
1783 Public method used to view the difference of a file to the Mercurial |
|
1784 repository side-by-side. |
|
1785 |
|
1786 @param name file name to be diffed (string) |
|
1787 @keyparam extended flag indicating the extended variant (boolean) |
|
1788 @keyparam revisions tuple of two revisions (tuple of strings) |
|
1789 @exception ValueError raised to indicate an invalid name parameter |
|
1790 """ |
|
1791 if isinstance(name, list): |
|
1792 raise ValueError("Wrong parameter type") |
|
1793 |
|
1794 if extended: |
|
1795 # find the root of the repo |
|
1796 repodir = self.splitPath(name)[0] |
|
1797 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1798 repodir = os.path.dirname(repodir) |
|
1799 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1800 return |
|
1801 |
|
1802 from .HgRevisionsSelectionDialog import HgRevisionsSelectionDialog |
|
1803 dlg = HgRevisionsSelectionDialog(self.hgGetTagsList(repodir), |
|
1804 self.hgGetBranchesList(repodir), |
|
1805 self.hgGetBookmarksList(repodir)) |
|
1806 if dlg.exec_() == QDialog.Accepted: |
|
1807 rev1, rev2 = dlg.getRevisions() |
|
1808 else: |
|
1809 return |
|
1810 elif revisions: |
|
1811 rev1, rev2 = revisions[0], revisions[1] |
|
1812 else: |
|
1813 rev1, rev2 = "", "" |
|
1814 |
|
1815 output1, error = self.__hgGetFileForRevision(name, rev=rev1) |
|
1816 if error: |
|
1817 E5MessageBox.critical( |
|
1818 self.__ui, |
|
1819 self.tr("Mercurial Side-by-Side Difference"), |
|
1820 error) |
|
1821 return |
|
1822 name1 = "{0} (rev. {1})".format(name, rev1 and rev1 or ".") |
|
1823 |
|
1824 if rev2: |
|
1825 output2, error = self.__hgGetFileForRevision(name, rev=rev2) |
|
1826 if error: |
|
1827 E5MessageBox.critical( |
|
1828 self.__ui, |
|
1829 self.tr("Mercurial Side-by-Side Difference"), |
|
1830 error) |
|
1831 return |
|
1832 name2 = "{0} (rev. {1})".format(name, rev2) |
|
1833 else: |
|
1834 try: |
|
1835 f1 = open(name, "r", encoding="utf-8") |
|
1836 output2 = f1.read() |
|
1837 f1.close() |
|
1838 name2 = "{0} (Work)".format(name) |
|
1839 except IOError: |
|
1840 E5MessageBox.critical( |
|
1841 self.__ui, |
|
1842 self.tr("Mercurial Side-by-Side Difference"), |
|
1843 self.tr( |
|
1844 """<p>The file <b>{0}</b> could not be read.</p>""") |
|
1845 .format(name)) |
|
1846 return |
|
1847 |
|
1848 if self.sbsDiff is None: |
|
1849 from UI.CompareDialog import CompareDialog |
|
1850 self.sbsDiff = CompareDialog() |
|
1851 self.sbsDiff.show() |
|
1852 self.sbsDiff.raise_() |
|
1853 self.sbsDiff.compare(output1, output2, name1, name2) |
|
1854 |
|
1855 def vcsLogBrowser(self, name, isFile=False): |
|
1856 """ |
|
1857 Public method used to browse the log of a file/directory from the |
|
1858 Mercurial repository. |
|
1859 |
|
1860 @param name file/directory name to show the log of (string) |
|
1861 @keyparam isFile flag indicating log for a file is to be shown |
|
1862 (boolean) |
|
1863 """ |
|
1864 if self.logBrowser is None: |
|
1865 from .HgLogBrowserDialog import HgLogBrowserDialog |
|
1866 self.logBrowser = HgLogBrowserDialog(self) |
|
1867 self.logBrowser.show() |
|
1868 self.logBrowser.raise_() |
|
1869 self.logBrowser.start(name, isFile=isFile) |
|
1870 |
|
1871 def hgIncoming(self, name): |
|
1872 """ |
|
1873 Public method used to view the log of incoming changes from the |
|
1874 Mercurial repository. |
|
1875 |
|
1876 @param name file/directory name to show the log of (string) |
|
1877 """ |
|
1878 if self.logBrowserIncoming is None: |
|
1879 from .HgLogBrowserDialog import HgLogBrowserDialog |
|
1880 self.logBrowserIncoming = HgLogBrowserDialog( |
|
1881 self, mode="incoming") |
|
1882 self.logBrowserIncoming.show() |
|
1883 self.logBrowserIncoming.raise_() |
|
1884 self.logBrowserIncoming.start(name) |
|
1885 |
|
1886 def hgOutgoing(self, name): |
|
1887 """ |
|
1888 Public method used to view the log of outgoing changes from the |
|
1889 Mercurial repository. |
|
1890 |
|
1891 @param name file/directory name to show the log of (string) |
|
1892 """ |
|
1893 if self.logBrowserOutgoing is None: |
|
1894 from .HgLogBrowserDialog import HgLogBrowserDialog |
|
1895 self.logBrowserOutgoing = HgLogBrowserDialog( |
|
1896 self, mode="outgoing") |
|
1897 self.logBrowserOutgoing.show() |
|
1898 self.logBrowserOutgoing.raise_() |
|
1899 self.logBrowserOutgoing.start(name) |
|
1900 |
|
1901 def hgPull(self, name, revisions=None): |
|
1902 """ |
|
1903 Public method used to pull changes from a remote Mercurial repository. |
|
1904 |
|
1905 @param name directory name of the project to be pulled to |
|
1906 @type str |
|
1907 @param revisions list of revisions to be pulled |
|
1908 @type list of str |
|
1909 @return flag indicating, that the update contained an add |
|
1910 or delete |
|
1911 @rtype bool |
|
1912 """ |
|
1913 if self.getPlugin().getPreferences("PreferUnbundle") and \ |
|
1914 self.bundleFile and \ |
|
1915 os.path.exists(self.bundleFile) and \ |
|
1916 revisions is None: |
|
1917 command = "unbundle" |
|
1918 title = self.tr('Apply changegroups') |
|
1919 else: |
|
1920 command = "pull" |
|
1921 title = self.tr('Pulling from a remote Mercurial repository') |
|
1922 |
|
1923 args = self.initCommand(command) |
|
1924 args.append('-v') |
|
1925 if self.getPlugin().getPreferences("PullUpdate"): |
|
1926 args.append('--update') |
|
1927 if command == "unbundle": |
|
1928 args.append(self.bundleFile) |
|
1929 if revisions: |
|
1930 for rev in revisions: |
|
1931 args.append("--rev") |
|
1932 args.append(rev) |
|
1933 |
|
1934 # find the root of the repo |
|
1935 repodir = self.splitPath(name)[0] |
|
1936 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1937 repodir = os.path.dirname(repodir) |
|
1938 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1939 return False |
|
1940 |
|
1941 dia = HgDialog(title, self) |
|
1942 res = dia.startProcess(args, repodir) |
|
1943 if res: |
|
1944 dia.exec_() |
|
1945 res = dia.hasAddOrDelete() |
|
1946 if self.bundleFile and \ |
|
1947 os.path.exists(self.bundleFile): |
|
1948 os.remove(self.bundleFile) |
|
1949 self.bundleFile = None |
|
1950 self.checkVCSStatus() |
|
1951 return res |
|
1952 |
|
1953 def hgPush(self, name, force=False, newBranch=False, rev=None): |
|
1954 """ |
|
1955 Public method used to push changes to a remote Mercurial repository. |
|
1956 |
|
1957 @param name directory name of the project to be pushed from (string) |
|
1958 @keyparam force flag indicating a forced push (boolean) |
|
1959 @keyparam newBranch flag indicating to push a new branch (boolean) |
|
1960 @keyparam rev revision to be pushed (including all ancestors) (string) |
|
1961 """ |
|
1962 args = self.initCommand("push") |
|
1963 args.append('-v') |
|
1964 if force: |
|
1965 args.append('-f') |
|
1966 if newBranch: |
|
1967 args.append('--new-branch') |
|
1968 if rev: |
|
1969 args.append('--rev') |
|
1970 args.append(rev) |
|
1971 |
|
1972 # find the root of the repo |
|
1973 repodir = self.splitPath(name)[0] |
|
1974 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
1975 repodir = os.path.dirname(repodir) |
|
1976 if os.path.splitdrive(repodir)[1] == os.sep: |
|
1977 return |
|
1978 |
|
1979 dia = HgDialog( |
|
1980 self.tr('Pushing to a remote Mercurial repository'), self) |
|
1981 res = dia.startProcess(args, repodir) |
|
1982 if res: |
|
1983 dia.exec_() |
|
1984 self.checkVCSStatus() |
|
1985 |
|
1986 def hgInfo(self, ppath, mode="heads"): |
|
1987 """ |
|
1988 Public method to show information about the heads of the repository. |
|
1989 |
|
1990 @param ppath local path to get the repository infos (string) |
|
1991 @keyparam mode mode of the operation (string, one of heads, parents, |
|
1992 tip) |
|
1993 """ |
|
1994 if mode not in ("heads", "parents", "tip"): |
|
1995 mode = "heads" |
|
1996 |
|
1997 info = [] |
|
1998 |
|
1999 args = self.initCommand(mode) |
|
2000 args.append('--template') |
|
2001 args.append('{rev}:{node|short}@@@{tags}@@@{author|xmlescape}@@@' |
|
2002 '{date|isodate}@@@{branches}@@@{parents}@@@{bookmarks}\n') |
|
2003 |
|
2004 output = "" |
|
2005 if self.__client is None: |
|
2006 # find the root of the repo |
|
2007 repodir = self.splitPath(ppath)[0] |
|
2008 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2009 repodir = os.path.dirname(repodir) |
|
2010 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2011 return |
|
2012 |
|
2013 process = QProcess() |
|
2014 process.setWorkingDirectory(repodir) |
|
2015 process.start('hg', args) |
|
2016 procStarted = process.waitForStarted(5000) |
|
2017 if procStarted: |
|
2018 finished = process.waitForFinished(30000) |
|
2019 if finished and process.exitCode() == 0: |
|
2020 output = str(process.readAllStandardOutput(), |
|
2021 self.getEncoding(), 'replace') |
|
2022 else: |
|
2023 output, error = self.__client.runcommand(args) |
|
2024 |
|
2025 if output: |
|
2026 index = 0 |
|
2027 for line in output.splitlines(): |
|
2028 index += 1 |
|
2029 changeset, tags, author, date, branches, parents, bookmarks = \ |
|
2030 line.split("@@@") |
|
2031 cdate, ctime = date.split()[:2] |
|
2032 info.append("""<p><table>""") |
|
2033 if mode == "heads": |
|
2034 info.append(QCoreApplication.translate( |
|
2035 "mercurial", |
|
2036 """<tr><td><b>Head #{0}</b></td><td></td></tr>\n""") |
|
2037 .format(index)) |
|
2038 elif mode == "parents": |
|
2039 info.append(QCoreApplication.translate( |
|
2040 "mercurial", |
|
2041 """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""") |
|
2042 .format(index)) |
|
2043 elif mode == "tip": |
|
2044 info.append(QCoreApplication.translate( |
|
2045 "mercurial", |
|
2046 """<tr><td><b>Tip</b></td><td></td></tr>\n""")) |
|
2047 info.append(QCoreApplication.translate( |
|
2048 "mercurial", |
|
2049 """<tr><td><b>Changeset</b></td><td>{0}</td></tr>""") |
|
2050 .format(changeset)) |
|
2051 if tags: |
|
2052 info.append(QCoreApplication.translate( |
|
2053 "mercurial", |
|
2054 """<tr><td><b>Tags</b></td><td>{0}</td></tr>""") |
|
2055 .format('<br/>'.join(tags.split()))) |
|
2056 if bookmarks: |
|
2057 info.append(QCoreApplication.translate( |
|
2058 "mercurial", |
|
2059 """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""") |
|
2060 .format('<br/>'.join(bookmarks.split()))) |
|
2061 if branches: |
|
2062 info.append(QCoreApplication.translate( |
|
2063 "mercurial", |
|
2064 """<tr><td><b>Branches</b></td><td>{0}</td></tr>""") |
|
2065 .format('<br/>'.join(branches.split()))) |
|
2066 if parents: |
|
2067 info.append(QCoreApplication.translate( |
|
2068 "mercurial", |
|
2069 """<tr><td><b>Parents</b></td><td>{0}</td></tr>""") |
|
2070 .format('<br/>'.join(parents.split()))) |
|
2071 info.append(QCoreApplication.translate( |
|
2072 "mercurial", |
|
2073 """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" |
|
2074 """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" |
|
2075 """<tr><td><b>Committed time</b></td><td>{2}</td></tr>\n""" |
|
2076 """</table></p>""") |
|
2077 .format(author, cdate, ctime)) |
|
2078 |
|
2079 dlg = VcsRepositoryInfoDialog(None, "\n".join(info)) |
|
2080 dlg.exec_() |
|
2081 |
|
2082 def hgConflicts(self, name): |
|
2083 """ |
|
2084 Public method used to show a list of files containing conflicts. |
|
2085 |
|
2086 @param name file/directory name to be resolved (string) |
|
2087 """ |
|
2088 if self.conflictsDlg is None: |
|
2089 from .HgConflictsListDialog import HgConflictsListDialog |
|
2090 self.conflictsDlg = HgConflictsListDialog(self) |
|
2091 self.conflictsDlg.show() |
|
2092 self.conflictsDlg.raise_() |
|
2093 self.conflictsDlg.start(name) |
|
2094 |
|
2095 def hgResolved(self, name, unresolve=False): |
|
2096 """ |
|
2097 Public method used to resolve conflicts of a file/directory. |
|
2098 |
|
2099 @param name file/directory name to be resolved (string) |
|
2100 @param unresolve flag indicating to mark the file/directory as |
|
2101 unresolved (boolean) |
|
2102 """ |
|
2103 args = self.initCommand("resolve") |
|
2104 if unresolve: |
|
2105 args.append("--unmark") |
|
2106 else: |
|
2107 args.append("--mark") |
|
2108 |
|
2109 if isinstance(name, list): |
|
2110 dname, fnames = self.splitPathList(name) |
|
2111 self.addArguments(args, name) |
|
2112 else: |
|
2113 dname, fname = self.splitPath(name) |
|
2114 args.append(name) |
|
2115 |
|
2116 # find the root of the repo |
|
2117 repodir = dname |
|
2118 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2119 repodir = os.path.dirname(repodir) |
|
2120 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2121 return |
|
2122 |
|
2123 if unresolve: |
|
2124 title = self.tr("Marking as 'unresolved'") |
|
2125 else: |
|
2126 title = self.tr("Marking as 'resolved'") |
|
2127 dia = HgDialog(title, self) |
|
2128 res = dia.startProcess(args, repodir) |
|
2129 if res: |
|
2130 dia.exec_() |
|
2131 self.checkVCSStatus() |
|
2132 |
|
2133 def hgCancelMerge(self, name): |
|
2134 """ |
|
2135 Public method to cancel an uncommitted merge. |
|
2136 |
|
2137 @param name file/directory name (string) |
|
2138 @return flag indicating, that the cancellation contained an add |
|
2139 or delete (boolean) |
|
2140 """ |
|
2141 dname, fname = self.splitPath(name) |
|
2142 |
|
2143 # find the root of the repo |
|
2144 repodir = dname |
|
2145 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2146 repodir = os.path.dirname(repodir) |
|
2147 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2148 return False |
|
2149 |
|
2150 if self.version >= (4, 5, 0): |
|
2151 args = self.initCommand("merge") |
|
2152 args.append("--abort") |
|
2153 else: |
|
2154 args = self.initCommand("update") |
|
2155 args.append("--clean") |
|
2156 |
|
2157 dia = HgDialog( |
|
2158 self.tr('Canceling uncommitted merge'), |
|
2159 self) |
|
2160 res = dia.startProcess(args, repodir, False) |
|
2161 if res: |
|
2162 dia.exec_() |
|
2163 res = dia.hasAddOrDelete() |
|
2164 self.checkVCSStatus() |
|
2165 return res |
|
2166 |
|
2167 def hgBranch(self, name): |
|
2168 """ |
|
2169 Public method used to create a branch in the Mercurial repository. |
|
2170 |
|
2171 @param name file/directory name to be branched (string) |
|
2172 """ |
|
2173 dname, fname = self.splitPath(name) |
|
2174 |
|
2175 # find the root of the repo |
|
2176 repodir = dname |
|
2177 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2178 repodir = os.path.dirname(repodir) |
|
2179 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2180 return |
|
2181 |
|
2182 from .HgBranchInputDialog import HgBranchInputDialog |
|
2183 dlg = HgBranchInputDialog(self.hgGetBranchesList(repodir)) |
|
2184 if dlg.exec_() == QDialog.Accepted: |
|
2185 name, commit = dlg.getData() |
|
2186 name = name.strip().replace(" ", "_") |
|
2187 args = self.initCommand("branch") |
|
2188 args.append(name) |
|
2189 |
|
2190 dia = HgDialog( |
|
2191 self.tr('Creating branch in the Mercurial repository'), |
|
2192 self) |
|
2193 res = dia.startProcess(args, repodir) |
|
2194 if res: |
|
2195 dia.exec_() |
|
2196 if commit: |
|
2197 self.vcsCommit( |
|
2198 repodir, |
|
2199 self.tr("Created new branch <{0}>.").format( |
|
2200 name)) |
|
2201 |
|
2202 def hgShowBranch(self, name): |
|
2203 """ |
|
2204 Public method used to show the current branch of the working directory. |
|
2205 |
|
2206 @param name file/directory name (string) |
|
2207 """ |
|
2208 dname, fname = self.splitPath(name) |
|
2209 |
|
2210 # find the root of the repo |
|
2211 repodir = dname |
|
2212 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2213 repodir = os.path.dirname(repodir) |
|
2214 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2215 return |
|
2216 |
|
2217 args = self.initCommand("branch") |
|
2218 |
|
2219 dia = HgDialog(self.tr('Showing current branch'), self) |
|
2220 res = dia.startProcess(args, repodir, False) |
|
2221 if res: |
|
2222 dia.exec_() |
|
2223 |
|
2224 def hgGetCurrentBranch(self, repodir): |
|
2225 """ |
|
2226 Public method to get the current branch of the working directory. |
|
2227 |
|
2228 @param repodir directory name of the repository |
|
2229 @type str |
|
2230 @return name of the current branch |
|
2231 @rtype str |
|
2232 """ |
|
2233 args = self.initCommand("branch") |
|
2234 |
|
2235 output = "" |
|
2236 if self.__client is None: |
|
2237 process = QProcess() |
|
2238 process.setWorkingDirectory(repodir) |
|
2239 process.start('hg', args) |
|
2240 procStarted = process.waitForStarted(5000) |
|
2241 if procStarted: |
|
2242 finished = process.waitForFinished(30000) |
|
2243 if finished and process.exitCode() == 0: |
|
2244 output = str(process.readAllStandardOutput(), |
|
2245 self.getEncoding(), 'replace') |
|
2246 else: |
|
2247 output, error = self.__client.runcommand(args) |
|
2248 |
|
2249 return output.strip() |
|
2250 |
|
2251 def hgEditUserConfig(self): |
|
2252 """ |
|
2253 Public method used to edit the user configuration file. |
|
2254 """ |
|
2255 from .HgUserConfigDialog import HgUserConfigDialog |
|
2256 dlg = HgUserConfigDialog(version=self.version) |
|
2257 dlg.exec_() |
|
2258 |
|
2259 def hgEditConfig(self, name, withLargefiles=True, largefilesData=None): |
|
2260 """ |
|
2261 Public method used to edit the repository configuration file. |
|
2262 |
|
2263 @param name file/directory name (string) |
|
2264 @param withLargefiles flag indicating to configure the largefiles |
|
2265 section (boolean) |
|
2266 @param largefilesData dictionary with data for the largefiles |
|
2267 section of the data dialog (dict) |
|
2268 """ |
|
2269 dname, fname = self.splitPath(name) |
|
2270 |
|
2271 # find the root of the repo |
|
2272 repodir = dname |
|
2273 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2274 repodir = os.path.dirname(repodir) |
|
2275 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2276 return |
|
2277 |
|
2278 cfgFile = os.path.join(repodir, self.adminDir, "hgrc") |
|
2279 if not os.path.exists(cfgFile): |
|
2280 # open dialog to enter the initial data |
|
2281 withLargefiles = (self.isExtensionActive("largefiles") and |
|
2282 withLargefiles) |
|
2283 from .HgRepoConfigDataDialog import HgRepoConfigDataDialog |
|
2284 dlg = HgRepoConfigDataDialog(withLargefiles=withLargefiles, |
|
2285 largefilesData=largefilesData) |
|
2286 if dlg.exec_() == QDialog.Accepted: |
|
2287 createContents = True |
|
2288 defaultUrl, defaultPushUrl = dlg.getData() |
|
2289 if withLargefiles: |
|
2290 lfMinSize, lfPattern = dlg.getLargefilesData() |
|
2291 else: |
|
2292 createContents = False |
|
2293 try: |
|
2294 cfg = open(cfgFile, "w") |
|
2295 if createContents: |
|
2296 # write the data entered |
|
2297 cfg.write("[paths]\n") |
|
2298 if defaultUrl: |
|
2299 cfg.write("default = {0}\n".format(defaultUrl)) |
|
2300 if defaultPushUrl: |
|
2301 cfg.write("default-push = {0}\n".format( |
|
2302 defaultPushUrl)) |
|
2303 if withLargefiles and \ |
|
2304 (lfMinSize, lfPattern) != (None, None): |
|
2305 cfg.write("\n[largefiles]\n") |
|
2306 if lfMinSize is not None: |
|
2307 cfg.write("minsize = {0}\n".format(lfMinSize)) |
|
2308 if lfPattern is not None: |
|
2309 cfg.write("patterns =\n") |
|
2310 cfg.write(" {0}\n".format( |
|
2311 "\n ".join(lfPattern))) |
|
2312 cfg.close() |
|
2313 self.__monitorRepoIniFile(repodir) |
|
2314 self.__iniFileChanged(cfgFile) |
|
2315 except IOError: |
|
2316 pass |
|
2317 self.repoEditor = MiniEditor(cfgFile, "Properties") |
|
2318 self.repoEditor.show() |
|
2319 |
|
2320 def hgVerify(self, name): |
|
2321 """ |
|
2322 Public method to verify the integrity of the repository. |
|
2323 |
|
2324 @param name file/directory name (string) |
|
2325 """ |
|
2326 dname, fname = self.splitPath(name) |
|
2327 |
|
2328 # find the root of the repo |
|
2329 repodir = dname |
|
2330 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2331 repodir = os.path.dirname(repodir) |
|
2332 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2333 return |
|
2334 |
|
2335 args = self.initCommand("verify") |
|
2336 |
|
2337 dia = HgDialog( |
|
2338 self.tr('Verifying the integrity of the Mercurial repository'), |
|
2339 self) |
|
2340 res = dia.startProcess(args, repodir) |
|
2341 if res: |
|
2342 dia.exec_() |
|
2343 |
|
2344 def hgShowConfig(self, name): |
|
2345 """ |
|
2346 Public method to show the combined configuration. |
|
2347 |
|
2348 @param name file/directory name (string) |
|
2349 """ |
|
2350 dname, fname = self.splitPath(name) |
|
2351 |
|
2352 # find the root of the repo |
|
2353 repodir = dname |
|
2354 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2355 repodir = os.path.dirname(repodir) |
|
2356 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2357 return |
|
2358 |
|
2359 args = self.initCommand("showconfig") |
|
2360 args.append("--untrusted") |
|
2361 |
|
2362 dia = HgDialog( |
|
2363 self.tr('Showing the combined configuration settings'), |
|
2364 self) |
|
2365 res = dia.startProcess(args, repodir, False) |
|
2366 if res: |
|
2367 dia.exec_() |
|
2368 |
|
2369 def hgShowPaths(self, name): |
|
2370 """ |
|
2371 Public method to show the path aliases for remote repositories. |
|
2372 |
|
2373 @param name file/directory name (string) |
|
2374 """ |
|
2375 dname, fname = self.splitPath(name) |
|
2376 |
|
2377 # find the root of the repo |
|
2378 repodir = dname |
|
2379 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2380 repodir = os.path.dirname(repodir) |
|
2381 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2382 return |
|
2383 |
|
2384 args = self.initCommand("paths") |
|
2385 |
|
2386 dia = HgDialog( |
|
2387 self.tr('Showing aliases for remote repositories'), |
|
2388 self) |
|
2389 res = dia.startProcess(args, repodir, False) |
|
2390 if res: |
|
2391 dia.exec_() |
|
2392 |
|
2393 def hgRecover(self, name): |
|
2394 """ |
|
2395 Public method to recover an interrupted transaction. |
|
2396 |
|
2397 @param name file/directory name (string) |
|
2398 """ |
|
2399 dname, fname = self.splitPath(name) |
|
2400 |
|
2401 # find the root of the repo |
|
2402 repodir = dname |
|
2403 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2404 repodir = os.path.dirname(repodir) |
|
2405 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2406 return |
|
2407 |
|
2408 args = self.initCommand("recover") |
|
2409 |
|
2410 dia = HgDialog( |
|
2411 self.tr('Recovering from interrupted transaction'), |
|
2412 self) |
|
2413 res = dia.startProcess(args, repodir, False) |
|
2414 if res: |
|
2415 dia.exec_() |
|
2416 |
|
2417 def hgIdentify(self, name): |
|
2418 """ |
|
2419 Public method to identify the current working directory. |
|
2420 |
|
2421 @param name file/directory name (string) |
|
2422 """ |
|
2423 dname, fname = self.splitPath(name) |
|
2424 |
|
2425 # find the root of the repo |
|
2426 repodir = dname |
|
2427 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2428 repodir = os.path.dirname(repodir) |
|
2429 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2430 return |
|
2431 |
|
2432 args = self.initCommand("identify") |
|
2433 |
|
2434 dia = HgDialog(self.tr('Identifying project directory'), self) |
|
2435 res = dia.startProcess(args, repodir, False) |
|
2436 if res: |
|
2437 dia.exec_() |
|
2438 |
|
2439 def hgCreateIgnoreFile(self, name, autoAdd=False): |
|
2440 """ |
|
2441 Public method to create the ignore file. |
|
2442 |
|
2443 @param name directory name to create the ignore file in (string) |
|
2444 @param autoAdd flag indicating to add it automatically (boolean) |
|
2445 @return flag indicating success |
|
2446 """ |
|
2447 status = False |
|
2448 ignorePatterns = [ |
|
2449 "glob:.eric6project", |
|
2450 "glob:.ropeproject", |
|
2451 "glob:.directory", |
|
2452 "glob:**.pyc", |
|
2453 "glob:**.pyo", |
|
2454 "glob:**.orig", |
|
2455 "glob:**.bak", |
|
2456 "glob:**.rej", |
|
2457 "glob:**~", |
|
2458 "glob:cur", |
|
2459 "glob:tmp", |
|
2460 "glob:__pycache__", |
|
2461 "glob:**.DS_Store", |
|
2462 ] |
|
2463 |
|
2464 ignoreName = os.path.join(name, Hg.IgnoreFileName) |
|
2465 if os.path.exists(ignoreName): |
|
2466 res = E5MessageBox.yesNo( |
|
2467 self.__ui, |
|
2468 self.tr("Create .hgignore file"), |
|
2469 self.tr("""<p>The file <b>{0}</b> exists already.""" |
|
2470 """ Overwrite it?</p>""").format(ignoreName), |
|
2471 icon=E5MessageBox.Warning) |
|
2472 else: |
|
2473 res = True |
|
2474 if res: |
|
2475 try: |
|
2476 # create a .hgignore file |
|
2477 ignore = open(ignoreName, "w") |
|
2478 ignore.write("\n".join(ignorePatterns)) |
|
2479 ignore.write("\n") |
|
2480 ignore.close() |
|
2481 status = True |
|
2482 except IOError: |
|
2483 status = False |
|
2484 |
|
2485 if status and autoAdd: |
|
2486 self.vcsAdd(ignoreName, noDialog=True) |
|
2487 project = e5App().getObject("Project") |
|
2488 project.appendFile(ignoreName) |
|
2489 |
|
2490 return status |
|
2491 |
|
2492 def hgBundle(self, name, bundleData=None): |
|
2493 """ |
|
2494 Public method to create a changegroup file. |
|
2495 |
|
2496 @param name file/directory name |
|
2497 @type str |
|
2498 @param bundleData dictionary containing the bundle creation information |
|
2499 @type dict |
|
2500 """ |
|
2501 dname, fname = self.splitPath(name) |
|
2502 |
|
2503 # find the root of the repo |
|
2504 repodir = dname |
|
2505 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2506 repodir = os.path.dirname(repodir) |
|
2507 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2508 return |
|
2509 |
|
2510 if bundleData is None: |
|
2511 from .HgBundleDialog import HgBundleDialog |
|
2512 dlg = HgBundleDialog(self.hgGetTagsList(repodir), |
|
2513 self.hgGetBranchesList(repodir), |
|
2514 self.hgGetBookmarksList(repodir), |
|
2515 version=self.version) |
|
2516 if dlg.exec_() != QDialog.Accepted: |
|
2517 return |
|
2518 |
|
2519 revs, baseRevs, compression, bundleAll = dlg.getParameters() |
|
2520 else: |
|
2521 revs = bundleData["revs"] |
|
2522 if bundleData["base"]: |
|
2523 baseRevs = [bundleData["base"]] |
|
2524 else: |
|
2525 baseRevs = [] |
|
2526 compression = "" |
|
2527 bundleAll = bundleData["all"] |
|
2528 |
|
2529 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
|
2530 None, |
|
2531 self.tr("Create changegroup"), |
|
2532 self.__lastChangeGroupPath or repodir, |
|
2533 self.tr("Mercurial Changegroup Files (*.hg)"), |
|
2534 None, |
|
2535 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
2536 |
|
2537 if not fname: |
|
2538 return # user aborted |
|
2539 |
|
2540 ext = QFileInfo(fname).suffix() |
|
2541 if not ext: |
|
2542 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
2543 if ex: |
|
2544 fname += ex |
|
2545 if QFileInfo(fname).exists(): |
|
2546 res = E5MessageBox.yesNo( |
|
2547 self.__ui, |
|
2548 self.tr("Create changegroup"), |
|
2549 self.tr("<p>The Mercurial changegroup file <b>{0}</b> " |
|
2550 "already exists. Overwrite it?</p>") |
|
2551 .format(fname), |
|
2552 icon=E5MessageBox.Warning) |
|
2553 if not res: |
|
2554 return |
|
2555 fname = Utilities.toNativeSeparators(fname) |
|
2556 self.__lastChangeGroupPath = os.path.dirname(fname) |
|
2557 |
|
2558 args = self.initCommand("bundle") |
|
2559 if bundleAll: |
|
2560 args.append("--all") |
|
2561 for rev in revs: |
|
2562 args.append("--rev") |
|
2563 args.append(rev) |
|
2564 for baseRev in baseRevs: |
|
2565 args.append("--base") |
|
2566 args.append(baseRev) |
|
2567 if compression: |
|
2568 args.append("--type") |
|
2569 args.append(compression) |
|
2570 args.append(fname) |
|
2571 |
|
2572 dia = HgDialog(self.tr('Create changegroup'), self) |
|
2573 res = dia.startProcess(args, repodir) |
|
2574 if res: |
|
2575 dia.exec_() |
|
2576 |
|
2577 def hgPreviewBundle(self, name): |
|
2578 """ |
|
2579 Public method used to view the log of incoming changes from a |
|
2580 changegroup file. |
|
2581 |
|
2582 @param name directory name on which to base the changegroup (string) |
|
2583 """ |
|
2584 dname, fname = self.splitPath(name) |
|
2585 |
|
2586 # find the root of the repo |
|
2587 repodir = dname |
|
2588 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2589 repodir = os.path.dirname(repodir) |
|
2590 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2591 return |
|
2592 |
|
2593 file = E5FileDialog.getOpenFileName( |
|
2594 None, |
|
2595 self.tr("Preview changegroup"), |
|
2596 self.__lastChangeGroupPath or repodir, |
|
2597 self.tr("Mercurial Changegroup Files (*.hg);;All Files (*)")) |
|
2598 if file: |
|
2599 self.__lastChangeGroupPath = os.path.dirname(file) |
|
2600 |
|
2601 if self.logBrowserIncoming is None: |
|
2602 from .HgLogBrowserDialog import HgLogBrowserDialog |
|
2603 self.logBrowserIncoming = \ |
|
2604 HgLogBrowserDialog(self, mode="incoming") |
|
2605 self.logBrowserIncoming.show() |
|
2606 self.logBrowserIncoming.raise_() |
|
2607 self.logBrowserIncoming.start(name, bundle=file) |
|
2608 |
|
2609 def hgUnbundle(self, name, files=None): |
|
2610 """ |
|
2611 Public method to apply changegroup files. |
|
2612 |
|
2613 @param name directory name |
|
2614 @type str |
|
2615 @param files list of bundle files to be applied |
|
2616 @type list of str |
|
2617 @return flag indicating, that the update contained an add |
|
2618 or delete |
|
2619 @rtype bool |
|
2620 """ |
|
2621 dname, fname = self.splitPath(name) |
|
2622 |
|
2623 # find the root of the repo |
|
2624 repodir = dname |
|
2625 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2626 repodir = os.path.dirname(repodir) |
|
2627 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2628 return False |
|
2629 |
|
2630 res = False |
|
2631 if not files: |
|
2632 files = E5FileDialog.getOpenFileNames( |
|
2633 None, |
|
2634 self.tr("Apply changegroups"), |
|
2635 self.__lastChangeGroupPath or repodir, |
|
2636 self.tr("Mercurial Changegroup Files (*.hg);;All Files (*)")) |
|
2637 |
|
2638 if files: |
|
2639 self.__lastChangeGroupPath = os.path.dirname(files[0]) |
|
2640 |
|
2641 update = E5MessageBox.yesNo( |
|
2642 self.__ui, |
|
2643 self.tr("Apply changegroups"), |
|
2644 self.tr("""Shall the working directory be updated?"""), |
|
2645 yesDefault=True) |
|
2646 |
|
2647 args = self.initCommand("unbundle") |
|
2648 if update: |
|
2649 args.append("--update") |
|
2650 args.append("--verbose") |
|
2651 args.extend(files) |
|
2652 |
|
2653 dia = HgDialog(self.tr('Apply changegroups'), self) |
|
2654 res = dia.startProcess(args, repodir) |
|
2655 if res: |
|
2656 dia.exec_() |
|
2657 res = dia.hasAddOrDelete() |
|
2658 self.checkVCSStatus() |
|
2659 |
|
2660 return res |
|
2661 |
|
2662 def hgBisect(self, name, subcommand): |
|
2663 """ |
|
2664 Public method to perform bisect commands. |
|
2665 |
|
2666 @param name file/directory name (string) |
|
2667 @param subcommand name of the subcommand (string, one of 'good', 'bad', |
|
2668 'skip' or 'reset') |
|
2669 @exception ValueError raised to indicate an invalid bisect subcommand |
|
2670 """ |
|
2671 if subcommand not in ("good", "bad", "skip", "reset"): |
|
2672 raise ValueError( |
|
2673 self.tr("Bisect subcommand ({0}) invalid.") |
|
2674 .format(subcommand)) |
|
2675 |
|
2676 dname, fname = self.splitPath(name) |
|
2677 |
|
2678 # find the root of the repo |
|
2679 repodir = dname |
|
2680 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2681 repodir = os.path.dirname(repodir) |
|
2682 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2683 return |
|
2684 |
|
2685 rev = "" |
|
2686 if subcommand in ("good", "bad", "skip"): |
|
2687 from .HgRevisionSelectionDialog import HgRevisionSelectionDialog |
|
2688 dlg = HgRevisionSelectionDialog(self.hgGetTagsList(repodir), |
|
2689 self.hgGetBranchesList(repodir), |
|
2690 self.hgGetBookmarksList(repodir)) |
|
2691 if dlg.exec_() == QDialog.Accepted: |
|
2692 rev = dlg.getRevision() |
|
2693 else: |
|
2694 return |
|
2695 |
|
2696 args = self.initCommand("bisect") |
|
2697 args.append("--{0}".format(subcommand)) |
|
2698 if rev: |
|
2699 args.append(rev) |
|
2700 |
|
2701 dia = HgDialog( |
|
2702 self.tr('Mercurial Bisect ({0})').format(subcommand), self) |
|
2703 res = dia.startProcess(args, repodir) |
|
2704 if res: |
|
2705 dia.exec_() |
|
2706 |
|
2707 def hgForget(self, name): |
|
2708 """ |
|
2709 Public method used to remove a file from the Mercurial repository. |
|
2710 |
|
2711 This will not remove the file from the project directory. |
|
2712 |
|
2713 @param name file/directory name to be removed (string or list of |
|
2714 strings)) |
|
2715 """ |
|
2716 args = self.initCommand("forget") |
|
2717 args.append('-v') |
|
2718 |
|
2719 if isinstance(name, list): |
|
2720 dname, fnames = self.splitPathList(name) |
|
2721 self.addArguments(args, name) |
|
2722 else: |
|
2723 dname, fname = self.splitPath(name) |
|
2724 args.append(name) |
|
2725 |
|
2726 # find the root of the repo |
|
2727 repodir = dname |
|
2728 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2729 repodir = os.path.dirname(repodir) |
|
2730 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2731 return |
|
2732 |
|
2733 dia = HgDialog( |
|
2734 self.tr('Removing files from the Mercurial repository only'), |
|
2735 self) |
|
2736 res = dia.startProcess(args, repodir) |
|
2737 if res: |
|
2738 dia.exec_() |
|
2739 if isinstance(name, list): |
|
2740 self.__forgotNames.extend(name) |
|
2741 else: |
|
2742 self.__forgotNames.append(name) |
|
2743 |
|
2744 def hgBackout(self, name): |
|
2745 """ |
|
2746 Public method used to backout an earlier changeset from the Mercurial |
|
2747 repository. |
|
2748 |
|
2749 @param name directory name (string or list of strings) |
|
2750 """ |
|
2751 dname, fname = self.splitPath(name) |
|
2752 |
|
2753 # find the root of the repo |
|
2754 repodir = dname |
|
2755 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2756 repodir = os.path.dirname(repodir) |
|
2757 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2758 return |
|
2759 |
|
2760 from .HgBackoutDialog import HgBackoutDialog |
|
2761 dlg = HgBackoutDialog(self.hgGetTagsList(repodir), |
|
2762 self.hgGetBranchesList(repodir), |
|
2763 self.hgGetBookmarksList(repodir)) |
|
2764 if dlg.exec_() == QDialog.Accepted: |
|
2765 rev, merge, date, user, message = dlg.getParameters() |
|
2766 if not rev: |
|
2767 E5MessageBox.warning( |
|
2768 self.__ui, |
|
2769 self.tr("Backing out changeset"), |
|
2770 self.tr("""No revision given. Aborting...""")) |
|
2771 return |
|
2772 |
|
2773 args = self.initCommand("backout") |
|
2774 args.append('-v') |
|
2775 if merge: |
|
2776 args.append('--merge') |
|
2777 if date: |
|
2778 args.append('--date') |
|
2779 args.append(date) |
|
2780 if user: |
|
2781 args.append('--user') |
|
2782 args.append(user) |
|
2783 args.append('--message') |
|
2784 args.append(message) |
|
2785 args.append(rev) |
|
2786 |
|
2787 dia = HgDialog(self.tr('Backing out changeset'), self) |
|
2788 res = dia.startProcess(args, repodir) |
|
2789 if res: |
|
2790 dia.exec_() |
|
2791 |
|
2792 def hgRollback(self, name): |
|
2793 """ |
|
2794 Public method used to rollback the last transaction. |
|
2795 |
|
2796 @param name directory name (string or list of strings) |
|
2797 """ |
|
2798 dname, fname = self.splitPath(name) |
|
2799 |
|
2800 # find the root of the repo |
|
2801 repodir = dname |
|
2802 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2803 repodir = os.path.dirname(repodir) |
|
2804 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2805 return |
|
2806 |
|
2807 res = E5MessageBox.yesNo( |
|
2808 None, |
|
2809 self.tr("Rollback last transaction"), |
|
2810 self.tr("""Are you sure you want to rollback the last""" |
|
2811 """ transaction?"""), |
|
2812 icon=E5MessageBox.Warning) |
|
2813 if res: |
|
2814 dia = HgDialog(self.tr('Rollback last transaction'), self) |
|
2815 res = dia.startProcess(["rollback"], repodir) |
|
2816 if res: |
|
2817 dia.exec_() |
|
2818 |
|
2819 def hgServe(self, name): |
|
2820 """ |
|
2821 Public method used to serve the project. |
|
2822 |
|
2823 @param name directory name (string) |
|
2824 """ |
|
2825 dname, fname = self.splitPath(name) |
|
2826 |
|
2827 # find the root of the repo |
|
2828 repodir = dname |
|
2829 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2830 repodir = os.path.dirname(repodir) |
|
2831 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2832 return |
|
2833 |
|
2834 from .HgServeDialog import HgServeDialog |
|
2835 self.serveDlg = HgServeDialog(self, repodir) |
|
2836 self.serveDlg.show() |
|
2837 |
|
2838 def hgImport(self, name): |
|
2839 """ |
|
2840 Public method to import a patch file. |
|
2841 |
|
2842 @param name directory name of the project to import into (string) |
|
2843 @return flag indicating, that the import contained an add, a delete |
|
2844 or a change to the project file (boolean) |
|
2845 """ |
|
2846 dname, fname = self.splitPath(name) |
|
2847 |
|
2848 # find the root of the repo |
|
2849 repodir = dname |
|
2850 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2851 repodir = os.path.dirname(repodir) |
|
2852 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2853 return False |
|
2854 |
|
2855 from .HgImportDialog import HgImportDialog |
|
2856 dlg = HgImportDialog() |
|
2857 if dlg.exec_() == QDialog.Accepted: |
|
2858 patchFile, noCommit, message, date, user, stripCount, force = \ |
|
2859 dlg.getParameters() |
|
2860 |
|
2861 args = self.initCommand("import") |
|
2862 args.append("--verbose") |
|
2863 if noCommit: |
|
2864 args.append("--no-commit") |
|
2865 else: |
|
2866 if message: |
|
2867 args.append('--message') |
|
2868 args.append(message) |
|
2869 if date: |
|
2870 args.append('--date') |
|
2871 args.append(date) |
|
2872 if user: |
|
2873 args.append('--user') |
|
2874 args.append(user) |
|
2875 if stripCount != 1: |
|
2876 args.append("--strip") |
|
2877 args.append(str(stripCount)) |
|
2878 if force: |
|
2879 args.append("--force") |
|
2880 args.append(patchFile) |
|
2881 |
|
2882 dia = HgDialog(self.tr("Import Patch"), self) |
|
2883 res = dia.startProcess(args, repodir) |
|
2884 if res: |
|
2885 dia.exec_() |
|
2886 res = dia.hasAddOrDelete() |
|
2887 self.checkVCSStatus() |
|
2888 else: |
|
2889 res = False |
|
2890 |
|
2891 return res |
|
2892 |
|
2893 def hgExport(self, name): |
|
2894 """ |
|
2895 Public method to export patches to files. |
|
2896 |
|
2897 @param name directory name of the project to export from (string) |
|
2898 """ |
|
2899 dname, fname = self.splitPath(name) |
|
2900 |
|
2901 # find the root of the repo |
|
2902 repodir = dname |
|
2903 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2904 repodir = os.path.dirname(repodir) |
|
2905 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2906 return |
|
2907 |
|
2908 from .HgExportDialog import HgExportDialog |
|
2909 dlg = HgExportDialog(self.hgGetBookmarksList(repodir), |
|
2910 self.version >= (4, 7, 0)) |
|
2911 if dlg.exec_() == QDialog.Accepted: |
|
2912 filePattern, revisions, bookmark, switchParent, allText, noDates, \ |
|
2913 git = dlg.getParameters() |
|
2914 |
|
2915 args = self.initCommand("export") |
|
2916 args.append("--output") |
|
2917 args.append(filePattern) |
|
2918 args.append("--verbose") |
|
2919 if switchParent: |
|
2920 args.append("--switch-parent") |
|
2921 if allText: |
|
2922 args.append("--text") |
|
2923 if noDates: |
|
2924 args.append("--nodates") |
|
2925 if git: |
|
2926 args.append("--git") |
|
2927 if bookmark: |
|
2928 args.append("--bookmark") |
|
2929 args.append(bookmark) |
|
2930 else: |
|
2931 for rev in revisions: |
|
2932 args.append(rev) |
|
2933 |
|
2934 dia = HgDialog(self.tr("Export Patches"), self) |
|
2935 res = dia.startProcess(args, repodir) |
|
2936 if res: |
|
2937 dia.exec_() |
|
2938 |
|
2939 def hgPhase(self, name, data=None): |
|
2940 """ |
|
2941 Public method to change the phase of revisions. |
|
2942 |
|
2943 @param name directory name of the project to export from (string) |
|
2944 @param data tuple giving phase data (list of revisions, phase, flag |
|
2945 indicating a forced operation) (list of strings, string, boolean) |
|
2946 @return flag indicating success (boolean) |
|
2947 @exception ValueError raised to indicate an invalid phase |
|
2948 """ |
|
2949 dname, fname = self.splitPath(name) |
|
2950 |
|
2951 # find the root of the repo |
|
2952 repodir = dname |
|
2953 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
2954 repodir = os.path.dirname(repodir) |
|
2955 if os.path.splitdrive(repodir)[1] == os.sep: |
|
2956 return False |
|
2957 |
|
2958 if data is None: |
|
2959 from .HgPhaseDialog import HgPhaseDialog |
|
2960 dlg = HgPhaseDialog() |
|
2961 if dlg.exec_() == QDialog.Accepted: |
|
2962 data = dlg.getData() |
|
2963 |
|
2964 if data: |
|
2965 revs, phase, force = data |
|
2966 |
|
2967 args = self.initCommand("phase") |
|
2968 if phase == "p": |
|
2969 args.append("--public") |
|
2970 elif phase == "d": |
|
2971 args.append("--draft") |
|
2972 elif phase == "s": |
|
2973 args.append("--secret") |
|
2974 else: |
|
2975 raise ValueError("Invalid phase given.") |
|
2976 if force: |
|
2977 args.append("--force") |
|
2978 for rev in revs: |
|
2979 args.append(rev) |
|
2980 |
|
2981 dia = HgDialog(self.tr("Change Phase"), self) |
|
2982 res = dia.startProcess(args, repodir) |
|
2983 if res: |
|
2984 dia.exec_() |
|
2985 res = dia.normalExitWithoutErrors() |
|
2986 else: |
|
2987 res = False |
|
2988 |
|
2989 return res |
|
2990 |
|
2991 def hgGraft(self, path, revs=None): |
|
2992 """ |
|
2993 Public method to copy changesets from another branch. |
|
2994 |
|
2995 @param path directory name of the project (string) |
|
2996 @param revs list of revisions to show in the revisions pane (list of |
|
2997 strings) |
|
2998 @return flag indicating that the project should be reread (boolean) |
|
2999 """ |
|
3000 # find the root of the repo |
|
3001 repodir = self.splitPath(path)[0] |
|
3002 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3003 repodir = os.path.dirname(repodir) |
|
3004 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3005 return False |
|
3006 |
|
3007 from .HgGraftDialog import HgGraftDialog |
|
3008 res = False |
|
3009 dlg = HgGraftDialog(self, revs) |
|
3010 if dlg.exec_() == QDialog.Accepted: |
|
3011 revs, (userData, currentUser, userName), \ |
|
3012 (dateData, currentDate, dateStr), log, dryrun, \ |
|
3013 noCommit = dlg.getData() |
|
3014 |
|
3015 args = self.initCommand("graft") |
|
3016 args.append("--verbose") |
|
3017 if userData: |
|
3018 if currentUser: |
|
3019 args.append("--currentuser") |
|
3020 else: |
|
3021 args.append("--user") |
|
3022 args.append(userName) |
|
3023 if dateData: |
|
3024 if currentDate: |
|
3025 args.append("--currentdate") |
|
3026 else: |
|
3027 args.append("--date") |
|
3028 args.append(dateStr) |
|
3029 if log: |
|
3030 args.append("--log") |
|
3031 if dryrun: |
|
3032 args.append("--dry-run") |
|
3033 if noCommit: |
|
3034 args.append("--no-commit") |
|
3035 args.extend(revs) |
|
3036 |
|
3037 dia = HgDialog(self.tr('Copy Changesets'), self) |
|
3038 res = dia.startProcess(args, repodir) |
|
3039 if res: |
|
3040 dia.exec_() |
|
3041 res = dia.hasAddOrDelete() |
|
3042 self.checkVCSStatus() |
|
3043 return res |
|
3044 |
|
3045 def __hgGraftSubCommand(self, path, subcommand, title): |
|
3046 """ |
|
3047 Private method to perform a Mercurial graft subcommand. |
|
3048 |
|
3049 @param path directory name of the project |
|
3050 @type str |
|
3051 @param subcommand subcommand flag |
|
3052 @type str |
|
3053 @param title tirle of the dialog |
|
3054 @type str |
|
3055 @return flag indicating that the project should be reread |
|
3056 @rtype bool |
|
3057 """ |
|
3058 # find the root of the repo |
|
3059 repodir = self.splitPath(path)[0] |
|
3060 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3061 repodir = os.path.dirname(repodir) |
|
3062 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3063 return False |
|
3064 |
|
3065 args = self.initCommand("graft") |
|
3066 args.append(subcommand) |
|
3067 args.append("--verbose") |
|
3068 |
|
3069 dia = HgDialog(title, self) |
|
3070 res = dia.startProcess(args, repodir) |
|
3071 if res: |
|
3072 dia.exec_() |
|
3073 res = dia.hasAddOrDelete() |
|
3074 self.checkVCSStatus() |
|
3075 return res |
|
3076 |
|
3077 def hgGraftContinue(self, path): |
|
3078 """ |
|
3079 Public method to continue copying changesets from another branch. |
|
3080 |
|
3081 @param path directory name of the project |
|
3082 @type str |
|
3083 @return flag indicating that the project should be reread |
|
3084 @rtype bool |
|
3085 """ |
|
3086 return self.__hgGraftSubCommand( |
|
3087 path, "--continue", self.tr('Copy Changesets (Continue)')) |
|
3088 |
|
3089 def hgGraftStop(self, path): |
|
3090 """ |
|
3091 Public method to stop an interrupted copying session. |
|
3092 |
|
3093 @param path directory name of the project |
|
3094 @type str |
|
3095 @return flag indicating that the project should be reread |
|
3096 @rtype bool |
|
3097 """ |
|
3098 return self.__hgGraftSubCommand( |
|
3099 path, "--stop", self.tr('Copy Changesets (Stop)')) |
|
3100 |
|
3101 def hgGraftAbort(self, path): |
|
3102 """ |
|
3103 Public method to abort an interrupted copying session and perform |
|
3104 a rollback. |
|
3105 |
|
3106 @param path directory name of the project |
|
3107 @type str |
|
3108 @return flag indicating that the project should be reread |
|
3109 @rtype bool |
|
3110 """ |
|
3111 return self.__hgGraftSubCommand( |
|
3112 path, "--abort", self.tr('Copy Changesets (Abort)')) |
|
3113 |
|
3114 def hgArchive(self): |
|
3115 """ |
|
3116 Public method to create an unversioned archive from the repository. |
|
3117 """ |
|
3118 # find the root of the repo |
|
3119 repodir = self.__projectHelper.getProject().getProjectPath() |
|
3120 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3121 repodir = os.path.dirname(repodir) |
|
3122 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3123 return |
|
3124 |
|
3125 from .HgArchiveDialog import HgArchiveDialog |
|
3126 dlg = HgArchiveDialog(self) |
|
3127 if dlg.exec_() == QDialog.Accepted: |
|
3128 archive, type_, prefix, subrepos = dlg.getData() |
|
3129 |
|
3130 args = self.initCommand("archive") |
|
3131 if type_: |
|
3132 args.append("--type") |
|
3133 args.append(type_) |
|
3134 if prefix: |
|
3135 args.append("--prefix") |
|
3136 args.append(prefix) |
|
3137 if subrepos: |
|
3138 args.append("--subrepos") |
|
3139 args.append(archive) |
|
3140 |
|
3141 dia = HgDialog(self.tr("Create Unversioned Archive"), self) |
|
3142 res = dia.startProcess(args, repodir) |
|
3143 if res: |
|
3144 dia.exec_() |
|
3145 |
|
3146 def hgDeleteBackups(self): |
|
3147 """ |
|
3148 Public method to delete all backup bundles in the backup area. |
|
3149 """ |
|
3150 # find the root of the repo |
|
3151 repodir = self.__projectHelper.getProject().getProjectPath() |
|
3152 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3153 repodir = os.path.dirname(repodir) |
|
3154 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3155 return |
|
3156 |
|
3157 backupdir = os.path.join(repodir, self.adminDir, "strip-backup") |
|
3158 yes = E5MessageBox.yesNo( |
|
3159 self.__ui, |
|
3160 self.tr("Delete All Backups"), |
|
3161 self.tr("""<p>Do you really want to delete all backup bundles""" |
|
3162 """ stored the backup area <b>{0}</b>?</p>""").format( |
|
3163 backupdir)) |
|
3164 if yes: |
|
3165 shutil.rmtree(backupdir, True) |
|
3166 |
|
3167 ########################################################################### |
|
3168 ## Methods to deal with subrepositories are below. |
|
3169 ########################################################################### |
|
3170 |
|
3171 def getHgSubPath(self): |
|
3172 """ |
|
3173 Public method to get the path to the .hgsub file containing the |
|
3174 definitions of sub-repositories. |
|
3175 |
|
3176 @return full path of the .hgsub file (string) |
|
3177 """ |
|
3178 ppath = self.__projectHelper.getProject().getProjectPath() |
|
3179 return os.path.join(ppath, ".hgsub") |
|
3180 |
|
3181 def hasSubrepositories(self): |
|
3182 """ |
|
3183 Public method to check, if the project might have sub-repositories. |
|
3184 |
|
3185 @return flag indicating the existence of sub-repositories (boolean) |
|
3186 """ |
|
3187 hgsub = self.getHgSubPath() |
|
3188 return os.path.isfile(hgsub) and os.stat(hgsub).st_size > 0 |
|
3189 |
|
3190 def hgAddSubrepository(self): |
|
3191 """ |
|
3192 Public method to add a sub-repository. |
|
3193 """ |
|
3194 from .HgAddSubrepositoryDialog import HgAddSubrepositoryDialog |
|
3195 ppath = self.__projectHelper.getProject().getProjectPath() |
|
3196 hgsub = self.getHgSubPath() |
|
3197 dlg = HgAddSubrepositoryDialog(ppath) |
|
3198 if dlg.exec_() == QDialog.Accepted: |
|
3199 relPath, subrepoType, subrepoUrl = dlg.getData() |
|
3200 if subrepoType == "hg": |
|
3201 url = subrepoUrl |
|
3202 else: |
|
3203 url = "[{0}]{1}".format(subrepoType, subrepoUrl) |
|
3204 entry = "{0} = {1}\n".format(relPath, url) |
|
3205 |
|
3206 contents = [] |
|
3207 if os.path.isfile(hgsub): |
|
3208 # file exists; check, if such an entry exists already |
|
3209 needsAdd = False |
|
3210 try: |
|
3211 f = open(hgsub, "r") |
|
3212 contents = f.readlines() |
|
3213 f.close() |
|
3214 except IOError as err: |
|
3215 E5MessageBox.critical( |
|
3216 self.__ui, |
|
3217 self.tr("Add Sub-repository"), |
|
3218 self.tr( |
|
3219 """<p>The sub-repositories file .hgsub could not""" |
|
3220 """ be read.</p><p>Reason: {0}</p>""") |
|
3221 .format(str(err))) |
|
3222 return |
|
3223 |
|
3224 if entry in contents: |
|
3225 E5MessageBox.critical( |
|
3226 self.__ui, |
|
3227 self.tr("Add Sub-repository"), |
|
3228 self.tr( |
|
3229 """<p>The sub-repositories file .hgsub already""" |
|
3230 """ contains an entry <b>{0}</b>.""" |
|
3231 """ Aborting...</p>""").format(entry)) |
|
3232 return |
|
3233 else: |
|
3234 needsAdd = True |
|
3235 |
|
3236 if contents and not contents[-1].endswith("\n"): |
|
3237 contents[-1] = contents[-1] + "\n" |
|
3238 contents.append(entry) |
|
3239 try: |
|
3240 f = open(hgsub, "w") |
|
3241 f.writelines(contents) |
|
3242 f.close() |
|
3243 except IOError as err: |
|
3244 E5MessageBox.critical( |
|
3245 self.__ui, |
|
3246 self.tr("Add Sub-repository"), |
|
3247 self.tr( |
|
3248 """<p>The sub-repositories file .hgsub could not""" |
|
3249 """ be written to.</p><p>Reason: {0}</p>""") |
|
3250 .format(str(err))) |
|
3251 return |
|
3252 |
|
3253 if needsAdd: |
|
3254 self.vcsAdd(hgsub) |
|
3255 self.__projectHelper.getProject().appendFile(hgsub) |
|
3256 |
|
3257 def hgRemoveSubrepositories(self): |
|
3258 """ |
|
3259 Public method to remove sub-repositories. |
|
3260 """ |
|
3261 hgsub = self.getHgSubPath() |
|
3262 |
|
3263 subrepositories = [] |
|
3264 if not os.path.isfile(hgsub): |
|
3265 E5MessageBox.critical( |
|
3266 self.__ui, |
|
3267 self.tr("Remove Sub-repositories"), |
|
3268 self.tr("""<p>The sub-repositories file .hgsub does not""" |
|
3269 """ exist. Aborting...</p>""")) |
|
3270 return |
|
3271 |
|
3272 try: |
|
3273 f = open(hgsub, "r") |
|
3274 subrepositories = [line.strip() for line in f.readlines()] |
|
3275 f.close() |
|
3276 except IOError as err: |
|
3277 E5MessageBox.critical( |
|
3278 self.__ui, |
|
3279 self.tr("Remove Sub-repositories"), |
|
3280 self.tr("""<p>The sub-repositories file .hgsub could not""" |
|
3281 """ be read.</p><p>Reason: {0}</p>""") |
|
3282 .format(str(err))) |
|
3283 return |
|
3284 |
|
3285 from .HgRemoveSubrepositoriesDialog import \ |
|
3286 HgRemoveSubrepositoriesDialog |
|
3287 dlg = HgRemoveSubrepositoriesDialog(subrepositories) |
|
3288 if dlg.exec_() == QDialog.Accepted: |
|
3289 subrepositories, removedSubrepos, deleteSubrepos = dlg.getData() |
|
3290 contents = "\n".join(subrepositories) + "\n" |
|
3291 try: |
|
3292 f = open(hgsub, "w") |
|
3293 f.write(contents) |
|
3294 f.close() |
|
3295 except IOError as err: |
|
3296 E5MessageBox.critical( |
|
3297 self.__ui, |
|
3298 self.tr("Remove Sub-repositories"), |
|
3299 self.tr( |
|
3300 """<p>The sub-repositories file .hgsub could not""" |
|
3301 """ be written to.</p><p>Reason: {0}</p>""") |
|
3302 .format(str(err))) |
|
3303 return |
|
3304 |
|
3305 if deleteSubrepos: |
|
3306 ppath = self.__projectHelper.getProject().getProjectPath() |
|
3307 for removedSubrepo in removedSubrepos: |
|
3308 subrepoPath = removedSubrepo.split("=", 1)[0].strip() |
|
3309 subrepoAbsPath = os.path.join(ppath, subrepoPath) |
|
3310 shutil.rmtree(subrepoAbsPath, True) |
|
3311 |
|
3312 ########################################################################### |
|
3313 ## Methods to handle configuration dependent stuff are below. |
|
3314 ########################################################################### |
|
3315 |
|
3316 def __checkDefaults(self): |
|
3317 """ |
|
3318 Private method to check, if the default and default-push URLs |
|
3319 have been configured. |
|
3320 """ |
|
3321 args = self.initCommand("showconfig") |
|
3322 args.append('paths') |
|
3323 |
|
3324 output = "" |
|
3325 if self.__client is None: |
|
3326 process = QProcess() |
|
3327 self.__repoDir and process.setWorkingDirectory(self.__repoDir) |
|
3328 process.start('hg', args) |
|
3329 procStarted = process.waitForStarted(5000) |
|
3330 if procStarted: |
|
3331 finished = process.waitForFinished(30000) |
|
3332 if finished and process.exitCode() == 0: |
|
3333 output = str(process.readAllStandardOutput(), |
|
3334 self.getEncoding(), 'replace') |
|
3335 else: |
|
3336 output, error = self.__client.runcommand(args) |
|
3337 |
|
3338 self.__defaultConfigured = False |
|
3339 self.__defaultPushConfigured = False |
|
3340 if output: |
|
3341 for line in output.splitlines(): |
|
3342 if line.startswith("paths.default=") and \ |
|
3343 not line.strip().endswith("="): |
|
3344 self.__defaultConfigured = True |
|
3345 if line.startswith("paths.default-push=") and \ |
|
3346 not line.strip().endswith("="): |
|
3347 self.__defaultPushConfigured = True |
|
3348 |
|
3349 def canPull(self): |
|
3350 """ |
|
3351 Public method to check, if pull is possible. |
|
3352 |
|
3353 @return flag indicating pull capability (boolean) |
|
3354 """ |
|
3355 return self.__defaultConfigured |
|
3356 |
|
3357 def canPush(self): |
|
3358 """ |
|
3359 Public method to check, if push is possible. |
|
3360 |
|
3361 @return flag indicating push capability (boolean) |
|
3362 """ |
|
3363 return self.__defaultPushConfigured or self.__defaultConfigured |
|
3364 |
|
3365 def __iniFileChanged(self, path): |
|
3366 """ |
|
3367 Private slot to handle a change of the Mercurial configuration file. |
|
3368 |
|
3369 @param path name of the changed file (string) |
|
3370 """ |
|
3371 if self.__client: |
|
3372 ok, err = self.__client.restartServer() |
|
3373 if not ok: |
|
3374 E5MessageBox.warning( |
|
3375 None, |
|
3376 self.tr("Mercurial Command Server"), |
|
3377 self.tr( |
|
3378 """<p>The Mercurial Command Server could not be""" |
|
3379 """ restarted.</p><p>Reason: {0}</p>""").format(err)) |
|
3380 self.__client = None |
|
3381 |
|
3382 self.__getExtensionsInfo() |
|
3383 |
|
3384 if self.__repoIniFile and path == self.__repoIniFile: |
|
3385 self.__checkDefaults() |
|
3386 |
|
3387 self.iniFileChanged.emit() |
|
3388 |
|
3389 def __monitorRepoIniFile(self, name): |
|
3390 """ |
|
3391 Private slot to add a repository configuration file to the list of |
|
3392 monitored files. |
|
3393 |
|
3394 @param name directory name pointing into the repository (string) |
|
3395 """ |
|
3396 dname, fname = self.splitPath(name) |
|
3397 |
|
3398 # find the root of the repo |
|
3399 repodir = dname |
|
3400 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3401 repodir = os.path.dirname(repodir) |
|
3402 if not repodir or os.path.splitdrive(repodir)[1] == os.sep: |
|
3403 return |
|
3404 |
|
3405 cfgFile = os.path.join(repodir, self.adminDir, "hgrc") |
|
3406 if os.path.exists(cfgFile): |
|
3407 self.__iniWatcher.addPath(cfgFile) |
|
3408 self.__repoIniFile = cfgFile |
|
3409 self.__checkDefaults() |
|
3410 |
|
3411 ########################################################################### |
|
3412 ## Methods to handle extensions are below. |
|
3413 ########################################################################### |
|
3414 |
|
3415 def __getExtensionsInfo(self): |
|
3416 """ |
|
3417 Private method to get the active extensions from Mercurial. |
|
3418 """ |
|
3419 activeExtensions = sorted(self.__activeExtensions) |
|
3420 self.__activeExtensions = [] |
|
3421 |
|
3422 args = self.initCommand("showconfig") |
|
3423 args.append('extensions') |
|
3424 |
|
3425 output = "" |
|
3426 if self.__client is None: |
|
3427 process = QProcess() |
|
3428 self.__repoDir and process.setWorkingDirectory(self.__repoDir) |
|
3429 process.start('hg', args) |
|
3430 procStarted = process.waitForStarted(5000) |
|
3431 if procStarted: |
|
3432 finished = process.waitForFinished(30000) |
|
3433 if finished and process.exitCode() == 0: |
|
3434 output = str(process.readAllStandardOutput(), |
|
3435 self.getEncoding(), 'replace') |
|
3436 else: |
|
3437 output, error = self.__client.runcommand(args) |
|
3438 |
|
3439 if output: |
|
3440 for line in output.splitlines(): |
|
3441 extensionName = \ |
|
3442 line.split("=", 1)[0].strip().split(".")[-1].strip() |
|
3443 self.__activeExtensions.append(extensionName) |
|
3444 |
|
3445 if activeExtensions != sorted(self.__activeExtensions): |
|
3446 self.activeExtensionsChanged.emit() |
|
3447 |
|
3448 def isExtensionActive(self, extensionName): |
|
3449 """ |
|
3450 Public method to check, if an extension is active. |
|
3451 |
|
3452 @param extensionName name of the extension to check for (string) |
|
3453 @return flag indicating an active extension (boolean) |
|
3454 """ |
|
3455 extensionName = extensionName.strip() |
|
3456 isActive = extensionName in self.__activeExtensions |
|
3457 |
|
3458 return isActive |
|
3459 |
|
3460 def getExtensionObject(self, extensionName): |
|
3461 """ |
|
3462 Public method to get a reference to an extension object. |
|
3463 |
|
3464 @param extensionName name of the extension (string) |
|
3465 @return reference to the extension object (boolean) |
|
3466 """ |
|
3467 return self.__extensions[extensionName] |
|
3468 |
|
3469 ########################################################################### |
|
3470 ## Methods to get the helper objects are below. |
|
3471 ########################################################################### |
|
3472 |
|
3473 def vcsGetProjectBrowserHelper(self, browser, project, |
|
3474 isTranslationsBrowser=False): |
|
3475 """ |
|
3476 Public method to instantiate a helper object for the different |
|
3477 project browsers. |
|
3478 |
|
3479 @param browser reference to the project browser object |
|
3480 @param project reference to the project object |
|
3481 @param isTranslationsBrowser flag indicating, the helper is requested |
|
3482 for the translations browser (this needs some special treatment) |
|
3483 @return the project browser helper object |
|
3484 """ |
|
3485 from .ProjectBrowserHelper import HgProjectBrowserHelper |
|
3486 return HgProjectBrowserHelper(self, browser, project, |
|
3487 isTranslationsBrowser) |
|
3488 |
|
3489 def vcsGetProjectHelper(self, project): |
|
3490 """ |
|
3491 Public method to instantiate a helper object for the project. |
|
3492 |
|
3493 @param project reference to the project object |
|
3494 @return the project helper object |
|
3495 """ |
|
3496 # find the root of the repo |
|
3497 repodir = project.getProjectPath() |
|
3498 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3499 repodir = os.path.dirname(repodir) |
|
3500 if not repodir or os.path.splitdrive(repodir)[1] == os.sep: |
|
3501 repodir = "" |
|
3502 break |
|
3503 if repodir: |
|
3504 self.__repoDir = repodir |
|
3505 |
|
3506 self.__projectHelper = self.__plugin.getProjectHelper() |
|
3507 self.__projectHelper.setObjects(self, project) |
|
3508 self.__monitorRepoIniFile(project.getProjectPath()) |
|
3509 |
|
3510 if repodir: |
|
3511 from .HgClient import HgClient |
|
3512 client = HgClient(repodir, "utf-8", self) |
|
3513 ok, err = client.startServer() |
|
3514 if ok: |
|
3515 self.__client = client |
|
3516 else: |
|
3517 E5MessageBox.warning( |
|
3518 None, |
|
3519 self.tr("Mercurial Command Server"), |
|
3520 self.tr( |
|
3521 """<p>The Mercurial Command Server could not be""" |
|
3522 """ started.</p><p>Reason: {0}</p>""").format(err)) |
|
3523 |
|
3524 return self.__projectHelper |
|
3525 |
|
3526 ########################################################################### |
|
3527 ## Status Monitor Thread methods |
|
3528 ########################################################################### |
|
3529 |
|
3530 def _createStatusMonitorThread(self, interval, project): |
|
3531 """ |
|
3532 Protected method to create an instance of the VCS status monitor |
|
3533 thread. |
|
3534 |
|
3535 @param interval check interval for the monitor thread in seconds |
|
3536 (integer) |
|
3537 @param project reference to the project object (Project) |
|
3538 @return reference to the monitor thread (QThread) |
|
3539 """ |
|
3540 from .HgStatusMonitorThread import HgStatusMonitorThread |
|
3541 return HgStatusMonitorThread(interval, project, self) |
|
3542 |
|
3543 ########################################################################### |
|
3544 ## Bookmarks methods |
|
3545 ########################################################################### |
|
3546 |
|
3547 def hgListBookmarks(self, path): |
|
3548 """ |
|
3549 Public method used to list the available bookmarks. |
|
3550 |
|
3551 @param path directory name of the project (string) |
|
3552 """ |
|
3553 self.bookmarksList = [] |
|
3554 |
|
3555 if self.bookmarksListDlg is None: |
|
3556 from .HgBookmarksListDialog import HgBookmarksListDialog |
|
3557 self.bookmarksListDlg = HgBookmarksListDialog(self) |
|
3558 self.bookmarksListDlg.show() |
|
3559 self.bookmarksListDlg.raise_() |
|
3560 self.bookmarksListDlg.start(path, self.bookmarksList) |
|
3561 |
|
3562 def hgGetBookmarksList(self, repodir): |
|
3563 """ |
|
3564 Public method to get the list of bookmarks. |
|
3565 |
|
3566 @param repodir directory name of the repository (string) |
|
3567 @return list of bookmarks (list of string) |
|
3568 """ |
|
3569 args = self.initCommand("bookmarks") |
|
3570 |
|
3571 client = self.getClient() |
|
3572 output = "" |
|
3573 if client: |
|
3574 output = client.runcommand(args)[0] |
|
3575 else: |
|
3576 process = QProcess() |
|
3577 process.setWorkingDirectory(repodir) |
|
3578 process.start('hg', args) |
|
3579 procStarted = process.waitForStarted(5000) |
|
3580 if procStarted: |
|
3581 finished = process.waitForFinished(30000) |
|
3582 if finished and process.exitCode() == 0: |
|
3583 output = str(process.readAllStandardOutput(), |
|
3584 self.getEncoding(), 'replace') |
|
3585 |
|
3586 self.bookmarksList = [] |
|
3587 for line in output.splitlines(): |
|
3588 li = line.strip().split() |
|
3589 if li[-1][0] in "1234567890": |
|
3590 # last element is a rev:changeset |
|
3591 del li[-1] |
|
3592 if li[0] == "*": |
|
3593 del li[0] |
|
3594 name = " ".join(li) |
|
3595 self.bookmarksList.append(name) |
|
3596 |
|
3597 return self.bookmarksList[:] |
|
3598 |
|
3599 def hgBookmarkDefine(self, name, revision=None, bookmark=None): |
|
3600 """ |
|
3601 Public method to define a bookmark. |
|
3602 |
|
3603 @param name file/directory name (string) |
|
3604 @param revision revision to set bookmark for (string) |
|
3605 @param bookmark name of the bookmark (string) |
|
3606 """ |
|
3607 # find the root of the repo |
|
3608 repodir = self.splitPath(name)[0] |
|
3609 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3610 repodir = os.path.dirname(repodir) |
|
3611 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3612 return |
|
3613 |
|
3614 if bool(revision) and bool(bookmark): |
|
3615 ok = True |
|
3616 else: |
|
3617 from .HgBookmarkDialog import HgBookmarkDialog |
|
3618 dlg = HgBookmarkDialog(HgBookmarkDialog.DEFINE_MODE, |
|
3619 self.hgGetTagsList(repodir), |
|
3620 self.hgGetBranchesList(repodir), |
|
3621 self.hgGetBookmarksList(repodir)) |
|
3622 if dlg.exec_() == QDialog.Accepted: |
|
3623 revision, bookmark = dlg.getData() |
|
3624 ok = True |
|
3625 else: |
|
3626 ok = False |
|
3627 |
|
3628 if ok: |
|
3629 args = self.initCommand("bookmarks") |
|
3630 if revision: |
|
3631 args.append("--rev") |
|
3632 args.append(revision) |
|
3633 args.append(bookmark) |
|
3634 |
|
3635 dia = HgDialog(self.tr('Mercurial Bookmark'), self) |
|
3636 res = dia.startProcess(args, repodir) |
|
3637 if res: |
|
3638 dia.exec_() |
|
3639 |
|
3640 def hgBookmarkDelete(self, name, bookmark=None): |
|
3641 """ |
|
3642 Public method to delete a bookmark. |
|
3643 |
|
3644 @param name file/directory name (string) |
|
3645 @param bookmark name of the bookmark (string) |
|
3646 """ |
|
3647 # find the root of the repo |
|
3648 repodir = self.splitPath(name)[0] |
|
3649 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3650 repodir = os.path.dirname(repodir) |
|
3651 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3652 return |
|
3653 |
|
3654 if bookmark: |
|
3655 ok = True |
|
3656 else: |
|
3657 bookmark, ok = QInputDialog.getItem( |
|
3658 None, |
|
3659 self.tr("Delete Bookmark"), |
|
3660 self.tr("Select the bookmark to be deleted:"), |
|
3661 [""] + sorted(self.hgGetBookmarksList(repodir)), |
|
3662 0, True) |
|
3663 if ok and bookmark: |
|
3664 args = self.initCommand("bookmarks") |
|
3665 args.append("--delete") |
|
3666 args.append(bookmark) |
|
3667 |
|
3668 dia = HgDialog(self.tr('Delete Mercurial Bookmark'), self) |
|
3669 res = dia.startProcess(args, repodir) |
|
3670 if res: |
|
3671 dia.exec_() |
|
3672 |
|
3673 def hgBookmarkRename(self, name, renameInfo=None): |
|
3674 """ |
|
3675 Public method to rename a bookmark. |
|
3676 |
|
3677 @param name file/directory name |
|
3678 @type str |
|
3679 @param renameInfo old and new names of the bookmark |
|
3680 @type tuple of str and str |
|
3681 """ |
|
3682 # find the root of the repo |
|
3683 repodir = self.splitPath(name)[0] |
|
3684 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3685 repodir = os.path.dirname(repodir) |
|
3686 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3687 return |
|
3688 |
|
3689 if not renameInfo: |
|
3690 from .HgBookmarkRenameDialog import HgBookmarkRenameDialog |
|
3691 dlg = HgBookmarkRenameDialog(self.hgGetBookmarksList(repodir)) |
|
3692 if dlg.exec_() == QDialog.Accepted: |
|
3693 renameInfo = dlg.getData() |
|
3694 |
|
3695 if renameInfo: |
|
3696 args = self.initCommand("bookmarks") |
|
3697 args.append("--rename") |
|
3698 args.append(renameInfo[0]) |
|
3699 args.append(renameInfo[1]) |
|
3700 |
|
3701 dia = HgDialog(self.tr('Rename Mercurial Bookmark'), self) |
|
3702 res = dia.startProcess(args, repodir) |
|
3703 if res: |
|
3704 dia.exec_() |
|
3705 |
|
3706 def hgBookmarkMove(self, name, revision=None, bookmark=None): |
|
3707 """ |
|
3708 Public method to move a bookmark. |
|
3709 |
|
3710 @param name file/directory name (string) |
|
3711 @param revision revision to set bookmark for (string) |
|
3712 @param bookmark name of the bookmark (string) |
|
3713 """ |
|
3714 # find the root of the repo |
|
3715 repodir = self.splitPath(name)[0] |
|
3716 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3717 repodir = os.path.dirname(repodir) |
|
3718 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3719 return |
|
3720 |
|
3721 if bool(revision) and bool(bookmark): |
|
3722 ok = True |
|
3723 else: |
|
3724 from .HgBookmarkDialog import HgBookmarkDialog |
|
3725 dlg = HgBookmarkDialog(HgBookmarkDialog.MOVE_MODE, |
|
3726 self.hgGetTagsList(repodir), |
|
3727 self.hgGetBranchesList(repodir), |
|
3728 self.hgGetBookmarksList(repodir)) |
|
3729 if dlg.exec_() == QDialog.Accepted: |
|
3730 revision, bookmark = dlg.getData() |
|
3731 ok = True |
|
3732 else: |
|
3733 ok = False |
|
3734 |
|
3735 if ok: |
|
3736 args = self.initCommand("bookmarks") |
|
3737 args.append("--force") |
|
3738 if revision: |
|
3739 args.append("--rev") |
|
3740 args.append(revision) |
|
3741 args.append(bookmark) |
|
3742 |
|
3743 dia = HgDialog(self.tr('Move Mercurial Bookmark'), self) |
|
3744 res = dia.startProcess(args, repodir) |
|
3745 if res: |
|
3746 dia.exec_() |
|
3747 |
|
3748 def hgBookmarkIncoming(self, name): |
|
3749 """ |
|
3750 Public method to show a list of incoming bookmarks. |
|
3751 |
|
3752 @param name file/directory name (string) |
|
3753 """ |
|
3754 from .HgBookmarksInOutDialog import HgBookmarksInOutDialog |
|
3755 self.bookmarksInOutDlg = HgBookmarksInOutDialog( |
|
3756 self, HgBookmarksInOutDialog.INCOMING) |
|
3757 self.bookmarksInOutDlg.show() |
|
3758 self.bookmarksInOutDlg.start(name) |
|
3759 |
|
3760 def hgBookmarkOutgoing(self, name): |
|
3761 """ |
|
3762 Public method to show a list of outgoing bookmarks. |
|
3763 |
|
3764 @param name file/directory name (string) |
|
3765 """ |
|
3766 from .HgBookmarksInOutDialog import HgBookmarksInOutDialog |
|
3767 self.bookmarksInOutDlg = HgBookmarksInOutDialog( |
|
3768 self, HgBookmarksInOutDialog.OUTGOING) |
|
3769 self.bookmarksInOutDlg.show() |
|
3770 self.bookmarksInOutDlg.start(name) |
|
3771 |
|
3772 def __getInOutBookmarks(self, repodir, incoming): |
|
3773 """ |
|
3774 Private method to get the list of incoming or outgoing bookmarks. |
|
3775 |
|
3776 @param repodir directory name of the repository (string) |
|
3777 @param incoming flag indicating to get incoming bookmarks (boolean) |
|
3778 @return list of bookmarks (list of string) |
|
3779 """ |
|
3780 bookmarksList = [] |
|
3781 |
|
3782 if incoming: |
|
3783 args = self.initCommand("incoming") |
|
3784 else: |
|
3785 args = self.initCommand("outgoing") |
|
3786 args.append('--bookmarks') |
|
3787 |
|
3788 client = self.getClient() |
|
3789 output = "" |
|
3790 if client: |
|
3791 output = client.runcommand(args)[0] |
|
3792 else: |
|
3793 process = QProcess() |
|
3794 process.setWorkingDirectory(repodir) |
|
3795 process.start('hg', args) |
|
3796 procStarted = process.waitForStarted(5000) |
|
3797 if procStarted: |
|
3798 finished = process.waitForFinished(30000) |
|
3799 if finished and process.exitCode() == 0: |
|
3800 output = str(process.readAllStandardOutput(), |
|
3801 self.getEncoding(), 'replace') |
|
3802 |
|
3803 for line in output.splitlines(): |
|
3804 if line.startswith(" "): |
|
3805 li = line.strip().split() |
|
3806 del li[-1] |
|
3807 name = " ".join(li) |
|
3808 bookmarksList.append(name) |
|
3809 |
|
3810 return bookmarksList |
|
3811 |
|
3812 def hgBookmarkPull(self, name, current=False, bookmark=None): |
|
3813 """ |
|
3814 Public method to pull a bookmark from a remote repository. |
|
3815 |
|
3816 @param name file/directory name |
|
3817 @type str |
|
3818 @param current flag indicating to pull the current bookmark |
|
3819 @type bool |
|
3820 @param bookmark name of the bookmark |
|
3821 @type str |
|
3822 """ |
|
3823 # find the root of the repo |
|
3824 repodir = self.splitPath(name)[0] |
|
3825 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3826 repodir = os.path.dirname(repodir) |
|
3827 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3828 return |
|
3829 |
|
3830 if current: |
|
3831 bookmark = "." |
|
3832 ok = True |
|
3833 elif bookmark: |
|
3834 ok = True |
|
3835 else: |
|
3836 bookmarks = self.__getInOutBookmarks(repodir, True) |
|
3837 bookmark, ok = QInputDialog.getItem( |
|
3838 None, |
|
3839 self.tr("Pull Bookmark"), |
|
3840 self.tr("Select the bookmark to be pulled:"), |
|
3841 [""] + sorted(bookmarks), |
|
3842 0, True) |
|
3843 |
|
3844 if ok and bookmark: |
|
3845 args = self.initCommand("pull") |
|
3846 args.append('--bookmark') |
|
3847 args.append(bookmark) |
|
3848 |
|
3849 dia = HgDialog(self.tr( |
|
3850 'Pulling bookmark from a remote Mercurial repository'), |
|
3851 self) |
|
3852 res = dia.startProcess(args, repodir) |
|
3853 if res: |
|
3854 dia.exec_() |
|
3855 |
|
3856 def hgBookmarkPush(self, name, current=False, bookmark=None): |
|
3857 """ |
|
3858 Public method to push a bookmark to a remote repository. |
|
3859 |
|
3860 @param name file/directory name |
|
3861 @type str |
|
3862 @param current flag indicating to push the current bookmark |
|
3863 @type bool |
|
3864 @param bookmark name of the bookmark |
|
3865 @type str |
|
3866 """ |
|
3867 # find the root of the repo |
|
3868 repodir = self.splitPath(name)[0] |
|
3869 while not os.path.isdir(os.path.join(repodir, self.adminDir)): |
|
3870 repodir = os.path.dirname(repodir) |
|
3871 if os.path.splitdrive(repodir)[1] == os.sep: |
|
3872 return |
|
3873 |
|
3874 if current: |
|
3875 bookmark = "." |
|
3876 ok = True |
|
3877 elif bookmark: |
|
3878 ok = True |
|
3879 else: |
|
3880 bookmarks = self.__getInOutBookmarks(repodir, False) |
|
3881 bookmark, ok = QInputDialog.getItem( |
|
3882 None, |
|
3883 self.tr("Push Bookmark"), |
|
3884 self.tr("Select the bookmark to be push:"), |
|
3885 [""] + sorted(bookmarks), |
|
3886 0, True) |
|
3887 |
|
3888 if ok and bookmark: |
|
3889 args = self.initCommand("push") |
|
3890 args.append('--bookmark') |
|
3891 args.append(bookmark) |
|
3892 |
|
3893 dia = HgDialog(self.tr( |
|
3894 'Pushing bookmark to a remote Mercurial repository'), |
|
3895 self) |
|
3896 res = dia.startProcess(args, repodir) |
|
3897 if res: |
|
3898 dia.exec_() |