|
# thgstrip.py - MQ strip dialog for TortoiseHg
#
# Copyright 2009 Yuki KODAMA <endflow.net@gmail.com>
# Copyright 2010 David Wilhelm <dave@jumbledpile.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from mercurial import hg, ui, error
from tortoisehg.util import hglib, paths
from tortoisehg.hgqt.i18n import _
from tortoisehg.hgqt import cmdui, cslist, qtlib, thgrepo
class StripDialog(QDialog):
"""Dialog to strip changesets"""
showBusyIcon = pyqtSignal(QString)
hideBusyIcon = pyqtSignal(QString)
def __init__(self, repo=None, rev=None, parent=None, opts={}):
super(StripDialog, self).__init__(parent)
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
self.setWindowIcon(qtlib.geticon('menudelete'))
self.ui = ui.ui()
if repo:
self.repo = repo
else:
root = paths.find_root()
if root:
self.repo = thgrepo.repository(self.ui, path=root)
else:
raise 'not repository'
# base layout box
box = QVBoxLayout()
box.setSpacing(6)
## main layout grid
self.grid = grid = QGridLayout()
grid.setSpacing(6)
box.addLayout(grid)
### target revision combo
self.rev_combo = combo = QComboBox()
combo.setEditable(True)
grid.addWidget(QLabel(_('Strip:')), 0, 0)
grid.addWidget(combo, 0, 1)
grid.addWidget(QLabel(_('Preview:')), 1, 0, Qt.AlignLeft | Qt.AlignTop)
self.status = QLabel("")
grid.addWidget(self.status, 1, 1, Qt.AlignLeft | Qt.AlignTop)
if rev is None:
rev = self.repo.dirstate.branch()
else:
rev = str(rev)
combo.addItem(hglib.tounicode(rev))
combo.setCurrentIndex(0)
for name in self.repo.namedbranches:
combo.addItem(name)
tags = list(self.repo.tags())
tags.sort()
tags.reverse()
for tag in tags:
combo.addItem(hglib.tounicode(tag))
### preview box, contained in scroll area, contains preview grid
self.cslist = cslist.ChangesetList()
self.cslistrow = cslistrow = 2
self.cslistcol = cslistcol = 1
grid.addWidget(self.cslist, cslistrow, cslistcol,
Qt.AlignLeft | Qt.AlignTop)
### options
optbox = QVBoxLayout()
optbox.setSpacing(6)
expander = qtlib.ExpanderLabel(_('Options:'), False)
expander.expanded.connect(self.show_options)
grid.addWidget(expander, 3, 0, Qt.AlignLeft | Qt.AlignTop)
grid.addLayout(optbox, 3, 1)
self.discard_chk = QCheckBox(_('Discard local changes, no backup (-f/--force)'))
self.nobackup_chk = QCheckBox(_('No backup (-n/--nobackup)'))
optbox.addWidget(self.discard_chk)
optbox.addWidget(self.nobackup_chk)
self.discard_chk.setChecked(bool(opts.get('force')))
self.nobackup_chk.setChecked(bool(opts.get('nobackup')))
## command widget
self.cmd = cmdui.Widget(True, True, self)
self.cmd.commandStarted.connect(self.command_started)
self.cmd.commandFinished.connect(self.command_finished)
self.cmd.commandCanceling.connect(self.command_canceling)
box.addWidget(self.cmd)
## bottom buttons
buttons = QDialogButtonBox()
self.cancel_btn = buttons.addButton(QDialogButtonBox.Cancel)
self.cancel_btn.clicked.connect(self.cancel_clicked)
self.close_btn = buttons.addButton(QDialogButtonBox.Close)
self.close_btn.clicked.connect(self.reject)
self.close_btn.setAutoDefault(False)
self.strip_btn = buttons.addButton(_('&Strip'),
QDialogButtonBox.ActionRole)
self.strip_btn.clicked.connect(self.strip)
self.detail_btn = buttons.addButton(_('Detail'),
QDialogButtonBox.ResetRole)
self.detail_btn.setAutoDefault(False)
self.detail_btn.setCheckable(True)
self.detail_btn.toggled.connect(self.detail_toggled)
grid.setRowStretch(cslistrow, 1)
grid.setColumnStretch(cslistcol, 1)
box.addWidget(buttons)
# signal handlers
self.rev_combo.editTextChanged.connect(lambda *a: self.preview())
self.rev_combo.lineEdit().returnPressed.connect(self.strip)
self.discard_chk.toggled.connect(lambda *a: self.preview())
# dialog setting
self.setLayout(box)
self.layout().setSizeConstraint(QLayout.SetMinAndMaxSize)
self.setWindowTitle(_('Strip - %s') % self.repo.displayname)
#self.setWindowIcon(qtlib.geticon('strip'))
# prepare to show
self.rev_combo.lineEdit().selectAll()
self.cslist.setHidden(False)
self.cmd.setHidden(True)
self.cancel_btn.setHidden(True)
self.detail_btn.setHidden(True)
self.nobackup_chk.setHidden(True)
self.preview()
### Private Methods ###
def resizeEvent(self, event):
w = self.grid.cellRect(self.cslistrow, self.cslistcol).width()
h = self.grid.cellRect(self.cslistrow, self.cslistcol).height()
self.cslist.resize(w, h)
def get_rev(self):
"""Return the integer revision number of the input or None"""
revstr = hglib.fromunicode(self.rev_combo.currentText())
if not revstr:
return None
try:
rev = self.repo[revstr].rev()
except (error.RepoError, error.LookupError):
return None
return rev
def updatecslist(self, uselimit=True):
"""Update the cs list and return the success status as a bool"""
rev = self.get_rev()
if not rev:
return False
striprevs = list(self.repo.changelog.descendants(rev))
striprevs.append(rev)
striprevs.sort()
self.cslist.clear()
self.cslist.update(self.repo, striprevs)
return True
def preview(self):
if self.updatecslist():
striprevs = self.cslist.curitems
cstext = _("<b>%d changesets</b> will be stripped") % len(striprevs)
self.status.setText(cstext)
self.strip_btn.setEnabled(True)
else:
self.cslist.clear()
self.cslist.updatestatus()
cstext = qtlib.markup(_('Unknown revision!'), fg='red',
weight='bold')
self.status.setText(cstext)
self.strip_btn.setDisabled(True)
def strip(self):
cmdline = ['strip', '--repository', self.repo.root, '--verbose']
rev = hglib.fromunicode(self.rev_combo.currentText())
if not rev:
return
cmdline.append(rev)
if self.discard_chk.isChecked():
cmdline.append('--force')
else:
try:
node = self.repo[rev]
except (error.LookupError, error.RepoLookupError, error.RepoError):
return
def isclean():
"""return whether WD is changed"""
wc = self.repo[None]
return not (wc.modified() or wc.added() or wc.removed())
if not isclean():
main = _("Detected uncommitted local changes.")
text = _("Do you want to discard them and continue?")
labels = ((QMessageBox.Yes, _('&Yes (--force)')),
(QMessageBox.No, _('&No')))
if qtlib.QuestionMsgBox(_('Confirm Strip'), main, text,
labels=labels, parent=self):
cmdline.append('--force')
else:
return
# backup options
if self.nobackup_chk.isChecked():
cmdline.append('--nobackup')
# start the strip
self.repo.incrementBusyCount()
self.cmd.run(cmdline)
### Signal Handlers ###
def cancel_clicked(self):
self.cmd.cancel()
self.reject()
def detail_toggled(self, checked):
self.cmd.setShowOutput(checked)
def show_options(self, visible):
self.nobackup_chk.setShown(visible)
def command_started(self):
self.cmd.setShown(True)
self.strip_btn.setHidden(True)
self.close_btn.setHidden(True)
self.cancel_btn.setShown(True)
self.detail_btn.setShown(True)
self.showBusyIcon.emit('hg-remove')
def command_finished(self, ret):
self.hideBusyIcon.emit('hg-remove')
self.repo.decrementBusyCount()
if ret is not 0 or self.cmd.outputShown():
self.detail_btn.setChecked(True)
self.close_btn.setShown(True)
self.close_btn.setAutoDefault(True)
self.close_btn.setFocus()
self.cancel_btn.setHidden(True)
else:
self.accept()
def command_canceling(self):
self.cancel_btn.setDisabled(True)
def run(ui, *pats, **opts):
rev = None
if opts.get('rev'):
rev = opts.get('rev')
elif len(pats) == 1:
rev = pats[0]
return StripDialog(rev=rev, opts=opts)
|
Loading...