Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
# 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.fromPyQt4.QtCoreimport*fromPyQt4.QtGuiimport*frommercurialimporterrorfrommercurialimportmergeasmergemodfromtortoisehg.utilimporthglibfromtortoisehg.hgqt.i18nimport_fromtortoisehg.hgqtimportqtlib,csinfo,i18n,cmdui,status,commit,resolvefromtortoisehg.hgqtimportqscilib,thgrepokeep=i18n.keepgettext()MERGE_PAGE=0COMMIT_PAGE=1RESULT_PAGE=2classMergeDialog(QWizard):def__init__(self,other,repo,parent):super(MergeDialog,self).__init__(parent)f=self.windowFlags()self.setWindowFlags(f&~Qt.WindowContextHelpButtonHint)self.repo=repoself.other=str(other)self.local=str(self.repo['.'].rev())self.setWindowTitle(_('Merge - %s')%self.repo.displayname)self.setWindowIcon(qtlib.geticon('hg-merge'))self.setMinimumSize(600,528)self.setOption(QWizard.DisabledBackButtonOnLastPage,True)self.setOption(QWizard.HelpButtonOnRight,False)self.setDefaultProperty('QComboBox','currentText','editTextChanged()')# set pagesself.setPage(MERGE_PAGE,MergePage(self))self.setPage(COMMIT_PAGE,CommitPage(self))self.setPage(RESULT_PAGE,ResultPage(self))repo.repositoryChanged.connect(self.repositoryChanged)repo.configChanged.connect(self.configChanged)defrepositoryChanged(self):self.currentPage().repositoryChanged()defconfigChanged(self):self.currentPage().configChanged()defreject(self):page=self.currentPage()ifhasattr(page,'need_cleanup')andpage.need_cleanup():main=_('Do you want to exit?')text=_('To finish merging, you need to commit ''the working directory.')labels=((QMessageBox.Yes,_('&Exit')),(QMessageBox.No,_('Cancel')))ifnotqtlib.QuestionMsgBox(_('Confirm Exit'),main,text,labels=labels,parent=self):returnpage.reject()super(MergeDialog,self).reject()defdone(self,ret):self.repo.repositoryChanged.disconnect(self.repositoryChanged)self.repo.configChanged.disconnect(self.configChanged)super(MergeDialog,self).done(ret)MAIN_PANE=0PERFORM_PANE=1classBasePage(QWizardPage):def__init__(self,parent=None):super(BasePage,self).__init__(parent)self.nextEnabled=Trueself.done=Falsedefswitch_pane(self,pane):self.setup_buttons(pane)self.layout().setCurrentIndex(pane)ifpane==MAIN_PANE:self.ready()elifpane==PERFORM_PANE:self.cmd.core.clearOutput()self.perform()else:raise'unknown pane: %s'%pane### Override Method ###defrepositoryChanged(self):'repository has detected a change to changelog or parents'passdefconfigChanged(self):'repository has detected a change to config files'passdefreject(self):passdefinitializePage(self):ifself.layout():returnstack=QStackedLayout()self.setLayout(stack)defwrap(layout):widget=QWidget()widget.setLayout(layout)returnwidget# main panefpane=self.get_pane()num=stack.addWidget(wrap(fpane))assertnum==MAIN_PANE# perform paneppane=QVBoxLayout()ppane.addSpacing(4)self.cmd=cmdui.Widget(True,True,self)self.cmd.setShowOutput(True)self.cmd.commandFinished.connect(self.command_finished)self.cmd.commandCanceling.connect(self.command_canceling)ppane.addWidget(self.cmd)num=stack.addWidget(wrap(ppane))assertnum==PERFORM_PANEdefsetVisible(self,visible):super(BasePage,self).setVisible(visible)ifvisible:self.switch_pane(MAIN_PANE)defvalidatePage(self):#When the user first clicks on the "Next" button ("Merge"/"Commit")#After any validation via overloading validatePage(),#we switch to the perform paneifself.layout().currentIndex()==MAIN_PANE:self.switch_pane(PERFORM_PANE)returnFalse#When the perform pane is done, it'll call this againreturnself.can_continue()### Method to be overridden ###defget_pane(self):returnQVBoxLayout()defget_perform_label(self):returnNonedefsetup_buttons(self,pane):ifpane==MAIN_PANE:label=self.get_perform_label()iflabel:self.wizard().setButtonText(QWizard.NextButton,label);self.nextEnabled=Trueelse:self.nextEnabled=Falseself.wizard().setOption(QWizard.HaveHelpButton,False)self.wizard().setOption(QWizard.HaveCustomButton1,False)self.wizard().setOption(QWizard.NoCancelButton,False)elifpane==PERFORM_PANE:button=QPushButton(_('Cancel'))self.wizard().setButton(QWizard.CustomButton1,button)self.wizard().setOption(QWizard.HaveCustomButton1,True)button.clicked.connect(self.cancel_clicked)self.nextEnabled=Falseself.wizard().setOption(QWizard.NoCancelButton,True)else:raise'unknown pane: %s'%panedefready(self):passdefperform(self):passdefcancel(self):self.cmd.cancel()defcan_continue(self):returnself.donedefneed_cleanup(self):returnFalse### Signal Handlers ###defcancel_clicked(self):self.cancel()defcommand_finished(self,ret):passdefcommand_canceling(self):passMARGINS=(8,0,0,0)classMergePage(BasePage):def__init__(self,parent=None):super(MergePage,self).__init__(parent)self.clean=Noneself.undo=Falseself.th=None### Override Methods ###defget_pane(self):repo=self.wizard().repobox=QVBoxLayout()contents=('ishead',)+csinfo.PANEL_DEFAULTstyle=csinfo.panelstyle(contents=contents)defmarkup_func(widget,item,value):ifitem=='ishead'andvalueisFalse:text=_('Not a head revision!')returnqtlib.markup(text,fg='red',weight='bold')raisecsinfo.UnknownItem(item)custom=csinfo.custom(markup=markup_func)create=csinfo.factory(repo,custom,style,withupdate=True)## merge targetother_sep=qtlib.LabeledSeparator(_('Merge from (other revision)'))box.addWidget(other_sep)try:other_info=create(self.wizard().other)other_info.setContentsMargins(5,0,0,0)box.addWidget(other_info)self.other_info=other_infoexcepterror.RepoLookupError:qtlib.InfoMsgBox(_('Unable to merge'),_('Merge revision not specified or not found'))QTimer.singleShot(0,self.wizard().close)## current revisionbox.addSpacing(6)local_sep=qtlib.LabeledSeparator(_('Merge to (working directory)'))box.addWidget(local_sep)local_info=create(self.wizard().local)local_info.setContentsMargins(5,0,0,0)box.addWidget(local_info)self.local_info=local_info## working directory statusbox.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_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 working directory is already <b>merged</b>. ''<a href="skip"><b>Continue</b></a> or ''<a href="discard"><b>discard</b></a> existing ''merge.'))wd_merged.setContentsMargins(*MARGINS)wd_merged.linkActivated.connect(self.link_activated)self.groups.add(wd_merged,'merged')box.addWidget(wd_merged)text=_('Before merging, you must <a href="commit"><b>commit</b></a>, ''<a href="shelve"><b>shelve</b></a> to patch, ''or <a href="discard"><b>discard</b></a> changes.')wd_text=QLabel(text)wd_text.setContentsMargins(*MARGINS)wd_text.linkActivated.connect(self.link_activated)self.wd_text=wd_textself.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(lambdac:self.completeChanged.emit())self.registerField('force',force_chk)self.groups.add(force_chk,'dirty')wdbox.addWidget(force_chk)wdbox.addStretch(0)box.addSpacing(6)### optionsexpander=qtlib.ExpanderLabel(_('Options'),False)expander.expanded.connect(self.show_options)box.addWidget(expander)self.expander=expanderoptbox=QVBoxLayout()optbox.setSpacing(6)box.addLayout(optbox)### discard optiondiscard_chk=QCheckBox(_('Discard all changes from merge target ''(other) revision'))self.registerField('discard',discard_chk)optbox.addWidget(discard_chk)self.discard_chk=discard_chk## auto-resolveautoresolve_chk=QCheckBox(_('Automatically resolve merge conflicts ''where possible'))autoresolve_chk.setChecked(repo.ui.configbool('tortoisehg','autoresolve',False))self.registerField('autoresolve',autoresolve_chk)optbox.addWidget(autoresolve_chk)self.autoresolve_chk=autoresolve_chkbox.addStretch(0)returnboxdefget_perform_label(self):return_('&Merge')defready(self):self.done=Falseself.setTitle(_('Merge another revision to the working directory'))self.groups.set_visible(False,'dirty')self.groups.set_visible(False,'merged')self.groups.set_visible(False,'detail')self.show_options(self.expander.is_expanded())self.check_status()ifself.undo:self.link_activated('discard:noconfirm')self.undo=FalsedefvalidatePage(self):#If we haven't already done the action, pop up a confirmation for#dummy merge.ifnotself.doneandself.field('discard').toBool():labels=[(QMessageBox.Yes,_('&Discard')),(QMessageBox.No,_('Cancel'))]ifnotqtlib.QuestionMsgBox(_('Confirm Discard Changes'),_('The changes from revision %s and all unmerged parents ''will be discarded.\n\n''Are you sure this is what you want to do?')%(self.other_info.get_data('revid')),labels=labels,parent=self):returnFalsereturnsuper(MergePage,self).validatePage();defperform(self):self.setTitle(_('Merging...'))self.setSubTitle(_('All conflicting files will be marked unresolved.'))ifself.field('discard').toBool():# '.' is safer than self.localrev, in case the user has# pulled a fast one on us and updated from the CLIcmdline=['--repository',self.wizard().repo.root,'debugsetparents','.',self.wizard().other]else:cmdline=['--repository',self.wizard().repo.root,'merge']ifself.field('force').toBool():cmdline.append('--force')tool=self.field('autoresolve').toBool()and'merge'or'fail'cmdline+=['--tool=internal:'+tool]cmdline.append(self.wizard().other)self.cmd.run(cmdline)defcancel(self):main=_('Cancel merge and discard changes?')# Does this restart "resolved" files too?text=_('Discard local changes and restart merge?')labels=((QMessageBox.Yes,_('&Discard')),(QMessageBox.No,_('Cancel')))ifqtlib.QuestionMsgBox(_('Confirm Clean Up'),main,text,labels=labels,parent=self):o=self.cmd.core.outputLogo.appendLog(_('Canceling merge...\n'),'control')o.appendLog(_('(Please close any running merge tools)\n'),'control')self.cmd.cancel()defisComplete(self):ifnotself.nextEnabled:returnFalseifself.clean:returnTruereturnself.field('force').toBool()### Signal Handlers ###defcommand_finished(self,ret):repo=self.wizard().repoifretin(0,1):repo.incrementBusyCount()repo.decrementBusyCount()self.done=Trueself.wizard().next()else:qtlib.InfoMsgBox(_('Merge failed'),_('Returning to first page'))self.link_activated('discard:noconfirm')self.switch_pane(MAIN_PANE)defrepositoryChanged(self):'repository has detected a change to changelog or parents'pctx=self.wizard().repo['.']self.local_info.update(pctx)self.wizard().local=str(pctx.rev())defreject(self):ifself.thisnotNoneandnotself.th.isFinished():self.th.cancel()self.th.wait()defshow_options(self,visible):self.discard_chk.setShown(visible)self.autoresolve_chk.setShown(visible)defcommand_canceling(self):self.wizard().button(QWizard.CustomButton1).setDisabled(True)deflink_activated(self,cmd):cmd=str(cmd)repo=self.wizard().repoifcmd=='commit':dlg=commit.CommitDialog([],dict(root=repo.root),self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()self.check_status()elifcmd=='shelve':fromtortoisehg.hgqtimportshelvedlg=shelve.ShelveDialog(repo,self.wizard())dlg.finished.connect(dlg.deleteLater)dlg.exec_()self.check_status()elifcmd.startswith('discard'):ifcmd!='discard:noconfirm':labels=[(QMessageBox.Yes,_('&Discard')),(QMessageBox.No,_('Cancel'))]ifnotqtlib.QuestionMsgBox(_('Confirm Discard'),_('Discard outstanding changes to working directory?'),labels=labels,parent=self):returndeffinished(ret):repo.decrementBusyCount()ifret==0:self.check_status()cmdline=['update','--clean','--repository',repo.root,'--rev','.']self.runner=cmdui.Runner(True,self)self.runner.commandFinished.connect(finished)repo.incrementBusyCount()self.runner.run(cmdline)elifcmd.startswith('rename:'):patch=cmd[7:]name,ok=QInputDialog.getText(self,_('Rename Patch'),_('Input a new patch name:'),text=patch)ifnotokorname==patch:returnoldpatch=hglib.fromunicode(patch)newpatch=hglib.fromunicode(name)deffinished(ret):repo.decrementBusyCount()ifret==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(True,self)self.runner.commandFinished.connect(finished)repo.incrementBusyCount()self.runner.run(['qrename','--repository',repo.root,oldpatch,newpatch])elifcmd=='view':dlg=status.StatusDialog([],{},repo.root,self)dlg.exec_()self.check_status()elifcmd=='skip':self.done=Trueself.wizard().next()else:raise'unknown command: %s'%str(cmd)### Private Methods ###defcheck_status(self,callback=None):repo=self.wizard().repoclassCheckThread(QThread):def__init__(self,parent):QThread.__init__(self,parent)self.results=(False,1)self.canceled=Falsedefrun(self):unresolved=Falseforroot,path,statusinthgrepo.recursiveMergeStatus(repo):ifself.canceled:returnifstatus=='u':unresolved=Truebreakwctx=repo[None]dirty=bool(wctx.dirty())orunresolvedself.results=(dirty,len(wctx.parents()))defcancel(self):self.canceled=Truedefcompleted():ifself.th.canceled:returnself.th.wait()dirty,parents=self.th.resultsself.clean=notdirtyself.groups.set_visible(False,'prog')self.groups.set_visible(dirty,'detail')ifdirty: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>'),'thg-warning')else:self.groups.set_visible(False,'dirty')self.groups.set_visible(False,'merged')self.wd_status.set_status(_('Clean','working dir state'),True)self.completeChanged.emit()ifcallable(callback):callback()self.th=CheckThread(self)self.th.finished.connect(completed)self.th.start()classCommitPage(BasePage):def__init__(self,parent=None):super(CommitPage,self).__init__(parent)### Override Methods ###defget_pane(self):repo=self.wizard().repobox=QVBoxLayout()self.reslabel=QLabel()self.reslabel.linkActivated.connect(self.link_activated)box.addWidget(self.reslabel)# csinfodeflabel_func(widget,item):ifitem=='rev':return_('Revision:')elifitem=='parents':return_('Parents')raisecsinfo.UnknownItem()defdata_func(widget,item,ctx):ifitem=='rev':return_('Working Directory'),str(ctx)elifitem=='parents':parents=[]cbranch=ctx.branch()forpctxinctx.parents():branch=Noneifhasattr(pctx,'branch')andpctx.branch()!=cbranch:branch=pctx.branch()parents.append((str(pctx.rev()),str(pctx),branch,pctx))returnparentsraisecsinfo.UnknownItem()defmarkup_func(widget,item,value):ifitem=='rev':text,rev=valuereturn'<a href="view">%s</a> (%s)'%(text,rev)elifitem=='parents':defbranch_markup(branch):opts=dict(fg='black',bg='#aaffaa')returnqtlib.markup(' %s '%branch,**opts)csets=[]forrnum,rid,branch,pctxinvalue:line='%s (%s)'%(rnum,rid)ifbranch:line='%s%s'%(line,branch_markup(branch))msg=widget.info.get_data('summary',widget,pctx,widget.custom)ifmsg:line='%s%s'%(line,msg)csets.append(line)returncsetsraisecsinfo.UnknownItem()custom=csinfo.custom(label=label_func,data=data_func,markup=markup_func)contents=('rev','user','dateage','branch','parents')style=csinfo.panelstyle(contents=contents,margin=6)# merged filesbox.addSpacing(12)rev_sep=qtlib.LabeledSeparator(_('Working Directory (merged)'))box.addWidget(rev_sep)rev_info=csinfo.create(repo,None,style,custom=custom,withupdate=True)page=self.wizard().page(MERGE_PAGE)rev_info.linkActivated.connect(page.link_activated)box.addWidget(rev_info)# commit message areamsg_sep=qtlib.LabeledSeparator(_('Commit message'))box.addWidget(msg_sep)msg_text=commit.MessageEntry(self)msg_text.installEventFilter(qscilib.KeyPressInterceptor(self))msg_text.refresh(repo)msg_text.loadSettings(QSettings(),'merge/message')engmsg=repo.ui.configbool('tortoisehg','engmsg',False)p1branch=repo[None].p1().branch()p2branch=repo[None].p2().branch()ifp1branch==p2branch:msgset=keep._('Merge ')else:# Show a default 'Merge with OTHER_BRANCH' message when merging# changesets from different branchesmsgset=keep._('Merge with %s')msg=engmsgandmsgset['id']ormsgset['str']ifp1branch!=p2branch:msg=unicode(msg)%hglib.tounicode(p2branch)msg_text.setText(msg)msg_text.textChanged.connect(self.completeChanged)self.msg_text=msg_textbox.addWidget(msg_text)deftryperform():ifself.isComplete():self.wizard().next()actionEnter=QAction('alt-enter',self)actionEnter.setShortcuts([Qt.CTRL+Qt.Key_Return,Qt.CTRL+Qt.Key_Enter])actionEnter.triggered.connect(tryperform)self.addAction(actionEnter)returnboxdeflink_activated(self,cmd):ifcmd=='resolve':dlg=resolve.ResolveDialog(self.wizard().repo,self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()self.completeChanged.emit()defget_perform_label(self):return_('&Commit')defsetup_buttons(self,pane):super(CommitPage,self).setup_buttons(pane)ifpane==MAIN_PANE:undo=QPushButton(_('Undo'))undo.clicked.connect(self.wizard().back)self.wizard().setButton(QWizard.HelpButton,undo)self.wizard().setOption(QWizard.HaveHelpButton,True)elifpane==PERFORM_PANE:self.wizard().setOption(QWizard.HaveHelpButton,False)else:raise'unknown pane: %s'%panedefready(self):self.setTitle(_('Commit merged files'))self.msg_text.moveCursorToEnd()defperform(self):self.setTitle(_('Committing...'))self.setSubTitle(_('Please wait while committing merged files.')) # merges must be committed without specifying file list
repo = self.wizard().repo
+ user = qtlib.getCurrentUsername(self, repo)+ if not user:+ return+ message = hglib.fromunicode(self.msg_text.text())
cmdline = ['commit', '--verbose', '--message', message,
- '--repository', repo.root]
+ '--repository', repo.root, '--user', user]
commandlines = [cmdline]
pushafter = repo.ui.config('tortoisehg', 'cipushafter')
if pushafter:
cmd=['push','--repository',repo.root,pushafter]commandlines.append(cmd)self.wizard().repo.incrementBusyCount()self.cmd.run(*commandlines)defisComplete(self):ifnotself.nextEnabled:returnFalserepo=self.wizard().repoforroot,path,statusinthgrepo.recursiveMergeStatus(repo):ifstatus=='u':self.reslabel.setText(_('There were <b>merge conflicts</b> ''that must be <a href="resolve">''<b>resolved</b></a>'))returnFalseelse:self.reslabel.setText(_('No merge conflicts, ready to commit'))returnlen(self.msg_text.text())>0defneed_cleanup(self):returnlen(self.wizard().repo.parents())==2defrepositoryChanged(self):'repository has detected a change to changelog or parents'ifself.done:returniflen(self.wizard().repo.parents())==1:self.wizard().restart()### Private Method ###defundo(self):page=self.wizard().page(MERGE_PAGE)page.undo=True### Signal Handlers ###defcommand_finished(self,ret):ifret==0:self.done=Trueself.wizard().repo.decrementBusyCount()self.msg_text.saveSettings(QSettings(),'merge/message')self.wizard().next()else:self.wizard().repo.decrementBusyCount()defcommand_canceling(self):page=self.wizard().page(MERGE_PAGE)page.undo=TrueclassResultPage(QWizardPage):def__init__(self,parent=None):super(ResultPage,self).__init__(parent)self.setTitle(_('Finished'))### Override Method ###defreject(self):passdefrepositoryChanged(self):'repository has detected a change to changelog or parents'passdefinitializePage(self):box=QVBoxLayout()self.setLayout(box)# merge changesetmerge_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)defrun(ui,*pats,**opts):fromtortoisehg.utilimportpathsrev=opts.get('rev')orNoneifnotrevandlen(pats):rev=pats[0]repo=thgrepo.repository(ui,path=paths.find_root())returnMergeDialog(rev,repo,None)
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.