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 (C) 2007 TK Soh <teekaysoh@gmail.com># Copyright (C) 2009 Steve Borho <steve@borho.org>#importosimportpygtkpygtk.require('2.0')importerrnoimportgtkimportpangoimporttempfileimportcStringIOfrommercurial.i18nimport_frommercurial.nodeimport*frommercurialimportui,hgfromshlibimportshell_notifyfromgdialogimport*fromstatusimport*fromhgcmdimportCmdDialogfromhglibimportfromutfclassGCommit(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._last_commit_id=Noneself.qnew=Falsedefparse_opts(self):GStatus.parse_opts(self)# Need an entry, because extdiff code expects itifnotself.test_opt('rev'):self.opts['rev']=''defget_title(self):root=os.path.basename(self.repo.root)user=self.opts.get('user')ifuser:user='as '+userdate=self.opts.get('date')pats=' '.join(self.pats)ifself.qnew:returnroot+' qnew'elifself.mqmode:patch=self.repo.mq.lookup('qtip')returnroot+' qrefresh '+patchreturn' '.join([root,'commit',pats,user,date])defget_icon(self):return'menucommit.ico'defauto_check(self):ifself.test_opt('check'):forentryinself.filemodel:ifentry[FM_STATUS]in'MAR':entry[FM_CHECKED]=Trueself._update_check_count()defsave_settings(self):settings=GStatus.save_settings(self)settings['gcommit']=self._vpaned.get_position()returnsettingsdefload_settings(self,settings):GStatus.load_settings(self,settings)ifsettings:self._setting_vpos=settings['gcommit']else:self._setting_vpos=-1defget_tbbuttons(self):tbbuttons=GStatus.get_tbbuttons(self)tbbuttons.insert(2,gtk.SeparatorToolItem())self._undo_button=self.make_toolbutton(gtk.STOCK_UNDO,_('_Undo'),self._undo_clicked,tip=_('undo recent commit'))self._commit_button=self.make_toolbutton(gtk.STOCK_OK,_('_Commit'),self._commit_clicked,tip=_('commit'))tbbuttons.insert(2,self._undo_button)tbbuttons.insert(2,self._commit_button)returntbbuttonsdefchanged_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=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)def_first_msg_popdown(self,combo,shown):combo.disconnect(self.popupid)self.popupid=Noneself._update_recent_messages()def_update_recent_messages(self,msg=None):ifmsgisnotNone:self._mru_messages.add(msg)self.settings.write()ifself.popupidisnotNone:returnliststore=self.msg_cbbox.get_model()liststore.clear()formsginself._mru_messages:sumline=msg.split("\n")[0]liststore.append([sumline,msg])defget_body(self):status_body=GStatus.get_body(self)vbox=gtk.VBox()mbox=gtk.HBox()label=gtk.Label(_('Branch: '))mbox.pack_start(label,False,False,2)self.branchentry=gtk.Entry()self.branchentry.set_width_chars(12)mbox.pack_start(self.branchentry,False,False,2)ifhasattr(self.repo,'mq'):label=gtk.Label('QNew: ')mbox.pack_start(label,False,False,2)self.qnew_name=gtk.Entry()self.qnew_name.set_width_chars(12)self.qnew_name.connect('changed',self._qnew_changed)mbox.pack_start(self.qnew_name,False,False,2)else:self.qnew_name=Noneliststore=gtk.ListStore(str,str)self.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)vbox.pack_start(mbox,False,False)self._mru_messages=self.settings.mrul('recent_messages')frame=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)vbox.pack_start(frame)self.text=gtk.TextView()self.text.set_wrap_mode(gtk.WRAP_WORD)self.text.modify_font(pango.FontDescription(self.fontcomment))scroller.add(self.text)self._vpaned=gtk.VPaned()self._vpaned.add1(vbox)self._vpaned.add2(status_body)self._vpaned.set_position(self._setting_vpos)# make ctrl-o trigger commit buttonaccel_group=gtk.AccelGroup()self.add_accel_group(accel_group)self._commit_button.add_accelerator("clicked",accel_group,ord("o"),gtk.gdk.CONTROL_MASK,gtk.ACCEL_VISIBLE)returnself._vpaneddefget_menu_info(self):"""Returns menu info in this order: merge, addrem, unknown, clean, ignored, deleted """merge,addrem,unknown,clean,ignored,deleted,unresolved,resolved \
=GStatus.get_menu_info(self)return(merge+(('_commit',self._commit_file),),addrem+(('_commit',self._commit_file),),unknown+(('_commit',self._commit_file),),clean,ignored,deleted+(('_commit',self._commit_file),),unresolved,resolved,)defshould_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():dialog=Confirm(_('Exit'),[],self,_('Save commit message at exit?'))res=dialog.run()ifres==gtk.RESPONSE_YES:begin,end=buf.get_bounds()self._update_recent_messages(buf.get_text(begin,end))elifres!=gtk.RESPONSE_NO:live=Trueifnotliveandself.main:self._destroying(widget)returnlivedefreload_status(self):ifnotself._ready:returnFalsesuccess=GStatus.reload_status(self)self.branchentry.set_text(self.repo.dirstate.branch())self._check_merge()self._check_patch_queue()self._check_undo()returnsuccess### End of overridable methods ###def_check_undo(self):can_undo=os.path.exists(self.repo.sjoin("undo"))and \
self._last_commit_idisnotNoneself._undo_button.set_sensitive(can_undo)def_check_merge(self):# disable the checkboxes on the filelist if repo in merging statemerged=len(self.repo.changectx(None).parents())>1self.get_toolbutton(_('Re_vert')).set_sensitive(notmerged)self.get_toolbutton(_('_Add')).set_sensitive(notmerged)self.get_toolbutton(_('_Remove')).set_sensitive(notmerged)self.get_toolbutton(_('Move')).set_sensitive(notmerged)ifmerged:# select all changes if repo is mergedforentryinself.filemodel:ifentry[FM_STATUS]in'MARD':entry[FM_CHECKED]=Trueself._update_check_count()# pre-fill commit messagebuf=self.text.get_buffer()buf.set_text(_('merge'))buf.set_modified(False)#else:# self.selectlabel.set_text(# _('toggle change hunks to leave them out of commit'))def_check_patch_queue(self):'''See if an MQ patch is applied, switch to qrefresh mode'''self.qheader=Noneifself.mqmode:patch=self.repo.mq.lookup('qtip')ph=self.repo.mq.readheaders(patch)self.qheader='\n'.join(ph.message)buf=self.text.get_buffer()ifbuf.get_char_count()==0ornotbuf.get_modified():buf.set_text(self.qheader)buf.set_modified(False)c_btn=self.get_toolbutton(_('_Commit'))ifself.qnew:c_btn.set_label(_('QNew'))c_btn.set_tooltip(self.tooltips,_('QNew'))self._hg_call_wrapper('Status',self._do_reload_status)else:c_btn.set_label(_('QRefresh'))c_btn.set_tooltip(self.tooltips,_('QRefresh'))elifself.qnew:c_btn=self.get_toolbutton(_('_Commit'))c_btn.set_label(_('QNew'))c_btn.set_tooltip(self.tooltips,_('QNew'))else:c_btn=self.get_toolbutton(('_Commit'))c_btn.set_label(_('_Commit'))c_btn.set_tooltip(self.tooltips,_('commit'))self.branchentry.set_sensitive(not(self.mqmodeorself.qnew))def_commit_clicked(self,toolbutton,data=None):ifnotself._ready_message():returnTrueiflen(self.repo.changectx(None).parents())>1:# as of Mercurial 1.0, merges must be committed without# specifying file list.self._hg_commit([])shell_notify(self._relevant_files('MAR'))self.reload_status()else:commitable='MAR'addremove_list=self._relevant_files('?!')iflen(addremove_list)andself._should_addremove(addremove_list):commitable+='?!'commit_list=self._relevant_files(commitable)iflen(commit_list)>0:self._commit_selected(commit_list)eliflen(self.filemodel)==0andself.qnew:self._commit_selected([])else:Prompt('Nothing Commited','No committable files selected',self).run()returnTruedef_commit_selected(self,files):# 1a. get list of chunks not rejectedrepo,chunks,ui=self.repo,self._shelve_chunks,self.uimodel=self.diff_modelfiles=[util.pconvert(f)forfinfiles]hlist=[x[DM_CHUNK_ID]forxinmodelifnotx[DM_REJECTED]]# 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:Prompt(_('Commit'),_('Unable to create ')+backupdir,self).run()returntry:# backup continuesforfinfiles:iffnotinself.modified:continuefh=self._filechunks.get(f)ifnotfhorlen(fh)<2:continue# unfiltered files do not go through backup-revert-patch cyclerejected=[xforxinfh[1:]ifmodel[x][DM_REJECTED]]iflen(rejected)==0:continuefd,tmpname=tempfile.mkstemp(prefix=f.replace('/','_')+'.',dir=backupdir)os.close(fd)ui.debug(_('backup %r as %r\n')%(f,tmpname))util.copyfile(repo.wjoin(f),tmpname)backups[f]=tmpnamefp=cStringIO.StringIO()forn,cinenumerate(chunks):ifc.filename()inbackupsandninhlist: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:ui.debug(_('applying patch\n'))ui.debug(fp.getvalue())pfiles={}patch.internalpatch(fp,ui,1,repo.root,files=pfiles)patch.updatedir(ui,repo,pfiles)exceptpatch.PatchError,err:s=str(err)ifs:raiseutil.Abort(s)else:Prompt(_('Commit'),_('Unable to apply patch'),self).run()raiseutil.Abort(_('patch failed to apply'))delfp# 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 rootcwd=os.getcwd()os.chdir(repo.root)try:self._hg_commit(files)finally:os.chdir(cwd)return0finally:# 5. finally restore backed-up filestry:forrealname,tmpnameinbackups.iteritems():ui.debug(_('restoring %r to %r\n')%(tmpname,realname))util.copyfile(tmpname,repo.wjoin(realname))os.unlink(tmpname)os.rmdir(backupdir)exceptOSError:passself.reload_status()def_commit_file(self,stat,file):ifself._ready_message():ifstatnotin'?!'orself._should_addremove([file]):self._hg_commit([file])returnTruedef_undo_clicked(self,toolbutton,data=None):response=Confirm(_('Undo commit'),[],self,_('Undo last commit')).run()ifresponse!=gtk.RESPONSE_YES:returntip=self._get_tip_rev(True)ifnottip==self._last_commit_id:Prompt(_('Undo commit'),_('Unable to undo!\n\n''Tip revision differs from last commit.'),self).run()returntry:self.repo.rollback()self._last_commit_id=Noneself.reload_status()except:Prompt(_('Undo commit'),_('Errors during rollback!'),self).run()def_should_addremove(self,files):ifself.test_opt('addremove'):returnTrueelse:response=Confirm(_('Add/Remove'),files,self).run()ifresponse==gtk.RESPONSE_YES:# This will stay set for further commits (meaning no more prompts). Problem?self.opts['addremove']=TruereturnTruereturnFalsedef_ready_message(self):buf=self.text.get_buffer()ifbuf.get_char_count()==0:Prompt(_('Nothing Commited'),_('Please enter commit message'),self).run()self.text.grab_focus()returnFalsebegin,end=buf.get_bounds()self.opts['message']=buf.get_text(begin,end)returnTruedef_hg_commit(self,files):ifnotself.repo.ui.config('ui','username'):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. fromthgconfigimportConfigDialogdlg=ConfigDialog(self.repo.root,False)dlg.show_all()dlg.focus_field('ui.username')dlg.run()dlg.hide()self.repo=hg.repository(ui.ui(),self.repo.root)self.ui=self.repo.uireturnnewbranch=fromutf(self.branchentry.get_text())ifnewbranch!=self.repo.dirstate.branch():ifnewbranchinself.repo.branchtags():ifnewbranchnotin[p.branch()forpinself.repo.parents()]:response=Confirm(_('Override Branch'),[],self,_('A branch named "%s" already exists,\n''override?')%newbranch).run()else:response=gtk.RESPONSE_YESelse:response=Confirm(_('New Branch'),[],self,_('Create new named branch "%s"?')%newbranch).run()ifresponse==gtk.RESPONSE_YES:self.repo.dirstate.setbranch(newbranch)elifresponse!=gtk.RESPONSE_NO:return# call the threaded CmdDialog to do the commit, so the the large commit# won't get locked up by potential large commit. CmdDialog will also# display the progress of the commit operation.cmdline=['hg','commit','--verbose','--repository',self.repo.root]ifself.qnew:cmdline[1]='qnew'cmdline.append('--force')elifself.qheaderisnotNone:cmdline[1]='qrefresh'ifself.opts['addremove']:cmdline+=['--addremove']ifself.opts['user']:cmdline.extend(['--user',self.opts['user']])ifself.opts['date']:cmdline.extend(['--date',self.opts['date']])cmdline+=['--message',fromutf(self.opts['message'])]ifself.qnew:cmdline+=[fromutf(self._get_qnew_name())]cmdline+=[self.repo.wjoin(x)forxinfiles]dialog=CmdDialog(cmdline,True)dialog.set_transient_for(self)dialog.run()dialog.hide()# refresh overlay icons and commit dialogifdialog.return_code()==0:shell_notify([self.cwd]+files)buf=self.text.get_buffer()ifbuf.get_modified():self._update_recent_messages(self.opts['message'])buf.set_modified(False) if self.qnew:
self.qnew_name.set_text('')
self.repo.invalidate()
- self.state = 'commit'
+ self.mode = 'commit'
self.qnew = False
_mq = self.repo.mq
_mq.__init__(_mq.ui, _mq.basepath, _mq.path)
elifself.qheaderisNone:self.text.set_buffer(gtk.TextBuffer())self._last_commit_id=self._get_tip_rev(True)def_get_tip_rev(self,refresh=False):ifrefresh:self.repo.invalidate()cl=self.repo.changelogtip=cl.node(nullrev+len(cl))returnhex(tip)def_get_qnew_name(self):returnself.qnew_nameandself.qnew_name.get_text().strip()or''def_qnew_changed(self,element):qnew=bool(self._get_qnew_name())ifself.qnew!=qnew:self.qnew=qnewself.mode=qnewand'status'or'commit'self.reload_status()self.qnew_name.grab_focus()# set focus backdeflaunch(root='',files=[],cwd='',main=True,**opts):u=ui.ui()u.updateopts(debug=False,traceback=False)repo=hg.repository(u,path=root)# move cwd to repo root if repo is merged, so we can show# all the changed filesiflen(repo.changectx(None).parents())>1andrepo.root!=cwd:cwd=repo.rootrepo=hg.repository(u,path=cwd)files=[cwd]ct=repo.ui.config('tortoisehg','extcommit',None)ifct=='qct':fromhglibimportthgdispatchargs=['--repository',root,ct]try:thgdispatch(repo.ui,args=args)exceptSystemExit:passreturncmdoptions={'user':opts.get('user',''),'date':opts.get('date',''),'logfile':'','message':'','modified':True,'added':True,'removed':True,'deleted':True,'unknown':True,'ignored':False,'exclude':[],'include':[],'check':True,'git':False,'addremove':False,}dialog=GCommit(u,repo,cwd,files,cmdoptions,main)dialog.display()returndialogdefrun(root='',files=[],cwd='',**opts):# If no files or directories were selected, take current dir# TODO: Not clear if this is best; user may expect repo wideifnotfilesandcwd:files=[cwd]iflaunch(root,files,cwd,True,**opts):gtk.gdk.threads_init()gtk.gdk.threads_enter()gtk.main()gtk.gdk.threads_leave()if__name__=="__main__":importsysfromhglibimportrootpathopts={}opts['cwd']=len(sys.argv)>1andsys.argv[1]oros.getcwd()opts['root']=rootpath(opts['cwd'])run(**opts)
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.