5 # This software may be used and distributed according to the terms of the |
5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. |
6 # GNU General Public License version 2 or any later version. |
7 """simple Phabricator integration |
7 """simple Phabricator integration |
8 |
8 |
9 This extension provides a ``phabsend`` command which sends a stack of |
9 This extension provides a ``phabsend`` command which sends a stack of |
10 changesets to Phabricator without amending commit messages. |
10 changesets to Phabricator without amending commit messages, and a ``phabread`` |
|
11 command which prints a stack of revisions in a format suitable |
|
12 for :hg:`import`. |
11 |
13 |
12 By default, Phabricator requires ``Test Plan`` which might prevent some |
14 By default, Phabricator requires ``Test Plan`` which might prevent some |
13 changeset from being sent. The requirement could be disabled by changing |
15 changeset from being sent. The requirement could be disabled by changing |
14 ``differential.require-test-plan-field`` config server side. |
16 ``differential.require-test-plan-field`` config server side. |
15 |
17 |
271 action = _('skipped') |
274 action = _('skipped') |
272 |
275 |
273 ui.write(_('D%s: %s - %s: %s\n') % (newrevid, action, ctx, |
276 ui.write(_('D%s: %s - %s: %s\n') % (newrevid, action, ctx, |
274 ctx.description().split('\n')[0])) |
277 ctx.description().split('\n')[0])) |
275 lastrevid = newrevid |
278 lastrevid = newrevid |
|
279 |
|
280 _summaryre = re.compile('^Summary:\s*', re.M) |
|
281 |
|
282 def readpatch(repo, params, recursive=False): |
|
283 """generate plain-text patch readable by 'hg import' |
|
284 |
|
285 params is passed to "differential.query". If recursive is True, also return |
|
286 dependent patches. |
|
287 """ |
|
288 # Differential Revisions |
|
289 drevs = callconduit(repo, 'differential.query', params) |
|
290 if len(drevs) == 1: |
|
291 drev = drevs[0] |
|
292 else: |
|
293 raise error.Abort(_('cannot get Differential Revision %r') % params) |
|
294 |
|
295 repo.ui.note(_('reading D%s\n') % drev[r'id']) |
|
296 |
|
297 diffid = max(int(v) for v in drev[r'diffs']) |
|
298 body = callconduit(repo, 'differential.getrawdiff', {'diffID': diffid}) |
|
299 desc = callconduit(repo, 'differential.getcommitmessage', |
|
300 {'revision_id': drev[r'id']}) |
|
301 header = '# HG changeset patch\n' |
|
302 |
|
303 # Remove potential empty "Summary:" |
|
304 desc = _summaryre.sub('', desc) |
|
305 |
|
306 # Try to preserve metadata (user, date) from hg:meta property |
|
307 diffs = callconduit(repo, 'differential.querydiffs', {'ids': [diffid]}) |
|
308 props = diffs[str(diffid)][r'properties'] # could be empty list or dict |
|
309 if props and r'hg:meta' in props: |
|
310 meta = props[r'hg:meta'] |
|
311 for k, v in meta.items(): |
|
312 header += '# %s %s\n' % (k.capitalize(), v) |
|
313 |
|
314 patch = ('%s%s\n%s') % (header, desc, body) |
|
315 |
|
316 # Check dependencies |
|
317 if recursive: |
|
318 auxiliary = drev.get(r'auxiliary', {}) |
|
319 depends = auxiliary.get(r'phabricator:depends-on', []) |
|
320 for phid in depends: |
|
321 patch = readpatch(repo, {'phids': [phid]}, recursive=True) + patch |
|
322 return patch |
|
323 |
|
324 @command('phabread', |
|
325 [('', 'stack', False, _('read dependencies'))], |
|
326 _('REVID [OPTIONS]')) |
|
327 def phabread(ui, repo, revid, **opts): |
|
328 """print patches from Phabricator suitable for importing |
|
329 |
|
330 REVID could be a Differential Revision identity, like ``D123``, or just the |
|
331 number ``123``, or a full URL like ``https://phab.example.com/D123``. |
|
332 |
|
333 If --stack is given, follow dependencies information and read all patches. |
|
334 """ |
|
335 try: |
|
336 revid = int(revid.split('/')[-1].replace('D', '')) |
|
337 except ValueError: |
|
338 raise error.Abort(_('invalid Revision ID: %s') % revid) |
|
339 patch = readpatch(repo, {'ids': [revid]}, recursive=opts.get('stack')) |
|
340 ui.write(patch) |