|
# update.py - TortoiseHg's dialog for updating repo
#
# Copyright 2007 TK Soh <teekaysoh@gmail.com>
# Copyright 2007 Steve Borho <steve@borho.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
import gtk
from mercurial import ui, error
from tortoisehg.util.i18n import _
from tortoisehg.util import hglib
from tortoisehg.hgtk import csinfo, gtklib, gdialog
class UpdateDialog(gdialog.GDialog):
""" Dialog to update Mercurial repo """
def __init__(self, rev=None):
gdialog.GDialog.__init__(self)
self.rev = rev
### Start of Overriding Section ###
def get_title(self, reponame):
return _('Update - %s') % reponame
def get_icon(self):
return 'menucheckout.ico'
def get_setting_name(self):
return 'update'
def get_body(self, vbox):
# layout table
table = gtklib.LayoutTable()
vbox.pack_start(table, False, False, 2)
self.table = table
## revision label & combobox
self.revcombo = combo = gtk.combo_box_entry_new_text()
entry = combo.child
entry.set_width_chars(38)
entry.connect('activate', lambda b: self.response(gtk.RESPONSE_OK))
table.add_row(_('Update to:'), combo, padding=False)
## fill list of combo
if self.rev != None:
combo.append_text(str(self.rev))
else:
combo.append_text(self.repo.dirstate.branch())
combo.set_active(0)
for name in hglib.getlivebranch(self.repo):
combo.append_text(name)
tags = list(self.repo.tags())
tags.sort()
tags.reverse()
for tag in tags:
combo.append_text(hglib.toutf(tag))
## changeset summaries
style = csinfo.labelstyle(contents=('%(rev)s', ' %(branch)s',
' %(tags)s', '\n%(summary)s'), selectable=True, width=350)
factory = csinfo.factory(self.repo, style=style)
def add_with_pad(title, cslabel):
label = gtk.Label(title)
label.set_alignment(1, 0)
headbox = gtk.VBox()
headbox.pack_start(label, False, False, 2)
headbox.pack_start(gtk.VBox())
table.add_row(headbox, cslabel, yhopt=gtk.FILL|gtk.EXPAND)
## summary of target revision
self.target_label = factory()
add_with_pad(_('Target:'), self.target_label)
## summary of parent 1 revision
self.parent1_label = factory()
## summary of parent 2 revision if needs
self.ctxs = self.repo[None].parents()
if len(self.ctxs) == 2:
add_with_pad(_('Parent 1:'), self.parent1_label)
self.parent2_label = factory()
add_with_pad(_('Parent 2:'), self.parent2_label)
else:
add_with_pad(_('Parent:'), self.parent1_label)
self.parent2_label = None
## option expander
self.expander = gtk.Expander(_('Options:'))
self.expander.connect('notify::expanded', self.options_expanded)
### update method (fixed)
self.opt_clean = gtk.CheckButton(_('Discard local changes, '
'no backup (-C/--clean)'))
table.add_row(self.expander, self.opt_clean)
### other options (foldable), put later
### automatically merge, if possible (similar to command-line behavior)
self.opt_merge = gtk.CheckButton(_('Always merge (when possible)'))
### always show command log widget
self.opt_showlog = gtk.CheckButton(_('Always show log'))
# signal handlers
self.revcombo.connect('changed', lambda b: self.update_summaries())
self.opt_clean.connect('toggled', lambda b: self.update_summaries())
# prepare to show
self.update_summaries()
def get_extras(self, vbox):
# append options
self.opttable = gtklib.LayoutTable()
vbox.pack_start(self.opttable, False, False)
self.opttable.add_row(None, self.opt_merge, ypad=0)
self.opttable.add_row(None, self.opt_showlog, ypad=0)
# layout group
layout = gtklib.LayoutGroup()
layout.add(self.table, self.opttable, force=True)
def get_buttons(self):
return [('update', _('Update'), gtk.RESPONSE_OK),
('close', gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)]
def get_default_button(self):
return 'update'
def get_action_map(self):
return {gtk.RESPONSE_OK: self.update}
def switch_to(self, normal, working, cmd):
self.table.set_sensitive(normal)
self.opttable.set_sensitive(normal)
self.buttons['update'].set_property('visible', normal)
self.buttons['close'].set_property('visible', normal)
if normal:
self.buttons['close'].grab_focus()
if working:
self.set_resizable(True)
self.vbox.set_child_packing(self.cmd, True, True, 6, gtk.PACK_START)
if cmd and self.opt_showlog.get_active():
self.cmd.show_log()
def command_done(self, returncode, useraborted, *args):
if returncode == 0:
self.cmd.set_result(_('Updated successfully'), style='ok')
elif useraborted:
self.cmd.set_result(_('Canceled updating'), style='error')
else:
self.cmd.set_result(_('Failed to update'), style='error')
def load_settings(self):
merge = self.settings.get_value('mergedefault', False)
showlog = self.settings.get_value('showlog', False)
self.opt_merge.set_active(merge)
self.opt_showlog.set_active(showlog)
def store_settings(self):
checked = self.opt_merge.get_active()
showlog = self.opt_showlog.get_active()
self.settings.set_value('mergedefault', checked)
self.settings.set_value('showlog', showlog)
self.settings.write()
### End of Overriding Section ###
def options_expanded(self, expander, *args):
if expander.get_expanded():
self.opttable.show_all()
else:
self.opttable.hide()
def update_summaries(self):
ctxs = self.ctxs
self.parent1_label.update(ctxs[0])
merge = len(ctxs) == 2
if merge:
self.parent2_label.update(ctxs[1])
newrev = hglib.fromutf(self.revcombo.get_active_text())
try:
new_ctx = self.repo[newrev]
if not merge and new_ctx.rev() == ctxs[0].rev():
self.target_label.set_label(_('(same as parent)'))
clean = self.opt_clean.get_active()
self.buttons['update'].set_sensitive(clean)
else:
self.target_label.update(self.repo[newrev])
self.buttons['update'].set_sensitive(True)
except (error.LookupError, error.RepoLookupError, error.RepoError):
self.target_label.set_label(_('unknown revision!'))
self.buttons['update'].set_sensitive(False)
def update(self):
cmdline = ['hg', 'update', '--verbose']
rev = hglib.fromutf(self.revcombo.get_active_text())
cmdline.append('--rev')
cmdline.append(rev)
if self.opt_clean.get_active():
cmdline.append('--clean')
else:
cur = self.repo['.']
node = self.repo[rev]
def isclean():
'''whether WD is changed'''
wc = self.repo[None]
return not (wc.modified() or wc.added() or wc.removed())
def ismergedchange():
'''whether the local changes are merged (have 2 parents)'''
wc = self.repo[None]
return len(wc.parents()) == 2
def iscrossbranch(p1, p2):
'''whether p1 -> p2 crosses branch'''
pa = p1.ancestor(p2)
return p1.branch() != p2.branch() or (p1 != pa and p2 != pa)
def islocalmerge(p1, p2, clean=None):
if clean is None:
clean = isclean()
pa = p1.ancestor(p2)
return not clean and (p1 == pa or p2 == pa)
def confirmupdate(clean=None):
if clean is None:
clean = isclean()
msg = _('Detected uncommitted local changes in working tree.\n'
'Please select to continue:\n\n')
data = {'discard': (_('&Discard'),
_('Discard - discard local changes, no backup')),
'shelve': (_('&Shelve'),
_('Shelve - launch Shelve tool and continue')),
'merge': (_('&Merge'),
_('Merge - allow to merge with local changes')),
'cancel': (_('&Cancel'), None)}
opts = [data['discard']]
if not ismergedchange():
opts.append(data['shelve'])
if islocalmerge(cur, node, clean):
opts.append(data['merge'])
opts.append(data['cancel'])
msg += '\n'.join([ desc for label, desc in opts if desc ])
buttons = [ label for label, desc in opts ]
cancel = len(opts) - 1
retcode = gdialog.CustomPrompt(_('Confirm Update'), msg, self,
buttons, default=cancel, esc=cancel).run()
retlabel = buttons[retcode]
retid = [ id for id, (label, desc) in data.items() \
if label == retlabel ][0]
return dict([(id, id == retid) for id in data.keys()])
# If merge-by-default, we want to merge whenever possible,
# without prompting user (similar to command-line behavior)
defaultmerge = self.opt_merge.get_active()
clean = isclean()
if clean:
cmdline.append('--check')
elif not (defaultmerge and islocalmerge(cur, node, clean)):
ret = confirmupdate(clean)
if ret['discard']:
cmdline.append('--clean')
elif ret['shelve']:
def launch_shelve():
from tortoisehg.hgtk import thgshelve
dlg = thgshelve.run(ui.ui())
dlg.set_transient_for(self)
dlg.set_modal(True)
dlg.display()
dlg.connect('destroy', lambda w: self.update())
gtklib.idle_add_single_call(launch_shelve)
return # retry later, no need to destroy
elif ret['merge']:
pass # no args
elif ret['cancel']:
self.cmd.log.append(_('[canceled by user]\n'), error=True)
self.do_switch_to(gdialog.MODE_WORKING)
self.abort()
return
else:
raise _('invalid dialog result: %s') % ret
# start updating
self.execute_command(cmdline)
def run(ui, *pats, **opts):
rev = None
if opts.get('rev'):
rev = opts.get('rev')[0]
return UpdateDialog(rev)
|
Loading...