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.
# thgpbranch.py - embeddable widget for the PatchBranch extension## Copyright 2009 Peer Sommerlund <peer.sommerlund@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.importgtkimportgobjectfrommercurialimportcmdutil,extensionsfromtortoisehg.util.i18nimport_fromtortoisehg.hgtkimporthgcmdfromtortoisehg.hgtkimportupdatefromtortoisehg.hgtkimportgtklib,dialogfromtortoisehg.hgtk.logviewimportgraphcell# Patch Branch model enumerationM_NODE=0M_IN_LINES=1M_OUT_LINES=2M_NAME=3M_STATUS=4M_TITLE=5M_MSG=6M_MSGESC=7# Patch Branch column enumerationC_GRAPH=0C_STATUS=1C_NAME=2C_TITLE=3C_MSG=4classPBranchWidget(gtk.VBox):__gproperties__={'graph-column-visible':(gobject.TYPE_BOOLEAN,'Graph','Show graph column',False,gobject.PARAM_READWRITE),'status-column-visible':(gobject.TYPE_BOOLEAN,'Status','Show status column',False,gobject.PARAM_READWRITE),'name-column-visible':(gobject.TYPE_BOOLEAN,'Name','Show name column',False,gobject.PARAM_READWRITE),'title-column-visible':(gobject.TYPE_BOOLEAN,'Title','Show title column',False,gobject.PARAM_READWRITE),'message-column-visible':(gobject.TYPE_BOOLEAN,'Title','Show title column',False,gobject.PARAM_READWRITE),'show-internal-branches':(gobject.TYPE_BOOLEAN,'ShowInternalBranches',"Show internal branches",False,gobject.PARAM_READWRITE)}__gsignals__={'repo-invalidated':(gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,()),}def__init__(self,parentwin,repo,statusbar,accelgroup=None,tooltips=None):gtk.VBox.__init__(self)self.parent_window=parentwinself.repo=repoself.pbranch=extensions.find('pbranch')self.statusbar=statusbar# top toolbartbar=gtklib.SlimToolbar(tooltips)## buttonsself.btn={}pmergebtn=tbar.append_stock(gtk.STOCK_CONVERT,_('Merge all pending dependencies'))pmergebtn.connect('clicked',self.pmerge_clicked)self.btn['pmerge']=pmergebtnpbackoutbtn=tbar.append_stock(gtk.STOCK_GO_BACK,_('Backout current patch branch'))pbackoutbtn.connect('clicked',self.pbackout_clicked)self.btn['pbackout']=pbackoutbtnreapplybtn=gtk.ToolButton(gtk.STOCK_GO_FORWARD)reapplybtn=tbar.append_stock(gtk.STOCK_GO_FORWARD,_('Backport part of a changeset to a dependency'))reapplybtn.connect('clicked',self.reapply_clicked)self.btn['reapply']=reapplybtnpnewbtn=tbar.append_stock(gtk.STOCK_NEW,_('Start a new patch branch'))pnewbtn.connect('clicked',self.pnew_clicked)self.btn['pnew']=pnewbtn## separatortbar.append_space()## drop-down menumenubtn=gtk.MenuToolButton('')menubtn.set_menu(self.create_view_menu())tbar.append_widget(menubtn,padding=0)self.btn['menu']=menubtndefafter_init():menubtn.child.get_children()[0].hide()gtklib.idle_add_single_call(after_init)self.pack_start(tbar,False,False)# center panemainbox=gtk.VBox()self.pack_start(mainbox,True,True)## scrolled panepane=gtk.ScrolledWindow()pane.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)pane.set_shadow_type(gtk.SHADOW_IN)mainbox.pack_start(pane)### patch list#### patch list modelself.model=gtk.ListStore(gobject.TYPE_PYOBJECT,# node infogobject.TYPE_PYOBJECT,# in-linesgobject.TYPE_PYOBJECT,# out-linesstr,# patch namestr,# patch statusstr,# patch titlestr,# patch messagestr)# patch message escaped#### patch list viewself.list=gtk.TreeView(self.model)# To support old PyGTK (<2.12)ifhasattr(self.list,'set_tooltip_column'):self.list.set_tooltip_column(M_MSGESC)self.list.connect('button-press-event',self.list_pressed)self.list.connect('row-activated',self.list_row_activated)self.list.connect('size-allocate',self.list_size_allocated)#### patch list columnsself.cols={}self.cells={}defaddcol(header,col_idx,model_idx=None,right=False,resizable=False,editable=False,editfunc=None,cell_renderer=None,properties=[]):header=(rightand'%s 'or' %s')%headercell=cell_rendererorgtk.CellRendererText()ifeditfunc:cell.set_property('editable',editable)cell.connect('edited',editfunc)col=gtk.TreeViewColumn(header,cell)ifcell_rendererisNone:col.add_attribute(cell,'text',model_idx)col.set_resizable(resizable)col.set_visible(self.get_property(self.col_to_prop(col_idx)))ifright:col.set_alignment(1)cell.set_property('xalign',1)for(property_name,model_index)inproperties:col.add_attribute(cell,property_name,model_index)self.list.append_column(col)self.cols[col_idx]=colself.cells[col_idx]=celldefcell_edited(cell,path,newname):row=self.model[path]patchname=row[M_NAME]ifnewname!=patchname:self.qrename(newname,patch=patchname)#### patch list columns and cell renderersaddcol(_('Graph'),C_GRAPH,resizable=True,cell_renderer=graphcell.CellRendererGraph(),properties=[("node",M_NODE),("in-lines",M_IN_LINES),("out-lines",M_OUT_LINES)])addcol(_('St'),C_STATUS,M_STATUS)addcol(_('Name'),C_NAME,M_NAME,editfunc=cell_edited)addcol(_('Title'),C_TITLE,M_TITLE)addcol(_('Message'),C_MSG,M_MSG)pane.add(self.list)## command widgetself.cmd=hgcmd.CmdWidget(style=hgcmd.STYLE_COMPACT,tooltips=tooltips)mainbox.pack_start(self.cmd,False,False)# acceleratorifaccelgroup:# TODOpass### public functions ###defrefresh(self):""" Refresh the list of patches. This operation will try to keep selection state. """ifnotself.pbranch:return# store selected patch nameselname=Nonemodel,paths=self.list.get_selection().get_selected_rows()iflen(paths)>0:selname=model[paths[0]][M_NAME]# compute model dataself.model.clear()opts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)graph=mgr.graphforopts(opts)ifnotself.get_property('show-internal-branches'):graph=mgr.patchonlygraph(graph)names=Nonepatch_list=graph.topolist(names)in_lines=[]ifpatch_list:dep_list=[patch_list[0]]cur_branch=self.repo['.'].branch()patch_status={}fornameinpatch_list:patch_status[name]=self.pstatus(name)fornameinpatch_list:parents=graph.deps(name)# Node propertiesifnameindep_list:node_column=dep_list.index(name)else:node_column=len(dep_list)node_colour=patch_status[name]and'#ff0000'or0node_status=(name==cur_branch)and4or0node=(node_column,node_colour,node_status)# Find next dependency listmy_deps=[]forpinparents:ifpnotindep_list:my_deps.append(p)next_dep_list=dep_list[:]next_dep_list[node_column:node_column+1]=my_deps# Dependency linesshift=len(parents)-1out_lines=[]forpinparents:dep_column=next_dep_list.index(p)colour=0# blackifpatch_status[p]:colour='#ff0000'# redstyle=0# solid linesout_lines.append((node_column,dep_column,colour,style))forlinesinin_lines:(start_column,end_column,colour,style)=linesifend_column==node_column:# Deps to current patch end herepasselse:# Find line continuationsdep=dep_list[end_column]dep_column=next_dep_list.index(dep)out_lines.append((end_column,dep_column,colour,style))stat=patch_status[name]and'M'or'C'# patch statuspatchname=namemsg=self.pmessage(name)# summaryifmsg:msg_esc=gtklib.markup_escape_text(msg)# escaped summary (utf-8)title=msg.split('\n')[0]else:msg_esc=Nonetitle=Noneself.model.append((node,in_lines,out_lines,patchname,stat,title,msg,msg_esc))# Loopin_lines=out_linesdep_list=next_dep_list# restore patch selectionifselname:iter=self.get_iter_by_patchname(selname)ifiter:self.list.get_selection().select_iter(iter)# update UI sensitivesself.update_sensitives()# report statusstatus_text=''idle_text=Noneifself.has_patch():status_text=self.pending_merges() \
and_('pending pmerges') \
or_('no pending pmerges')self.statusbar.set_right3_text(status_text)self.statusbar.set_idle_text(idle_text)defpgraph(self):""" [pbranch] Execute 'pgraph' command. :returns: A list of patches and dependencies """ifself.pbranchisNone:returnNoneopts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)returnmgr.graphforopts(opts)defpatch_list(self,opts={}):"""List all patches in pbranch dependency DAG"""mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)graph=mgr.graphforopts(opts)names=Nonereturngraph.topolist(names)defpending_merges(self):"""Return True if there are pending pmerge operations"""forpatchinself.patch_list():ifself.pstatus(patch):returnTruereturnFalsedefpstatus(self,patch_name):""" [pbranch] Execute 'pstatus' command. :param patch_name: Name of patch-branch :retv: list of status messages. If empty there is no pending merges """ifself.pbranchisNone:returnNonestatus=[]opts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)graph=mgr.graphforopts(opts)heads=self.repo.branchheads(patch_name)iflen(heads)>1:status.append(_('needs merge of %i heads\n')%len(heads))fordep,throughingraph.pendingmerges(patch_name):ifthrough:status.append(_('needs merge with %s (through %s)\n')%(dep,", ".join(through)))else:status.append(_('needs merge with %s\n')%dep)fordepingraph.pendingrebases(patch_name):status.append(_('needs update of diff base to tip of %s\n')%dep)returnstatusdefpmessage(self,patch_name):""" Get patch message :param patch_name: Name of patch-branch
:retv: Full patch message. If you extract the first line
- you will get the patch title.
+ you will get the patch title. If the repo does not contain+ message or patch, the function returns None """
opts = {}
mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- return mgr.patchdesc(patch_name)
+ try:+ return mgr.patchdesc(patch_name)
+ except:+ return None def peditmessage(self, patch_name):
"""
Edit patch message :param patch_name: Name of patch-branch """ifnotpatch_nameinself.patch_list():returncmdline=['hg','peditmessage',patch_name]self.cmd.execute(cmdline,self.cmd_done)defpnew_ui(self):""" Create new patch. Propmt user for new patch name. Patch is created on current branch. """parent=Nonetitle=_('New Patch Name')new_name=dialog.entry_dialog(parent,title)ifnotnew_name:returnFalseself.pnew(new_name)returnTruedefpnew(self,patch_name):""" [pbranch] Execute 'pnew' command. :param patch_name: Name of new patch-branch """ifself.pbranchisNone:returnFalseself.pbranch.cmdnew(self.repo.ui,self.repo,patch_name)self.emit('repo-invalidated')returnTruedefpmerge(self,patch_name=None):""" [pbranch] Execute 'pmerge' command. :param patch_name: Merge to this patch-branch """ifnotself.has_patch():returncmdline=['hg','pmerge']ifpatch_name:cmdline+=[patch_name]else:cmdline+=['--all']self.cmd.execute(cmdline,self.cmd_done)defpbackout(self):""" [pbranch] Execute 'pbackout' command. """assertFalsedefhas_pbranch(self):""" return True if pbranch extension can be used """returnself.pbranchisnotNonedefhas_patch(self):""" return True if pbranch extension is in use on repo """returnself.has_pbranch()andself.pgraph()!=[]defcur_patch(self):current_patch=self.repo.dirstate.branch()ifcurrent_patch=='default':returnNoneifcurrent_patchnotinself.patch_list():returnNonereturncurrent_patch### internal functions ###defget_iter_by_patchname(self,name):""" return iter has specified patch name """ifname:forrowinself.model:ifrow[M_NAME]==name:returnrow.iterreturnNonedefget_path_by_patchname(self,name):""" return path has specified patch name """returnself.model.get_path(self.get_iter_by_patchname(name))defupdate_sensitives(self):""" Update the sensitives of entire UI """defdisable_pbranchcmd():fornamein('pbackout','pmerge','pnew','reapply'):self.btn[name].set_sensitive(False)ifself.pbranch:self.list.set_sensitive(True)self.btn['menu'].set_sensitive(True)in_pbranch=True#TODOself.btn['pmerge'].set_sensitive(in_pbranch)self.btn['pbackout'].set_sensitive(in_pbranch)self.btn['pnew'].set_sensitive(True)self.btn['reapply'].set_sensitive(True)else:self.list.set_sensitive(False)self.btn['menu'].set_sensitive(False)disable_pbranchcmd()defscroll_to_current(self):""" Scroll to current patch in the patch list. If the patch is selected, it will do nothing. """ifself.list.get_selection().count_selected_rows()>0:returncurpatch=self.cur_patch()ifnotcurpatch:returnpath=self.get_path_by_patchname(curpatch)ifpath:self.list.scroll_to_cell(path)defshow_patch_cmenu(self,list,path):"""Context menu for selected patch"""row=self.model[path]menu=gtk.Menu()defappend(label,handler=None):item=gtk.MenuItem(label,True)item.set_border_width(1)ifhandler:item.connect('activate',handler,row)menu.append(item)has_pbranch=self.has_pbranch()is_current=self.has_patch()andself.cur_patch()==row[M_NAME]is_patch=row[M_NAME]!='default'ifhas_pbranch:append(_('_new'),self.pnew_activated)ifnotis_current:append(_('_goto (update workdir)'),self.goto_activated)ifis_patch:append(_('_edit message'),self.edit_message_activated)append(_('_rename'),self.rename_activated)append(_('_delete'),self.delete_activated)iflen(menu.get_children())>0:menu.show_all()menu.popup(None,None,None,0,0)defcreate_view_menu(self):"""Top right menu for selection of columns and view configuration."""menu=gtk.Menu()defappend(item=None,handler=None,check=False,active=False,sep=False):ifsep:item=gtk.SeparatorMenuItem()else:ifisinstance(item,str):ifcheck:item=gtk.CheckMenuItem(item)item.set_active(active)else:item=gtk.MenuItem(item)item.set_border_width(1)ifhandler:item.connect('activate',handler)menu.append(item)returnitemdefcolappend(label,col_idx,active=True):defhandler(menuitem):col=self.cols[col_idx]col.set_visible(menuitem.get_active())propname=self.col_to_prop(col_idx)item=append(label,handler,check=True,active=active)self.vmenu[propname]=itemself.vmenu={}colappend(_('Show graph'),C_GRAPH)colappend(_('Show status'),C_STATUS,active=False)colappend(_('Show name'),C_NAME)colappend(_('Show title'),C_TITLE,active=False)colappend(_('Show message'),C_MSG,active=False)append(sep=True)defenable_editable(item):self.cells[C_NAME].set_property('editable',item.get_active())item=append(_('Enable editable cells'),enable_editable,check=True,active=False)self.vmenu['editable-cell']=itemitem=append(_("Show internal branches"),lambdaitem:self.refresh(),check=True,active=False)self.vmenu['show-internal-branches']=itemmenu.show_all()returnmenudefshow_dialog(self,dlg):"""Show modal dialog and block application See also show_dialog in history.py """dlg.set_transient_for(self.parent_window)dlg.show_all()dlg.run()ifgtk.pygtk_version<(2,12,0):# Workaround for old PyGTK (< 2.12.0) issue.# See background of this: f668034aeda3dlg.set_transient_for(None)defupdate_by_row(self,row):branch=row[M_NAME]rev=cmdutil.revrange(self.repo,[branch])parents=[x.node()forxinself.repo.parents()]dialog=update.UpdateDialog(rev[0])self.show_dialog(dialog)self.update_completed(parents)defupdate_completed(self,oldparents):self.repo.invalidate()self.repo.dirstate.invalidate()newparents=[x.node()forxinself.repo.parents()]ifnotoldparents==newparents:self.emit('repo-invalidated')defcmd_done(self,returncode,useraborted,noemit=False):ifreturncode==0:ifself.cmd.get_pbar():self.cmd.set_result(_('Succeed'),style='ok')elifuseraborted:self.cmd.set_result(_('Canceled'),style='error')else:self.cmd.set_result(_('Failed'),style='error')self.refresh()ifnotnoemit:self.emit('repo-invalidated')defdo_get_property(self,property):try:returnself.vmenu[property.name].get_active()except:raiseAttributeError,'unknown property %s'%property.namedefdo_set_property(self,property,value):try:self.vmenu[property.name].set_active(value)except:raiseAttributeError,'unknown property %s'%property.namedefcol_to_prop(self,col_idx):ifcol_idx==C_GRAPH:return'graph-column-visible'ifcol_idx==C_STATUS:return'status-column-visible'ifcol_idx==C_NAME:return'name-column-visible'ifcol_idx==C_TITLE:return'title-column-visible'ifcol_idx==C_MSG:return'message-column-visible'return''### signal handlers ###deflist_pressed(self,list,event):x,y=int(event.x),int(event.y)pathinfo=list.get_path_at_pos(x,y)ifevent.button==1:ifnotpathinfo:# HACK: clear selection after this function calling,# against selection by getting focusdefunselect():selection=list.get_selection()selection.unselect_all()gtklib.idle_add_single_call(unselect)elifevent.button==3:ifpathinfo:self.show_patch_cmenu(self.list,pathinfo[0])deflist_sel_changed(self,list):path,focus=list.get_cursor()row=self.model[path]patchname=row[M_NAME]try:ctx=self.repo[patchname]revid=ctx.rev()excepthglib.RepoError:revid=-1self.emit('patch-selected',revid,patchname)deflist_row_activated(self,list,path,column):self.update_by_row(self.model[path])deflist_size_allocated(self,list,req):ifself.has_patch():self.scroll_to_current()defpbackout_clicked(self,toolbutton):passdefpmerge_clicked(self,toolbutton):self.pmerge()defpnew_clicked(self,toolbutton):self.pnew_ui()defreapply_clicked(self,toolbutton):pass### context menu signal handlers ###defpnew_activated(self,menuitem,row):"""Insert new patch after this row"""ifself.cur_patch()==row[M_NAME]:self.pnew_ui()return# pnew from patch different than currentassertFalseifself.wdir_modified():# Ask user if current changes should be discarded# Abort if user does not agreepass# remember prev branch# Update to row[M_NAME]# pnew_ui# if aborted, update back to prev branchpassdefedit_message_activated(self,menuitem,row):self.peditmessage(row[M_NAME])defgoto_activated(self,menuitem,row):self.update_by_row(row)defdelete_activated(self,menuitem,row):assertFalsedefrename_activated(self,menuitem,row):assertFalse
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.