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.
# reporegistry.py - registry for a user's repositories## Copyright 2010 Adrian Buehlmann <adrian@cadifra.com>## This software may be used and distributed according to the terms of the# GNU General Public License version 2 or any later version.importosfrommercurialimporterror,hg,ui,utilfromtortoisehg.utilimporthglib,pathsfromtortoisehg.hgqt.i18nimport_fromtortoisehg.hgqtimportqtlib,repotreemodel,clone,settingsfromPyQt4.QtCoreimport*fromPyQt4.QtGuiimport*importqtlibdefsettingsfilename():"""Return path to thg-reporegistry.xml as unicode"""s=QSettings()dir=os.path.dirname(unicode(s.fileName()))returndir+'/'+'thg-reporegistry.xml'classRepoTreeView(QTreeView):showMessage=pyqtSignal(QString)menuRequested=pyqtSignal(object,object)openRepo=pyqtSignal(QString,bool)dropAccepted=pyqtSignal()def__init__(self,parent):QTreeView.__init__(self,parent,allColumnsShowFocus=True)self.selitem=Noneself.msg=''self.setHeaderHidden(True)self.setExpandsOnDoubleClick(False)self.setMouseTracking(True)# enable drag and drop# (see http://doc.qt.nokia.com/4.6/model-view-dnd.html)self.setDragEnabled(True)self.setAcceptDrops(True)self.setAutoScroll(True)self.setDragDropMode(QAbstractItemView.DragDrop)self.setDefaultDropAction(Qt.MoveAction)self.setDropIndicatorShown(True)self.setEditTriggers(QAbstractItemView.DoubleClicked)self.setSelectionBehavior(QAbstractItemView.SelectRows)QShortcut('Return',self,self.showFirstTabOrOpen).setContext(Qt.WidgetShortcut)QShortcut('Enter',self,self.showFirstTabOrOpen).setContext(Qt.WidgetShortcut)defcontextMenuEvent(self,event):ifnotself.selitem:returnself.menuRequested.emit(event.globalPos(),self.selitem)defdragEnterEvent(self,event):ifevent.source()isself:# Use the default event handler for internal draggingsuper(RepoTreeView,self).dragEnterEvent(event)returnd=event.mimeData()foruind.urls():root=paths.find_root(hglib.fromunicode(u.toLocalFile()))ifroot:event.setDropAction(Qt.LinkAction)event.accept()self.setState(QAbstractItemView.DraggingState)breakdefdropLocation(self,event):index=self.indexAt(event.pos())# Determine where the item was dropped.# Depth in tree: 1 = group, 2 = repo, and (eventually) 3+ = subrepodepth=self.model().depth(index)ifdepth==1:group=indexrow=-1elifdepth==2:indicator=self.dropIndicatorPosition()group=index.parent()row=index.row()ifindicator==QAbstractItemView.BelowItem:row=index.row()+1else:index=group=row=Nonereturnindex,group,rowdefstartDrag(self,supportedActions):indexes=self.selectedIndexes()# Make sure that all selected items are of the same typeiflen(indexes)==0:# Nothing to drag!return# Make sure that all items that we are dragging are of the same typefirstItem=indexes[0].internalPointer()selectionInstanceType=type(firstItem)foridxinindexes[1:]:ifselectionInstanceType!=type(idx.internalPointer()):# Cannot drag mixed type itemsreturn# Each item type may support different drag & drop actions# For instance, suprepo items support Copy actions onlysupportedActions=firstItem.getSupportedDragDropActions()super(RepoTreeView,self).startDrag(supportedActions)defdropEvent(self,event):data=event.mimeData()index,group,row=self.dropLocation(event)ifindex:ifevent.source()isself:# Event is an internal move, so pass it to the modelcol=0drop=self.model().dropMimeData(data,event.dropAction(),row,col,group)ifdrop:event.accept()self.dropAccepted.emit()else:# Event is a drop of an external repoaccept=Falseforuindata.urls():root=paths.find_root(hglib.fromunicode(u.toLocalFile()))ifrootandnotself.model().getRepoItem(root):self.model().addRepo(group,root,row)accept=Trueifaccept:event.setDropAction(Qt.LinkAction)event.accept()self.dropAccepted.emit()self.setAutoScroll(False)self.setState(QAbstractItemView.NoState)self.viewport().update()self.setAutoScroll(True)defmouseMoveEvent(self,event):self.msg=''pos=event.pos()idx=self.indexAt(pos)ifidx.isValid():item=idx.internalPointer()self.msg=item.details()self.showMessage.emit(self.msg)ifevent.buttons()==Qt.NoButton:# Bail out early to avoid tripping over this bug:# http://bugreports.qt.nokia.com/browse/QTBUG-10180returnsuper(RepoTreeView,self).mouseMoveEvent(event)defleaveEvent(self,event):ifself.msg!='':self.showMessage.emit('')defmouseDoubleClickEvent(self,event):ifself.selitemandself.selitem.internalPointer().isRepo():# We can only open mercurial repositories and subrepositoriesrepotype=self.selitem.internalPointer().repotype()ifrepotype=='hg':self.showFirstTabOrOpen()else:qtlib.WarningMsgBox(_('Unsupported repository type (%s)')%repotype,_('Cannot open non mercurial repositories or subrepositories'),parent=self)else:# a double-click on non-repo rows opens an editorsuper(RepoTreeView,self).mouseDoubleClickEvent(event)defselectionChanged(self,selected,deselected):selection=self.selectedIndexes()iflen(selection)==0:self.selitem=Noneelse:self.selitem=selection[0]defsizeHint(self):size=super(RepoTreeView,self).sizeHint()size.setWidth(QFontMetrics(self.font()).width('M')*15)returnsizedefshowFirstTabOrOpen(self):'Enter or double click events, show existing or open a new repowidget'ifself.selitemandself.selitem.internalPointer().isRepo():root=self.selitem.internalPointer().rootpath()self.openRepo.emit(hglib.tounicode(root),True)classRepoRegistryView(QDockWidget):showMessage=pyqtSignal(QString)openRepo=pyqtSignal(QString,bool)def__init__(self,parent,showSubrepos=False,showNetworkSubrepos=False,showShortPaths=False):QDockWidget.__init__(self,parent)self.watcher=Noneself.showSubrepos=showSubreposself.showNetworkSubrepos=showNetworkSubreposself.showShortPaths=showShortPathsself.setFeatures(QDockWidget.DockWidgetClosable|QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetFloatable)self.setWindowTitle(_('Repository Registry'))mainframe=QFrame()mainframe.setLayout(QVBoxLayout())self.setWidget(mainframe)mainframe.layout().setContentsMargins(0,0,0,0)self.contextmenu=QMenu(self)self.tview=tv=RepoTreeView(self)sfile=settingsfilename()tv.setModel(repotreemodel.RepoTreeModel(sfile,self,showSubrepos=self.showSubrepos,showNetworkSubrepos=self.showNetworkSubrepos))mainframe.layout().addWidget(tv)tv.setIndentation(10)tv.setFirstColumnSpanned(0,QModelIndex(),True)tv.setColumnHidden(1,True)tv.showMessage.connect(self.showMessage)tv.menuRequested.connect(self.onMenuRequest)tv.openRepo.connect(self.openRepo)tv.dropAccepted.connect(self.dropAccepted)self.createActions()QTimer.singleShot(0,self.expand)# Setup a file system watcher to update the reporegistry# anytime it is modified by another thg instance# Note that we must make sure that the settings file exists before# setting thefile watcherifnotos.path.exists(sfile):tv.model().write(sfile)self.watcher=QFileSystemWatcher(self)self.watcher.addPath(sfile)self.watcher.fileChanged.connect(self.modifiedSettings)self._pendingReloadModel=Falseself._activeTabRepo=NonedefsetShowSubrepos(self,show,reloadModel=True):ifself.showSubrepos!=show:self.showSubrepos=showifreloadModel:self.reloadModel()defsetShowNetworkSubrepos(self,show,reloadModel=True):ifself.showNetworkSubrepos!=show:self.showNetworkSubrepos=showifreloadModel:self.reloadModel()defsetShowShortPaths(self,show):ifself.showShortPaths!=show:self.showShortPaths=show#self.tview.model().showShortPaths = showself.tview.model().updateCommonPaths(show)self.tview.dataChanged(QModelIndex(),QModelIndex())defupdateSettingsFile(self):# If there is a settings watcher, we must briefly stop watching the# settings file while we save it, otherwise we'll get the update signal# that we do not wantsfile=settingsfilename()ifself.watcher:self.watcher.removePath(sfile)self.tview.model().write(sfile)ifself.watcher:self.watcher.addPath(sfile)# Whenver the settings file must be updated, it is also time to ensure# that the commonPaths are up to dateQTimer.singleShot(0,self.tview.model().updateCommonPaths)@pyqtSlot() def dropAccepted(self):
# Whenever a drag and drop operation is completed, update the settings
# file
- self.updateSettingsFile()
+QTimer.singleShot(0, self.updateSettingsFile)
@pyqtSlot(QString)
def modifiedSettings(self):
UPDATE_DELAY=2# seconds# Do not update the repo registry more often than# once every UPDATE_DELAY secondsifnotself._pendingReloadModel:# There are no pending updates:# -> schedule and update in UPDATE_DELAY seconds.# If other update notifications arrive from now# until now + UPDATE_DELAY, they will be ignored and "rolled into"# the pending updateself._pendingReloadModel=TrueQTimer.singleShot(1000*UPDATE_DELAY,self.reloadModel)defreloadModel(self):self.tview.setModel(repotreemodel.RepoTreeModel(settingsfilename(),self,self.showSubrepos,self.showNetworkSubrepos,self.showShortPaths))self.expand()self._pendingReloadModel=Falsedefexpand(self):self.tview.expandToDepth(0)defaddRepo(self,root):'workbench has opened a new repowidget, ensure it is in the registry'm=self.tview.model()it=m.getRepoItem(root,lookForSubrepos=True)ifit==None:m.addRepo(None,root,-1)self.updateSettingsFile()defsetActiveTabRepo(self,root):"""" The selected tab has changed on the workbench Unmark the previously selected tab and mark the new one as selected on the Repo Registry as well """root=hglib.fromunicode(root)ifself._activeTabRepo:self._activeTabRepo.setActive(False)m=self.tview.model()it=m.getRepoItem(root,lookForSubrepos=True)ifit:self._activeTabRepo=itit.setActive(True)self.tview.dataChanged(QModelIndex(),QModelIndex())defshowPaths(self,show):self.tview.setColumnHidden(1,notshow)self.tview.setHeaderHidden(notshow)ifshow:self.tview.resizeColumnToContents(0)self.tview.resizeColumnToContents(1)defclose(self):# We must stop monitoring the settings file and then we can save itsfile=settingsfilename()self.watcher.removePath(sfile)self.tview.model().write(sfile)def_action_defs(self):a=[("reloadRegistry",_("Refresh repository list"),'view-refresh',_("Refresh the Repository Registry list"),self.reloadModel),("open",_("Open"),'thg-repository-open',_("Open the repository in a new tab"),self.open),("openAll",_("Open All"),'thg-repository-open',_("Open all repositories in new tabs"),self.openAll),("newGroup",_("New Group"),'new-group',_("Create a new group"),self.newGroup),("rename",_("Rename"),None,_("Rename the entry"),self.startRename),("settings",_("Settings..."),'settings_user',_("View the repository's settings"),self.startSettings),("remove",_("Remove from registry"),'menudelete',_("Remove the node and all its subnodes."" Repositories are not deleted from disk."),self.removeSelected),("clone",_("Clone..."),'hg-clone',_("Clone Repository"),self.cloneRepo),("explore",_("Explore"),'system-file-manager',_("Open the repository in a file browser"),self.explore),("terminal",_("Terminal"),'utilities-terminal',_("Open a shell terminal in the repository root"),self.terminal),("add",_("Add repository..."),'hg',_("Add a repository to this group"),self.addNewRepo),("addsubrepo",_("Add a subrepository..."),'thg-add-subrepo',_("Convert an existing repository into a subrepository"),self.addSubrepo),("copypath",_("Copy path"),'',_("Copy the root path of the repository to the clipboard"),self.copyPath),]returnadefcreateActions(self):self._actions={}forname,desc,icon,tip,cbinself._action_defs():self._actions[name]=QAction(desc,self)QTimer.singleShot(0,self.configureActions)defconfigureActions(self):forname,desc,icon,tip,cbinself._action_defs():act=self._actions[name]ificon:act.setIcon(qtlib.getmenuicon(icon))iftip:act.setStatusTip(tip)ifcb:act.triggered.connect(cb)self.addAction(act)defonMenuRequest(self,point,selitem):menulist=selitem.internalPointer().menulist()ifnotmenulist:returnself.contextmenu.clear()foractinmenulist:ifact:self.contextmenu.addAction(self._actions[act])else:self.contextmenu.addSeparator()self.selitem=selitemself.contextmenu.exec_(point)### Menu action handlers#defcloneRepo(self):root=self.selitem.internalPointer().rootpath()d=clone.CloneDialog(args=[root,root+'-clone'],parent=self)d.finished.connect(d.deleteLater)d.clonedRepository.connect(self.open)d.show()defexplore(self):root=self.selitem.internalPointer().rootpath()QDesktopServices.openUrl(QUrl.fromLocalFile(root))defterminal(self):repoitem=self.selitem.internalPointer()qtlib.openshell(repoitem.rootpath(),repoitem.shortname())defaddNewRepo(self):'menu action handler for adding a new repository'caption=_('Select repository directory to add')FD=QFileDialogpath=FD.getExistingDirectory(caption=caption,options=FD.ShowDirsOnly|FD.ReadOnly)ifpath:root=paths.find_root(hglib.fromunicode(path))ifrootandnotself.tview.model().getRepoItem(root):try:self.tview.model().addRepo(self.selitem,root)excepterror.RepoError:qtlib.WarningMsgBox(_('Failed to add repository'),_('%s is not a valid repository')%path,parent=self)returndefaddSubrepo(self):'menu action handler for adding a new subrepository'root=self.selitem.internalPointer().rootpath()caption=_('Select an existing repository to add as a subrepo')FD=QFileDialogpath=hglib.fromunicode(FD.getExistingDirectory(caption=caption,directory=root,options=FD.ShowDirsOnly|FD.ReadOnly))ifpath:sroot=paths.find_root(path)ifsroot!=rootandroot==paths.find_root(os.path.dirname(path)):# The selected path is the root of a repository that is inside# the selected repository# Use forward slashes for relative subrepo root pathssrelroot=sroot[len(root)+1:]srelroot=util.pconvert(srelroot)# Is is already on the selected repository substate list?try:repo=hg.repository(ui.ui(),root)except:qtlib.WarningMsgBox(_('Cannot open repository'),_('The selected repository:<br><br>%s<br><br>''cannot be open!')%root,parent=self)returnifsrelrootinrepo['.'].substate:qtlib.WarningMsgBox(_('Subrepository already exists'),_('The selected repository:<br><br>%s<br><br>''is already a subrepository of:<br><br>%s<br><br>''as: "%s"')%(sroot,root,srelroot),parent=self)returnelse:# Already a subrepo!# Read the current .hgsub file contentslines=[]ifos.path.exists(repo.wjoin('.hgsub')):try:fsub=repo.wopener('.hgsub','r')lines=fsub.readlines()fsub.close()except:qtlib.WarningMsgBox(_('Failed to add repository'),_('Cannot open the .hgsub file in:<br><br>%s') \
%root,parent=self)# Make sure that the selected subrepo (or one of its# subrepos!) is not already on the .hgsub filelinesep=''forlineinlines:spath=line.split("=")[0].strip()ifnotspath:continueifnotlinesep:linesep=hglib.getLineSeparator(line)spath=util.pconvert(spath)ifline.startswith(srelroot):qtlib.WarningMsgBox(_('Failed to add repository'),_('The .hgsub file already contains the ''line:<br><br>%s')%line,parent=self)return# Append the new subrepo to the end of the .hgsub filelines.append('%s = %s'%(srelroot,srelroot))lines=[line.strip(linesep)forlineinlines]# and update the .hgsub filetry:fsub=repo.wopener('.hgsub','w')fsub.write(linesep.join(lines))fsub.close()qtlib.InfoMsgBox(_('Subrepo added to .hgsub file'),_('The selected subrepo:<br><br><i>%s</i><br><br>''has been added to the .hgsub file.<br><br>''Remember that in order to finish adding the ''subrepo<br><i>you must still commit</i> the ''.hgsub file changes.') \
%root,parent=self)except:qtlib.WarningMsgBox(_('Failed to add repository'),_('Cannot update the .hgsub file in:<br><br>%s') \
%root,parent=self)returnqtlib.WarningMsgBox(_('Failed to add repository'),_('"%s" is not a valid repository inside "%s"')% \
(path,root),parent=self)returndefstartSettings(self):root=self.selitem.internalPointer().rootpath()sd=settings.SettingsDialog(configrepo=True,focus='web.name',parent=self,root=root)sd.finished.connect(sd.deleteLater)sd.exec_()defopenAll(self):forrootinself.selitem.internalPointer().childRoots():self.openRepo.emit(hglib.tounicode(root),False)defopen(self,root=None):'open context menu action, open repowidget unconditionally'ifnotroot:root=self.selitem.internalPointer().rootpath()repotype=self.selitem.internalPointer().repotype()else:root=hglib.fromunicode(root)ifos.path.exists(os.path.join(root,'.hg')):repotype='hg'else:repotype='unknown'ifrepotype=='hg':self.openRepo.emit(hglib.tounicode(root),False)else:qtlib.WarningMsgBox(_('Unsupported repository type (%s)')%repotype,_('Cannot open non mercurial repositories or subrepositories'),parent=self)defcopyPath(self):clip=QApplication.clipboard()clip.setText(self.selitem.internalPointer().rootpath())defstartRename(self):self.tview.edit(self.selitem)defnewGroup(self):self.tview.model().addGroup(_('New Group'))defremoveSelected(self):s=self.selitemitem=s.internalPointer()ifnotitem.okToDelete():labels=[(QMessageBox.Yes,_('&Delete')),(QMessageBox.No,_('Cancel'))]ifnotqtlib.QuestionMsgBox(_('Confirm Delete'),_("Delete Group '%s' and all its entries?")%item.name,labels=labels,parent=self):returnm=self.tview.model()row=s.row()parent=s.parent()m.removeRows(row,1,parent)self.tview.selectionChanged(None,None)self.updateSettingsFile()@pyqtSlot(QString,QString)defshortNameChanged(self,uroot,uname):it=self.tview.model().getRepoItem(hglib.fromunicode(uroot))ifit:it.setShortName(uname)self.tview.model().layoutChanged.emit()@pyqtSlot(QString,object)defbaseNodeChanged(self,uroot,basenode):it=self.tview.model().getRepoItem(hglib.fromunicode(uroot))ifit:it.setBaseNode(basenode)@pyqtSlot(QString)defrepoChanged(self,uroot):m=self.tview.model()changedrootpath=hglib.fromunicode(QDir.fromNativeSeparators(uroot))defisAboveOrBelowUroot(testedpath):"""Return True if rootpath is contained or contains uroot"""r1=hglib.fromunicode(QDir.fromNativeSeparators(testedpath))+"/"r2=changedrootpath+"/"returnr1.startswith(r2)orr2.startswith(r1)m.loadSubrepos(m.rootItem,isAboveOrBelowUroot)
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.