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.
# sync.py - TortoiseHg's sync widget## Copyright 2010 Adrian Buehlmann <adrian@cadifra.com># Copyright 2010 Steve Borho <steve@borho.org>## This software may be used and distributed according to the terms of the# GNU General Public License version 2 or any later version.importosimportreimporttempfileimporturlparsefromPyQt4.QtCoreimport*fromPyQt4.QtGuiimport*frommercurialimporthg,ui,url,util,errorfrommercurialimportmergeasmergemodfromtortoisehg.utilimporthglib,wconfigfromtortoisehg.hgqt.i18nimport_fromtortoisehg.hgqtimportqtlib,cmdui,thgrepo,rebase,resolvefrombinasciiimporthexlify# TODO# Write keyring help, connect to help button_schemes=['local','ssh','http','https']classSyncWidget(QWidget):outgoingNodes=pyqtSignal(object)incomingBundle=pyqtSignal(QString)showMessage=pyqtSignal(unicode)output=pyqtSignal(QString,QString)progress=pyqtSignal(QString,object,QString,QString,object)makeLogVisible=pyqtSignal(bool)def__init__(self,repo,embedded=False,parent=None,**opts):QWidget.__init__(self,parent)layout=QVBoxLayout()layout.setContentsMargins(0,0,0,0)layout.setSpacing(0)self.setLayout(layout)self.setAcceptDrops(True)self.repo=repoself.finishfunc=Noneself.curuser=Noneself.curpw=Noneself.updateInProgress=Falseself.opts={}self.cmenu=Noneself.repo.configChanged.connect(self.configChanged)ifembedded:layout.setContentsMargins(2,2,2,2)else:self.setWindowTitle(_('TortoiseHg Sync'))self.resize(850,550)hbox=QHBoxLayout()hbox.setContentsMargins(0,0,0,0)hbox.setSpacing(4)tb=QToolBar(self)sactions=[]fortip,icon,cbin((_('Preview incoming changesets from specified URL'),'incoming',self.inclicked),(_('Pull incoming changesets from specified URL'),'pull',self.pullclicked),(_('Filter outgoing changesets to specified URL'),'outgoing',self.outclicked),(_('Push outgoing changesets to specified URL'),'push',self.pushclicked),(_('Email outgoing changesets for specified URL'),'mail-forward',self.emailclicked)):a=QAction(self)a.setToolTip(tip)a.setIcon(qtlib.geticon(icon))a.triggered.connect(cb)sactions.append(a)tb.addAction(a)self.stopAction=a=QAction(self)a.setToolTip(_('Stop current operation'))a.setIcon(qtlib.geticon('process-stop'))a.triggered.connect(self.stopclicked)tb.addAction(a) hbox.addWidget(tb)
self.layout().addLayout(hbox)
- self.detailsbutton = QPushButton(_('Options'))
+ self.optionsbutton = QPushButton(_('Options'))
self.postpullbutton = QPushButton()
hbox.addWidget(self.postpullbutton)
- hbox.addWidget(self.detailsbutton)
+ hbox.addWidget(self.optionsbutton)
tb.setMaximumHeight(self.postpullbutton.sizeHint().height())
if 'perfarce' in self.repo.extensions():
self.p4pbutton = QPushButton(_('p4pending'))
self.p4pbutton.clicked.connect(self.p4pending)hbox.addWidget(self.p4pbutton)else:self.p4pbutton=Noneself.targetcombo=QComboBox()self.targetcombo.setEnabled(False)self.targetcheckbox=QCheckBox(_('Target:'))self.targetcheckbox.toggled.connect(self.targetcombo.setEnabled)hbox.addWidget(self.targetcheckbox)hbox.addWidget(self.targetcombo)ifnotembedded:self.targetcombo.setHidden(True)self.targetcheckbox.setHidden(True)hbox.addStretch(1)self.urllabel=QLabel()self.urllabel.setMargin(4)self.urllabel.setTextInteractionFlags(Qt.TextSelectableByMouse)self.urllabel.setAcceptDrops(False)hbox.addWidget(self.urllabel)hbox=QHBoxLayout()hbox.setContentsMargins(0,0,0,0)hbox.setSpacing(4)self.schemecombo=QComboBox()forsin_schemes:self.schemecombo.addItem(s)self.schemecombo.currentIndexChanged.connect(self.refreshUrl)hbox.addWidget(self.schemecombo)hbox.addWidget(QLabel(_('Hostname:')))self.hostentry=QLineEdit()self.hostentry.setAcceptDrops(False)self.hostentry.textChanged.connect(self.refreshUrl)hbox.addWidget(self.hostentry)hbox.addWidget(QLabel(_('Port:')))self.portentry=QLineEdit()self.portentry.setAcceptDrops(False)fontm=QFontMetrics(self.font())self.portentry.setFixedWidth(8*fontm.width('9'))self.portentry.textChanged.connect(self.refreshUrl)hbox.addWidget(self.portentry)hbox.addWidget(QLabel(_('Path:')))self.pathentry=QLineEdit()self.pathentry.setAcceptDrops(False)self.pathentry.textChanged.connect(self.refreshUrl)hbox.addWidget(self.pathentry,1)self.authbutton=QPushButton(_('Authentication'))hbox.addWidget(self.authbutton)self.savebutton=QPushButton(_('Save'))hbox.addWidget(self.savebutton)self.layout().addLayout(hbox)hbox=QHBoxLayout()hbox.setContentsMargins(0,0,0,0)self.hgrctv=PathsTree(self,True)self.hgrctv.clicked.connect(self.pathSelected)self.hgrctv.removeAlias.connect(self.removeAlias)self.hgrctv.menuRequest.connect(self.menuRequest)pathsframe=QFrame()pathsframe.setFrameStyle(QFrame.StyledPanel|QFrame.Raised)pathsbox=QVBoxLayout()pathsbox.setContentsMargins(0,0,0,0)pathsframe.setLayout(pathsbox)lbl=QLabel(_('<b>Configured Paths</b>'))pathsbox.addWidget(lbl)pathsbox.addWidget(self.hgrctv)hbox.addWidget(pathsframe)self.reltv=PathsTree(self,False)self.reltv.clicked.connect(self.pathSelected)self.reltv.menuRequest.connect(self.menuRequest)self.reltv.clicked.connect(self.hgrctv.clearSelection)self.hgrctv.clicked.connect(self.reltv.clearSelection)pathsframe=QFrame()pathsframe.setFrameStyle(QFrame.StyledPanel|QFrame.Raised)pathsbox=QVBoxLayout()pathsbox.setContentsMargins(0,0,0,0)pathsframe.setLayout(pathsbox)lbl=QLabel(_('<b>Related Paths</b>'))pathsbox.addWidget(lbl)pathsbox.addWidget(self.reltv)hbox.addWidget(pathsframe)self.layout().addLayout(hbox,1) self.savebutton.clicked.connect(self.saveclicked)
self.authbutton.clicked.connect(self.authclicked)
self.postpullbutton.clicked.connect(self.postpullclicked)
- self.detailsbutton.pressed.connect(self.details)
+ self.optionsbutton.pressed.connect(self.editOptions)
self.opbuttons = sactions + [self.p4pbutton]
cmd=cmdui.Widget(notembedded,self)cmd.commandStarted.connect(self.commandStarted)cmd.commandFinished.connect(self.commandFinished)cmd.makeLogVisible.connect(self.makeLogVisible)cmd.output.connect(self.output)cmd.progress.connect(self.progress)layout.addWidget(cmd)cmd.setVisible(False)self.cmd=cmdself.embedded=embeddedself.reload()if'default'inself.paths:self.setUrl(self.paths['default'])self.curalias='default'else:self.curalias=NonedefloadTargets(self,rev):self.targetcombo.clear()self.targetcombo.addItem(_('rev: ')+str(rev),str(rev))fornameinself.repo.namedbranches:uname=hglib.tounicode(name)self.targetcombo.addItem(_('branch: ')+uname,hexlify(name))forname,nodeinself.repo.bookmarks.items():uname=hglib.tounicode(name)self.targetcombo.addItem(_('bookmark: ')+uname,node)defrefreshTargets(self,rev):iftype(rev)isnotint:returnifrev>=len(self.repo):returnself.loadTargets(rev)ctx=self.repo.changectx(rev)target=str(rev)ifctx.thgbranchhead():target=hexlify(ctx.branch())fortaginctx.thgtags():iftaginself.repo.bookmarks.keys():target=ctx.node()index=self.targetcombo.findData(target)ifindex<0:index=0self.targetcombo.setCurrentIndex(index)defapplyTargetOption(self,cmdline):ifself.targetcheckbox.isChecked():revtext=hglib.fromunicode(self.targetcombo.currentText())args=revtext.split(': ')ifargs[0]=='rev':cmdline+=['--rev',args[1]]elifargs[0]=='branch':cmdline+=['--branch',args[1]]elifargs[0]=='bookmark':cmdline+=['--bookmark',args[1]]returncmdlinedefconfigChanged(self): 'Repository is reporting its config files have changed'
self.reload()
- def details(self):
+ def editOptions(self):
dlg = OptionsDialog(self.opts, self)
dlg.setWindowFlags(Qt.Sheet)
dlg.setWindowModality(Qt.WindowModal)
ifdlg.exec_()==QDialog.Accepted:self.opts.update(dlg.outopts)defreload(self):# Refresh configured pathsself.paths={}fn=self.repo.join('hgrc')fn,cfg=loadIniFile([fn],self)if'paths'incfg:foraliasincfg['paths']:self.paths[alias]=cfg['paths'][alias]tm=PathsModel(self.paths.items(),self)self.hgrctv.setModel(tm)# Refresh post-pullself.cachedpp=self.repo.postpullname=_('Post Pull: ')+self.repo.postpull.title()self.postpullbutton.setText(name)# Refresh related pathsknown=set(self.paths.values())known.add(self.repo.root)related={}repoid=self.repo[0].node()forrepointhgrepo._repocache.values():ifrepo[0].node()!=repoid:continueifrepo.rootnotinknown:related[repo.root]=repo.shortnameknown.add(repo.root)foralias,pathinrepo.ui.configitems('paths'):ifpathnotinknown:related[path]=aliasknown.add(path)pairs=[(alias,path)forpath,aliasinrelated.items()]tm=PathsModel(pairs,self)self.reltv.setModel(tm)defrefreshUrl(self):'User has changed schema/host/port/path'ifself.updateInProgress:returnself.urllabel.setText(hglib.tounicode(self.currentUrl(True)))schemeIndex=self.schemecombo.currentIndex()self.hostentry.setEnabled(schemeIndex!=0)self.portentry.setEnabled(schemeIndex!=0)self.authbutton.setEnabled(schemeIndex>1)defcurrentUrl(self,hidepw):scheme=_schemes[self.schemecombo.currentIndex()]ifscheme=='local':returnhglib.fromunicode(self.pathentry.text())else:path=self.pathentry.text()host=self.hostentry.text()port=self.portentry.text()parts=[scheme,'://']ifself.curuser:parts.append(self.curuser)ifself.curpw:parts.append(':')parts.append(hidepwand'***'orself.curpw)parts.append('@')parts.append(hglib.fromunicode(host))ifport:parts.extend([':',hglib.fromunicode(port)])parts.extend(['/',hglib.fromunicode(path)])return''.join(parts)defpathSelected(self,index):path=index.model().realUrl(index)self.setUrl(path)aliasindex=index.sibling(index.row(),0)alias=aliasindex.data(Qt.DisplayRole).toString()self.curalias=hglib.fromunicode(alias)defsetUrl(self,newurl):'User has selected a new URL: newurl is expected in local encoding'try:user,host,port,folder,passwd,scheme=self.urlparse(newurl)exceptTypeError:returnself.updateInProgress=Truefori,valinenumerate(_schemes):ifscheme==val:self.schemecombo.setCurrentIndex(i)breakself.hostentry.setText(hglib.tounicode(hostor''))self.portentry.setText(hglib.tounicode(portor''))self.pathentry.setText(hglib.tounicode(folderor''))self.curuser=userself.curpw=passwdself.updateInProgress=Falseself.refreshUrl()defdragEnterEvent(self,event):event.acceptProposedAction()defdragMoveEvent(self,event):event.acceptProposedAction()defdropEvent(self,event):data=event.mimeData()ifdata.hasUrls():url=data.urls()[0]self.setUrl(hglib.fromunicode(url.toString()))event.accept()elifdata.hasText():text=data.text()self.setUrl(hglib.fromunicode(text))event.accept()defurlparse(self,path):m=re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$',path)ifm:user=m.group(2)host=m.group(3)port=m.group(5)folder=m.group(7)or'.'passwd=''scheme='ssh'elifpath.startswith('http://')orpath.startswith('https://'):snpaqf=urlparse.urlparse(path)scheme,netloc,folder,params,query,fragment=snpaqfhost,port,user,passwd=url.netlocsplit(netloc)iffolder.startswith('/'):folder=folder[1:]else:user,host,port,passwd=['']*4folder=pathscheme='local'returnuser,host,port,folder,passwd,schemedefcanExit(self):returnnotself.cmd.core.running()@pyqtSlot(QPoint,QString,QString,bool)defmenuRequest(self,point,url,alias,editable):'menu event emitted by one of the two URL lists'ifnotself.cmenu:acts=[]menu=QMenu(self)fortext,cbin((_('Explore'),self.exploreurl),(_('Terminal'),self.terminalurl),(_('Remove'),self.removeurl)):act=QAction(text,self)act.triggered.connect(cb)acts.append(act)menu.addAction(act)self.cmenu=menuself.acts=actsself.menuurl=urlself.menualias=aliasself.acts[-1].setEnabled(editable)self.cmenu.exec_(point)defexploreurl(self):url=hglib.fromunicode(self.menuurl)u,h,p,folder,pw,scheme=self.urlparse(url)ifscheme=='local':QDesktopServices.openUrl(QUrl.fromLocalFile(folder))else:QDesktopServices.openUrl(QUrl(url))defterminalurl(self):url=hglib.fromunicode(self.menuurl)u,h,p,folder,pw,scheme=self.urlparse(url)ifscheme!='local':qtlib.InfoMsgBox(_('Repository not local'),_('A terminal shell cannot be opened for remote'))returnshell=self.repo.shell()ifshell:cwd=os.getcwd()try:os.chdir(folder)QProcess.startDetached(shell)finally:os.chdir(cwd)else:qtlib.InfoMsgBox(_('No shell configured'),_('A terminal shell must be configured'))defremoveurl(self):ifqtlib.QuestionMsgBox(_('Confirm path delete'),_('Delete %s from your repo configuration file?')%self.menualias,parent=self):self.removeAlias(self.menualias)defkeyPressEvent(self,event):ifevent.matches(QKeySequence.Refresh):self.reload()elifevent.key()==Qt.Key_Escape:ifself.cmd.core.running():self.cmd.cancel()elifnotself.embedded:self.close()else:returnsuper(SyncWidget,self).keyPressEvent(event)defstopclicked(self):ifself.cmd.core.running():self.cmd.cancel()defsaveclicked(self):ifself.curalias:alias=self.curaliaselif'default'notinself.paths:alias='default'else:alias='new'url=hglib.fromunicode(self.urllabel.text())dlg=SaveDialog(self.repo,alias,url,self)dlg.setWindowFlags(Qt.Sheet)dlg.setWindowModality(Qt.WindowModal)ifdlg.exec_()==QDialog.Accepted:self.curalias=hglib.fromunicode(dlg.aliasentry.text())defauthclicked(self):host=hglib.fromunicode(self.hostentry.text())user=self.curuseror''pw=self.curpwor''dlg=AuthDialog(self.repo,host,user,pw,self)dlg.setWindowFlags(Qt.Sheet)dlg.setWindowModality(Qt.WindowModal)ifdlg.exec_()==QDialog.Accepted:self.curuser,self.curpw='',''defcommandStarted(self):forbinself.opbuttons:ifb:b.setEnabled(False)self.stopAction.setEnabled(True)ifnotself.embedded:self.cmd.setShowOutput(True)self.cmd.setVisible(True)defcommandFinished(self,ret):self.repo.decrementBusyCount()forbinself.opbuttons:ifb:b.setEnabled(True)self.stopAction.setEnabled(False)ifself.finishfunc:output=self.cmd.core.rawoutput()self.finishfunc(ret,output)defrun(self,cmdline,details):ifself.cmd.core.running():returnfornameinlist(details)+['remotecmd']:val=self.opts.get(name)ifnotval:continueifisinstance(val,bool):ifval:cmdline.append('--'+name)elifval:cmdline.append('--'+name)cmdline.append(val)if'rev'indetailsand'--rev'notincmdline:cmdline=self.applyTargetOption(cmdline)url=self.currentUrl(False)ifnoturl:qtlib.InfoMsgBox(_('No URL selected'),_('An URL must be selected for this operation.'),parent=self)returnsafeurl=self.currentUrl(True)display=' '.join(cmdline+[safeurl]).replace('\n','^M')cmdline.append(url)self.repo.incrementBusyCount()self.cmd.run(cmdline,display=display,useproc='p4://'inurl)#### Workbench toolbar buttons##defincoming(self):ifself.cmd.core.running():self.showMessage.emit(_('sync command already running'))else:self.inclicked()defpull(self):ifself.cmd.core.running():self.showMessage.emit(_('sync command already running'))else:self.pullclicked()defoutgoing(self):ifself.cmd.core.running():self.showMessage.emit(_('sync command already running'))else:self.outclicked()defpush(self):ifself.cmd.core.running():self.showMessage.emit(_('sync command already running'))else:self.pushclicked()defpullBundle(self,bundle,rev):'accept bundle changesets'ifself.cmd.core.running():self.output.emit(_('sync command already running'),'control')returnsave=self.currentUrl(False)orev=self.opts.get('rev')self.setUrl(bundle)ifrevisnotNone:self.opts['rev']=str(rev)self.pullclicked()self.setUrl(save)self.opts['rev']=orev#### Sync dialog buttons##definclicked(self):self.showMessage.emit(_('Incoming...'))url=self.currentUrl(True)ifself.embeddedandnoturl.startswith('p4://')and \
notself.opts.get('subrepos'):deffinished(ret,output):ifret==0andos.path.exists(bfile):self.showMessage.emit(_('Incoming changesets found'))self.incomingBundle.emit(bfile)elifret==1:self.showMessage.emit(_('No incoming changesets'))else:self.showMessage.emit(_('Incoming aborted, ret %d')%ret)bfile=urlforbadcharin(':','*','\\','?','#'):bfile=bfile.replace(badchar,'')bfile=bfile.replace('/','_')bfile=tempfile.mktemp('.hg',bfile+'_',qtlib.gettempdir())self.finishfunc=finishedcmdline=['--repository',self.repo.root,'incoming','--bundle',bfile]self.run(cmdline,('force','branch','rev'))else:deffinished(ret,output):ifret==0:self.showMessage.emit(_('Incoming changesets found'))elifret==1:self.showMessage.emit(_('No incoming changesets'))else:self.showMessage.emit(_('Incoming aborted, ret %d')%ret)self.finishfunc=finishedcmdline=['--repository',self.repo.root,'incoming']self.run(cmdline,('force','branch','rev','subrepos'))defpullclicked(self):deffinished(ret,output):ifret==0:self.showMessage.emit(_('Pull completed successfully'))else:self.showMessage.emit(_('Pull aborted, ret %d')%ret)# handle file conflicts during rebaseifos.path.exists(self.repo.join('rebasestate')):dlg=rebase.RebaseDialog(self.repo,self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()return# handle file conflicts during updatems=mergemod.mergestate(self.repo)forpathinms:ifms[path]=='u':qtlib.InfoMsgBox(_('Merge caused file conflicts'),_('File conflicts need to be resolved'))dlg=resolve.ResolveDialog(self.repo,self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()returnself.finishfunc=finishedself.showMessage.emit(_('Pulling...'))cmdline=['--repository',self.repo.root,'pull','--verbose']uimerge=self.repo.ui.configbool('tortoisehg','autoresolve') \
and'ui.merge=internal:merge'or'ui.merge=internal:fail'ifself.cachedpp=='rebase':cmdline+=['--rebase','--config',uimerge]elifself.cachedpp=='update':cmdline+=['--update','--config',uimerge]elifself.cachedpp=='fetch':cmdline[2]='fetch'self.run(cmdline,('force','branch','rev'))defoutclicked(self):self.showMessage.emit(_('Outgoing...'))ifself.embeddedandnotself.opts.get('subrepos'):defoutputnodes(ret,data):ifret==0:nodes=[nfornindata.splitlines()iflen(n)==40]self.outgoingNodes.emit(nodes)self.showMessage.emit(_('%d outgoing changesets')%len(nodes))elifret==1:self.showMessage.emit(_('No outgoing changesets'))else:self.showMessage.emit(_('Outgoing aborted, ret %d')%ret)self.finishfunc=outputnodescmdline=['--repository',self.repo.root,'outgoing','--quiet','--template','{node}\n']self.run(cmdline,('force','branch','rev'))else:self.finishfunc=Nonecmdline=['--repository',self.repo.root,'outgoing']self.run(cmdline,('force','branch','rev','subrepos'))defp4pending(self):p4url=self.currentUrl(False)deffinished(ret,output):pending={}ifret==0:forlineinoutput.splitlines():ifline.startswith('ignoring hg revision'):continuetry:hashes=line.split(' ')changelist=hashes.pop(0)clnum=int(changelist)iflen(hashes)>1andlen(hashes[0])==1:state=hashes.pop(0)ifstate=='s':changelist=_('%s (submitted)')%changelistelifstate=='p':changelist=_('%s (pending)')%changelistelse:raiseValueErrorpending[changelist]=hashesexcept(ValueError,IndexError):text=_('Unable to parse p4pending output')ifpending:text=_('%d pending changelists found')%len(pending)else:text=_('No pending Perforce changelists')elifretisNone:text=_('Aborted p4pending')else:text=_('Unable to determine pending changesets')self.showMessage.emit(text)ifpending:fromtortoisehg.hgqt.p4pendingimportPerforcePendingdlg=PerforcePending(self.repo,pending,p4url,self)dlg.showMessage.connect(self.showMessage)dlg.output.connect(self.output)dlg.makeLogVisible.connect(self.makeLogVisible)dlg.exec_()self.finishfunc=finishedself.showMessage.emit(_('Perforce pending...'))self.run(['--repository',self.repo.root,'p4pending','--verbose'],())defpushclicked(self):self.showMessage.emit(_('Pushing...'))deffinished(ret,output):ifret==0:self.showMessage.emit(_('Push completed successfully'))else:self.showMessage.emit(_('Push aborted, ret %d')%ret)self.finishfunc=finishedcmdline=['--repository',self.repo.root,'push']self.run(cmdline,('force','new-branch','branch','rev'))defpostpullclicked(self):dlg=PostPullDialog(self.repo,self)dlg.setWindowFlags(Qt.Sheet)dlg.setWindowModality(Qt.WindowModal)dlg.exec_()defemailclicked(self):self.showMessage.emit(_('Determining outgoing changesets to email...'))defoutputnodes(ret,data):ifret==0:nodes=[nfornindata.splitlines()iflen(n)==40]self.showMessage.emit(_('%d outgoing changesets')%len(nodes))try:outgoingrevs=[cmdline[cmdline.index('--rev')+1]]exceptValueError:outgoingrevs=Nonefromtortoisehg.hgqtimportrunas_run_run.email(ui.ui(),repo=self.repo,rev=nodes,outgoing=True,outgoingrevs=outgoingrevs)elifret==1:self.showMessage.emit(_('No outgoing changesets'))else:self.showMessage.emit(_('Outgoing aborted, ret %d')%ret)self.finishfunc=outputnodescmdline=['--repository',self.repo.root,'outgoing','--quiet','--template','{node}\n']self.run(cmdline,('force','branch','rev'))@pyqtSlot(QString)defremoveAlias(self,alias):alias=hglib.fromunicode(alias)fn=self.repo.join('hgrc')fn,cfg=loadIniFile([fn],self)ifnothasattr(cfg,'write'):qtlib.WarningMsgBox(_('Unable to remove URL'),_('Iniparse must be installed.'),parent=self)returniffnisNone:returnifaliasincfg['paths']:delcfg['paths'][alias]self.repo.incrementBusyCount()try:wconfig.writefile(cfg,fn)exceptEnvironmentError,e:qtlib.WarningMsgBox(_('Unable to write configuration file'),hglib.tounicode(e),parent=self)self.repo.decrementBusyCount()classPostPullDialog(QDialog):def__init__(self,repo,parent):super(PostPullDialog,self).__init__(parent)self.repo=repolayout=QVBoxLayout()self.setLayout(layout)self.setWindowTitle(_('Post Pull Behavior'))self.setWindowFlags(self.windowFlags()&~Qt.WindowContextHelpButtonHint)lbl=QLabel(_('Select post-pull operation for this repository'))layout.addWidget(lbl)self.none=QRadioButton(_('None - simply pull changesets'))self.update=QRadioButton(_('Update - pull, then try to update'))layout.addWidget(self.none)layout.addWidget(self.update)if'fetch'inrepo.extensions()orrepo.postpull=='fetch':if'fetch'inrepo.extensions():btntxt=_('Fetch - use fetch (auto merge pulled changes)')else:btntxt=_('Fetch - use fetch extension (fetch is not active!)')self.fetch=QRadioButton(btntxt)layout.addWidget(self.fetch)else:self.fetch=Noneif'rebase'inrepo.extensions()orrepo.postpull=='rebase':if'rebase'inrepo.extensions():btntxt=_('Rebase - rebase local commits above pulled changes')else:btntxt=_('Rebase - use rebase extension (rebase is not active!)')self.rebase=QRadioButton(btntxt)layout.addWidget(self.rebase)self.none.setChecked(True)ifrepo.postpull=='update':self.update.setChecked(True)elifrepo.postpull=='fetch':self.fetch.setChecked(True)elifrepo.postpull=='rebase':self.rebase.setChecked(True)self.autoresolve_chk=QCheckBox(_('Automatically resolve merge conflicts ''where possible'))self.autoresolve_chk.setChecked(repo.ui.configbool('tortoisehg','autoresolve',False))layout.addWidget(self.autoresolve_chk)cfglabel=QLabel(_('<a href="config">Launch settings tool...</a>'))cfglabel.linkActivated.connect(self.linkactivated)layout.addWidget(cfglabel)BB=QDialogButtonBoxbb=QDialogButtonBox(BB.Save|BB.Cancel)bb.accepted.connect(self.accept)bb.rejected.connect(self.reject)self.bb=bblayout.addWidget(bb)deflinkactivated(self,command):ifcommand=='config':fromtortoisehg.hgqt.settingsimportSettingsDialogsd=SettingsDialog(configrepo=False,focus='tortoisehg.postpull',parent=self,root=self.repo.root)sd.exec_()defgetValue(self):ifself.none.isChecked():return'none'elifself.update.isChecked():return'update'elif(self.fetchandself.fetch.isChecked()):return'fetch'else:return'rebase'defaccept(self):path=self.repo.join('hgrc')fn,cfg=loadIniFile([path],self)ifnothasattr(cfg,'write'):qtlib.WarningMsgBox(_('Unable to save post pull operation'),_('Iniparse must be installed.'),parent=self)returniffnisNone:returnself.repo.incrementBusyCount()try:cfg.set('tortoisehg','postpull',self.getValue())cfg.set('tortoisehg','autoresolve',self.autoresolve_chk.isChecked())wconfig.writefile(cfg,fn)exceptEnvironmentError,e:qtlib.WarningMsgBox(_('Unable to write configuration file'),hglib.tounicode(e),parent=self)self.repo.decrementBusyCount()super(PostPullDialog,self).accept()defreject(self):super(PostPullDialog,self).reject()classSaveDialog(QDialog):def__init__(self,repo,alias,url,parent):super(SaveDialog,self).__init__(parent)self.repo=repolayout=QVBoxLayout()self.setLayout(layout)hbox=QHBoxLayout()hbox.addWidget(QLabel(_('Alias')))self.aliasentry=QLineEdit(alias)hbox.addWidget(self.aliasentry,1)layout.addLayout(hbox)hbox=QHBoxLayout()hbox.addWidget(QLabel(_('URL')))self.urlentry=QLineEdit(url)fontm=QFontMetrics(self.font())self.urlentry.setFixedWidth(fontm.width(url)+5)hbox.addWidget(self.urlentry,1)layout.addLayout(hbox)BB=QDialogButtonBoxbb=QDialogButtonBox(BB.Save|BB.Cancel)bb.accepted.connect(self.accept)bb.rejected.connect(self.reject)bb.button(BB.Save).setAutoDefault(True)self.bb=bblayout.addWidget(bb)self.aliasentry.selectAll()self.setWindowTitle(_('Save Peer Path'))self.setWindowFlags(self.windowFlags()&~Qt.WindowContextHelpButtonHint)QTimer.singleShot(0,lambda:self.aliasentry.setFocus())defaccept(self):fn=self.repo.join('hgrc')fn,cfg=loadIniFile([fn],self)ifnothasattr(cfg,'write'):qtlib.WarningMsgBox(_('Unable to save an URL'),_('Iniparse must be installed.'),parent=self)returniffnisNone:returnalias=hglib.fromunicode(self.aliasentry.text())path=hglib.fromunicode(self.urlentry.text())ifaliasincfg['paths']:ifnotqtlib.QuestionMsgBox(_('Confirm URL replace'),_('%s already exists, replace URL?')%alias):returncfg.set('paths',alias,path)self.repo.incrementBusyCount()try:wconfig.writefile(cfg,fn)exceptEnvironmentError,e:qtlib.WarningMsgBox(_('Unable to write configuration file'),hglib.tounicode(e),parent=self)self.repo.decrementBusyCount()super(SaveDialog,self).accept()defreject(self):super(SaveDialog,self).reject()classAuthDialog(QDialog):def__init__(self,repo,host,user,pw,parent):super(AuthDialog,self).__init__(parent)self.repo=repoself.setLayout(QVBoxLayout())form=QFormLayout()self.aliasentry=QLineEdit(host.split('.',1)[0])form.addRow(_('Site Alias'),self.aliasentry)self.schemes=QComboBox()self.schemes.addItems(('http https','http','https'))form.addRow(_('Schemes'),self.schemes)self.prefixentry=QLineEdit(host)form.addRow(_('Prefix'),self.prefixentry)self.userentry=QLineEdit(user)form.addRow(_('Username'),self.userentry)self.pwentry=QLineEdit(pw)self.pwentry.setEchoMode(QLineEdit.Password)form.addRow(_('Password'),self.pwentry)self.layout().addLayout(form)self.globalcb=QCheckBox(_('Save this configuration globally'))self.globalcb.setChecked(True)self.layout().addWidget(self.globalcb)BB=QDialogButtonBoxbb=QDialogButtonBox(BB.Help|BB.Save|BB.Cancel)bb.rejected.connect(self.reject)bb.accepted.connect(self.accept)bb.helpRequested.connect(self.keyringHelp)self.bb=bbself.layout().addWidget(bb)self.setWindowTitle(_('Authentication: ')+host)self.setWindowFlags(self.windowFlags()& \
~Qt.WindowContextHelpButtonHint)self.userentry.selectAll()QTimer.singleShot(0,lambda:self.userentry.setFocus())defkeyringHelp(self):passdefaccept(self):ifself.globalcb:path=util.user_rcpath()else:path=[self.repo.join('hgrc')]fn,cfg=loadIniFile(path,self)ifnothasattr(cfg,'write'):qtlib.WarningMsgBox(_('Unable to save authentication'),_('Iniparse must be installed.'),parent=self)returniffnisNone:returnschemes=hglib.fromunicode(self.schemes.currentText())prefix=hglib.fromunicode(self.prefixentry.text())username=hglib.fromunicode(self.userentry.text())password=hglib.fromunicode(self.pwentry.text())alias=hglib.fromunicode(self.aliasentry.text())ifalias+'.prefix'incfg['auth']:ifnotqtlib.QuestionMsgBox(_('Confirm authentication replace'),_('Authentication info for %s already ''exists, replace?')%alias):returncfg.set('auth',alias+'.schemes',schemes)cfg.set('auth',alias+'.username',username)cfg.set('auth',alias+'.prefix',prefix)key=alias+'.password'ifpassword:cfg.set('auth',key,password)elifnotpasswordandkeyincfg['auth']:delcfg['auth'][key]self.repo.incrementBusyCount()try:wconfig.writefile(cfg,fn)exceptEnvironmentError,e:qtlib.WarningMsgBox(_('Unable to write configuration file'),hglib.tounicode(e),parent=self)self.repo.decrementBusyCount()super(AuthDialog,self).accept()defreject(self):super(AuthDialog,self).reject()classPathsTree(QTreeView):removeAlias=pyqtSignal(QString)menuRequest=pyqtSignal(QPoint,QString,QString,bool)def__init__(self,parent,editable):QTreeView.__init__(self,parent)self.setSelectionMode(QTreeView.SingleSelection)self.editable=editabledefcontextMenuEvent(self,event):forindexinself.selectedRows():alias=index.data(Qt.DisplayRole).toString()url=index.sibling(index.row(),1).data(Qt.DisplayRole).toString()self.menuRequest.emit(event.globalPos(),url,alias,self.editable)returndefkeyPressEvent(self,event):ifself.editableandevent.matches(QKeySequence.Delete):self.deleteSelected()else:returnsuper(PathsTree,self).keyPressEvent(event)defdeleteSelected(self):forindexinself.selectedRows():alias=index.data(Qt.DisplayRole).toString()r=qtlib.QuestionMsgBox(_('Confirm path delete'),_('Delete %s from your repo configuration file?')%alias,parent=self)ifr:self.removeAlias.emit(alias)defselectedUrls(self):forindexinself.selectedRows():yieldindex.sibling(index.row(),1).data(Qt.DisplayRole).toString()defdragObject(self):urls=[]forurlinself.selectedUrls():u=QUrl()u.setPath(url)urls.append(u)ifurls:d=QDrag(self)m=QMimeData()m.setUrls(urls)d.setMimeData(m)d.start(Qt.CopyAction)defmousePressEvent(self,event):self.pressPos=event.pos()self.pressTime=QTime.currentTime()returnsuper(PathsTree,self).mousePressEvent(event)defmouseMoveEvent(self,event):d=event.pos()-self.pressPosifd.manhattanLength()<QApplication.startDragDistance():returnQTreeView.mouseMoveEvent(self,event)elapsed=self.pressTime.msecsTo(QTime.currentTime())ifelapsed<QApplication.startDragTime():returnsuper(PathsTree,self).mouseMoveEvent(event)self.dragObject()returnsuper(PathsTree,self).mouseMoveEvent(event)defselectedRows(self):returnself.selectionModel().selectedRows()classPathsModel(QAbstractTableModel):def__init__(self,pathlist,parent=None):QAbstractTableModel.__init__(self,parent)self.headers=(_('Alias'),_('URL'))self.rows=[]foralias,pathinpathlist:safepath=url.hidepassword(path)ualias=hglib.tounicode(alias)usafepath=hglib.tounicode(safepath)self.rows.append([ualias,usafepath,path])defrowCount(self,parent):ifparent.isValid():return0# no childreturnlen(self.rows)defcolumnCount(self,parent):ifparent.isValid():return0# no childreturnlen(self.headers)defdata(self,index,role):ifnotindex.isValid():returnQVariant()ifrole==Qt.DisplayRole:returnQVariant(self.rows[index.row()][index.column()])returnQVariant()defheaderData(self,col,orientation,role):ifrole!=Qt.DisplayRoleororientation!=Qt.Horizontal:returnQVariant()else:returnQVariant(self.headers[col])defflags(self,index):flags=Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabledreturnflagsdefrealUrl(self,index):returnself.rows[index.row()][2]defloadIniFile(rcpath,parent):forfninrcpath:ifos.path.exists(fn):breakelse:forfninrcpath:# Try to create a file from rcpathtry:f=open(fn,'w')f.write('# Generated by TortoiseHg\n')f.close()breakexceptEnvironmentError:passelse:qtlib.WarningMsgBox(_('Unable to create a config file'),_('Insufficient access rights.'),parent=parent)returnNone,{}returnfn,wconfig.readfile(fn)classOptionsDialog(QDialog):'Utility dialog for configuring uncommon options'def__init__(self,opts,parent):QDialog.__init__(self,parent)self.setWindowTitle('%s - sync options'%parent.repo.displayname)self.repo=parent.repolayout=QFormLayout()self.setLayout(layout)self.newbranchcb=QCheckBox(_('Allow push of a new branch'))self.newbranchcb.setChecked(opts.get('new-branch',False))layout.addRow(self.newbranchcb,None)self.forcecb=QCheckBox(_('Force push or pull (override safety'' checks)'))self.forcecb.setChecked(opts.get('force',False))layout.addRow(self.forcecb,None)self.subrepocb=QCheckBox(_('Recurse into subrepositories'))self.subrepocb.setChecked(opts.get('subrepos',False))layout.addRow(self.subrepocb,None)lbl=QLabel(_('Remote command:'))self.remotele=QLineEdit()ifopts.get('remotecmd'):self.remotele.setText(hglib.tounicode(opts['remotecmd']))layout.addRow(lbl,self.remotele)BB=QDialogButtonBoxbb=QDialogButtonBox(BB.Ok|BB.Cancel)bb.accepted.connect(self.accept)bb.rejected.connect(self.reject)self.bb=bblayout.addWidget(bb)defaccept(self):outopts={}forname,lein(('remotecmd',self.remotele),):outopts[name]=hglib.fromunicode(le.text()).strip()outopts['subrepos']=self.subrepocb.isChecked()outopts['force']=self.forcecb.isChecked()outopts['new-branch']=self.newbranchcb.isChecked()self.outopts=outoptsQDialog.accept(self)defrun(ui,*pats,**opts):fromtortoisehg.utilimportpathsfromtortoisehg.hgqtimportthgreporepo=thgrepo.repository(ui,path=paths.find_root())returnSyncWidget(repo,**opts)
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.