PageRenderTime 43ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/tortoisehg/hgqt/rebase.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 254 lines | 218 code | 30 blank | 6 comment | 39 complexity | 4f01408d6c675e36fcd5a1ac166df84f MD5 | raw file
Possible License(s): GPL-2.0
  1. # rebase.py - Rebase dialog for TortoiseHg
  2. #
  3. # Copyright 2010 Steve Borho <steve@borho.org>
  4. #
  5. # This software may be used and distributed according to the terms of the
  6. # GNU General Public License version 2, incorporated herein by reference.
  7. from PyQt4.QtCore import *
  8. from PyQt4.QtGui import *
  9. import os
  10. from mercurial import util, merge as mergemod
  11. from tortoisehg.util import hglib
  12. from tortoisehg.hgqt.i18n import _
  13. from tortoisehg.hgqt import qtlib, csinfo, cmdui, resolve, commit
  14. BB = QDialogButtonBox
  15. class RebaseDialog(QDialog):
  16. showMessage = pyqtSignal(QString)
  17. def __init__(self, repo, parent, **opts):
  18. super(RebaseDialog, self).__init__(parent)
  19. f = self.windowFlags()
  20. self.setWindowFlags(f & ~Qt.WindowContextHelpButtonHint)
  21. self.repo = repo
  22. self.opts = opts
  23. box = QVBoxLayout()
  24. box.setSpacing(8)
  25. box.setContentsMargins(*(6,)*4)
  26. self.setLayout(box)
  27. style = csinfo.panelstyle(selectable=True)
  28. srcb = QGroupBox( _('Rebase changeset and descendants'))
  29. srcb.setLayout(QVBoxLayout())
  30. srcb.layout().setContentsMargins(*(2,)*4)
  31. s = opts.get('source', '.')
  32. source = csinfo.create(self.repo, s, style, withupdate=True)
  33. srcb.layout().addWidget(source)
  34. self.layout().addWidget(srcb)
  35. destb = QGroupBox( _('To rebase destination'))
  36. destb.setLayout(QVBoxLayout())
  37. destb.layout().setContentsMargins(*(2,)*4)
  38. d = opts.get('dest', '.')
  39. dest = csinfo.create(self.repo, d, style, withupdate=True)
  40. destb.layout().addWidget(dest)
  41. self.destcsinfo = dest
  42. self.layout().addWidget(destb)
  43. sep = qtlib.LabeledSeparator(_('Options'))
  44. self.layout().addWidget(sep)
  45. self.keepchk = QCheckBox(_('Keep original changesets'))
  46. self.keepchk.setChecked(opts.get('keep', False))
  47. self.layout().addWidget(self.keepchk)
  48. self.detachchk = QCheckBox(_('Force detach of rebased changesets '
  49. 'from their original branch'))
  50. self.detachchk.setChecked(opts.get('detach', True))
  51. self.layout().addWidget(self.detachchk)
  52. self.autoresolvechk = QCheckBox(_('Automatically resolve merge conflicts '
  53. 'where possible'))
  54. self.autoresolvechk.setChecked(
  55. repo.ui.configbool('tortoisehg', 'autoresolve', False))
  56. self.layout().addWidget(self.autoresolvechk)
  57. if 'hgsubversion' in repo.extensions():
  58. self.svnchk = QCheckBox(_('Rebase unpublished onto Subversion head'
  59. ' (override source, destination)'))
  60. self.layout().addWidget(self.svnchk)
  61. else:
  62. self.svnchk = None
  63. self.cmd = cmdui.Widget()
  64. self.cmd.commandFinished.connect(self.commandFinished)
  65. self.showMessage.connect(self.cmd.stbar.showMessage)
  66. self.cmd.stbar.linkActivated.connect(self.linkActivated)
  67. self.layout().addWidget(self.cmd, 2)
  68. bbox = QDialogButtonBox()
  69. self.cancelbtn = bbox.addButton(QDialogButtonBox.Cancel)
  70. self.cancelbtn.clicked.connect(self.reject)
  71. self.rebasebtn = bbox.addButton(_('Rebase'),
  72. QDialogButtonBox.ActionRole)
  73. self.rebasebtn.clicked.connect(self.rebase)
  74. self.abortbtn = bbox.addButton(_('Abort'),
  75. QDialogButtonBox.ActionRole)
  76. self.abortbtn.clicked.connect(self.abort)
  77. self.layout().addWidget(bbox)
  78. self.bbox = bbox
  79. if self.checkResolve() or not (s or d):
  80. for w in (srcb, destb, sep, self.keepchk, self.detachchk):
  81. w.setHidden(True)
  82. self.cmd.setShowOutput(True)
  83. else:
  84. self.showMessage.emit(_('Checking...'))
  85. QTimer.singleShot(0, self.checkStatus)
  86. self.setMinimumWidth(480)
  87. self.setMaximumHeight(800)
  88. self.resize(0, 340)
  89. self.setWindowTitle(_('Rebase - %s') % self.repo.displayname)
  90. def checkStatus(self):
  91. repo = self.repo
  92. class CheckThread(QThread):
  93. def __init__(self, parent):
  94. QThread.__init__(self, parent)
  95. self.dirty = False
  96. def run(self):
  97. wctx = repo[None]
  98. if len(wctx.parents()) > 1:
  99. self.dirty = True
  100. elif wctx.dirty():
  101. self.dirty = True
  102. else:
  103. ms = mergemod.mergestate(repo)
  104. unresolved = False
  105. for path in ms:
  106. if ms[path] == 'u':
  107. self.dirty = True
  108. break
  109. def completed():
  110. self.th.wait()
  111. if self.th.dirty:
  112. self.rebasebtn.setEnabled(False)
  113. txt = _('Before rebase, you must <a href="commit">'
  114. '<b>commit</b></a> or <a href="discard">'
  115. '<b>discard</b></a> changes.')
  116. else:
  117. self.rebasebtn.setEnabled(True)
  118. txt = _('You may continue the rebase')
  119. self.showMessage.emit(txt)
  120. self.th = CheckThread(self)
  121. self.th.finished.connect(completed)
  122. self.th.start()
  123. def rebase(self):
  124. self.cancelbtn.setShown(False)
  125. self.keepchk.setEnabled(False)
  126. self.detachchk.setEnabled(False)
  127. cmdline = ['rebase', '--repository', self.repo.root]
  128. cmdline += ['--config', 'ui.merge=internal:' +
  129. (self.autoresolvechk.isChecked() and 'merge' or 'fail')]
  130. if os.path.exists(self.repo.join('rebasestate')):
  131. cmdline += ['--continue']
  132. else:
  133. if self.keepchk.isChecked():
  134. cmdline += ['--keep']
  135. if self.detachchk.isChecked():
  136. cmdline += ['--detach']
  137. if self.svnchk is not None and self.svnchk.isChecked():
  138. cmdline += ['--svn']
  139. else:
  140. source = self.opts.get('source')
  141. dest = self.opts.get('dest')
  142. cmdline += ['--source', str(source), '--dest', str(dest)]
  143. self.repo.incrementBusyCount()
  144. self.cmd.run(cmdline)
  145. def abort(self):
  146. cmdline = ['rebase', '--repository', self.repo.root, '--abort']
  147. self.repo.incrementBusyCount()
  148. self.cmd.run(cmdline)
  149. def commandFinished(self, ret):
  150. self.repo.decrementBusyCount()
  151. if self.checkResolve() is False:
  152. self.showMessage.emit(_('Rebase is complete'))
  153. self.rebasebtn.setText(_('Close'))
  154. self.rebasebtn.clicked.disconnect(self.rebase)
  155. self.rebasebtn.clicked.connect(self.accept)
  156. def checkResolve(self):
  157. ms = mergemod.mergestate(self.repo)
  158. for path in ms:
  159. if ms[path] == 'u':
  160. txt = _('Rebase generated merge <b>conflicts</b> that must '
  161. 'be <a href="resolve"><b>resolved</b></a>')
  162. self.rebasebtn.setEnabled(False)
  163. break
  164. else:
  165. self.rebasebtn.setEnabled(True)
  166. txt = _('You may continue the rebase')
  167. self.showMessage.emit(txt)
  168. if os.path.exists(self.repo.join('rebasestate')):
  169. self.abortbtn.setEnabled(True)
  170. self.rebasebtn.setText('Continue')
  171. return True
  172. else:
  173. self.abortbtn.setEnabled(False)
  174. return False
  175. def linkActivated(self, cmd):
  176. if cmd == 'resolve':
  177. dlg = resolve.ResolveDialog(self.repo, self)
  178. dlg.exec_()
  179. self.checkResolve()
  180. elif cmd == 'commit':
  181. dlg = commit.CommitDialog([], dict(root=self.repo.root), self)
  182. dlg.finished.connect(dlg.deleteLater)
  183. dlg.exec_()
  184. self.destcsinfo.update(self.repo['.'])
  185. self.checkStatus()
  186. elif cmd == 'discard':
  187. labels = [(QMessageBox.Yes, _('&Discard')),
  188. (QMessageBox.No, _('Cancel'))]
  189. if not qtlib.QuestionMsgBox(_('Confirm Discard'), _('Discard'
  190. ' outstanding changes in working directory?'),
  191. labels=labels, parent=self):
  192. return
  193. def finished(ret):
  194. self.repo.decrementBusyCount()
  195. if ret == 0:
  196. self.checkStatus()
  197. cmdline = ['update', '--clean', '--repository', self.repo.root,
  198. '--rev', '.']
  199. self.runner = cmdui.Runner(_('Discard - TortoiseHg'), True, self)
  200. self.runner.commandFinished.connect(finished)
  201. self.repo.incrementBusyCount()
  202. self.runner.run(cmdline)
  203. def reject(self):
  204. if os.path.exists(self.repo.join('rebasestate')):
  205. main = _('Rebase is incomplete, exiting is not recommended')
  206. text = _('Abort is recommended before exit.')
  207. labels = ((QMessageBox.Yes, _('&Exit')),
  208. (QMessageBox.No, _('Cancel')))
  209. if not qtlib.QuestionMsgBox(_('Confirm Exit'), main, text,
  210. labels=labels, parent=self):
  211. return
  212. super(RebaseDialog, self).reject()
  213. def run(ui, *pats, **opts):
  214. from tortoisehg.util import paths
  215. from tortoisehg.hgqt import thgrepo
  216. repo = thgrepo.repository(ui, path=paths.find_root())
  217. if os.path.exists(repo.join('rebasestate')):
  218. qtlib.InfoMsgBox(_('Rebase already in progress'),
  219. _('Resuming rebase already in progress'))
  220. elif not opts['source'] or not opts['dest']:
  221. qtlib.ErrorMsgBox(_('Abort'),
  222. _('You must provide source and dest arguments'))
  223. import sys; sys.exit()
  224. return RebaseDialog(repo, None, **opts)