Mercurial > public > mercurial-scm > hg-stable
comparison contrib/phabricator.py @ 33443:e48082e0a8d5
phabricator: verify local tags before trusting them
Previously we trust local tags blindly and that could cause wrong
Differential Revision to be updated, when people switch between Phabricator
instances.
This patch adds verification logic to detect such issue and remove
problematic tags. For example, a tag "D19" was on node "X", the code will
fetch all diffs attached to D19, and check if nodes server-side overlaps
with nodes in precursors. If they do not overlap, create a new Differential
Revision.
Test Plan:
Use a test Phabricator instance, send patches using `hg phabsend`, then
change the local tag manually to a wrong Differential Revision number.
Amend the patch and send again. Make sure the tag gets ignored and deleted.
Differential Revision: https://phab.mercurial-scm.org/D36
author | Jun Wu <quark@fb.com> |
---|---|
date | Tue, 11 Jul 2017 08:17:29 -0700 |
parents | 3ab0d5767b54 |
children | b7a75b9a3386 |
comparison
equal
deleted
inserted
replaced
33442:3ab0d5767b54 | 33443:e48082e0a8d5 |
---|---|
33 from __future__ import absolute_import | 33 from __future__ import absolute_import |
34 | 34 |
35 import json | 35 import json |
36 import re | 36 import re |
37 | 37 |
38 from mercurial.node import bin, nullid | |
38 from mercurial.i18n import _ | 39 from mercurial.i18n import _ |
39 from mercurial import ( | 40 from mercurial import ( |
40 encoding, | 41 encoding, |
41 error, | 42 error, |
42 mdiff, | 43 mdiff, |
156 url, token = readurltoken(repo) | 157 url, token = readurltoken(repo) |
157 unfi = repo.unfiltered() | 158 unfi = repo.unfiltered() |
158 nodemap = unfi.changelog.nodemap | 159 nodemap = unfi.changelog.nodemap |
159 | 160 |
160 result = {} # {node: (oldnode or None, drev)} | 161 result = {} # {node: (oldnode or None, drev)} |
162 toconfirm = {} # {node: (oldnode, {precnode}, drev)} | |
161 for node in nodelist: | 163 for node in nodelist: |
162 ctx = unfi[node] | 164 ctx = unfi[node] |
163 # Check tags like "D123" | 165 # For tags like "D123", put them into "toconfirm" to verify later |
164 for n in obsolete.allprecursors(unfi.obsstore, [node]): | 166 precnodes = list(obsolete.allprecursors(unfi.obsstore, [node])) |
167 for n in precnodes: | |
165 if n in nodemap: | 168 if n in nodemap: |
166 for tag in unfi.nodetags(n): | 169 for tag in unfi.nodetags(n): |
167 m = _differentialrevisiontagre.match(tag) | 170 m = _differentialrevisiontagre.match(tag) |
168 if m: | 171 if m: |
169 result[node] = (n, int(m.group(1))) | 172 toconfirm[node] = (n, set(precnodes), int(m.group(1))) |
170 continue | 173 continue |
171 | 174 |
172 # Check commit message | 175 # Check commit message |
173 m = _differentialrevisiondescre.search(ctx.description()) | 176 m = _differentialrevisiondescre.search(ctx.description()) |
174 if m: | 177 if m: |
175 result[node] = (None, int(m.group(1))) | 178 result[node] = (None, int(m.group(1))) |
179 | |
180 # Double check if tags are genuine by collecting all old nodes from | |
181 # Phabricator, and expect precursors overlap with it. | |
182 if toconfirm: | |
183 confirmed = {} # {drev: {oldnode}} | |
184 drevs = [drev for n, precs, drev in toconfirm.values()] | |
185 diffs = callconduit(unfi, 'differential.querydiffs', | |
186 {'revisionIDs': drevs}) | |
187 for diff in diffs.values(): | |
188 drev = int(diff[r'revisionID']) | |
189 oldnode = bin(encoding.unitolocal(getdiffmeta(diff).get(r'node'))) | |
190 if node: | |
191 confirmed.setdefault(drev, set()).add(oldnode) | |
192 for newnode, (oldnode, precset, drev) in toconfirm.items(): | |
193 if bool(precset & confirmed.get(drev, set())): | |
194 result[newnode] = (oldnode, drev) | |
195 else: | |
196 tagname = 'D%d' % drev | |
197 tags.tag(repo, tagname, nullid, message=None, user=None, | |
198 date=None, local=True) | |
199 unfi.ui.warn(_('D%s: local tag removed - does not match ' | |
200 'Differential history\n') % drev) | |
176 | 201 |
177 return result | 202 return result |
178 | 203 |
179 def getdiff(ctx, diffopts): | 204 def getdiff(ctx, diffopts): |
180 """plain-text diff without header (user, commit message, etc)""" | 205 """plain-text diff without header (user, commit message, etc)""" |