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.
# status.py - status dialog for TortoiseHg## Copyright 2007 Brad Schick, brad at gmail . com# Copyright 2007 TK Soh <teekaysoh@gmail.com># Copyright 2008 Steve Borho <steve@borho.org># Copyright 2008 Emmanuel Rosa <goaway1000@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.importosimportcStringIOimportgtkimportgobjectimportpangoimportthreadingfrommercurialimportcmdutil,util,commands,patch,mdiff,error,hgfrommercurialimportmergeasmerge_fromtortoisehg.util.i18nimport_fromtortoisehg.utilimporthglib,shlib,paths,hgshelvefromtortoisehg.hgtkimportdialog,gdialog,gtklib,guess,hgignore,statusbar# file model row enumerationsFM_CHECKED=0FM_STATUS=1FM_PATH_UTF8=2FM_PATH=3FM_MERGE_STATUS=4FM_PARTIAL_SELECTED=5# diffmodel row enumerationsDM_REJECTED=0DM_DISP_TEXT=1DM_IS_HEADER=2DM_PATH=3DM_CHUNK_ID=4DM_FONT=5defhunk_markup(text):'Format a diff hunk for display in a TreeView row with markup'hunk=''# don't use splitlines, should split with only LF for the patchlines=hglib.tounicode(text).split(u'\n')forlineinlines:line=hglib.toutf(line[:512])+'\n'ifline.startswith('---')orline.startswith('+++'):hunk+=gtklib.markup(line,color=gtklib.DBLUE)elifline.startswith('-'):hunk+=gtklib.markup(line,color=gtklib.DRED)elifline.startswith('+'):hunk+=gtklib.markup(line,color=gtklib.DGREEN)elifline.startswith('@@'):hunk=gtklib.markup(line,color='#FF8000')else:hunk+=gtklib.markup(line)returnhunkdefhunk_unmarkup(text):'Format a diff hunk for display in a TreeView row without markup'hunk=''# don't use splitlines, should split with only LF for the patchlines=hglib.tounicode(text).split(u'\n')forlineinlines:hunk+=gtklib.markup(hglib.toutf(line[:512]))+'\n'returnhunkclassGStatus(gdialog.GWindow):"""GTK+ based dialog for displaying repository status Also provides related operations like add, delete, remove, revert, refresh, ignore, diff, and edit. The following methods are meant to be overridden by subclasses. At this point GCommit is really the only intended subclass. auto_check(self) """### Following methods are meant to be overridden by subclasses ###definit(self):gdialog.GWindow.init(self)self.mode='status' self.ready = False
self.filerowstart = {}
self.filechunks = {}
+ self.status = (None,) * 7 self.status_error = None
self.preview_tab_name_label = None
self.subrepos = []
self.colorstyle=self.repo.ui.config('tortoisehg','diffcolorstyle')defauto_check(self):# Only auto-check files once, and only if a pattern was given.ifself.patsandself.opts.get('check'):forentryinself.filemodel:ifentry[FM_PATH]notinself.excludes:entry[FM_CHECKED]=Trueself.update_check_count()self.opts['check']=Falsedefget_custom_menus(self):return[]### End of overridable methods ###### Overrides of base class methods ###defparse_opts(self):# Disable refresh while we toggle checkboxesself.ready=False# Determine which files to displayifself.test_opt('all'):forcheckinself._show_checks.values():check.set_active(True)else:foroptinself.opts:ifoptinself._show_checksandself.opts[opt]:self._show_checks[opt].set_active(True)ifself.pats:forname,checkinself._show_checks.iteritems():check.set_sensitive(False)self.ready=Truedefget_title(self):root=self.get_reponame()revs=self.opts.get('rev')name=self.patsand_('filtered status')or_('status')r=revsand':'.join(revs)or''returnroot+' - '+' '.join([name,r])defget_icon(self):return'menushowchanged.ico'defget_defsize(self):returnself._setting_defsizedefget_tbbuttons(self):tbuttons=[]ifself.count_revs()==2:tbuttons+=[self.make_toolbutton(gtk.STOCK_SAVE_AS,_('Save As'),self.save_clicked,tip=_('Save selected changes'))]else:tbuttons+=[self.make_toolbutton(gtk.STOCK_JUSTIFY_FILL,_('_Diff'),self.diff_clicked,name='diff',tip=_('Visual diff checked files')),self.make_toolbutton(gtk.STOCK_MEDIA_REWIND,_('Re_vert'),self.revert_clicked,name='revert',tip=_('Revert checked files')),self.make_toolbutton(gtk.STOCK_ADD,_('_Add'),self.add_clicked,name='add',tip=_('Add checked files')),self.make_toolbutton(gtk.STOCK_JUMP_TO,_('Move'),self.move_clicked,name='move',tip=_('Move checked files to other directory')),self.make_toolbutton(gtk.STOCK_DELETE,_('_Remove'),self.remove_clicked,name='remove',tip=_('Remove or delete checked files')),self.make_toolbutton(gtk.STOCK_CLEAR,_('_Forget'),self.forget_clicked,name='forget',tip=_('Forget checked files on next commit')),gtk.SeparatorToolItem(),self.make_toolbutton(gtk.STOCK_REFRESH,_('Re_fresh'),self.refresh_clicked,tip=_('refresh')),gtk.SeparatorToolItem()]returntbuttonsdefsave_settings(self):settings=gdialog.GWindow.save_settings(self)settings['gstatus-hpane']=self.diffpane.get_position()settings['gstatus-lastpos']=self.setting_lastpossettings['gstatus-type-expander']=self.types_expander.get_expanded()returnsettingsdefload_settings(self,settings):gdialog.GWindow.load_settings(self,settings)self.setting_pos=270self.setting_lastpos=64000self.setting_types_expanded=Falsetry:self.setting_pos=settings['gstatus-hpane']self.setting_lastpos=settings['gstatus-lastpos']self.setting_types_expanded=settings['gstatus-type-expander']exceptKeyError:passself.mqmode,repo=None,self.repoifhasattr(repo,'mq')andrepo.mq.appliedandrepo['.']==repo['qtip']:self.mqmode=Truedefis_merge(self):returnself.count_revs()<2andlen(self.repo.parents())==2defget_accelgroup(self):accelgroup=gtk.AccelGroup()mod=gtklib.get_thg_modifier()gtklib.add_accelerator(self.filetree,'thg-diff',accelgroup,mod+'d')self.filetree.connect('thg-diff',self.thgdiff)self.connect('thg-refresh',self.thgrefresh)# set CTRL-c accelerator for copy-clipboardgtklib.add_accelerator(self.difftree,'copy-clipboard',accelgroup,mod+'c')self.difftree.connect('copy-clipboard',self.copy_to_clipboard)defscroll_diff_notebook(widget,direction=gtk.SCROLL_PAGE_DOWN):page_num=self.diff_notebook.get_current_page()page=self.diff_notebook.get_nth_page(page_num)page.emit("scroll-child",direction,False)deftoggle_filetree_selection(*arguments):self.sel_clicked(notself.selcb.get_active())defnext_diff_notebook_page(*arguments):notebook=self.diff_notebookifnotebook.get_current_page()>=len(notebook)-1:notebook.set_current_page(0)else:notebook.next_page()defprevious_diff_notebook_page(*arguments):notebook=self.diff_notebookifnotebook.get_current_page()<=0:notebook.set_current_page(len(notebook)-1)else:notebook.prev_page()# signal, accelerator key, handler, (parameters)status_accelerators=[('status-scroll-down','bracketright',scroll_diff_notebook,(gtk.SCROLL_PAGE_DOWN,)),('status-scroll-up','bracketleft',scroll_diff_notebook,(gtk.SCROLL_PAGE_UP,)),('status-next-file','period',gtklib.move_treeview_selection,(self.filetree,1)),('status-previous-file','comma',gtklib.move_treeview_selection,(self.filetree,-1)),('status-select-all','u',toggle_filetree_selection,()),('status-next-page','p',next_diff_notebook_page,()),('status-previous-page','<Shift>p',previous_diff_notebook_page,()),]forsignal,accelerator,handler,parametersinstatus_accelerators:gtklib.add_accelerator(self,signal,accelgroup,mod+accelerator)self.connect(signal,handler,*parameters)returnaccelgroupdefget_body(self):is_merge=self.is_merge()# model stores the file list.fm=gtk.ListStore(bool,str,str,str,str,bool)fm.set_sort_func(1001,self.sort_by_stat)fm.set_default_sort_func(self.sort_by_stat)self.filemodel=fmself.filetree=gtk.TreeView(self.filemodel)self.filetree.connect('popup-menu',self.tree_popup_menu)self.filetree.connect('button-press-event',self.tree_button_press)self.filetree.connect('button-release-event',self.tree_button_release)self.filetree.connect('row-activated',self.tree_row_act)self.filetree.connect('key-press-event',self.tree_key_press)self.filetree.set_reorderable(False)self.filetree.set_enable_search(True)self.filetree.set_search_equal_func(self.search_filelist)ifhasattr(self.filetree,'set_rubber_banding'):self.filetree.set_rubber_banding(True)self.filetree.modify_font(self.fonts['list'])self.filetree.set_headers_clickable(True)toggle_cell=gtk.CellRendererToggle()toggle_cell.connect('toggled',self.select_toggle)toggle_cell.set_property('activatable',True)path_cell=gtk.CellRendererText()stat_cell=gtk.CellRendererText()# file selection checkboxescol0=gtk.TreeViewColumn('',toggle_cell)col0.set_visible(notis_merge)# hide when mergingcol0.add_attribute(toggle_cell,'active',FM_CHECKED)col0.add_attribute(toggle_cell,'radio',FM_PARTIAL_SELECTED)col0.set_resizable(False)self.filetree.append_column(col0)self.selcb=self.add_header_checkbox(col0,self.sel_clicked)self.file_sel_column=col0col1=gtk.TreeViewColumn(_('st'),stat_cell)col1.add_attribute(stat_cell,'text',FM_STATUS)col1.set_cell_data_func(stat_cell,self.text_color)col1.set_sort_column_id(1001)col1.set_resizable(False)self.filetree.append_column(col1)# merge status columncol=gtk.TreeViewColumn(_('ms'),stat_cell)col.set_visible(self.count_revs()<=1)col.add_attribute(stat_cell,'text',FM_MERGE_STATUS)col.set_sort_column_id(4)col.set_resizable(False)self.filetree.append_column(col)self.merge_state_column=colcol2=gtk.TreeViewColumn(_('path'),path_cell)col2.add_attribute(path_cell,'text',FM_PATH_UTF8)col2.set_cell_data_func(path_cell,self.text_color)col2.set_sort_column_id(2)col2.set_resizable(True)self.filetree.append_column(col2)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)scroller.add(self.filetree)# Status Types expander# We don't assign an expander child. We instead monitor the# expanded property and do the hiding ourselvesexpander=gtk.Expander(_('View'))self.types_expander=expanderexpander.connect("notify::expanded",self.types_expander_expanded)exp_labelbox=gtk.HBox()exp_labelbox.pack_start(expander,False,False)exp_labelbox.pack_start(gtk.Label(),True,True)self.counter=gtk.Label('')exp_labelbox.pack_end(self.counter,False,False,2)self.status_types=self.get_status_types()ifself.setting_types_expanded:expander.set_expanded(True)self.status_types.show()else:self.status_types.hide()expander_box=gtk.VBox()expander_box.pack_start(exp_labelbox)expander_box.pack_start(self.status_types)tvbox=gtk.VBox()tvbox.pack_start(scroller,True,True,0)tvbox.pack_start(gtk.HSeparator(),False,False)tvbox.pack_start(expander_box,False,False)ifself.pats:button=gtk.Button(_('Remove filter, show root'))button.connect('pressed',self.remove_filter)tvbox.pack_start(button,False,False,2)tree_frame=gtk.Frame()tree_frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)tree_frame.add(tvbox)diff_frame=gtk.Frame()diff_frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)self.diff_notebook=gtk.Notebook()self.diff_notebook.set_tab_pos(gtk.POS_BOTTOM)self.diff_notebook_pages={}self.difffont=self.fonts['diff']self.clipboard=Noneself.diff_text=gtk.TextView()self.diff_text.set_wrap_mode(gtk.WRAP_NONE)self.diff_text.set_editable(False)self.diff_text.modify_font(self.difffont)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)scroller.add(self.diff_text)self.append_page('text-diff',scroller,gtk.Label(_('Text Diff')))# use treeview to show selectable diff hunksself.clipboard=gtk.Clipboard()self.diffmodel=gtk.ListStore(bool,# DM_REJECTEDstr,# DM_DISP_TEXTbool,# DM_IS_HEADERstr,# DM_PATHint,# DM_CHUNK_IDpango.FontDescription)difftree=gtk.TreeView(self.diffmodel)difftree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)difftree.set_headers_visible(False)difftree.set_enable_search(False)ifgetattr(difftree,'enable-grid-lines',None)isnotNone:difftree.set_property('enable-grid-lines',True)difftree.connect('row-activated',self.diff_tree_row_act)self.difftree=difftreecell=gtk.CellRendererText()diffcol=gtk.TreeViewColumn('diff',cell)diffcol.set_resizable(True)diffcol.add_attribute(cell,'markup',DM_DISP_TEXT)# differentiate header chunkscell.set_property('cell-background','#DDDDDD')diffcol.add_attribute(cell,'cell_background_set',DM_IS_HEADER)self.headerfont=self.difffont.copy()self.headerfont.set_weight(pango.WEIGHT_HEAVY)# differentiate rejected hunksself.rejfont=self.difffont.copy()self.rejfont.set_weight(pango.WEIGHT_LIGHT)diffcol.add_attribute(cell,'font-desc',DM_FONT)cell.set_property('background','#EEEEEE')cell.set_property('foreground','#888888')diffcol.add_attribute(cell,'background-set',DM_REJECTED)diffcol.add_attribute(cell,'foreground-set',DM_REJECTED)difftree.append_column(diffcol)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)scroller.add(difftree)self.append_page('hunk-selection',scroller,gtk.Label(_('Hunk Selection')))# Add a page for commit previewself.preview_text=gtk.TextView()self.preview_text.set_wrap_mode(gtk.WRAP_NONE)self.preview_text.set_editable(False)self.preview_text.modify_font(self.difffont)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)scroller.add(self.preview_text)self.preview_tab_name_label=gtk.Label(self.get_preview_tab_name())self.append_page('commit-preview',scroller,self.preview_tab_name_label)diff_frame.add(self.diff_notebook)ifself.diffbottom:self.diffpane=gtk.VPaned()else:self.diffpane=gtk.HPaned()self.diffpane.pack1(tree_frame,shrink=False)self.diffpane.pack2(diff_frame,shrink=False)self.filetree.set_headers_clickable(True)sel=self.filetree.get_selection()sel.set_mode(gtk.SELECTION_MULTIPLE)self.treeselid=sel.connect('changed',self.tree_sel_changed,difftree)self.diff_notebook.connect('switch-page',self.page_switched,sel,difftree)# add keyboard acceleratorsaccelgroup=self.get_accelgroup()self.add_accel_group(accelgroup)returnself.diffpanedefappend_page(self,name,child,label):num=self.diff_notebook.append_page(child,label)self.diff_notebook_pages[num]=namedefpage_switched(self,notebook,page,page_num,filesel,difftree):self.tree_sel_changed(filesel,difftree,page_num)defget_extras(self):self.stbar=statusbar.StatusBar()returnself.stbardefadd_header_checkbox(self,col,post=None,pre=None,toggle=False):defcbclick(hdr,cb):state=cb.get_active()ifpre:pre(state)iftoggle:cb.set_active(notstate)ifpost:post(notstate)cb=gtk.CheckButton(col.get_title())cb.show()col.set_widget(cb)wgt=cb.get_parent()whilewgt:iftype(wgt)==gtk.Button:wgt.connect('clicked',cbclick,cb)returncbwgt=wgt.get_parent()returndefupdate_check_count(self):file_count=0check_count=0forrowinself.filemodel:file_count=file_count+1ifrow[FM_CHECKED]:check_count=check_count+1self.counter.set_text(_('%d selected, %d total')%(check_count,file_count))ifself.selcb:self.selcb.set_active(file_countandfile_count==check_count)ifself.count_revs()==2:returnsensitive=check_countandnotself.is_merge()forcmdin('diff','revert','add','remove','move','forget'):self.cmd_set_sensitive(cmd,sensitive)ifself.diff_notebook.get_current_page()==2:self.update_commit_preview()defprepare_display(self):val=self.repo.ui.config('tortoisehg','ciexclude','')self.excludes=[i.strip()foriinval.split(',')ifi.strip()]gtklib.idle_add_single_call(self.realize_status_settings)defrefresh_complete(self):passdefget_preview_tab_name(self):ifself.count_revs()==2:res=_('Save Preview')elifself.mqmode:res=_('Patch Preview')elifself.mode=='shelve':res=_('Shelf Preview')else:res=_('Commit Preview')returnres### End of overrides ###defset_preview_tab_name(self,name=None):ifself.preview_tab_name_label==None:returnifname==None:name=self.get_preview_tab_name()self.preview_tab_name_label.set_text(name)deftypes_expander_expanded(self,expander,dummy):ifexpander.get_expanded():self.status_types.show()else:self.status_types.hide()defget_status_types(self):# Tuple: (onmerge, ctype, translated label)allchecks=[(False,'unknown',_('?: unknown')),(True,'modified',_('M: modified')),(False,'ignored',_('I: ignored')),(True,'added',_('A: added')),(False,'clean',_('C: clean')),(True,'removed',_('R: removed')),(False,'deleted',_('!: deleted')),(True,'subrepo',_('S: subrepo'))]checks=[]nomerge=(self.count_revs()<=1)foronmerge,button,textinallchecks:ifonmergeornomerge:checks.append((button,text))table=gtk.Table(rows=2,columns=3)table.set_col_spacings(8)self._show_checks={}row,col=0,0forname,labeltextinchecks:button=gtk.CheckButton(labeltext)widget=buttonbutton.connect('toggled',self.show_toggle,name)self._show_checks[name]=buttontable.attach(widget,col,col+1,row,row+1)col+=rowrow=notrowhbox=gtk.HBox()hbox.pack_start(table,False,False)returnhboxdefrealize_status_settings(self):ifnotself.types_expander.get_expanded():self.status_types.hide()self.diffpane.set_position(self.setting_pos)self.reload_status()defremove_filter(self,button):button.hide()self.pats=[]forname,checkinself._show_checks.iteritems():check.set_sensitive(True)self.reload_status()defsearch_filelist(self,model,column,key,iter):'case insensitive filename search'key=key.lower()ifkeyinmodel.get_value(iter,FM_PATH).lower():returnFalsereturnTruedefthgdiff(self,treeview):selection=treeview.get_selection()model,tpaths=selection.get_selected_rows()files=[model[p][FM_PATH]forpintpaths]self._do_diff(files,self.opts)defthgrefresh(self,window):self.reload_status()defcopy_to_clipboard(self,treeview):'Write highlighted hunks to the clipboard'ifnottreeview.is_focus():w=self.get_focus()w.emit('copy-clipboard')returnFalsesaves={}model,tpaths=treeview.get_selection().get_selected_rows()forrow,intpaths:wfile,cid=model[row][DM_PATH],model[row][DM_CHUNK_ID]ifwfilenotinsaves:saves[wfile]=[cid]else:saves[wfile].append(cid)fp=cStringIO.StringIO()forwfileinsaves.keys():chunks=self.filechunks[wfile]chunks[0].write(fp)forcidinsaves[wfile]:ifcid!=0:chunks[cid].write(fp)fp.seek(0)self.clipboard.set_text(fp.read())defrefresh_file_tree(self):"""Clear out the existing ListStore model and reload it from the repository status. Also recheck and reselect files that remain in the list. """is_merge=self.is_merge()self.file_sel_column.set_visible(notis_merge)self.merge_state_column.set_visible(self.count_revs()<=1)selection=self.filetree.get_selection()ifselectionisNone:return(M,A,R,D,U,I,C)=self.statuschangetypes=(('M','modified',M),('A','added',A),('R','removed',R),('!','deleted',D),('?','unknown',U),('I','ignored',I),('C','clean',C))# List of the currently checked and selected files to pass on to# the new datamodel,tpaths=selection.get_selected_rows()model=self.filemodelreselect=[model[path][FM_PATH]forpathintpaths]waschecked={}forrowinmodel:waschecked[row[FM_PATH]]=row[FM_CHECKED],row[FM_PARTIAL_SELECTED]# merge-state of filesms=merge_.mergestate(self.repo)# Load the new data into the tree's modelself.filetree.hide()selection.handler_block(self.treeselid)self.filemodel.clear()types=[ctforctinchangetypesifself.opts.get(ct[1])]forstat,_,wfilesintypes:forwfileinwfiles:mst=wfileinmsandms[wfile].upper()or""lfile=util.localpath(wfile)defcheck=statin'MAR'andlfilenotinself.excludesck,p=waschecked.get(lfile,(defcheck,False))model.append([ck,stat,hglib.toutf(lfile),lfile,mst,p])ifself.test_opt('subrepo')orself.is_merge():forsdirinself.subrepos:lfile=util.localpath(sdir)defcheck=lfilenotinself.excludesck,p=waschecked.get(lfile,(defcheck,False))model.append([ck,'S',hglib.toutf(lfile),lfile,'',p])self.auto_check()# may check more files# manually refresh partially selected filesfori,rowinenumerate(model):ifrow[FM_PARTIAL_SELECTED]:self.update_hunk_model(i,self.filetree)self.diffmodel.clear()# recover selectionsfirstrow=Nonefori,rowinenumerate(model):ifrow[FM_PATH]inreselect:iffirstrowisNone:firstrow=ielse:selection.select_iter(row.iter)selection.handler_unblock(self.treeselid)iflen(model):selection.select_path((firstrowor0,))else:# clear diff pane if no filesself.diff_text.set_buffer(gtk.TextBuffer())self.preview_text.set_buffer(gtk.TextBuffer())ifnotis_merge:self.diffmodel.clear()self.filetree.show()ifself.mode=='commit':self.text.grab_focus()else:self.filetree.grab_focus()returnTruedefreload_status(self):ifnotself.ready:returnFalsedefget_repo_status():# Create a new repo objectrepo=hg.repository(self.ui,path=self.repo.root)self.newrepo=repoifself.mqmodeandself.mode!='status':# when a patch is applied, show diffs to parent of top patchqtip=repo['.']n1=qtip.parents()[0].node()n2=Noneelse:# node2 is None (the working dir) when 0 or 1 rev is specificedn1,n2=cmdutil.revpair(repo,self.opts.get('rev'))self._node1,self._node2,=n1,n2self.status_error=Nonematcher=cmdutil.match(repo,self.pats,self.opts)unknown=self.test_opt('unknown')andnotself.is_merge()clean=self.test_opt('clean')andnotself.is_merge()ignored=self.test_opt('ignored')andnotself.is_merge()try:status=repo.status(node1=n1,node2=n2,match=matcher,ignored=ignored,clean=clean,unknown=unknown)self.status=statusexcept(IOError,util.Abort),e:self.status_error=str(e)self.subrepos=[]wctx=repo[None]try:forsinwctx.substate:ifmatcher(s)andwctx.sub(s).dirty():self.subrepos.append(s)except(IOError,error.ConfigError),e:self.status_error=str(e)defstatus_wait(thread):ifthread.isAlive():returnTrueelse:ifself.status_error:self.ready=Trueself.update_check_count()self.stbar.end()self.stbar.set_text(self.status_error)returnFalseself.repo=self.newrepoself.ui=self.repo.uiself.refresh_file_tree()self.update_check_count()self.refresh_complete()self.ready=Trueself.stbar.end()returnFalseself.set_preview_tab_name()repo=self.repohglib.invalidaterepo(repo)ifhasattr(repo,'mq'):self.mqmode=repo.mq.appliedandrepo['.']==repo['qtip']self.set_title(self.get_title())self.ready=Falseself.stbar.begin()thread=threading.Thread(target=get_repo_status)thread.setDaemon(True)thread.start()gobject.timeout_add(50,status_wait,thread)returnTruedefset_file_states(self,paths,state=True):forpinpaths:self.filemodel[p][FM_CHECKED]=stateself.update_chunk_state(self.filemodel[p])self.update_check_count()defselect_toggle(self,cellrenderer,path):'User manually toggled file status via checkbox'self.filemodel[path][FM_CHECKED]=notself.filemodel[path][FM_CHECKED]self.update_chunk_state(self.filemodel[path])self.update_check_count()returnTruedefupdate_chunk_state(self,fileentry):'Update chunk toggle state to match file toggle state'fileentry[FM_PARTIAL_SELECTED]=Falsewfile=fileentry[FM_PATH]selected=fileentry[FM_CHECKED]ifwfilenotinself.filechunks:returnchunks=self.filechunks[wfile]forchunkinchunks:chunk.active=selected# this file's chunks may not be in diffmodelifwfilenotinself.filerowstart:returnrowstart=self.filerowstart[wfile]forn,chunkinenumerate(chunks):ifn==0:continueself.diffmodel[rowstart+n][DM_REJECTED]=notselectedself.update_diff_hunk(self.diffmodel[rowstart+n])self.update_diff_header(self.diffmodel,wfile,selected)defupdate_diff_hunk(self,row):'Update the contents of a diff row based on its chunk state'wfile=row[DM_PATH]chunks=self.filechunks[wfile]chunk=chunks[row[DM_CHUNK_ID]]buf=cStringIO.StringIO()chunk.pretty(buf)buf.seek(0)ifchunk.active:row[DM_REJECTED]=Falserow[DM_FONT]=self.difffontrow[DM_DISP_TEXT]=hunk_markup(buf.read())else:row[DM_REJECTED]=Truerow[DM_FONT]=self.rejfontrow[DM_DISP_TEXT]=hunk_unmarkup(buf.read())defupdate_diff_header(self,dmodel,wfile,selected):try:hc=self.filerowstart[wfile]chunks=self.filechunks[wfile]exceptIndexError:returnlasthunk=len(chunks)-1sel=lambdax:x>=lasthunkornotdmodel[hc+x+1][DM_REJECTED]newtext=chunks[0].selpretty(sel)ifnotselected:newtext="<span foreground='#888888'>"+newtext+"</span>"dmodel[hc][DM_DISP_TEXT]=newtextdefupdated_codes(self):types=[('modified','M'),('added','A'),('removed','R'),('unknown','?'),('deleted','!'),('ignored','I'),('clean','C')]codes=''try:forname,codeintypes:ifself.opts[name]:codes+=codeexceptKeyError:passself.types_expander.set_label(_("View '%s'")%codes)defshow_toggle(self,check,toggletype):self.opts[toggletype]=check.get_active()self.reload_status()self.updated_codes()returnTruedefsort_by_stat(self,model,iter1,iter2):order='MAR!?SIC'lhs,rhs=(model.get_value(iter1,FM_STATUS),model.get_value(iter2,FM_STATUS))# GTK+ bug that calls sort before a full row is inserted causing# values to be None. When this happens, just return any value# since the call is irrelevant and will be followed by another# with the correct (non-None) valueifNonein(lhs,rhs):return0result=order.find(lhs)-order.find(rhs)returnmin(max(result,-1),1)deftext_color(self,column,text_renderer,model,row_iter):stat=model[row_iter][FM_STATUS]ifstat=='M':text_renderer.set_property('foreground',gtklib.DBLUE)elifstat=='A':text_renderer.set_property('foreground',gtklib.DGREEN)elifstat=='R':text_renderer.set_property('foreground',gtklib.DRED)elifstat=='C':text_renderer.set_property('foreground','black')elifstat=='!':text_renderer.set_property('foreground','red')elifstat=='?':text_renderer.set_property('foreground','#AA5000')elifstat=='I':text_renderer.set_property('foreground','black')else:text_renderer.set_property('foreground','black')defrename_file(self,wfile):fdir,fname=os.path.split(wfile)utf_fname=hglib.toutf(fname)newfile=dialog.entry_dialog(self,_('Rename file to:'),True,utf_fname)ifnewfileandnewfile!=utf_fname:self.hg_move([wfile,os.path.join(fdir,hglib.fromutf(newfile))])returnTruedefcopy_file(self,wfile):wfile=self.repo.wjoin(wfile)fdir,fname=os.path.split(wfile)result=gtklib.NativeSaveFileDialogWrapper(title=_('Copy file to'),initial=fdir,filename=fname).run()ifnotresult:returnifresult!=wfile:self.hg_copy([wfile,result])returnTruedefhg_remove(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]ifself.count_revs()>1:gdialog.Prompt(_('Nothing Removed'),_('Remove is not enabled when multiple revisions are specified.'),self).run()return# Create new opts, so nothing unintented gets throughremoveopts=self.merge_opts(commands.table['^remove|rm'][1],('include','exclude'))defdohgremove():commands.remove(self.ui,self.repo,*wfiles,**removeopts)success,outtext=self._hg_call_wrapper('Remove',dohgremove)ifsuccess:self.reload_status()defhg_move(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]ifself.count_revs()>1:gdialog.Prompt(_('Nothing Moved'),_('Move is not enabled when ''multiple revisions are specified.'),self).run()return# Create new opts, so nothing unintented gets throughmoveopts=self.merge_opts(commands.table['rename|mv'][1],('include','exclude'))defdohgmove():#moveopts['force'] = Truecommands.rename(self.ui,self.repo,*wfiles,**moveopts)success,outtext=self._hg_call_wrapper('Move',dohgmove)ifsuccess:self.reload_status()defhg_copy(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]ifself.count_revs()>1:gdialog.Prompt(_('Nothing Copied'),_('Copy is not enabled when ''multiple revisions are specified.'),self).run()return# Create new opts, so nothing unintented gets throughcmdopts=self.merge_opts(commands.table['copy|cp'][1],('include','exclude'))defdohgcopy():commands.copy(self.ui,self.repo,*wfiles,**cmdopts)success,outtext=self._hg_call_wrapper('Copy',dohgcopy)ifsuccess:self.reload_status()deftree_sel_changed(self,selection,tree,page_num=None):'Selection changed in file tree'# page_num may be supplied, if called from switch-page eventmodel,paths=selection.get_selected_rows()ifnotpaths:returnrow=paths[0]# desensitize the text diff and hunk selection tabs# if a non-MAR file is selectedstatus=model[row][FM_STATUS]enable=(statusin'MAR')self.enable_page('text-diff',enable)self.enable_page('hunk-selection',enableandnotself.is_merge())ifpage_numisNone:page_num=self.diff_notebook.get_current_page()pname=self.get_page_name(page_num)ifpname=='text-diff':buf=self.generate_text_diffs(row)self.diff_text.set_buffer(buf)elifpname=='hunk-selection':self.update_hunk_model(row,tree)elifpname=='commit-preview':self.update_commit_preview()defget_page_name(self,num):try:returnself.diff_notebook_pages[num]exceptKeyError:return''defenable_page(self,name,enable):forpnuminself.diff_notebook_pages:pname=self.get_page_name(pnum)ifpname==name:child=self.diff_notebook.get_nth_page(pnum)ifchild:child.set_sensitive(enable)lb=self.diff_notebook.get_tab_label(child)lb.set_sensitive(enable)returndefupdate_commit_preview(self):ifself.is_merge():opts=patch.diffopts(self.ui,self.opts)opts.git=Truewctx=self.repo[None]pctx1,pctx2=wctx.parents()difftext=[_('===== Diff to first parent %d:%s =====\n')%(pctx1.rev(),str(pctx1))]forsinpatch.diff(self.repo,pctx1.node(),None,opts=opts):difftext.extend(s.splitlines(True))difftext.append(_('\n===== Diff to second parent %d:%s =====\n')%(pctx2.rev(),str(pctx2)))forsinpatch.diff(self.repo,pctx2.node(),None,opts=opts):difftext.extend(s.splitlines(True))else:buf=cStringIO.StringIO()forrowinself.filemodel:ifnotrow[FM_CHECKED]:continuewfile=row[FM_PATH]ifwfileinself.filechunks:chunks=self.filechunks[wfile]else:chunks=self.read_file_chunks(wfile)forcinchunks:c.active=Trueself.filechunks[wfile]=chunksfori,chunkinenumerate(chunks):ifi==0:chunk.write(buf)elifchunk.active:chunk.write(buf)difftext=buf.getvalue().splitlines(True)self.preview_text.set_buffer(self.diff_highlight_buffer(difftext))defdiff_highlight_buffer(self,difftext):buf=gtk.TextBuffer()ifself.colorstyle=='background':buf.create_tag('removed',paragraph_background=gtklib.PRED)buf.create_tag('added',paragraph_background=gtklib.PGREEN)elifself.colorstyle=='none':buf.create_tag('removed')buf.create_tag('added')else:buf.create_tag('removed',foreground=gtklib.DRED)buf.create_tag('added',foreground=gtklib.DGREEN)buf.create_tag('position',foreground='#FF8000')buf.create_tag('header',foreground=gtklib.DBLUE)bufiter=buf.get_start_iter()forlineindifftext:line=hglib.toutf(line)ifline.startswith('---')orline.startswith('+++'):buf.insert_with_tags_by_name(bufiter,line,'header')elifline.startswith('-'):line=hglib.diffexpand(line)buf.insert_with_tags_by_name(bufiter,line,'removed')elifline.startswith('+'):line=hglib.diffexpand(line)buf.insert_with_tags_by_name(bufiter,line,'added')elifline.startswith('@@'):buf.insert_with_tags_by_name(bufiter,line,'position')else:line=hglib.diffexpand(line)buf.insert(bufiter,line)returnbufdefgenerate_text_diffs(self,row):wfile=self.filemodel[row][FM_PATH]pfile=util.pconvert(wfile)matcher=cmdutil.matchfiles(self.repo,[pfile])opts=patch.diffopts(self.ui,self.opts)opts.git=Truedifftext=[]ifself.is_merge():wctx=self.repo[None]pctx1,pctx2=wctx.parents()difftext=[_('===== Diff to first parent %d:%s =====\n')%(pctx1.rev(),str(pctx1))]forsinpatch.diff(self.repo,pctx1.node(),None,match=matcher,opts=opts):difftext.extend(s.splitlines(True))difftext.append(_('\n===== Diff to second parent %d:%s =====\n')%(pctx2.rev(),str(pctx2)))forsinpatch.diff(self.repo,pctx2.node(),None,match=matcher,opts=opts):difftext.extend(s.splitlines(True))else:forsinpatch.diff(self.repo,self._node1,self._node2,match=matcher,opts=opts):difftext.extend(s.splitlines(True))returnself.diff_highlight_buffer(difftext)defupdate_hunk_model(self,path,tree):# Read this file's diffs into hunk selection modelwfile=self.filemodel[path][FM_PATH]self.filerowstart={}self.diffmodel.clear()ifnotself.is_merge():self.append_diff_hunks(wfile)iflen(self.diffmodel):tree.scroll_to_cell(0,use_align=True,row_align=0.0)defread_file_chunks(self,wfile):'Get diffs of working file, parse into (c)hunks'difftext=cStringIO.StringIO()ctx=self.repo[self._node1]try:pfile=util.pconvert(wfile)fctx=ctx.filectx(pfile)excepterror.LookupError:fctx=Noneiffctxandfctx.size()>hglib.getmaxdiffsize(self.repo.ui):# Fake patch that displays size warninglines=['diff --git a/%s b/%s\n'%(wfile,wfile)]lines.append(_('File is larger than the specified max size.\n'))lines.append(_('Hunk selection is disabled for this file.\n'))lines.append('--- a/%s\n'%wfile)lines.append('+++ b/%s\n'%wfile)difftext.writelines(lines)difftext.seek(0)else:matcher=cmdutil.matchfiles(self.repo,[pfile])diffopts=mdiff.diffopts(git=True,nodates=True)forsinpatch.diff(self.repo,self._node1,self._node2,match=matcher,opts=diffopts):difftext.writelines(s.splitlines(True))difftext.seek(0)returnhgshelve.parsepatch(difftext)defappend_diff_hunks(self,wfile):'Append diff hunks of one file to the diffmodel'chunks=self.read_file_chunks(wfile)ifnotchunks:ifwfileinself.filechunks:delself.filechunks[wfile]returnforfrinself.filemodel:iffr[FM_PATH]==wfile:breakelse:# should not be possiblereturnrows=[]forn,chunkinenumerate(chunks):ifisinstance(chunk,hgshelve.header):# header chunk is always activechunk.active=Truerows.append([False,'',True,wfile,n,self.headerfont])ifchunk.special():chunks=chunks[:1]breakelse:# chunks take file's selection state by defaultchunk.active=fr[FM_CHECKED]rows.append([False,'',False,wfile,n,self.difffont])# recover old chunk selection/rejection states, match fromlineifwfileinself.filechunks:ochunks=self.filechunks[wfile]next=1forocinochunks[1:]:forninxrange(next,len(chunks)):nc=chunks[n]ifoc.fromline==nc.fromline:nc.active=oc.activenext=n+1breakelifnc.fromline>oc.fromline:breakself.filerowstart[wfile]=len(self.diffmodel)self.filechunks[wfile]=chunks# Set row status based on chunk staterej,nonrej=False,Falseforn,rowinenumerate(rows):ifnotrow[DM_IS_HEADER]:ifchunks[n].active:nonrej=Trueelse:rej=Truerow[DM_REJECTED]=notchunks[n].activeself.update_diff_hunk(row)self.diffmodel.append(row)iflen(rows)==1:newvalue=fr[FM_CHECKED]else:newvalue=nonrejpartial=rejandnonrejiffr[FM_PARTIAL_SELECTED]!=partial:fr[FM_PARTIAL_SELECTED]=partialiffr[FM_CHECKED]!=newvalue:fr[FM_CHECKED]=newvalueself.update_check_count()self.update_diff_header(self.diffmodel,wfile,newvalue)defdiff_tree_row_act(self,dtree,path,column):'Row in diff tree (hunk) activated/toggled'dmodel=dtree.get_model()row=dmodel[path]wfile=row[DM_PATH]try:startrow=self.filerowstart[wfile]chunks=self.filechunks[wfile]exceptIndexError:passchunkrows=xrange(startrow+1,startrow+len(chunks))forfrinself.filemodel:iffr[FM_PATH]==wfile:breakifrow[DM_IS_HEADER]:forn,chunkinenumerate(chunks[1:]):chunk.active=notfr[FM_CHECKED]self.update_diff_hunk(dmodel[startrow+n+1])newvalue=notfr[FM_CHECKED]partial=Falseelse:chunk=chunks[row[DM_CHUNK_ID]]chunk.active=notchunk.activeself.update_diff_hunk(row)rej=[nforninchunkrowsifdmodel[n][DM_REJECTED]]nonrej=[nforninchunkrowsifnotdmodel[n][DM_REJECTED]]newvalue=nonrejandTrueorFalsepartial=rejandnonrejandTrueorFalse# Update file's check statusiffr[FM_PARTIAL_SELECTED]!=partial:fr[FM_PARTIAL_SELECTED]=partialiffr[FM_CHECKED]!=newvalue:fr[FM_CHECKED]=newvaluechunks[0].active=newvalueself.update_check_count()self.update_diff_header(dmodel,wfile,newvalue)defrefresh_clicked(self,toolbutton,data=None):self.reload_status()returnTruedefsave_clicked(self,toolbutton,data=None):'Write selected diff hunks to a patch file'revrange=self.opts.get('rev')[0]filename="%s.patch"%revrange.replace(':','_to_')result=gtklib.NativeSaveFileDialogWrapper(title=_('Save patch to'),initial=self.repo.root,filename=filename).run()ifnotresult:returnbuf=cStringIO.StringIO()dmodel=self.diffmodelforrowinself.filemodel:ifnotrow[FM_CHECKED]:continuewfile=row[FM_PATH]ifwfileinself.filechunks:chunks=self.filechunks[wfile]else:chunks=self.read_file_chunks(wfile)forcinchunks:c.active=Truefori,chunkinenumerate(chunks):ifi==0:chunk.write(buf)elifchunk.active:chunk.write(buf)buf.seek(0)try:try:fp=open(result,"w")fp.write(buf.read())exceptOSError:passfinally:fp.close()defdiff_clicked(self,toolbutton,data=None):diff_list=self.relevant_checked_files('MAR!')iflen(diff_list)>0:self._do_diff(diff_list,self.opts)else:gdialog.Prompt(_('Nothing Diffed'),_('No diffable files selected'),self).run()returnTruedefrevert_clicked(self,toolbutton,data=None):revert_list=self.relevant_checked_files('MAR!')iflen(revert_list)>0:self.hg_revert(revert_list)else:gdialog.Prompt(_('Nothing Reverted'),_('No revertable files selected'),self).run()returnTruedefhg_revert(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]ifself.count_revs()>1:gdialog.Prompt(_('Nothing Reverted'),_('Revert not allowed when viewing revision range.'),self).run()return# Create new opts, so nothing unintented gets through.revertopts=self.merge_opts(commands.table['revert'][1],('include','exclude','rev'))defdohgrevert():commands.revert(self.ui,self.repo,*wfiles,**revertopts)deffilelist(files):text='\n'.join(files[:5])iflen(files)>5:text+=' ...\n'returnhglib.toutf(text)ifself.is_merge():res=gdialog.CustomPrompt(_('Uncommited merge - please select a parent revision'),_('Revert files to local or other parent?'),self,(_('&Local'),_('&Other'),_('&Cancel')),2).run()ifres==0:revertopts['rev']=self.repo[None].p1().rev()elifres==1:revertopts['rev']=self.repo[None].p2().rev()else:returnresponse=Noneelse:# response: 0=Yes, 1=Yes,no backup, 2=Cancelrevs=revertopts['rev']ifrevsandtype(revs)==list:revertopts['rev']=revs[0]else:revertopts['rev']=str(self.repo['.'].rev())response=gdialog.CustomPrompt(_('Confirm Revert'),_('Revert files to revision %s?\n\n%s')%(revertopts['rev'],filelist(files)),self,(_('&Yes (backup changes)'),_('Yes (&discard changes)'),_('&Cancel')),2,2).run()ifresponsein(None,0,1):ifresponse==1:revertopts['no_backup']=Truesuccess,outtext=self._hg_call_wrapper('Revert',dohgrevert)ifsuccess:shlib.shell_notify(wfiles)self.reload_status()defhg_forget(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]commands.forget(self.ui,self.repo,*wfiles)self.reload_status()defadd_clicked(self,toolbutton,data=None):add_list=self.relevant_checked_files('?I')iflen(add_list)>0:self.hg_add(add_list)else:gdialog.Prompt(_('Nothing Added'),_('No addable files selected'),self).run()returnTruedefhg_add(self,files):wfiles=[self.repo.wjoin(x)forxinfiles]# Create new opts, so nothing unintented gets throughaddopts=self.merge_opts(commands.table['^add'][1],('include','exclude'))defdohgadd():commands.add(self.ui,self.repo,*wfiles,**addopts)success,outtext=self._hg_call_wrapper('Add',dohgadd)ifsuccess:shlib.shell_notify(wfiles)self.reload_status()defremove_clicked(self,toolbutton,data=None):remove_list=self.relevant_checked_files('C!')delete_list=self.relevant_checked_files('?I')iflen(remove_list)>0:self.hg_remove(remove_list)iflen(delete_list)>0:self.delete_files(delete_list)ifnotremove_listandnotdelete_list:gdialog.Prompt(_('Nothing Removed'),_('No removable files selected'),self).run()returnTruedefmove_clicked(self,toolbutton,data=None):move_list=self.relevant_checked_files('C')ifmove_list:# get destination directory to files intodlg=gtklib.NativeFolderSelectDialog(title=_('Move files to directory...'),initial=self.repo.root)destdir=dlg.run()ifnotdestdir:returnTrue# verify directorydestroot=paths.find_root(destdir)ifdestroot!=self.repo.root:gdialog.Prompt(_('Nothing Moved'),_('Cannot move outside repo!'),self).run()returnTrue# move the files to dest directorymove_list.append(hglib.fromutf(destdir))self.hg_move(move_list)else:gdialog.Prompt(_('Nothing Moved'),_('No movable files selected\n\n''Note: only clean files can be moved.'),self).run()returnTruedefforget_clicked(self,toolbutton,data=None):forget_list=self.relevant_checked_files('CM')iflen(forget_list)>0:self.hg_forget(forget_list)else:gdialog.Prompt(_('Nothing Forgotten'),_('No clean files selected'),self).run()defdelete_files(self,files):dlg=gdialog.Confirm(_('Confirm Delete Unrevisioned'),files,self,_('Delete the following unrevisioned files?'))ifdlg.run()==gtk.RESPONSE_YES:errors=''forwfileinfiles:try:os.unlink(self.repo.wjoin(wfile))exceptException,inst:errors+=str(inst)+'\n\n'iferrors:errors=errors.replace('\\\\','\\')iflen(errors)>500:errors=errors[:errors.find('\n',500)]+'\n...'gdialog.Prompt(_('Delete Errors'),errors,self).run()self.reload_status()returnTruedefignoremask_updated(self):'''User has changed the ignore mask in hgignore dialog'''self.opts['check']=Trueself.reload_status()defrelevant_checked_files(self,stats):return[item[FM_PATH]foriteminself.filemodel \
ifitem[FM_CHECKED]anditem[FM_STATUS]instats]defsel_clicked(self,state):'selection header checkbox clicked'forentryinself.filemodel:ifentry[FM_CHECKED]!=state:entry[FM_CHECKED]=stateself.update_chunk_state(entry)self.update_check_count()deftree_button_press(self,treeview,event):'''Selection management for filetree right-click If the user right-clicks on a currently-selected item in the filetree, preserve their entire existing selection for the popup menu. http://www.daa.com.au/pipermail/pygtk/2005-June/010465.html '''ifevent.button!=3:returnFalseclicked_row=treeview.get_path_at_pos(int(event.x),int(event.y))ifnotclicked_row:returnFalseselection=treeview.get_selection()selected_rows=selection.get_selected_rows()[1]# If they didn't right-click on a currently selected row,# change the selectionifclicked_row[0]notinselected_rows:selection.unselect_all()selection.select_path(clicked_row[0])returnTruedeftree_button_release(self,treeview,event):ifevent.button!=3:returnFalseself.tree_popup_menu(treeview)returnTruedeftree_popup_menu(self,treeview):model,tpaths=treeview.get_selection().get_selected_rows()types={'M':[],'A':[],'R':[],'!':[],'I':[],'?':[],'C':[],'r':[],'u':[],'S':[]}all=[]pathmap={}forpintpaths:row=model[p]file=util.pconvert(row[FM_PATH])ms=row[FM_MERGE_STATUS]ifms=='R':types['r'].append(file)elifms=='U':types['u'].append(file)else:types[row[FM_STATUS]].append(file)all.append(file)pathmap[file]=pdefmake(label,func,stats,icon=None,sens=True,paths=False):files=[]fortinstats:files.extend(types[t])ifnotfiles:returnargs=[files]ifpaths:p=[pathmap[f]forfinfiles]args.append(p)item=menu.append(label,func,icon,args=args,sensitive=sens)returnfilesdefvdiff(menuitem,files):self._do_diff(files,self.opts)defviewmissing(menuitem,files):self._view_files(files,True)defedit(menuitem,files):self._view_files(files,False)defother(menuitem,files):self._view_files(files,True)defrevert(menuitem,files):self.hg_revert(files)defremove(menuitem,files):self.hg_remove(files)deflog(menuitem,files):fromtortoisehg.hgtkimporthistorydlg=history.run(self.ui,canonpats=files)dlg.display()defforget(menuitem,files,paths):self.hg_forget(files)self.set_file_states(paths,state=False)defadd(menuitem,files,paths):self.hg_add(files)self.set_file_states(paths,state=True)defdelete(menuitem,files):self.delete_files(files)defunmark(menuitem,files):ms=merge_.mergestate(self.repo)forwfileinfiles:ms.mark(wfile,"u")self.reload_status()defmark(menuitem,files):ms=merge_.mergestate(self.repo)forwfileinfiles:ms.mark(wfile,"r")self.reload_status()defresolve(stat,files):wctx=self.repo[None]mctx=wctx.parents()[-1]forwfileinfiles:ms=merge_.mergestate(self.repo)ms.resolve(wfile,wctx,mctx)self.reload_status()defresolve_with(stat,tool,files):iftool:oldmergeenv=os.environ.get('HGMERGE')os.environ['HGMERGE']=toolresolve(stat,files)iftool:ifoldmergeenv:os.environ['HGMERGE']=oldmergeenvelse:delos.environ['HGMERGE']defrename(menuitem,files):self.rename_file(files[0])defcopy(menuitem,files):self.copy_file(files[0])defguess_rename(menuitem,files):dlg=guess.DetectRenameDialog()dlg.show_all()dlg.set_notify_func(self.ignoremask_updated)defignore(menuitem,files):dlg=hgignore.HgIgnoreDialog(files[0])dlg.show_all()dlg.set_notify_func(self.ignoremask_updated)menu=gtklib.MenuBuilder()make(_('_Visual Diff'),vdiff,'MAR!ru',gtk.STOCK_JUSTIFY_FILL)make(_('Edit'),edit,'MACI?ru',gtk.STOCK_EDIT)make(_('View missing'),viewmissing,'R!')make(_('View other'),other,'MAru',None,self.is_merge())menu.append_sep()make(_('_Revert'),revert,'MAR!ru',gtk.STOCK_MEDIA_REWIND)menu.append_sep()make(_('L_og'),log,'MARC!ru','menulog.ico')menu.append_sep()make(_('_Forget'),forget,'MAC!ru',gtk.STOCK_CLEAR,paths=True)make(_('_Add'),add,'I?',gtk.STOCK_ADD,paths=True)make(_('_Guess Rename...'),guess_rename,'?!','detect_rename.ico')make(_('_Ignore'),ignore,'?','ignore.ico')make(_('Remove versioned'),remove,'C','menudelete.ico')make(_('_Delete unversioned'),delete,'?I',gtk.STOCK_DELETE)iflen(all)==1:menu.append_sep()make(_('_Copy...'),copy,'MC',gtk.STOCK_COPY)make(_('Rename...'),rename,'MC','general.ico')menu.append_sep()f=make(_('Restart Merge...'),resolve,'u','menumerge.ico')make(_('Mark unresolved'),unmark,'r',gtk.STOCK_NO)make(_('Mark resolved'),mark,'u',gtk.STOCK_YES)iff:rmenu=gtk.Menu()fortoolinhglib.mergetools(self.repo.ui):item=gtk.MenuItem(tool,True)item.connect('activate',resolve_with,tool,f)item.set_border_width(1)rmenu.append(item)menu.append_submenu(_('Restart merge with'),rmenu,'menumerge.ico')forlabel,func,stats,iconinself.get_custom_menus():make(label,func,stats,icon)menu=menu.build()iflen(menu.get_children())>0:menu.show_all()menu.popup(None,None,None,0,0)returnTruedeftree_key_press(self,tree,event):'Make spacebar toggle selected rows'ifevent.keyval==32:deftoggler(model,path,bufiter):model[path][FM_CHECKED]=notmodel[path][FM_CHECKED]self.update_chunk_state(model[path])selection=self.filetree.get_selection()selection.selected_foreach(toggler)self.update_check_count()returnTruereturnFalsedeftree_row_act(self,tree,path,column):'Activation (return) triggers visual diff of selected rows'# Ignore activations (like double click) on the first columnifcolumn.get_sort_column_id()==0:returnTruemodel,tpaths=self.filetree.get_selection().get_selected_rows()files=[model[p][FM_PATH]forpintpaths]self._do_diff(files,self.opts)returnTruedefisuptodate(self):oldparents=self.repo.dirstate.parents()self.repo.dirstate.invalidate()ifoldparents==self.repo.dirstate.parents():returnTrueresponse=gdialog.CustomPrompt(_('not up to date'),_('The parents have changed since the last refresh.\n''Continue anyway?'),self,(_('&Yes'),_('&Refresh'),_('&Cancel')),1,2).run()ifresponse==0:# YesreturnTrueifresponse==1:self.reload_status()returnFalsedefrun(ui,*pats,**opts):showclean=patsandTrueorFalserev=opts.get('rev',[])cmdoptions={'all':False,'clean':showclean,'ignored':False,'modified':True,'added':True,'removed':True,'deleted':True,'unknown':True,'exclude':[],'include':[],'debug':True,'verbose':True,'git':False,'rev':rev,'check':True,'subrepo':True}returnGStatus(ui,None,None,pats,cmdoptions)
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.