diff --git a/contrib/phabricator.py b/contrib/phabricator.py --- a/contrib/phabricator.py +++ b/contrib/phabricator.py @@ -35,6 +35,7 @@ import json import re +from mercurial.node import bin, nullid from mercurial.i18n import _ from mercurial import ( encoding, @@ -158,15 +159,17 @@ nodemap = unfi.changelog.nodemap result = {} # {node: (oldnode or None, drev)} + toconfirm = {} # {node: (oldnode, {precnode}, drev)} for node in nodelist: ctx = unfi[node] - # Check tags like "D123" - for n in obsolete.allprecursors(unfi.obsstore, [node]): + # For tags like "D123", put them into "toconfirm" to verify later + precnodes = list(obsolete.allprecursors(unfi.obsstore, [node])) + for n in precnodes: if n in nodemap: for tag in unfi.nodetags(n): m = _differentialrevisiontagre.match(tag) if m: - result[node] = (n, int(m.group(1))) + toconfirm[node] = (n, set(precnodes), int(m.group(1))) continue # Check commit message (make sure URL matches) @@ -178,6 +181,28 @@ unfi.ui.warn(_('%s: Differential Revision URL ignored - host ' 'does not match config\n') % ctx) + # Double check if tags are genuine by collecting all old nodes from + # Phabricator, and expect precursors overlap with it. + if toconfirm: + confirmed = {} # {drev: {oldnode}} + drevs = [drev for n, precs, drev in toconfirm.values()] + diffs = callconduit(unfi, 'differential.querydiffs', + {'revisionIDs': drevs}) + for diff in diffs.values(): + drev = int(diff[r'revisionID']) + oldnode = bin(encoding.unitolocal(getdiffmeta(diff).get(r'node'))) + if node: + confirmed.setdefault(drev, set()).add(oldnode) + for newnode, (oldnode, precset, drev) in toconfirm.items(): + if bool(precset & confirmed.get(drev, set())): + result[newnode] = (oldnode, drev) + else: + tagname = 'D%d' % drev + tags.tag(repo, tagname, nullid, message=None, user=None, + date=None, local=True) + unfi.ui.warn(_('D%s: local tag removed - does not match ' + 'Differential history\n') % drev) + return result def getdiff(ctx, diffopts):