doc: use --succ in prune help
--succ is the new only way.
# Copyright 2011 Logilab SA <contact@logilab.fr>"""synchronize patches queues and evolving changesets"""importrefromcStringIOimportStringIOimportjsonfrommercurial.i18nimport_frommercurialimportcommandsfrommercurialimportpatchfrommercurialimportutilfrommercurial.nodeimportnullid,hex,short,binfrommercurialimportcmdutilfrommercurialimporthgfrommercurialimportscmutilfrommercurialimporterrorfrommercurialimportextensionsfrommercurialimportphasesfrommercurialimportobsolete### old compat code#############################BRANCHNAME="qsubmit2"### new command#############################cmdtable={}command=cmdutil.command(cmdtable)@command('^qsync|sync',[('a','review-all',False,_('mark all touched patches ready for review (no editor)')),],'')defcmdsync(ui,repo,**opts):'''Export draft changeset as mq patch in a mq patches repository commit. This command get all changesets in draft phase and create an mq changeset: * on a "qsubmit2" branch (based on the last changeset) * one patch per draft changeset * a series files listing all generated patch * qsubmitdata holding useful information It does use obsolete relation to update patches that already existing in the qsubmit2 branch. Already existing patch which became public, draft or got killed are remove from the mq repo. Patch name are generated using the summary line for changeset description. .. warning:: Series files is ordered topologically. So two series with interleaved changeset will appear interleaved. '''review='edit'ifopts['review_all']:review='all'mqrepo=repo.mq.qrepo()ifmqrepoisNone:raiseutil.Abort('No patches repository')try:parent=mqrepo[BRANCHNAME]excepterror.RepoLookupError:parent=initqsubmit(mqrepo)store,data,touched=fillstore(repo,parent)try:ifnottouched:raiseutil.Abort('Nothing changed')files=['qsubmitdata','series']+touched# mark some as ready for reviewmessage='qsubmit commit\n\n'review_list=[]applied_list=[]ifreview:olddata=get_old_data(parent)oldfiles=dict([(name,bin(ctxhex))forctxhex,nameinolddata])forpatch_nameintouched:try:store.getfile(patch_name)review_list.append(patch_name)exceptIOError:oldnode=oldfiles[patch_name]newnodes=obsolete.successorssets(repo,oldnode)ifnewnodes:newnodes=[nforninnewnodesifnandn[0]inrepo]# remove killingifnotnewnodes:# changeset has been killed (eg. reject)passelse:assertlen(newnodes)==1# conflict!!!newnode=newnodes[0]assertlen(newnode)==1# split unsupported for nownewnode=list(newnode)[0]# XXX unmanaged case where a cs is obsoleted by an unavailable one#if newnode.node() not in repo.changelog.nodemap:# raise util.Abort('%s is obsoleted by an unknown node %s'% (oldnode, newnode))ctx=repo[newnode]ifctx.phase()==phases.public:# appliedapplied_list.append(patch_name)elifctx.phase()==phases.secret:# already exported changeset is now secretrepo.ui.warn("An already exported changeset is now secret!!!")else:# draftassertFalse,"Should be exported"ifreview:ifapplied_list:message+='\n'.join('* applied %s'%xforxinapplied_list)+'\n'ifreview_list:message+='\n'.join('* %s ready for review'%xforxinreview_list)+'\n'memctx=patch.makememctx(mqrepo,(parent.node(),nullid),message,None,None,parent.branch(),files,store,editor=None)ifreview=='edit':memctx._text=cmdutil.commitforceeditor(mqrepo,memctx,[])mqrepo.savecommitmessage(memctx.description())n=memctx.commit()finally:store.close()return0defmakename(ctx):"""create a patch name form a changeset"""descsummary=ctx.description().splitlines()[0]descsummary=re.sub(r'\s+','_',descsummary)descsummary=re.sub(r'\W+','',descsummary)iflen(descsummary)>45:descsummary=descsummary[:42]+'.'return'%s-%s.diff'%(ctx.branch().upper(),descsummary)defget_old_data(mqctx):"""read qsubmit data to fetch previous export data get old data from the content of an mq commit"""try:old_data=mqctx['qsubmitdata']returnjson.loads(old_data.data())excepterror.LookupError:return[]defget_current_data(repo):"""Return what would be exported if no previous data exists"""data=[]forctxinrepo.set('draft() - (obsolete() + merge())'):name=makename(ctx)data.append([ctx.hex(),makename(ctx)])merges=repo.revs('draft() and merge()')ifmerges:repo.ui.warn('ignoring %i merge\n'%len(merges))returndatadefpatchmq(repo,store,olddata,newdata):"""export the mq patches and return all useful data to be exported"""finaldata=[]touched=set()currentdrafts=set(d[0]fordinnewdata)usednew=set()usedold=set()evolve=extensions.find('evolve')foroldhex,oldnameinolddata:ifoldhexinusedold:continue# no duplicateusedold.add(oldhex)oldname=str(oldname)oldnode=bin(oldhex)newnodes=obsolete.successorssets(repo,oldnode)ifnewnodes:newnodes=[nforninnewnodesifnandn[0]inrepo]# remove killingiflen(newnodes)>1:newnodes=[short(nodes[0])fornodesinnewnodes]raiseutil.Abort('%s have more than one newer version: %s'%(oldname,newnodes))ifnewnodes:# else, changeset have been killednewnode=list(newnodes)[0][0]ctx=repo[newnode]ifctx.hex()!=oldhexandctx.phase():fp=StringIO()cmdutil.export(repo,[ctx.rev()],fp=fp)data=fp.getvalue()store.setfile(oldname,data,(None,None))finaldata.append([ctx.hex(),oldname])usednew.add(ctx.hex())touched.add(oldname)continueifoldhexincurrentdrafts:# else changeset is now public or secretfinaldata.append([oldhex,oldname])usednew.add(ctx.hex())continuetouched.add(oldname)fornewhex,newnameinnewdata:ifnewhexinusednew:continuenewnode=bin(newhex)ctx=repo[newnode]fp=StringIO()cmdutil.export(repo,[ctx.rev()],fp=fp)data=fp.getvalue()store.setfile(newname,data,(None,None))finaldata.append([ctx.hex(),newname])touched.add(newname)# sort by branchrev numberfinaldata.sort(key=lambdax:sort_key(repo[x[0]]))# sort touched too (ease review list)stouched=[f[1]forfinfinaldataiff[1]intouched]stouched+=[xforxintouchedifxnotinstouched]returnfinaldata,stoucheddefsort_key(ctx):"""ctx sort key: (branch, rev)"""return(ctx.branch(),ctx.rev())deffillstore(repo,basemqctx):"""fill store with patch data"""olddata=get_old_data(basemqctx)newdata=get_current_data(repo)store=patch.filestore()try:data,touched=patchmq(repo,store,olddata,newdata)# put all name in the seriesseries='\n'.join(d[1]fordindata)+'\n'store.setfile('series',series,(False,False))# export data to ease futur workstore.setfile('qsubmitdata',json.dumps(data,indent=True),(False,False))except:store.close()raisereturnstore,data,toucheddefinitqsubmit(mqrepo):"""create initial qsubmit branch"""store=patch.filestore()try:files=set()store.setfile('DO-NOT-EDIT-THIS-WORKING-COPY-BY-HAND','WE WARNED YOU!',(False,False))store.setfile('.hgignore','^status$\n',(False,False))memctx=patch.makememctx(mqrepo,(nullid,nullid),'qsubmit init',None,None,BRANCHNAME,('.hgignore',),store,editor=None)mqrepo.savecommitmessage(memctx.description())n=memctx.commit()finally:store.close()returnmqrepo[n]