Patchwork [3,of,3] phabricator: add phabread command to read patches

login
register
mail settings
Submitter Jun Wu
Date July 3, 2017, 3:10 a.m.
Message ID <ee0c338d2115294aba8d.1499051420@x1c>
Download mbox | patch
Permalink /patch/21946/
State Accepted
Headers show

Comments

Jun Wu - July 3, 2017, 3:10 a.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1499051289 25200
#      Sun Jul 02 20:08:09 2017 -0700
# Node ID ee0c338d2115294aba8d844d62b9615d48517b94
# Parent  edb97a6c5f1065227180e41010076ad09697b408
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r ee0c338d2115
phabricator: add phabread command to read patches

This patch adds a `phabread` command generating plain-text patches from
Phabricator, suitable for `hg import`. It respects `hg:meta` so user and
date information might be preserved. And it removes `Summary:` field name
which makes the commit message a bit tidier.

To support stacked diffs, a `--stack` flag was added to read dependent
patches recursively.

Patch

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -8,5 +8,6 @@ 
 
 This extension provides a ``phabsend`` command which sends a stack of
-changesets to Phabricator without amending commit messages.
+changesets to Phabricator without amending commit messages, and a ``phabread``
+command which could serialize a stack of diffs to plain-text patches.
 
 By default, Phabricator requires ``Test Plan`` which might prevent some
@@ -274,2 +275,64 @@  def phabsend(ui, repo, *revs, **opts):
                                             ctx.description().split('\n')[0]))
         lastrevid = newrevid
+
+_summaryre = re.compile('^Summary:\s*', re.M)
+
+def readpatch(repo, params, recursive=False):
+    """generate plain-text patch readable by 'hg import'
+
+    params is passed to "differential.query". If recursive is True, also return
+    dependent patches.
+    """
+    # Differential Revisions
+    drevs = callconduit(repo, 'differential.query', params)
+    if len(drevs) == 1:
+        drev = drevs[0]
+    else:
+        raise error.Abort(_('cannot get Differential Revision %r') % params)
+
+    repo.ui.note(_('reading D%s\n') % drev[r'id'])
+
+    diffid = max(int(v) for v in drev[r'diffs'])
+    body = callconduit(repo, 'differential.getrawdiff', {'diffID': diffid})
+    desc = callconduit(repo, 'differential.getcommitmessage',
+                       {'revision_id': drev[r'id']})
+    header = '# HG changeset patch\n'
+
+    # Remove potential empty "Summary:"
+    desc = _summaryre.sub('', desc)
+
+    # Try to preserve metadata (user, date) from hg:meta property
+    diffs = callconduit(repo, 'differential.querydiffs', {'ids': [diffid]})
+    props = diffs[str(diffid)][r'properties'] # could be empty list or dict
+    if props and r'hg:meta' in props:
+        meta = props[r'hg:meta']
+        for k, v in meta.items():
+            header += '# %s %s\n' % (k.capitalize(), v)
+
+    patch = ('%s%s\n%s') % (header, desc, body)
+
+    # Check dependencies
+    if recursive:
+        auxiliary = drev.get(r'auxiliary', {})
+        depends = auxiliary.get(r'phabricator:depends-on', [])
+        for phid in depends:
+            patch = readpatch(repo, {'phids': [phid]}, recursive=True) + patch
+    return patch
+
+@command('phabread',
+         [('', 'stack', False, _('read dependencies'))],
+         _('REVID [OPTIONS]'))
+def phabread(ui, repo, revid, **opts):
+    """print patches from Phabricator suitable for importing
+
+    REVID could be a Differential Revision identity, like ``D123``, or just the
+    number ``123``, or a full URL like ``https://phab.example.com/D123``.
+
+    If --stack is given, follow dependencies information and read all patches.
+    """
+    try:
+        revid = int(revid.split('/')[-1].replace('D', ''))
+    except ValueError:
+        raise error.Abort(_('invalid Revision ID: %s') % revid)
+    patch = readpatch(repo, {'ids': [revid]}, recursive=opts.get('stack'))
+    ui.write(patch)