# stack.py - code related to stack workflow## This software may be used and distributed according to the terms of the# GNU General Public License version 2 or any later version.frommercurial.i18nimport_frommercurialimport(destutil,context,error,node,util,)from.evolvebitsimportbuilddependencies,_orderrevs,_singlesuccessorshort=node.short# TODO: compatifnotutil.safehasattr(context.basectx,'orphan'):context.basectx.orphan=context.basectx.unstableifnotutil.safehasattr(context.basectx,'isunstable'):context.basectx.isunstable=context.basectx.troubleddefgetstack(repo,branch=None,topic=None):# XXX need sortingiftopicisnotNoneandbranchisnotNone:raiseerror.ProgrammingError('both branch and topic specified (not defined yet)')eliftopicisnotNone:trevs=repo.revs("topic(%s) - obsolete()",topic)elifbranchisnotNone:trevs=repo.revs("branch(%s) - public() - obsolete() - topic()",branch)else:raiseerror.ProgrammingError('neither branch and topic specified (not defined yet)')revs=_orderrevs(repo,trevs)ifrevs:pt1=repo[revs[0]].p1()ifpt1.obsolete():pt1=repo[_singlesuccessor(repo,pt1)]revs.insert(0,pt1.rev())returnrevsdeflabelsgen(prefix,labelssuffix):""" Takes a label prefix and a list of suffixes. Returns a string of the prefix formatted with each suffix separated with a space. """return' '.join(prefix%suffixforsuffixinlabelssuffix)defshowstack(ui,repo,branch=None,topic=None,opts=None):ifoptsisNone:opts={}iftopicisnotNoneandbranchisnotNone:msg='both branch and topic specified [%s]{%s}(not defined yet)'msg%=(branch,topic)raiseerror.ProgrammingError(msg)eliftopicisnotNone:prefix='t'iftopicnotinrepo.topics:raiseerror.Abort(_('cannot resolve "%s": no such topic found')%topic)elifbranchisnotNone:prefix='b'else:raiseerror.ProgrammingError('neither branch and topic specified (not defined yet)')fm=ui.formatter('topicstack',opts)prev=Noneentries=[]idxmap={}label='topic'iftopic==repo.currenttopic:label='topic.active'data=stackdata(repo,branch=branch,topic=topic)iftopicisnotNone:fm.plain(_('### topic: %s')%ui.label(topic,label),label='topic.stack.summary.topic')if1<data['headcount']:fm.plain(' (')fm.plain('%d heads'%data['headcount'],label='topic.stack.summary.headcount.multiple')fm.plain(')')fm.plain('\n')fm.plain(_('### branch: %s')%'+'.join(data['branches']),# XXX handle multi brancheslabel='topic.stack.summary.branches')iftopicisNone:if1<data['headcount']:fm.plain(' (')fm.plain('%d heads'%data['headcount'],label='topic.stack.summary.headcount.multiple')fm.plain(')')else:ifdata['behindcount']==-1:fm.plain(', ')fm.plain('ambigious rebase destination',label='topic.stack.summary.behinderror')elifdata['behindcount']:fm.plain(', ')fm.plain('%d behind'%data['behindcount'],label='topic.stack.summary.behindcount')fm.plain('\n')foridx,rinenumerate(getstack(repo,branch=branch,topic=topic),0):ctx=repo[r]# special case for t0, b0 as it's hard to plugin into rest of the logicifidx==0:# t0, b0 can be Noneifr==-1:continueentries.append((idx,False,ctx))prev=ctx.rev()continuep1=ctx.p1()ifp1.obsolete():p1=repo[_singlesuccessor(repo,p1)]ifp1.rev()!=prevandp1.node()!=node.nullid:entries.append((idxmap.get(p1.rev()),False,p1))entries.append((idx,True,ctx))idxmap[ctx.rev()]=idxprev=r# super crude initial versionforidx,isentry,ctxinentries[::-1]:states=[]iscurrentrevision=repo.revs('%d and parents()',ctx.rev())ifnotisentry:symbol='^'# "base" is kind of a "ghost" entry# skip other label for them (no current, no unstable)states=['base']elifctx.orphan():# current revision can be unstable also, so in that case show both# the states and the symbol '@' (issue5553)ifiscurrentrevision:states.append('current')symbol='@'symbol='$'states.append('unstable')elifiscurrentrevision:states.append('current')symbol='@'else:symbol=':'states.append('clean')fm.startitem()fm.data(isentry=isentry)ifidxisNone:fm.plain(' ')ifui.verbose:fm.plain(' ')else:fm.write('topic.stack.index','%s%%d'%prefix,idx,label='topic.stack.index '+labelsgen('topic.stack.index.%s',states))ifui.verbose:fm.write('topic.stack.shortnode','(%s)',short(ctx.node()),label='topic.stack.shortnode '+labelsgen('topic.stack.shortnode.%s',states))fm.write('topic.stack.state.symbol','%s',symbol,label='topic.stack.state '+labelsgen('topic.stack.state.%s',states))fm.plain(' ')fm.write('topic.stack.desc','%s',ctx.description().splitlines()[0],label='topic.stack.desc '+labelsgen('topic.stack.desc.%s',states))fm.condwrite(states!=['clean']andidxisnotNone,'topic.stack.state',' (%s)',fm.formatlist(states,'topic.stack.state'),label='topic.stack.state '+labelsgen('topic.stack.state.%s',states))fm.plain('\n')fm.end()defstackdata(repo,branch=None,topic=None):"""get various data about a stack :changesetcount: number of non-obsolete changesets in the stack :troubledcount: number on troubled changesets :headcount: number of heads on the topic :behindcount: number of changeset on rebase destination """data={}revs=getstack(repo,branch,topic)[1:]data['changesetcount']=len(revs)data['troubledcount']=len([rforrinrevsifrepo[r].isunstable()])deps,rdeps=builddependencies(repo,revs)data['headcount']=len([rforrinrevsifnotrdeps[r]])data['behindcount']=0ifrevs:minroot=[min(rforrinrevsifnotdeps[r])]try:dest=destutil.destmerge(repo,action='rebase',sourceset=minroot,onheadcheck=False)data['behindcount']=len(repo.revs("only(%d, %ld)",dest,minroot))excepterror.NoMergeDestAbort:data['behindcount']=0excepterror.ManyMergeDestAbort:data['behindcount']=-1data['branches']=sorted(set(repo[r].branch()forrinrevs))returndata