Changeset 9cd29d482d10…
Parent 26fabf247f40…
by
Changes to 7 files · Browse files at 9cd29d482d10 Showing diff from parent 26fabf247f40 Diff from another changeset...
|
|
@@ -0,0 +1,171 @@ + <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.47 r22583"
+ version="1.0"
+ sodipodi:docname="merge.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="C:\src\thg\new-icons\icons\tortoise\commit-32.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"
+ style="display:inline">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3897">
+ <stop
+ style="stop-color:#427fbf;stop-opacity:1;"
+ offset="0"
+ id="stop3899" />
+ <stop
+ style="stop-color:#a6c2e1;stop-opacity:0.99130434;"
+ offset="1"
+ id="stop3901" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3687">
+ <stop
+ style="stop-color:#94ec3e;stop-opacity:1;"
+ offset="0"
+ id="stop3689" />
+ <stop
+ style="stop-color:#67bd13;stop-opacity:1;"
+ offset="1"
+ id="stop3691" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3679">
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:1;"
+ offset="0"
+ id="stop3681" />
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:0;"
+ offset="1"
+ id="stop3683" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective2892"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3802"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3891"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2596"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3687"
+ id="linearGradient3374"
+ gradientUnits="userSpaceOnUse"
+ x1="65.14286"
+ y1="99.933609"
+ x2="65.14286"
+ y2="115.93361"
+ gradientTransform="matrix(1,0,0,-1,0,217.86722)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6568543"
+ inkscape:cx="-36.162495"
+ inkscape:cy="15.56913"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:snap-bbox="true"
+ inkscape:snap-nodes="true"
+ inkscape:window-width="1024"
+ inkscape:window-height="753"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:snap-global="true"
+ inkscape:window-maximized="0">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2898"
+ visible="true"
+ enabled="true"
+ empspacing="2"
+ dotted="false"
+ spacingx="0.5px"
+ spacingy="0.5px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>Merge</dc:title>
+ <dc:date>2008-04-26</dc:date>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Peer Sommerlund</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:description />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Vector"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-49.142857,-92.933609)"
+ style="display:inline">
+ <path
+ style="fill:url(#linearGradient3374);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1.04799998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 73.142857,123.43361 3.5,-3.5 c -3.5,-3.5 -9,-6 -9,-12 l 0,-6.5 5,0 -7.5,-7.000001 -7,7.000001 4.5,0 0,6.5 c 0,6 -7,10 -9,12 l 3.5,3.5 c 2.512717,-3.00682 5,-3.5 8,-8 2.5,3.5 5.5,5.5 8,8 z"
+ id="path2900"
+ sodipodi:nodetypes="ccccccccccccc" />
+ </g>
+</svg>
|
|
|
@@ -0,0 +1,605 @@ + # merge.py - Merge dialog for TortoiseHg
+#
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.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 Qt, pyqtSignal, QThread
+from PyQt4.QtGui import QWizard, QWizardPage, QVBoxLayout, QHBoxLayout, QLabel
+from PyQt4.QtGui import QCheckBox, QTextEdit, QTextCursor, QLineEdit, QWidget
+from PyQt4.QtGui import QComboBox, QStackedLayout, QPushButton, QProgressBar
+from PyQt4.QtGui import QMessageBox, QInputDialog
+
+from mercurial import hg, ui
+
+from tortoisehg.util import hglib, paths
+from tortoisehg.hgqt.i18n import _
+from tortoisehg.hgqt import qtlib, csinfo, i18n, cmdui
+
+keep = i18n.keepgettext()
+
+MERGE_PAGE = 0
+COMMIT_PAGE = 1
+RESULT_PAGE = 2
+
+class MergeDialog(QWizard):
+
+ repoInvalidated = pyqtSignal()
+
+ def __init__(self, other, repo=None, parent=None):
+ super(MergeDialog, self).__init__(parent)
+ self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
+
+ self.ui = ui.ui()
+ if repo:
+ self.repo = repo
+ else:
+ root = paths.find_root()
+ if root:
+ self.repo = hg.repository(self.ui, path=root)
+ else:
+ raise 'not repository'
+
+ self.other = str(other)
+ self.local = str(self.repo.parents()[0].rev())
+
+ reponame = hglib.get_reponame(self.repo)
+ self.setWindowTitle(_('Merge - %s') % hglib.tounicode(reponame))
+ self.setWindowIcon(qtlib.geticon('merge'))
+ self.setMinimumSize(600, 512)
+ self.setOption(QWizard.DisabledBackButtonOnLastPage, True)
+ self.setOption(QWizard.HelpButtonOnRight, False)
+ self.setDefaultProperty('QComboBox', 'currentText', 'editTextChanged()')
+
+ # set pages
+ self.setPage(MERGE_PAGE, MergePage(self))
+ self.setPage(COMMIT_PAGE, CommitPage(self))
+ self.setPage(RESULT_PAGE, ResultPage(self))
+
+ def reject(self):
+ page = self.currentPage()
+ if hasattr(page, 'need_cleanup') and page.need_cleanup():
+ main = _('Do you want to exit?')
+ text = _('To complete merging, you need to commit merged files '
+ 'in working directory.')
+ labels = ((QMessageBox.Yes, _('&Exit')),
+ (QMessageBox.No, _('Cancel')))
+ if not qtlib.QuestionMsgBox(_('Confirm Exit'), main, text,
+ labels=labels, parent=self):
+ return
+ super(MergeDialog, self).reject()
+
+MAIN_PANE = 0
+PERFORM_PANE = 1
+
+class BasePage(QWizardPage):
+
+ def __init__(self, parent=None):
+ super(BasePage, self).__init__(parent)
+
+ self.done = False
+
+ def switch_pane(self, pane):
+ self.setup_buttons(pane)
+ self.layout().setCurrentIndex(pane)
+ if pane == MAIN_PANE:
+ self.ready()
+ elif pane == PERFORM_PANE:
+ self.cmd.core.clear_output()
+ self.perform()
+ else:
+ raise 'unknown pane: %s' % pane
+
+ ### Override Method ###
+
+ def initializePage(self):
+ if self.layout():
+ return
+
+ stack = QStackedLayout()
+ self.setLayout(stack)
+
+ def wrap(layout):
+ widget = QWidget()
+ widget.setLayout(layout)
+ return widget
+
+ # main pane
+ fpane = self.get_pane()
+ num = stack.addWidget(wrap(fpane))
+ assert num == MAIN_PANE
+
+ # perform pane
+ ppane = QVBoxLayout()
+ ppane.addSpacing(4)
+ self.cmd = cmdui.Widget()
+ self.cmd.show_output(True)
+ self.cmd.commandFinished.connect(self.command_finished)
+ self.cmd.commandCanceling.connect(self.command_canceling)
+ ppane.addWidget(self.cmd)
+ num = stack.addWidget(wrap(ppane))
+ assert num == PERFORM_PANE
+
+ def setVisible(self, visible):
+ super(BasePage, self).setVisible(visible)
+ if visible:
+ self.switch_pane(MAIN_PANE)
+
+ def validatePage(self):
+ return self.can_continue()
+
+ ### Method to be overridden ###
+
+ def get_pane(self):
+ return QVBoxLayout()
+
+ def get_perform_label(self):
+ return None
+
+ def setup_buttons(self, pane):
+ if pane == MAIN_PANE:
+ label = self.get_perform_label()
+ if label:
+ btn = QPushButton(label)
+ self.wizard().setButton(QWizard.NextButton, btn)
+ self.wizard().button(QWizard.NextButton).clicked.connect(
+ self.perform_clicked)
+ self.wizard().button(QWizard.NextButton).setShown(True)
+ self.wizard().setOption(QWizard.HaveHelpButton, False)
+ self.wizard().setOption(QWizard.HaveCustomButton1, False)
+ elif pane == PERFORM_PANE:
+ button = QPushButton(_('Cancel'))
+ self.wizard().setButton(QWizard.CustomButton1, button)
+ self.wizard().setOption(QWizard.HaveCustomButton1, True)
+ button.clicked.connect(self.cancel_clicked)
+ self.wizard().button(QWizard.NextButton).setHidden(True)
+ self.wizard().button(QWizard.CancelButton).setHidden(True)
+ else:
+ raise 'unknown pane: %s' % pane
+
+ def ready(self):
+ pass
+
+ def perform(self):
+ pass
+
+ def cancel(self):
+ self.cmd.cancel()
+
+ def can_continue(self):
+ return self.done
+
+ def need_cleanup(self):
+ return False
+
+ ### Signal Handlers ###
+
+ def perform_clicked(self):
+ self.switch_pane(PERFORM_PANE)
+
+ def cancel_clicked(self):
+ self.cancel()
+
+ def command_finished(self, wrapper):
+ pass
+
+ def command_canceling(self):
+ pass
+
+MARGINS = (8, 0, 0, 0)
+
+class MergePage(BasePage):
+
+ def __init__(self, parent=None):
+ super(MergePage, self).__init__(parent)
+
+ self.clean = None
+ self.undo = False
+
+ ### Override Methods ###
+
+ def get_pane(self):
+ repo = self.wizard().repo
+ box = QVBoxLayout()
+
+ contents = ('ishead',) + csinfo.PANEL_DEFAULT
+ style = csinfo.panelstyle(contents=contents)
+ def markup_func(widget, item, value):
+ if item == 'ishead' and value is False:
+ text = _('Not a head revision!')
+ return qtlib.markup(text, fg='red', weight='bold')
+ raise csinfo.UnknownItem(item)
+ custom = csinfo.custom(markup=markup_func)
+ create = csinfo.factory(repo, custom, style, withupdate=True)
+
+ ## merge target
+ other_sep = qtlib.LabeledSeparator(_('Merge target (other)'))
+ box.addWidget(other_sep)
+ other_info = create(self.wizard().other)
+ other_info.setContentsMargins(5, 0, 0, 0)
+ box.addWidget(other_info)
+
+ ## options
+ obox = QHBoxLayout()
+ obox.setContentsMargins(*MARGINS)
+ box.addLayout(obox)
+
+ ### merge tools
+ label = QLabel(_('Merge tools:'))
+ obox.addWidget(label)
+
+ combo = QComboBox()
+ self.registerField('mergetool', combo)
+ obox.addWidget(combo)
+ obox.addSpacing(8)
+
+ prev = False
+ for tool in hglib.mergetools(repo.ui):
+ cur = tool.startswith('internal:')
+ if prev != cur:
+ combo.insertSeparator(combo.count())
+ combo.addItem(hglib.tounicode(tool))
+ prev = cur
+ uimerge = repo.ui.config('ui', 'merge', '')
+ combo.setEditText(hglib.tounicode(uimerge))
+
+ ### discard option
+ discard_chk = QCheckBox(_('Discard all changes from merge target '
+ '(other) revision'))
+ self.registerField('discard', discard_chk)
+ obox.addWidget(discard_chk)
+ obox.addStretch(0)
+
+ ## current revision
+ box.addSpacing(6)
+ local_sep = qtlib.LabeledSeparator(_('Current revision (local)'))
+ box.addWidget(local_sep)
+ local_info = create(self.wizard().local)
+ local_info.setContentsMargins(5, 0, 0, 0)
+ box.addWidget(local_info)
+
+ ## working directory status
+ box.addSpacing(6)
+ wd_sep = qtlib.LabeledSeparator(_('Working directory status'))
+ box.addWidget(wd_sep)
+
+ self.groups = qtlib.WidgetGroups()
+
+ wdbox = QHBoxLayout()
+ wdbox.setContentsMargins(*MARGINS)
+ box.addLayout(wdbox)
+ self.wd_status = qtlib.StatusLabel()
+ self.wd_status.set_status(_('Checking...'))
+ wdbox.addWidget(self.wd_status)
+ wd_prog = QProgressBar()
+ wd_prog.setMaximum(0)
+ wd_prog.setTextVisible(False)
+ self.groups.add(wd_prog, 'prog')
+ wdbox.addWidget(wd_prog, 1)
+ wd_button = QPushButton(_('Stop'))
+ self.groups.add(wd_button, 'prog')
+ wdbox.addWidget(wd_button)
+ wd_detail = QLabel(_('<a href="view">View changes...</a>'))
+ wd_detail.linkActivated.connect(self.link_activated)
+ self.groups.add(wd_detail, 'detail')
+ wdbox.addWidget(wd_detail)
+ wdbox.addSpacing(4)
+
+ wd_merged = QLabel(_('The files look like already <b>merged</b>. '
+ '<a href="skip"><b>Commit</b></a> them? or '
+ '<a href="discard"><b>disard</b></a> all.'))
+ wd_merged.setContentsMargins(*MARGINS)
+ wd_merged.linkActivated.connect(self.link_activated)
+ self.groups.add(wd_merged, 'merged')
+ box.addWidget(wd_merged)
+
+ wd_text = QLabel(_('To start merging, you need to '
+ '<a href="shelve"><b>shelve</b></a> them, '
+ '<a href="mq"><b>save</b></a> as MQ patch or '
+ '<a href="discard"><b>discard</b></a> all.'))
+ wd_text.setContentsMargins(*MARGINS)
+ wd_text.linkActivated.connect(self.link_activated)
+ self.wd_text = wd_text
+ self.groups.add(wd_text, 'dirty')
+ box.addWidget(wd_text)
+
+ wdbox = QHBoxLayout()
+ wdbox.setContentsMargins(*MARGINS)
+ box.addLayout(wdbox)
+ wd_alt = QLabel(_('Or use:'))
+ self.groups.add(wd_alt, 'dirty')
+ wdbox.addWidget(wd_alt)
+ force_chk = QCheckBox(_('Force a merge with outstanding changes '
+ '(-f/--force)'))
+ force_chk.toggled.connect(lambda c: self.completeChanged.emit())
+ self.registerField('force', force_chk)
+ self.groups.add(force_chk, 'dirty')
+ wdbox.addWidget(force_chk)
+ wdbox.addStretch(0)
+
+ box.addStretch(0)
+
+ return box
+
+ def get_perform_label(self):
+ return _('&Merge')
+
+ def ready(self):
+ self.done = False
+ self.setTitle(_('Merge working directory with another revision'))
+ self.groups.set_visible(False, 'dirty')
+ self.groups.set_visible(False, 'merged')
+ self.groups.set_visible(False, 'detail')
+ self.check_status()
+
+ if self.undo:
+ self.link_activated('discard:noconfirm')
+ self.undo = False
+
+ def perform(self):
+ self.setTitle(_('Merging...'))
+ self.setSubTitle(_('Please finish merging with launched merge tool.'))
+
+ if self.field('discard').toBool():
+ # '.' is safer than self.localrev, in case the user has
+ # pulled a fast one on us and updated from the CLI
+ cmdline = ['debugsetparents', '.', self.wizard().other]
+ else:
+ cmdline = []
+ tool = self.field('mergetool').toString()
+ if tool:
+ cmdline.extend(['--config', 'ui.merge=%s' % tool])
+ cmdline.extend(['merge', '--rev', self.wizard().other])
+ self.cmd.run(cmdline)
+
+ def cancel(self):
+ main = _('Cancel merging and clean up?')
+ text = _('After canceling, TortoiseHg will clean up unfinished local'
+ ' changes.')
+ labels = ((QMessageBox.Yes, _('&Clean up')),
+ (QMessageBox.No, _('Cancel')))
+ if qtlib.QuestionMsgBox(_('Confirm Clean Up'), main, text,
+ labels=labels, parent=self):
+ self.cmd.core.append_output(_('Canceling a merge...\n'))
+ self.cmd.core.append_output(_('(Please close launched merge '
+ 'tool if exists)\n'))
+ self.cmd.cancel()
+
+ def isComplete(self):
+ if self.clean:
+ return True
+ return self.wizard().field('force').toBool()
+
+ ### Signal Handlers ###
+
+ def command_finished(self, wrapper):
+ if wrapper.data == 0:
+ repo = self.wizard().repo
+ hglib.invalidaterepo(repo)
+ self.done = True
+ self.wizard().next()
+ else:
+ self.link_activated('discard:noconfirm')
+ self.switch_pane(MAIN_PANE)
+
+ def command_canceling(self):
+ self.wizard().button(QWizard.CustomButton1).setDisabled(True)
+
+ def link_activated(self, cmd):
+ cmd = str(cmd)
+ if cmd == 'shelve':
+ # TODO: shelve local changes
+ print 'Not implemented: shelve'
+ elif cmd == 'mq':
+ # TODO: need to check existing patches
+ patch = 'patch1'
+ def finished(wrapper):
+ if wrapper.data == 0:
+ hglib.invalidaterepo(self.wizard().repo)
+ def callback():
+ text = _('Outstanding changes are saved to <b>'
+ '%(name)s</b> in the patch queue. <a href'
+ '="rename:%(name)s"><b>Rename</b></a> it?')
+ self.wd_text.setText(text % dict(name=patch))
+ self.wd_text.setShown(True)
+ self.check_status(callback)
+ self.runner = cmdui.Runner(_('MQ - TortoiseHg'), self)
+ self.runner.commandFinished.connect(finished)
+ self.runner.run(['qnew', patch],
+ ['qpop', '--all'])
+ elif cmd.startswith('discard'):
+ if cmd != 'discard:noconfirm':
+ labels = [(QMessageBox.Yes, _('&Discard')),
+ (QMessageBox.No, _('Cancel'))]
+ if not qtlib.QuestionMsgBox(_('Confirm Discard'), _('Discard'
+ ' outstanding changes in working directory?'),
+ labels=labels, parent=self):
+ return
+ def finished(wrapper):
+ if wrapper.data == 0:
+ hglib.invalidaterepo(self.wizard().repo)
+ self.check_status()
+ cmdline = ['update', '--clean', '--rev', self.wizard().local]
+ self.runner = cmdui.Runner(_('Discard - TortoiseHg'), self)
+ self.runner.commandFinished.connect(finished)
+ self.runner.run(cmdline)
+ elif cmd.startswith('rename:'):
+ patch = cmd[7:]
+ name, ok = QInputDialog.getText(self, _('Rename Patch'),
+ _('Input a new patch name:'), text=patch)
+ if not ok or name == patch:
+ return
+ oldpatch = hglib.fromunicode(patch)
+ newpatch = hglib.fromunicode(name)
+ def finished(wrapper):
+ if wrapper.data == 0:
+ text = _('The patch <b>%(old)s</b> is renamed to <b>'
+ '%(new)s</b>. <a href="rename:%(new)s"><b>'
+ 'Rename</b></a> again?')
+ self.wd_text.setText(text % dict(old=patch, new=name))
+ self.runner = cmdui.Runner(_('Rename - TortoiseHg'), self)
+ self.runner.commandFinished.connect(finished)
+ self.runner.run(['qrename', oldpatch, newpatch])
+ elif cmd == 'view':
+ # TODO: show Status dialog to show local changes
+ print 'Not implemented: view'
+ elif cmd == 'skip':
+ self.done = True
+ self.wizard().next()
+ else:
+ raise 'unknown command: %s' % str(cmd)
+
+ ### Private Methods ###
+
+ def check_status(self, callback=None):
+ repo = self.wizard().repo
+ class CheckThread(QThread):
+ completed = pyqtSignal(bool, int)
+ def run(self):
+ wctx = repo[None]
+ self.completed.emit(bool(wctx.dirty()), len(wctx.parents()))
+ def completed(dirty, parents):
+ self.clean = not dirty
+ self.groups.set_visible(False, 'prog')
+ self.groups.set_visible(dirty, 'detail')
+ if dirty:
+ self.groups.set_visible(parents == 2, 'merged')
+ self.groups.set_visible(parents == 1, 'dirty')
+ self.wd_status.set_status(_('<b>Uncommitted local changes '
+ 'are detected</b>'), 'warning')
+ else:
+ self.groups.set_visible(False, 'dirty')
+ self.groups.set_visible(False, 'merged')
+ self.wd_status.set_status(_('Clean'), True)
+ self.completeChanged.emit()
+ if callable(callback):
+ callback()
+ self.th = CheckThread()
+ self.th.completed.connect(completed)
+ self.th.start()
+
+class CommitPage(BasePage):
+
+ def __init__(self, parent=None):
+ super(CommitPage, self).__init__(parent)
+
+ ### Override Methods ###
+
+ def get_pane(self):
+ repo = self.wizard().repo
+ box = QVBoxLayout()
+
+ box.addSpacing(12)
+ rev_sep = qtlib.LabeledSeparator(_('Working Directory (merged)'))
+ box.addWidget(rev_sep)
+ style = csinfo.panelstyle(margin=6)
+ rev_info = csinfo.create(repo, None, style, withupdate=True)
+ box.addWidget(rev_info)
+
+ # TODO
+ todo = QLabel('<b>TODO:</b> Merged files are shown in here')
+ box.addWidget(todo)
+
+ msg_sep = qtlib.LabeledSeparator(_('Commit message'))
+ box.addWidget(msg_sep)
+ msg_text = QTextEdit()
+ engmsg = repo.ui.configbool('tortoisehg', 'engmsg', False)
+ msgset = keep._('Merge ')
+ msg_text.setText(engmsg and msgset['id'] or msgset['str'])
+ msg_text.textChanged.connect(lambda: self.completeChanged.emit())
+ self.msg_text = msg_text
+ box.addWidget(msg_text)
+
+ return box
+
+ def get_perform_label(self):
+ return _('&Commit')
+
+ def setup_buttons(self, pane):
+ super(CommitPage, self).setup_buttons(pane)
+
+ if pane == MAIN_PANE:
+ undo = QPushButton(_('Undo'))
+ undo.clicked.connect(self.wizard().back)
+ self.wizard().setButton(QWizard.HelpButton, undo)
+ self.wizard().setOption(QWizard.HaveHelpButton, True)
+ elif pane == PERFORM_PANE:
+ self.wizard().setOption(QWizard.HaveHelpButton, False)
+ else:
+ raise 'unknown pane: %s' % pane
+
+ def ready(self):
+ self.setTitle(_('Commit merged files'))
+
+ # move cursor to end of commit message
+ self.msg_text.setFocus()
+ cursor = self.msg_text.textCursor()
+ cursor.movePosition(QTextCursor.EndOfBlock)
+ self.msg_text.setTextCursor(cursor)
+
+ def perform(self):
+ self.setTitle(_('Committing...'))
+ self.setSubTitle(_('Please wait while committing merged files.'))
+
+ # merges must be committed without specifying file list
+ message = hglib.fromunicode(self.msg_text.toPlainText())
+ cmdline = ['commit', '--verbose', '--message', message]
+ self.cmd.run(cmdline)
+
+ def isComplete(self):
+ return len(self.msg_text.toPlainText()) > 0
+
+ def need_cleanup(self):
+ return len(self.wizard().repo.parents()) == 2
+
+ def cleanupPage(self):
+ self.undo()
+
+ ### Private Method ###
+
+ def undo(self):
+ page = self.wizard().page(MERGE_PAGE)
+ page.undo = True
+
+ ### Signal Handlers ###
+
+ def command_finished(self, wrapper):
+ if wrapper.data == 0:
+ hglib.invalidaterepo(self.wizard().repo)
+ self.done = True
+ self.wizard().next()
+
+ def command_canceling(self):
+ page = self.wizard().page(MERGE_PAGE)
+ page.undo = True
+
+class ResultPage(QWizardPage):
+
+ def __init__(self, parent=None):
+ super(ResultPage, self).__init__(parent)
+
+ self.setTitle(_('Finished'))
+
+ ### Override Method ###
+
+ def initializePage(self):
+ box = QVBoxLayout()
+ self.setLayout(box)
+
+ # merge changeset
+ merge_sep = qtlib.LabeledSeparator(_('Merge changeset'))
+ box.addWidget(merge_sep)
+ merge_info = csinfo.create(self.wizard().repo, 'tip', withupdate=True)
+ box.addWidget(merge_info)
+ box.addStretch(0)
+
+ self.wizard().setOption(QWizard.HaveHelpButton, False)
+ self.wizard().setOption(QWizard.NoCancelButton, True)
+ self.wizard().setOption(QWizard.HaveCustomButton1, False)
+
+def run(ui, *pats, **opts):
+ rev = opts.get('rev') or None
+ if not rev and len(pats):
+ rev = pats[0]
+ return MergeDialog(rev)
|
@@ -112,6 +112,8 @@ self.showAtRev),
('update', _('Update...'), 'update', None, None,
self.updateToRev),
+ ('merge', _('Merge with...'), 'merge', None, None,
+ self.mergeWithRev),
('tag', _('Tag...'), 'tag', None, None,
self.tagToRev),
('backout', _('Backout...'), None, None, None,
@@ -144,6 +146,9 @@ def updateToRev(self):
self.emit(SIGNAL('updateToRevision'), self.current_rev)
+ def mergeWithRev(self):
+ self.emit(SIGNAL('mergeWithRevision'), self.current_rev)
+
def tagToRev(self):
self.emit(SIGNAL('tagToRevision'), self.current_rev)
@@ -152,8 +157,8 @@
def contextMenuEvent(self, event):
menu = QtGui.QMenu(self)
- for act in ['update', 'manifest', 'tag', 'backout', None,
- 'back', 'forward']:
+ for act in ['update', 'manifest', 'merge', 'tag', 'backout',
+ None, 'back', 'forward']:
if act:
menu.addAction(self._actions[act])
else:
|
@@ -18,7 +18,7 @@
from tortoisehg.hgqt.qtlib import geticon
from tortoisehg.hgqt.repomodel import HgRepoListModel
-from tortoisehg.hgqt import cmdui, update, tag, manifestdialog, backout
+from tortoisehg.hgqt import cmdui, update, tag, manifestdialog, backout, merge
from tortoisehg.hgqt.config import HgConfig
from repoview import HgRepoView
@@ -183,6 +183,7 @@ connect(view, SIGNAL('revisionSelected'), self.revision_selected)
connect(view, SIGNAL('revisionActivated'), self.revision_activated)
connect(view, SIGNAL('updateToRevision'), self.updateToRevision)
+ connect(view, SIGNAL('mergeWithRevision'), self.mergeWithRevision)
connect(view, SIGNAL('tagToRevision'), self.tagToRevision)
connect(view, SIGNAL('backoutToRevision'), self.backoutToRevision)
#self.attachQuickBar(view.goto_toolbar)
@@ -224,6 +225,17 @@ dlg.quitsignal.connect(quit)
dlg.show()
+ def mergeWithRevision(self, rev):
+ saved = self.setScanForRepoChanges(False)
+ dlg = merge.MergeDialog(rev, self.repo, self)
+ def finished(ret):
+ self.setScanForRepoChanges(saved)
+ dlg.finished.connect(finished)
+ def invalidated():
+ self.reload() # TODO: implement something less drastic than a full reload
+ dlg.repoInvalidated.connect(invalidated)
+ dlg.show()
+
def tagToRevision(self, rev):
saved = self.setScanForRepoChanges(False)
dlg = tag.TagDialog(self.repo, rev=str(rev), parent=self)
|
@@ -373,6 +373,11 @@ from tortoisehg.hgqt.hgemail import run
qtrun(run, ui, *pats, **opts)
+def merge(ui, *pats, **opts):
+ """merge wizard"""
+ from tortoisehg.hgqt.merge import run
+ qtrun(run, ui, *pats, **opts)
+
def guess(ui, *pats, **opts):
"""guess previous renames or copies"""
from tortoisehg.hgqt.guess import run
@@ -725,6 +730,10 @@ (log,
[('l', 'limit', '', _('limit number of changes displayed'))],
_('thg log [OPTIONS] [FILE]')),
+ "^merge":
+ (merge,
+ [('r', 'rev', '', _('revision to merge'))],
+ _('thg merge [[-r] REV]')),
"remove|rm": (remove, [], _('thg remove [FILE]...')),
"revert": (revert, [], _('thg revert [FILE]...')),
"forget": (forget, [], _('thg forget [FILE]...')),
|
@@ -32,5 +32,6 @@ <file>icons/clone.svg</file>
<file>icons/update.svg</file>
<file>icons/sync.svg</file>
+ <file>icons/merge.svg</file>
</qresource>
</RCC>
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
Loading...