hgext/show.py
changeset 33197 c5a07a3abe7d
parent 33137 99ce2f586cd4
child 33208 9e7efe421395
equal deleted inserted replaced
33196:439b4d005b4a 33197:c5a07a3abe7d
    30 from mercurial.i18n import _
    30 from mercurial.i18n import _
    31 from mercurial.node import nullrev
    31 from mercurial.node import nullrev
    32 from mercurial import (
    32 from mercurial import (
    33     cmdutil,
    33     cmdutil,
    34     commands,
    34     commands,
       
    35     destutil,
    35     error,
    36     error,
    36     formatter,
    37     formatter,
    37     graphmod,
    38     graphmod,
       
    39     phases,
    38     pycompat,
    40     pycompat,
    39     registrar,
    41     registrar,
    40     revset,
    42     revset,
    41     revsetlang,
    43     revsetlang,
    42 )
    44 )
   169         fm.write('bookmark', '%s', bm)
   171         fm.write('bookmark', '%s', bm)
   170         fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
   172         fm.write('node', fm.hexfunc(node), fm.hexfunc(node))
   171         fm.data(active=bm == active,
   173         fm.data(active=bm == active,
   172                 longestbookmarklen=longestname)
   174                 longestbookmarklen=longestname)
   173 
   175 
       
   176 @showview('stack', csettopic='stack')
       
   177 def showstack(ui, repo, displayer):
       
   178     """current line of work"""
       
   179     wdirctx = repo['.']
       
   180     if wdirctx.rev() == nullrev:
       
   181         raise error.Abort(_('stack view only available when there is a '
       
   182                             'working directory'))
       
   183 
       
   184     if wdirctx.phase() == phases.public:
       
   185         ui.write(_('(empty stack; working directory is a published '
       
   186                    'changeset)\n'))
       
   187         return
       
   188 
       
   189     # TODO extract "find stack" into a function to facilitate
       
   190     # customization and reuse.
       
   191 
       
   192     baserev = destutil.stackbase(ui, repo)
       
   193     basectx = None
       
   194 
       
   195     if baserev is None:
       
   196         baserev = wdirctx.rev()
       
   197         stackrevs = {wdirctx.rev()}
       
   198     else:
       
   199         stackrevs = set(repo.revs('%d::.', baserev))
       
   200 
       
   201     ctx = repo[baserev]
       
   202     if ctx.p1().rev() != nullrev:
       
   203         basectx = ctx.p1()
       
   204 
       
   205     # And relevant descendants.
       
   206     branchpointattip = False
       
   207     cl = repo.changelog
       
   208 
       
   209     for rev in cl.descendants([wdirctx.rev()]):
       
   210         ctx = repo[rev]
       
   211 
       
   212         # Will only happen if . is public.
       
   213         if ctx.phase() == phases.public:
       
   214             break
       
   215 
       
   216         stackrevs.add(ctx.rev())
       
   217 
       
   218         if len(ctx.children()) > 1:
       
   219             branchpointattip = True
       
   220             break
       
   221 
       
   222     stackrevs = list(reversed(sorted(stackrevs)))
       
   223 
       
   224     # Find likely target heads for the current stack. These are likely
       
   225     # merge or rebase targets.
       
   226     if basectx:
       
   227         # TODO make this customizable?
       
   228         newheads = set(repo.revs('heads(%d::) - %ld - not public()',
       
   229                                  basectx.rev(), stackrevs))
       
   230     else:
       
   231         newheads = set()
       
   232 
       
   233     try:
       
   234         cmdutil.findcmd('rebase', commands.table)
       
   235         haverebase = True
       
   236     except error.UnknownCommand:
       
   237         haverebase = False
       
   238 
       
   239     # TODO use templating.
       
   240     # TODO consider using graphmod. But it may not be necessary given
       
   241     # our simplicity and the customizations required.
       
   242     # TODO use proper graph symbols from graphmod
       
   243 
       
   244     shortesttmpl = formatter.maketemplater(ui, '{shortest(node, 5)}')
       
   245     def shortest(ctx):
       
   246         return shortesttmpl.render({'ctx': ctx, 'node': ctx.hex()})
       
   247 
       
   248     # We write out new heads to aid in DAG awareness and to help with decision
       
   249     # making on how the stack should be reconciled with commits made since the
       
   250     # branch point.
       
   251     if newheads:
       
   252         # Calculate distance from base so we can render the count and so we can
       
   253         # sort display order by commit distance.
       
   254         revdistance = {}
       
   255         for head in newheads:
       
   256             # There is some redundancy in DAG traversal here and therefore
       
   257             # room to optimize.
       
   258             ancestors = cl.ancestors([head], stoprev=basectx.rev())
       
   259             revdistance[head] = len(list(ancestors))
       
   260 
       
   261         sourcectx = repo[stackrevs[-1]]
       
   262 
       
   263         sortedheads = sorted(newheads, key=lambda x: revdistance[x],
       
   264                              reverse=True)
       
   265 
       
   266         for i, rev in enumerate(sortedheads):
       
   267             ctx = repo[rev]
       
   268 
       
   269             if i:
       
   270                 ui.write(': ')
       
   271             else:
       
   272                 ui.write('  ')
       
   273 
       
   274             ui.write(('o  '))
       
   275             displayer.show(ctx)
       
   276             displayer.flush(ctx)
       
   277             ui.write('\n')
       
   278 
       
   279             if i:
       
   280                 ui.write(':/')
       
   281             else:
       
   282                 ui.write(' /')
       
   283 
       
   284             ui.write('    (')
       
   285             ui.write(_('%d commits ahead') % revdistance[rev],
       
   286                      label='stack.commitdistance')
       
   287 
       
   288             if haverebase:
       
   289                 # TODO may be able to omit --source in some scenarios
       
   290                 ui.write('; ')
       
   291                 ui.write(('hg rebase --source %s --dest %s' % (
       
   292                          shortest(sourcectx), shortest(ctx))),
       
   293                          label='stack.rebasehint')
       
   294 
       
   295             ui.write(')\n')
       
   296 
       
   297         ui.write(':\n:    ')
       
   298         ui.write(_('(stack head)\n'), label='stack.label')
       
   299 
       
   300     if branchpointattip:
       
   301         ui.write(' \\ /  ')
       
   302         ui.write(_('(multiple children)\n'), label='stack.label')
       
   303         ui.write('  |\n')
       
   304 
       
   305     for rev in stackrevs:
       
   306         ctx = repo[rev]
       
   307         symbol = '@' if rev == wdirctx.rev() else 'o'
       
   308 
       
   309         if newheads:
       
   310             ui.write(': ')
       
   311         else:
       
   312             ui.write('  ')
       
   313 
       
   314         ui.write(symbol, '  ')
       
   315         displayer.show(ctx)
       
   316         displayer.flush(ctx)
       
   317         ui.write('\n')
       
   318 
       
   319     # TODO display histedit hint?
       
   320 
       
   321     if basectx:
       
   322         # Vertically and horizontally separate stack base from parent
       
   323         # to reinforce stack boundary.
       
   324         if newheads:
       
   325             ui.write(':/   ')
       
   326         else:
       
   327             ui.write(' /   ')
       
   328 
       
   329         ui.write(_('(stack base)'), '\n', label='stack.label')
       
   330         ui.write(('o  '))
       
   331 
       
   332         displayer.show(basectx)
       
   333         displayer.flush(basectx)
       
   334         ui.write('\n')
       
   335 
   174 @revsetpredicate('_underway([commitage[, headage]])')
   336 @revsetpredicate('_underway([commitage[, headage]])')
   175 def underwayrevset(repo, subset, x):
   337 def underwayrevset(repo, subset, x):
   176     args = revset.getargsdict(x, 'underway', 'commitage headage')
   338     args = revset.getargsdict(x, 'underway', 'commitage headage')
   177     if 'commitage' not in args:
   339     if 'commitage' not in args:
   178         args['commitage'] = None
   340         args['commitage'] = None