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.
# commit.py - Commit dialog for TortoiseHg## Copyright 2007 Brad Schick, brad at gmail . com# Copyright 2007 TK Soh <teekaysoh@gmail.com># Copyright 2007 Steve Borho <steve@borho.org>## This software may be used and distributed according to the terms of the# GNU General Public License version 2, incorporated herein by reference.importosimportsysimporterrnoimportgtkimportgobjectimporttempfileimportcStringIOimporttimefrommercurialimporthg,util,patch,cmdutil,extensionsfromtortoisehg.util.i18nimport_fromtortoisehg.utilimportshlib,hglib,i18nkeep=i18n.keepgettext()fromtortoisehg.hgtk.statusimportGStatus,FM_STATUS,FM_CHECKEDfromtortoisehg.hgtk.statusimportFM_PATH,FM_PATH_UTF8fromtortoisehg.hgtkimportcsinfo,gtklib,thgconfig,gdialog,hgcmdfromtortoisehg.hgtkimportthgmq,textviewclassBranchOperationDialog(gtk.Dialog):def__init__(self,branch,close,repo):gtk.Dialog.__init__(self,parent=None,flags=gtk.DIALOG_MODAL,buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL),title=_('Branch Operations'))gtklib.set_tortoise_icon(self,'branch.ico')gtklib.set_tortoise_keys(self)self.set_resizable(False)self.set_has_separator(False)self.newbranch=Noneself.closebranch=Falseiflen(repo.parents())==2:lbl=gtk.Label(_('Select branch of merge commit'))branchcombo=gtk.combo_box_new_text()forpinrepo.parents():branchcombo.append_text(p.branch())branchcombo.set_active(0)self.vbox.pack_start(lbl,True,True,2)self.vbox.pack_start(branchcombo,True,True,2)self.connect('response',self.merge_response,branchcombo)self.show_all()return# create widgetsnochanges=gtk.RadioButton(None,_('No branch changes'))self.newbranchradio=gtk.RadioButton(nochanges,_('Open a new named branch'))self.newbranchradio.set_active(True)branchcombo=gtk.combo_box_entry_new_text()fornameinhglib.getlivebranch(repo):branchcombo.append_text(name)self.branchentry=branchcombo.childself.closebranchradio=gtk.RadioButton(nochanges,_('Close current named branch'))# layout tabletable=gtklib.LayoutTable()self.vbox.pack_start(table,True,True,2)lbl=gtk.Label()lbl.set_markup(gtklib.markup(_('Changes take effect on next commit'),weight='bold'))table.add_row(lbl,padding=False,ypad=6)table.add_row(nochanges)table.add_row(self.newbranchradio,branchcombo)table.add_row(self.closebranchradio)# signal handlersself.connect('response',self.response)self.newbranchradio.connect('toggled',self.nbtoggle,branchcombo)self.branchentry.connect('activate',self.activated)# prepare to showifbranch:self.newbranch=branchself.branchentry.set_text(branch)self.newbranchradio.set_active(True)elifclose:self.closebranch=closeself.closebranchradio.set_active(True)else:nochanges.set_active(True)ifrepo[None].branch()=='default':self.closebranchradio.set_sensitive(False)self.show_all()defnbtoggle(self,radio,combo):combo.set_sensitive(radio.get_active())ifradio.get_active():self.branchentry.grab_focus()defactivated(self,entry):self.response(self,response_id=gtk.RESPONSE_OK)defresponse(self,widget,response_id):ifresponse_id==gtk.RESPONSE_OK:ifself.newbranchradio.get_active():self.newbranch=self.branchentry.get_text()elifself.closebranchradio.get_active():self.closebranch=Trueelse:self.newbranch=Noneself.closebranch=Falseself.destroy()defmerge_response(self,widget,response_id,combo):self.closebranch=Falseifresponse_id==gtk.RESPONSE_OK:row=combo.get_active()ifrow==1:self.newbranch=combo.get_model()[row][0]self.destroy()classGCommit(GStatus):"""GTK+ based dialog for displaying repository status and committing changes. Also provides related operations like add, delete, remove, revert, refresh, ignore, diff, and edit. """### Overrides of base class methods ###definit(self):GStatus.init(self)self.mode='commit'self.nextbranch=Noneself.closebranch=Falseself.last_commit_id=Noneself.qnew=Falseself.notify_func=Noneself.patch_text=Noneself.qheader=Noneself.runner=hgcmd.CmdRunner()try:self.mqloaded=bool(extensions.find('mq'))exceptKeyError:self.mqloaded=Falsedefget_help_url(self):return'commit.html'defset_notify_func(self,func,*args):self.notify_func=funcself.notify_args=argsdefparse_opts(self):GStatus.parse_opts(self)defget_title(self):root=self.get_reponame()ifself.is_merge():root=_('merging ')+rootuser=self.opts.get('user')or''ifuser:user='as '+userdate=self.opts.get('date')or''pats=' '.join(self.pats)or''ifself.qnew:returnroot+_(' - qnew')elifself.mqmode:patch=self.repo.mq.lookup('qtip')returnroot+_(' - qrefresh ')+patchreturnroot+' '.join([_(' - commit'),pats,user,date])defget_icon(self):return'menucommit.ico'defget_default_setting(self):return'ui.username'defauto_check(self):ifnotself.test_opt('check'):returnforrowinself.filemodel:ifrow[FM_STATUS]in'MAR'androw[FM_PATH]notinself.excludes:row[FM_CHECKED]=Trueself.update_check_count()self.opts['check']=Falsedefget_menu_list(self):defrefresh(menu):self.reload_status()deftoggle(button,type):show=button.get_active()statename='show'+typeifgetattr(self,statename)!=show:frame=getattr(self,type+'_frame')ifshow:frame.show()else:frame.hide()setattr(self,statename,show)deftoggle_showoutput(button):active=button.get_active()ifself.showoutput!=active:self.showoutput=activedeftoggle_showtoolbar(button):self.showtoolbar=button.get_active()self._show_toolbar(self.showtoolbar)defdisable_maxdiff(menuitem):ifmenuitem.get_active():hglib._maxdiff=sys.maxintelse:hglib._maxdiff=Noneifself.mqloaded:mq_item=[dict(text=_('Patch Queue'),name='mq',ascheck=True,func=self.mq_clicked,check=self.setting_mqvis)]else:mq_item=[]return[dict(text=_('_View'),subitems=[dict(text=_('Toolbar'),ascheck=True,check=self.showtoolbar,func=toggle_showtoolbar),dict(text=_('Advanced'),ascheck=True,func=toggle,args=['advanced'],check=self.showadvanced),dict(text=_('Parents'),ascheck=True,func=toggle,args=['parents'],check=self.showparents),]+mq_item+[dict(text='----'),dict(text=_('Refresh'),func=refresh,icon=gtk.STOCK_REFRESH),dict(text='----'),dict(name='ignore-max-diff',text=_('Ignore Max Diff Size'),ascheck=True,func=disable_maxdiff),dict(name='always-show-output',text=_('Always Show Output'),ascheck=True,func=toggle_showoutput,check=self.showoutput)]),dict(text=_('_Operations'),subitems=[dict(text=_('_Commit'),func=self.commit_clicked,icon=gtk.STOCK_OK),dict(name='undo',text=_('_Undo'),func=self.undo_clicked,icon=gtk.STOCK_UNDO),dict(text='----'),dict(name='diff',text=_('_Diff'),func=self.diff_clicked,icon=gtk.STOCK_JUSTIFY_FILL),dict(name='revert',text=_('Re_vert'),func=self.revert_clicked,icon=gtk.STOCK_MEDIA_REWIND),dict(name='add',text=_('_Add'),func=self.add_clicked,icon=gtk.STOCK_ADD),dict(name='remove',text=_('_Remove'),func=self.remove_clicked,icon=gtk.STOCK_DELETE),dict(name='move',text=_('Move'),func=self.move_clicked,icon=gtk.STOCK_JUMP_TO),dict(name='forget',text=_('_Forget'),func=self.forget_clicked,icon=gtk.STOCK_CLEAR)])]defsave_settings(self):settings=GStatus.save_settings(self)settings['commit-vpane']=self.vpaned.get_position()settings['show-toolbar']=self.showtoolbarsettings['showparents']=self.showparentssettings['showadvanced']=self.showadvancedsettings['show-output']=self.showoutputifhasattr(self,'mqpaned')andself.mqwidget.has_patch():curpos=self.mqpaned.get_position()settings['mqpane']=curposorself.setting_mqhpossettings['mqvis']=bool(curpos)else:settings['mqpane']=self.setting_mqhpossettings['mqvis']=self.setting_mqvisreturnsettingsdefload_settings(self,settings):GStatus.load_settings(self,settings)self.setting_vpos=settings.get('commit-vpane',-1)self.showtoolbar=settings.get('show-toolbar',True)self.showparents=settings.get('showparents',True)self.showadvanced=settings.get('showadvanced',False)self.showoutput=settings.get('show-output',False)self.setting_mqhpos=settings.get('mqpane',140)or140self.setting_mqvis=settings.get('mqvis',False)defshow_toolbar_on_start(self):returnself.showtoolbardefget_tbbuttons(self):# insert to head of toolbartbbuttons=GStatus.get_tbbuttons(self)tbbuttons.insert(0,gtk.SeparatorToolItem())undo_btn=self.make_toolbutton(gtk.STOCK_UNDO,_('_Undo'),self.undo_clicked,name='undo',tip=_('undo recent commit'))commit_btn=self.make_toolbutton(gtk.STOCK_OK,_('_Commit'),self.commit_clicked,name='commit',tip=_('commit'))tbbuttons.insert(0,undo_btn)tbbuttons.insert(0,commit_btn)ifself.mqloaded:mq_btn=self.make_toolbutton(gtk.STOCK_DIRECTORY,_('Patch Queue'),self.mq_clicked,name='mq',tip=_('Show/Hide Patch Queue'),toggle=True,icon='menupatch.ico')tbbuttons.append(mq_btn)self.mqtb=mq_btnreturntbbuttonsdefshould_live(self,widget=None,event=None):# If there are more than a few character typed into the commit# message, ask if the exit should continue.live=Falsebuf=self.text.get_buffer()ifbuf.get_char_count()>10andbuf.get_modified():# response: 0=Yes, 1=No, 2=Cancelresponse=gdialog.CustomPrompt(_('Confirm Exit'),_('Save commit message at exit?'),self,(_('&Yes'),_('&No'),_('&Cancel')),2,2).run()ifresponse==0:begin,end=buf.get_bounds()self.update_recent_messages(buf.get_text(begin,end))buf.set_modified(False)elifresponse==2:live=Trueifnotlive:self._destroying(widget)returnlivedefrefresh_complete(self):self.check_merge()self.check_patch_queue()self.check_undo()self.refresh_branchop()self.update_parent_labels()self.update_commit_button()ifnotself.committer_cbbox.get_active_text():user=self.opts['user']oros.environ.get('HGUSER')or \
self.repo.ui.config('ui','username')ifuser:self.update_recent_committers(hglib.toutf(user))ifnotself.autoinc_entry.get_text():autoinc=self.repo.ui.config('tortoisehg','autoinc','')self.autoinc_entry.set_text(hglib.toutf(autoinc))ifself.qnew_name:self.qnew_name.set_sensitive(notself.is_merge())ifself.qnew:self.qnew_name.grab_focus()# set focus backself.qnew_name.set_position(-1)ifhasattr(self,'mqwidget'):self.mqwidget.set_repo(self.repo)self.mqwidget.refresh()defget_body(self):self.connect('delete-event',self.delete)status_body=GStatus.get_body(self)midpane=gtk.VBox()# Advanced barself.advanced_frame=gtk.VBox()adv_hbox=gtk.HBox()self.advanced_frame.pack_start(adv_hbox,False,False)adv_hbox.pack_start(gtk.Label(_('Committer:')),False,False,2)liststore=gtk.ListStore(str)self.committer_cbbox=gtk.ComboBoxEntry(liststore)cell=gtk.CellRendererText()self.committer_cbbox.pack_start(cell,True)adv_hbox.pack_start(self.committer_cbbox,True,True,2)self._mru_committers=self.settings.mrul('recent_committers')self.update_recent_committers()committer=os.environ.get('HGUSER')orself.repo.ui.config('ui','username')ifcommitter:self.update_recent_committers(hglib.toutf(committer))self.committer_cbbox.set_active(0)adv_hbox.pack_start(gtk.Label(_('Auto-includes:')),False,False,2)self.autoinc_entry=gtk.Entry()adv_hbox.pack_start(self.autoinc_entry,False,False,2)self.autopush=gtk.CheckButton(_('Push after commit'))pushafterci=self.repo.ui.configbool('tortoisehg','pushafterci')self.autopush.set_active(pushafterci)adv_hbox.pack_start(self.autopush,False,False,2)midpane.pack_start(self.advanced_frame,False,False,2)mbox=gtk.HBox()self.connect('thg-accept',self.thgaccept)self.branchbutton=gtk.Button(use_underline=False)self.branchbutton.connect('clicked',self.branch_clicked)mbox.pack_start(self.branchbutton,False,False,2)ifself.is_merge():branches=[p.branch()forpinself.repo.parents()]ifbranches[0]==branches[1]:self.branchbutton.set_sensitive(False)ifhasattr(self.repo,'mq'):label=gtk.Label('QNew: ')mbox.pack_start(label,False,False,2)self.qnew_name=gtk.Entry()self.qnew_name.set_sensitive(notself.is_merge())self.qnew_name.set_width_chars(20)self.qnew_name.connect('changed',self.qnew_changed)self.qnew_name.connect('activate',self.qnew_activated)mbox.pack_start(self.qnew_name,False,False,2)else:self.qnew_name=Noneliststore=gtk.ListStore(str,# summary line (utf-8)str)# full commit messageself.msg_cbbox=gtk.ComboBox(liststore)cell=gtk.CellRendererText()self.msg_cbbox.pack_start(cell,True)self.msg_cbbox.add_attribute(cell,'text',0)liststore.append([_('Recent commit messages...'),''])self.msg_cbbox.set_active(0)self.popupid=self.msg_cbbox.connect('notify::popup-shown',self.first_msg_popdown)self.msg_cbbox.connect('changed',self.changed_cb)mbox.pack_start(self.msg_cbbox)midpane.pack_start(mbox,False,False)self._mru_messages=self.settings.mrul('recent_messages')# change message fieldframe=gtk.Frame()frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)frame.add(scroller)midpane.pack_start(frame)accelgroup=gtk.AccelGroup()self.add_accel_group(accelgroup)mod=gtklib.get_thg_modifier()key,modifier=gtk.accelerator_parse('<alt>q')self.add_accelerator('thg-reflow',accelgroup,key,modifier,gtk.ACCEL_VISIBLE)self.text=textview.UndoableTextView(accelgroup=accelgroup)self.text.connect('populate-popup',self.msg_add_to_popup)self.connect('thg-reflow',self.thgreflow,self.text)self.text.modify_font(self.fonts['comment'])scroller.add(self.text)gtklib.addspellcheck(self.text,self.repo.ui)buf=self.text.get_buffer()buf.connect('changed',self.msg_changed)buf.create_tag('over',paragraph_background=gtklib.PRED)# MQ panelifself.mqloaded:self.mqwidget=thgmq.MQWidget(self.repo,accelgroup,self.tooltips)self.mqwidget.connect('repo-invalidated',self.repo_invalidated)self.mqwidget.connect('close-mq',lambda*a:self.enable_mqpanel(False))defwrapframe(widget):frame=gtk.Frame()frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)frame.add(widget)returnframeself.mqpaned=gtk.HPaned()self.mqpaned.add1(wrapframe(self.mqwidget))self.mqpaned.add2(wrapframe(midpane))# register signal handlerdefnotify(paned,gparam):ifnothasattr(self,'mqtb'):returnpos=paned.get_position()ifself.cmd_get_active('mq'):ifpos<140:paned.set_position(140)else:ifpos!=0:paned.set_position(0)self.mqpaned.connect('notify::position',notify)midpane=self.mqpanedbotbox=gtk.VBox()botbox.pack_start(status_body)# parent changeset infopframe=gtk.VBox()self.parents_frame=pframepvbox=gtk.VBox(spacing=1)style=csinfo.labelstyle(contents=('%(athead)s ',_('Parent: %(rev)s'),' %(branch)s',' %(tags)s',' %(summary)s'),selectable=True)defdata_func(widget,item,ctx):ifitem=='athead':returnwidget.get_data('ishead')orbool(self.mqmode)raisecsinfo.UnknownItem(item)defmarkup_func(widget,item,value):ifitem=='athead'andvalueisFalse:text='[%s]'%_('Not at head')returngtklib.markup(text,weight='bold',color=gtklib.DRED)raisecsinfo.UnknownItem(item)custom=csinfo.custom(data=data_func,markup=markup_func)factory=csinfo.factory(self.repo,custom,style)defadd_parent():label=factory()pvbox.pack_start(label,False,False)returnlabelself.parent1_label=add_parent()self.parent2_label=add_parent()phbox=gtk.HBox()phbox.pack_start(pvbox,False,False,5)pframe.pack_start(phbox,False,False)pframe.pack_start(gtk.HSeparator(),False,False)botbox.pack_start(pframe,False,False,2)self.vpaned=gtk.VPaned()self.vpaned.pack1(midpane,shrink=False)self.vpaned.pack2(botbox,shrink=False)gtklib.idle_add_single_call(self.realize_settings)returnself.vpaneddefget_preview_tab_name(self):ifself.qneworself.mqmode:res=_('Patch Preview')else:res=_('Commit Preview')returnresdefprepare_display(self):GStatus.prepare_display(self)self.enable_mqpanel()### End of overridable methods ###defexecute_command(self,cmd,callback=None,status=None,title=None,force=False):ifself.showoutputorforce:dlg=hgcmd.CmdDialog(cmd)dlg.set_transient_for(self)dlg.run()dlg.hide()callbackandcallback(dlg.return_code(),dlg.get_buffer())returndlgdefwrapper(*args):self.stbar.end()callbackandcallback(*args)self.stbar.begin(*(statusand(status,)or()))iftitle:self.runner.set_title(title)returnself.runner.execute(cmd,wrapper)defupdate_recent_committers(self,name=None):""" 'name' argument must be in UTF-8. """ifnameisnotNone:self._mru_committers.add(name)self._mru_committers.compact()self.settings.write()liststore=self.committer_cbbox.get_model()liststore.clear()fornameinself._mru_committers:liststore.append([name])defchanged_cb(self,combobox):model=combobox.get_model()index=combobox.get_active()ifindex>=0:buf=self.text.get_buffer()ifbuf.get_char_count()andbuf.get_modified():response=gdialog.Confirm(_('Confirm Discard Message'),[],self,_('Discard current commit message?')).run()ifresponse!=gtk.RESPONSE_YES:combobox.set_active(-1)returnbuf.set_text(model[index][1])buf.set_modified(False)combobox.set_active(-1)deffirst_msg_popdown(self,combo,shown):combo.disconnect(self.popupid)self.popupid=Noneself.update_recent_messages()defupdate_recent_messages(self,msg=None):ifmsg:self._mru_messages.add(msg)self.settings.write()ifself.popupidisnotNone:returnliststore=self.msg_cbbox.get_model()liststore.clear()formsginself._mru_messages:ifnotmsg:continuesumline=hglib.toutf(hglib.tounicode(msg).splitlines()[0])liststore.append([sumline,msg])defbranch_clicked(self,button):dialog=BranchOperationDialog(self.nextbranch,self.closebranch,self.repo)dialog.run()self.nextbranch=Noneself.closebranch=Falseifdialog.newbranch:self.nextbranch=dialog.newbranchelifdialog.closebranch:self.closebranch=Trueself.refresh_branchop()defrepo_invalidated(self,mqwidget):self.reload_status()defenable_mqpanel(self,enable=None):ifnothasattr(self,'mqpaned'):returnifenableisNone:enable=self.setting_mqvisandself.mqwidget.has_patch()# set the state of MQ toolbuttonself.cmd_handler_block_by_func('mq',self.mq_clicked)self.cmd_set_active('mq',enable)self.cmd_handler_unblock_by_func('mq',self.mq_clicked)self.cmd_set_sensitive('mq',self.mqwidget.has_mq())# show/hide MQ paneoldpos=self.mqpaned.get_position()self.mqpaned.set_position(enableandself.setting_mqhposor0)ifnotenableandoldpos:self.setting_mqhpos=oldposdefmq_clicked(self,widget,*args):self.enable_mqpanel(widget.get_active())defupdate_commit_button(self):label=_('Commit')tooltip=_('commit')ifself.qnew:label=_('QNew')tooltip=_('create new MQ patch')elifself.mqmode:label=_('QRefresh')tooltip=_('refresh top MQ patch')else:plus,minus=_('_Commit (+1 head)'),_('_Commit (-1 head)')ctxs,isheads,ismerge=self.get_head_info()ifismerge:b1,b2=[c.branch()forcinctxs]ifisheads[0]andisheads[1]andb1==b2:label=minustooltip=_('commit to merge one head')elifnotisheads[0]andnotisheads[1]:label=plustooltip=_('neither parent is a head, ''commit to add a new head')else:ifnotisheads[0]:label=plustooltip=_('parent is not a head, ''commit to add a new head')btn=self.get_toolbutton('commit')btn.set_label(label)self.tooltips.set_tip(btn,tooltip)defget_head_info(self):defishead(ctx):childbranches=[cctx.branch()forcctxinctx.children()]returnctx.branch()notinchildbranchesifself.mqmode:ctxs=self.repo['.'].parents()else:ctxs=self.repo[None].parents()isheads=[ishead(ctx)forctxinctxs]returnctxs,isheads,len(ctxs)==2defupdate_parent_labels(self):ctxs,isheads,ismerge=self.get_head_info()self.parent1_label.info.clear_cache()self.parent1_label.update(ctxs[0],repo=self.repo)ifnotismerge:self.parent2_label.hide()else:self.parent2_label.info.clear_cache()self.parent2_label.update(ctxs[1],repo=self.repo)self.parent2_label.show()defthgreflow(self,window,textview):buffer=textview.get_buffer()pos=buffer.get_property('cursor-position')start=buffer.get_iter_at_offset(0)end=start.copy()end.forward_to_end()text=hglib.tounicode(buffer.get_text(start,end))ifpos==len(text):pos-=1sentence_begin=text.rfind('\n\n',0,pos)whilesentence_begin>=0andtext.rfind('\n\n',0,pos-1)==sentence_begin-1:sentence_begin-=1ifsentence_begin==-1:sentence_begin=0whilesentence_begin<len(text)andtext[sentence_begin]=='\n':sentence_begin+=1sentence_end=text.find('\n\n',sentence_begin+1)ifsentence_end==-1:sentence_end=len(text)pre_sentence=text[:sentence_begin]post_sentence=text[sentence_end:]sentence=text[sentence_begin:sentence_end]parts=sentence.replace('\r','').replace('\n',' ').replace('\t',' ').split(' ')line_width=int(self.repo.ui.config('tortoisehg','messagewrap',80))new_sentence=['']forpartinparts:iflen(new_sentence[-1])+len(part)+1>line_width:new_sentence.append('')new_sentence[-1]+='%s '%partsentence=u'\n'.join([x.strip()forxinnew_sentence]).encode('utf-8')new_pos=len(pre_sentence+sentence)buffer.set_text(pre_sentence+sentence+post_sentence)new_pos_iter=buffer.get_iter_at_offset(new_pos)buffer.place_cursor(new_pos_iter)m=buffer.create_mark('newcurspos',new_pos_iter,False)self.text.scroll_mark_onscreen(m)buffer.delete_mark(m)defrealize_settings(self):self.vpaned.set_position(self.setting_vpos)ifnotself.showparents:self.parents_frame.hide()ifnotself.showadvanced:self.advanced_frame.hide()ifhasattr(self,'mqpaned')andself.mqtb.get_active():self.mqpaned.set_position(self.setting_mqhpos)defthgaccept(self,window):self.commit_clicked(None)defget_custom_menus(self):defcommit(menuitem,files):ifself.ready_message()andself.isuptodate():defcallback():self.reload_status()abs=[self.repo.wjoin(file)forfileinfiles]shlib.shell_notify(abs)self.commit_selected(files,callback)ifself.is_merge():return()else:return[(_('_Commit'),commit,'MARS',gtk.STOCK_OK),]defdelete(self,window,event):ifnotself.should_live():self.destroy()else:returnTruedefrefresh_branchop(self):ifself.nextbranch:text=_('new branch: ')+self.nextbranchelifself.closebranch:text=_('close branch: ')+self.repo[None].branch()else:text=_('branch: ')+self.repo[None].branch()self.branchbutton.set_label(text)defcheck_undo(self):can_undo=Falseifos.path.exists(self.repo.sjoin('undo')):can_undo=(self.last_commit_idisnotNone)try:# when hg-1.5 support is dropped, self.last_commit_id# can be removedargs=self.repo.opener('undo.desc','r').read().split(',')can_undo=args[1]=='commit'except(IOError,IndexError):passself.cmd_set_sensitive('undo',can_undo)defcheck_merge(self):ifself.is_merge():# select all changes if repo is mergedforentryinself.filemodel:ifentry[FM_STATUS]in'MARD':entry[FM_CHECKED]=Trueself.update_check_count()# pre-fill commit message, if not modifiedbuf=self.text.get_buffer()ifbuf.get_modified():returnengmsg=self.repo.ui.configbool('tortoisehg','engmsg',False)msgset=keep._('Merge ')buf.set_text(engmsgandmsgset['id']ormsgset['str'])buf.set_modified(False)defcheck_patch_queue(self):'See if an MQ patch is applied, switch to qrefresh mode'self.qheader=Noneifself.mqmode:qtipctx=self.repo['qtip']self.qheader=qtipctx.description()self.committer_cbbox.child.set_text(hglib.toutf(qtipctx.user()))buf=self.text.get_buffer()ifbuf.get_char_count()==0ornotbuf.get_modified():ifself.qnew:buf.set_text('')else:buf.set_text(self.qheader)buf.set_modified(False)ifself.qnew:self.reload_status()self.qnew_name.grab_focus()self.qnew_name.set_position(-1)ifself.patch_text:self.diff_notebook.remove_page(self.ppage)self.patch_text=Noneelse:ifnotself.patch_text:self.patch_text=gtk.TextView()self.patch_text.set_wrap_mode(gtk.WRAP_NONE)self.patch_text.set_editable(False)self.patch_text.modify_font(self.difffont)scroller=gtk.ScrolledWindow()scroller.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)scroller.add(self.patch_text)self.ppage=self.diff_notebook.append_page(scroller,gtk.Label(_('Patch Contents')))self.diff_notebook.show_all()revs=cmdutil.revrange(self.repo,['tip'])fp=cStringIO.StringIO()opts=patch.diffopts(self.ui,self.opts)hglib.export(self.repo,revs,fp=fp,opts=opts)text=fp.getvalue().splitlines(True)buf=self.diff_highlight_buffer(text)self.patch_text.set_buffer(buf)elifself.qnew:buf=self.text.get_buffer()ifnotbuf.get_modified():buf.set_text('')buf.set_modified(False)ifself.patch_text:self.diff_notebook.remove_page(self.ppage)self.patch_text=Noneelifself.patch_text:buf=self.text.get_buffer()ifnotbuf.get_modified():buf.set_text('')buf.set_modified(False)self.diff_notebook.remove_page(self.ppage)self.patch_text=Noneself.branchbutton.set_sensitive(not(self.mqmodeorself.qnew))defcommit_clicked(self,toolbutton,data=None):ifnotself.isuptodate():returndefget_list(addremove=True):commitable='MARS'ifaddremove:ar_list=self.relevant_checked_files('?!')iflen(ar_list)>0andself.should_addremove(ar_list):commitable+='?!'returnself.relevant_checked_files(commitable)defcallback():self.reload_status()files=[self.repo.wjoin(x)forxincommit_list]shlib.shell_notify(files)ifself.qnew:commit_list=get_list()self.commit_selected(commit_list,callback)self.enable_mqpanel(True)else:ifnotself.ready_message():returnifself.is_merge():commit_list=get_list(addremove=False)# merges must be committed without specifying file list.self.hg_commit([],callback)else:commit_list=get_list()iflen(commit_list)>0:self.commit_selected(commit_list,callback)elifself.qheaderisnotNone:self.commit_selected([],callback)elifself.closebranchorself.nextbranch:self.commit_selected([],callback)else:gdialog.Prompt(_('Nothing Commited'),_('No committable files selected'),self).run()returndefcommit_selected(self,files,callback):# 1a. get list of chunks not rejectedrepo,ui=self.repo,self.repo.uicwd=os.getcwd()# 2. backup changed files, so we can restore them in the endbackups={}backupdir=repo.join('record-backups')try:os.mkdir(backupdir)exceptOSError,err:iferr.errno!=errno.EEXIST: gdialog.Prompt(_('Commit'),
_('Unable to create ') + backupdir, self).run()
return
++ def finish():+ os.chdir(cwd)+ # restore backup files+ try:+ for realname, tmpname in backups.iteritems():+ util.copyfile(tmpname, repo.wjoin(realname))+ os.unlink(tmpname)+ os.rmdir(backupdir)+ except OSError:+ pass+ callback()+ try:
# backup continues
allchunks = []
forfinfiles:cf=util.pconvert(f)ifcfinself.subrepos:continueifcfnotinself.status[0]:continueiffnotinself.filechunks:continuechunks=self.filechunks[f]iflen(chunks)<2:continue# unfiltered files do not go through backup-revert-patch cyclerejected=[cforcinchunks[1:]ifnotc.active]iflen(rejected)==0:continueallchunks.extend(chunks)fd,tmpname=tempfile.mkstemp(prefix=cf.replace('/','_')+'.',dir=backupdir)os.close(fd)util.copyfile(repo.wjoin(cf),tmpname)backups[cf]=tmpnamefp=cStringIO.StringIO()forn,cinenumerate(allchunks):ifc.filename()inbackupsandc.active:c.write(fp)dopatch=fp.tell()fp.seek(0)ifbackups:ifself.qheaderisnotNone:# 3a. apply filtered patch to top patch's parenthg.revert(repo,self._node1,backups.has_key)else:# 3a. apply filtered patch to clean repo (clean)hg.revert(repo,repo.dirstate.parents()[0],backups.has_key)# 3b. (apply)ifdopatch:try:pfiles={}patch.internalpatch(fp,ui,1,repo.root,files=pfiles,eolmode=None)patch.updatedir(ui,repo,pfiles)exceptpatch.PatchError,err:s=str(err)ifs:raiseutil.Abort(s)else:gdialog.Prompt(_('Commit'), _('Unable to apply patch'), self).run()
return
- def finish():- os.chdir(cwd)- # restore backup files- try:- for realname, tmpname in backups.iteritems():- util.copyfile(tmpname, repo.wjoin(realname))- os.unlink(tmpname)- os.rmdir(backupdir)- except OSError:- pass- callback()- # 4. We prepared working directory according to filtered patch.
# Now is the time to delegate the job to commit/qrefresh
# or the like!
# it is important to first chdir to repo root -- we'll call a# highlevel command with list of pathnames relative to repo rootdelfpcwd=os.getcwd()os.chdir(repo.root)self.hg_commit(files,finish)except:finish()defundo_clicked(self,toolbutton,data=None):response=gdialog.Confirm(_('Confirm Undo Commit'),[],self,_('Undo last commit?')).run()ifresponse!=gtk.RESPONSE_YES:returntip=self.get_tip_rev(True)ifnottip==self.last_commit_id:gdialog.Prompt(_('Undo Commit'),_('Unable to undo!\n\n''Tip revision differs from last commit.'),self).run()returntry:self.repo.ui.quiet=Trueself.repo.rollback()self.repo.ui.quiet=Falseself.last_commit_id=Noneself.reload_status()time.sleep(0.5)# give fs some time to pick up changesshlib.shell_notify([os.getcwd()])except:gdialog.Prompt(_('Undo Commit'),_('Errors during rollback!'),self).run()defshould_addremove(self,files):ifself.test_opt('addremove'):returnTrueresponse=gdialog.Confirm(_('Confirm Add/Remove'),files,self,_('Add/Remove the following files?')).run()ifresponse!=gtk.RESPONSE_YES:returnFalse# This will stay set for further commits (meaning no more# prompts). Problem?self.opts['addremove']=Trueifself.qneworself.qheaderisnotNone:cmdline=['hg','addremove','--verbose','--']cmdline+=[self.repo.wjoin(x)forxinfiles]self.execute_command(cmdline,force=True)returnTruedefready_message(self):buf=self.text.get_buffer()ifbuf.get_char_count()==0:gdialog.Prompt(_('Nothing Commited'),_('Please enter commit message'),self).run()self.text.grab_focus()returnFalsetry:sumlen,maxlen=self.get_lengths(noexcept=False)exceptValueError:gdialog.Prompt(_('Error'),_('Message format configuration error'),self).run()self.msg_config(None)returnFalselines=hglib.tounicode(buf.get_text(buf.get_start_iter(),buf.get_end_iter())).splitlines()ifsumlenandlen(lines[0].rstrip())>sumlen:resp=gdialog.Confirm(_('Confirm Commit'),[],self,_('The summary line length of %i is greater than'' %i.\n\nIgnore format policy and continue'' commit?')%(len(lines[0].rstrip()),sumlen)).run()ifresp!=gtk.RESPONSE_YES:returnFalseifsumlenandlen(lines)>1andlen(lines[1].strip()):resp=gdialog.Confirm(_('Confirm Commit'),[],self,_('The summary line is not followed by a blank'' line.\n\nIgnore format policy and continue'' commit?')).run()ifresp!=gtk.RESPONSE_YES:returnFalseifmaxlen:start=int(sumlen>0)tmp=[len(x.rstrip())>maxlenforxinlines[start:]]errs=[str(x[1]+start+1)forxinzip(tmp,range(len(tmp)))ifx[0]]iferrs:resp=gdialog.Confirm(_('Confirm Commit'),[],self,_('The following lines are over the %i-''character limit: %s.\n\nIgnore format'' policy and continue commit?')%(maxlen,', '.join(errs))).run()ifresp!=gtk.RESPONSE_YES:returnFalsereturnTruedefhg_commit(self,files,callback):# get advanced optionsuser=hglib.fromutf(self.committer_cbbox.get_active_text())ifnotuser:gdialog.Prompt(_('Commit: Invalid username'),_('Your username has not been configured.\n\n''Please configure your username and try again'),self).run()# bring up the config dialog for user to enter their username.# But since we can't be sure they will do it right, we will# have them to retry, to re-trigger the checking mechanism.dlg=thgconfig.ConfigDialog(False,focus='ui.username')dlg.show_all()dlg.run()dlg.hide()self.refreshui()self.refresh_complete()returnself.update_recent_committers(hglib.toutf(user))incs=hglib.fromutf(self.autoinc_entry.get_text())self.opts['include']=[i.strip()foriinincs.split(',')ifi.strip()]autopush=self.autopush.get_active()buf=self.text.get_buffer()begin,end=buf.get_bounds()self.opts['message']=buf.get_text(begin,end)cmdline=['hg','commit','--verbose']ifself.nextbranch:# response: 0=Yes, 1=No, 2=Cancelifself.nextbranchinself.repo.branchtags():pb=[p.branch()forpinself.repo.parents()]ifself.nextbranchinpb:resp=0else:rev=self.repo[self.nextbranch].rev()resp=gdialog.CustomPrompt(_('Confirm Branch Change'),_('Named branch "%s" already exists, ''last used in revision %d\n''Yes\t- Make commit restarting this named branch\n''No\t- Make commit without changing branch\n''Cancel - Cancel this commit')%(self.nextbranch,rev),self,(_('&Yes'),_('&No'),_('&Cancel')),2,2).run()else:resp=gdialog.CustomPrompt(_('Confirm New Branch'),_('Create new named branch "%s" with this commit?\n''Yes\t- Start new branch with this commit\n''No\t- Make commit without branch change\n''Cancel - Cancel this commit')%self.nextbranch,self,(_('&Yes'),_('&No'),_('&Cancel')),2,2).run()ifresp==0:self.repo.dirstate.setbranch(self.nextbranch)elifresp==2:returnelifself.closebranch:cmdline.append('--close-branch')# Use threaded executers (CmdDialog or CmdRunner) so that the Commit# dialog can continue UI handling if it's large commit.ifself.qnew:cmdline[1]='qnew'cmdline.append('--force')ifnotfiles:cmdline+=['-X',self.repo.root]elifself.qheaderisnotNone:cmdline[1]='qrefresh'ifnotfiles:cmdline+=['-X',self.repo.root]elifself.opts['addremove']:cmdline+=['--addremove']ifself.opts['user']oruser:cmdline.extend(['--user',self.opts['user']oruser])ifself.opts['date']:cmdline.extend(['--date',self.opts['date']])files+=self.opts['include']cmdline+=['--message',hglib.fromutf(self.opts['message'])]ifself.qnew:cmdline+=[hglib.fromutf(self.get_qnew_name())]cmdline.append('--')cmdline+=filesifautopush:cmdline=(cmdline,['hg','push'])defdone(return_code,*args):ifreturn_code==0:# refresh overlay icons and commit dialogself.closebranch=Falseself.nextbranch=Noneself.filechunks={}# force re-read of chunksbuf=self.text.get_buffer()ifbuf.get_modified():self.update_recent_messages(self.opts['message'])buf.set_modified(False)ifself.qnew:self.qnew_name.set_text('')hglib.invalidaterepo(self.repo)self.mode='commit'self.qnew=Falseelifself.qheaderisNone:self.text.set_buffer(textview.UndoableTextBuffer())self.msg_cbbox.set_active(-1)self.last_commit_id=self.get_tip_rev(True)ifself.notify_func:self.notify_func(*self.notify_args)ifautopush:text=_('Finish committing and pushing')else:text=_('Finish committing')elifreturn_codeisNone:text=_('Aborted committing')else:text=_('Failed to commit')self.stbar.set_idle_text(text)callback()ifnotself.execute_command(cmdline,done,status=_('Committing changes...'),title=_('Commit')):gdialog.Prompt(_('Cannot run now'),_('Please try again after running ''operation is completed'),self).run()defget_tip_rev(self,refresh=False):ifrefresh:self.repo.invalidate()returnself.repo['tip'].node()defget_qnew_name(self):returnself.qnew_nameandself.qnew_name.get_text().strip()or''defqnew_changed(self,entry):qnew=bool(self.get_qnew_name())ifself.qnew!=qnew:self.qnew=qnewself.mode=qnewand'status'or'commit'self.reload_status()defqnew_activated(self,entry):self.commit_clicked(None)defmsg_add_to_popup(self,textview,menu):defadd(label,handler,icon):item=gtklib.create_menuitem(label,handler,icon)menu.append(item)menu.append(gtk.SeparatorMenuItem())add(_('Paste _Filenames'),self.msg_paste_fnames,gtk.STOCK_PASTE)add(_('App_ly Format'),self.msg_word_wrap,gtk.STOCK_APPLY)add(_('C_onfigure Format...'),self.msg_config,gtk.STOCK_PREFERENCES)menu.show_all()defget_lengths(self,noexcept=True):try:sumlen=int(self.repo.ui.config('tortoisehg','summarylen',0))maxlen=int(self.repo.ui.config('tortoisehg','messagewrap',0))except(TypeError,ValueError):ifnotnoexcept:raiseValueErrorsumlen=0maxlen=0returnsumlen,maxlendefmsg_changed(self,buf):ifbuf.get_char_count()==0:return# no textsumlen,maxlen=self.get_lengths()ifnot(sumlenormaxlen):return# not configured# clear all highlightsstart,end=buf.get_start_iter(),buf.get_end_iter()buf.remove_all_tags(start,end)# highlight overed linesforline_numinrange(buf.get_line_count()):start=buf.get_iter_at_line(line_num)end=start.copy()end.forward_line()line=buf.get_text(start,end).rstrip('\n\r')limit=(line_num==0)andsumlenormaxleniflimitandlen(line)>limit:buf.apply_tag_by_name('over',start,end)defmsg_paste_fnames(self,sender):buf=self.text.get_buffer()fnames=[file[FM_PATH_UTF8]forfileinself.filemodeliffile[FM_CHECKED]]buf.delete_selection(True,True)buf.insert_at_cursor('\n'.join(fnames))defmsg_word_wrap(self,sender):sumlen,maxlen=self.get_lengths()ifnot(sumlenormaxlen):gdialog.Prompt(_('Info Required'),_('Message format needs to be configured'),self).run()self.msg_config(None)returnbuf=self.text.get_buffer()lines=buf.get_text(buf.get_start_iter(),buf.get_end_iter()).splitlines()ifnotlines:returnifsumlenandlen(lines[0].rstrip())>sumlen:gdialog.Prompt(_('Warning'),_('The summary line length of %i is greater than %i')%(len(lines[0].rstrip()),sumlen),self).run()ifsumlenandlen(lines)>1andlen(lines[1].strip()):gdialog.Prompt(_('Warning'),_('The summary line is not followed by a blank line'),self).run()ifnotmaxlen:returnlnum=int(sumlen>0)whilelnum<len(lines):lines[lnum]=lines[lnum].rstrip()+' 'iflines[lnum].endswith('. '):lines[lnum]+=' 'iflen(lines[lnum].rstrip())>maxlen:ind=lines[lnum].rfind(' ',0,maxlen+1)+1ifind>0:iflnum==len(lines)-1ornotlines[lnum+1].strip():lines.insert(lnum+1,lines[lnum][ind:].lstrip())else:lines[lnum+1]=lines[lnum][ind:].lstrip() \
+lines[lnum+1]lines[lnum]=lines[lnum][0:ind].rstrip()lnum+=1buf.set_text('\n'.join(lines))defmsg_config(self,sender):dlg=thgconfig.ConfigDialog(True,focus='tortoisehg.summarylen')dlg.show_all()dlg.run()dlg.hide()self.refreshui()returndefrun(_ui,*pats,**opts):cmdoptions={'user':opts.get('user',''),'date':opts.get('date',''),'logfile':'','message':'','modified':True,'added':True,'removed':True,'deleted':True,'unknown':True,'ignored':False,'subrepo':True,'exclude':[],'include':[],'rev':[],'check':True,'git':False,'addremove':False,}returnGCommit(_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.