diff --git a/hgext/phabricator.py b/hgext/phabricator.py --- a/hgext/phabricator.py +++ b/hgext/phabricator.py @@ -11,6 +11,10 @@ revisions in a format suitable for :hg:`import`, and a ``phabupdate`` command to update statuses in batch. +A "phabstatus" view for :hg:`show` is also provided; it displays status +information of Phabricator differentials associated with unfinished +changesets. + By default, Phabricator requires ``Test Plan`` which might prevent some changeset from being sent. The requirement could be disabled by changing ``differential.require-test-plan-field`` config server side. @@ -60,9 +64,12 @@ encoding, error, exthelper, + formatter, + graphmod, httpconnection as httpconnectionmod, match, mdiff, + logcmdutil, obsutil, parser, patch, @@ -80,6 +87,8 @@ procutil, stringutil, ) +from hgext.show import showview + # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -462,6 +471,29 @@ return result +def getdrevmap(repo, nodelist): + """Return a dict mapping each node in `nodelist` to their Differential + Revision ID. + """ + result = {} + for node in nodelist: + result[node] = None + ctx = repo[node] + # Check commit message + m = _differentialrevisiondescre.search(ctx.description()) + if m: + result[node] = int(m.group('id')) + continue + # Check tags + for tag in repo.nodetags(node): + m = _differentialrevisiontagre.match(tag) + if m: + result[node] = int(m.group(1)) + break + + return result + + def getdiff(ctx, diffopts): """plain-text diff without header (user, commit message, etc)""" output = util.stringio() @@ -1648,3 +1680,41 @@ return templateutil.hybriddict({b'url': url, b'id': t,}) return None + + +phabstatus_tmpl = ( + b'{label("changeset.{phase}{if(troubles, \' changeset.troubled\')}", ' + b'shortest(node, 5))} ' + b'[{label("log.branch", branch)}] ' + b'{label("log.description", desc|firstline)} ' + b'({label("log.user", author|user)})\n' +) + + +@showview(b'phabstatus') +def phabstatusshowview(ui, repo): + """Phabricator differiential status""" + revs = repo.revs('sort(_underway(), topo)') + drevmap = getdrevmap(repo, [repo[r].node() for r in revs]) + revs, drevids, revsbydrevid = [], [], {} + for node, drevid in pycompat.iteritems(drevmap): + if drevid is not None: + revs.append(repo[node].rev()) + drevids.append(drevid) + revsbydrevid[drevid] = repo[node].rev() + + revs = smartset.baseset(revs) + drevs = callconduit(ui, b'differential.query', {b'ids': drevids}) + drevsbyrev = {revsbydrevid[int(drev[b'id'])]: drev for drev in drevs} + + def phabstatus(ctx): + drev = drevsbyrev[ctx.rev()] + ui.write(b"%(uri)s %(statusName)s\n" % drev) + + revdag = graphmod.dagwalker(repo, revs) + + ui.setconfig(b'experimental', b'graphshorten', True) + spec = formatter.lookuptemplate(ui, None, phabstatus_tmpl) + displayer = logcmdutil.changesettemplater(ui, repo, spec, buffered=True) + displayer._exthook = phabstatus + logcmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)