Mercurial > public > mercurial-scm > hg
comparison hgext/phabricator.py @ 44644:dbe9182c90f5
phabricator: combine commit messages into the review when folding commits
No visible changes here, until an option to enable it is added to `phabsend`.
This combines the Differential fields like Arcanist does, rather than simply
concatenating the text blocks. Aside from populating everything properly in the
web interface, Phabricator fails the review create/update if repeated fields are
seen as would happen with simple concatenation.
On the flip side, now that the Summary and Test Plan fields can contain data
from multiple commits, we can't just join these fields together to determine if
an amend is needed. If that were to happen, every single commit in the folded
range would get amended with the combined commit message, which seems clearly
wrong. Aside from making a minor assumption about the content of the
Differential Revision field (it seems they allow some minor variances with
spacing), this means that for folded reviews, you can't post it, go to the web
page add a missing Test Plan, and then get it added to the commit message by
re-posting it. I don't think that's a big deal.
Differential Revision: https://phab.mercurial-scm.org/D8309
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Fri, 06 Mar 2020 17:03:04 -0500 |
parents | 0437959de6f5 |
children | 419fec8237b7 |
comparison
equal
deleted
inserted
replaced
44643:0437959de6f5 | 44644:dbe9182c90f5 |
---|---|
1065 ) | 1065 ) |
1066 | 1066 |
1067 if actions: | 1067 if actions: |
1068 transactions += actions | 1068 transactions += actions |
1069 | 1069 |
1070 # Parse commit message and update related fields. | 1070 # When folding multiple local commits into a single review, arcanist will |
1071 desc = ctx.description() | 1071 # take the summary line of the first commit as the title, and then |
1072 info = callconduit( | 1072 # concatenate the rest of the remaining messages (including each of their |
1073 repo.ui, b'differential.parsecommitmessage', {b'corpus': desc} | 1073 # first lines) to the rest of the first commit message (each separated by |
1074 ) | 1074 # an empty line), and use that as the summary field. Do the same here. |
1075 for k, v in info[b'fields'].items(): | 1075 # For commits with only a one line message, there is no summary field, as |
1076 if k in [b'title', b'summary', b'testPlan']: | 1076 # this gets assigned to the title. |
1077 transactions.append({b'type': k, b'value': v}) | 1077 fields = util.sortdict() # sorted for stable wire protocol in tests |
1078 | |
1079 for i, _ctx in enumerate([ctx]): | |
1080 # Parse commit message and update related fields. | |
1081 desc = _ctx.description() | |
1082 info = callconduit( | |
1083 repo.ui, b'differential.parsecommitmessage', {b'corpus': desc} | |
1084 ) | |
1085 | |
1086 for k in [b'title', b'summary', b'testPlan']: | |
1087 v = info[b'fields'].get(k) | |
1088 if not v: | |
1089 continue | |
1090 | |
1091 if i == 0: | |
1092 # Title, summary and test plan (if present) are taken verbatim | |
1093 # for the first commit. | |
1094 fields[k] = v.rstrip() | |
1095 continue | |
1096 elif k == b'title': | |
1097 # Add subsequent titles (i.e. the first line of the commit | |
1098 # message) back to the summary. | |
1099 k = b'summary' | |
1100 | |
1101 # Append any current field to the existing composite field | |
1102 fields[k] = b'\n\n'.join(filter(None, [fields.get(k), v.rstrip()])) | |
1103 | |
1104 for k, v in fields.items(): | |
1105 transactions.append({b'type': k, b'value': v}) | |
1078 | 1106 |
1079 params = {b'transactions': transactions} | 1107 params = {b'transactions': transactions} |
1080 if revid is not None: | 1108 if revid is not None: |
1081 # Update an existing Differential Revision | 1109 # Update an existing Differential Revision |
1082 params[b'objectIdentifier'] = revid | 1110 params[b'objectIdentifier'] = revid |
1264 mapping = {} # {oldnode: [newnode]} | 1292 mapping = {} # {oldnode: [newnode]} |
1265 for i, rev in enumerate(revs): | 1293 for i, rev in enumerate(revs): |
1266 old = unfi[rev] | 1294 old = unfi[rev] |
1267 drevid = drevids[i] | 1295 drevid = drevids[i] |
1268 drev = [d for d in drevs if int(d[b'id']) == drevid][0] | 1296 drev = [d for d in drevs if int(d[b'id']) == drevid][0] |
1269 newdesc = getdescfromdrev(drev) | 1297 newdesc = get_amended_desc(drev, old, False) |
1270 # Make sure commit message contain "Differential Revision" | 1298 # Make sure commit message contain "Differential Revision" |
1271 if old.description() != newdesc: | 1299 if old.description() != newdesc: |
1272 if old.phase() == phases.public: | 1300 if old.phase() == phases.public: |
1273 ui.warn( | 1301 ui.warn( |
1274 _(b"warning: not updating public commit %s\n") | 1302 _(b"warning: not updating public commit %s\n") |
1591 testplan = b'Test Plan:\n%s' % testplan | 1619 testplan = b'Test Plan:\n%s' % testplan |
1592 uri = b'Differential Revision: %s' % drev[b'uri'] | 1620 uri = b'Differential Revision: %s' % drev[b'uri'] |
1593 return b'\n\n'.join(filter(None, [title, summary, testplan, uri])) | 1621 return b'\n\n'.join(filter(None, [title, summary, testplan, uri])) |
1594 | 1622 |
1595 | 1623 |
1624 def get_amended_desc(drev, ctx, folded): | |
1625 """similar to ``getdescfromdrev``, but supports a folded series of commits | |
1626 | |
1627 This is used when determining if an individual commit needs to have its | |
1628 message amended after posting it for review. The determination is made for | |
1629 each individual commit, even when they were folded into one review. | |
1630 """ | |
1631 if not folded: | |
1632 return getdescfromdrev(drev) | |
1633 | |
1634 uri = b'Differential Revision: %s' % drev[b'uri'] | |
1635 | |
1636 # Since the commit messages were combined when posting multiple commits | |
1637 # with --fold, the fields can't be read from Phabricator here, or *all* | |
1638 # affected local revisions will end up with the same commit message after | |
1639 # the URI is amended in. Append in the DREV line, or update it if it | |
1640 # exists. At worst, this means commit message or test plan updates on | |
1641 # Phabricator aren't propagated back to the repository, but that seems | |
1642 # reasonable for the case where local commits are effectively combined | |
1643 # in Phabricator. | |
1644 m = _differentialrevisiondescre.search(ctx.description()) | |
1645 if not m: | |
1646 return b'\n\n'.join([ctx.description(), uri]) | |
1647 | |
1648 return _differentialrevisiondescre.sub(uri, ctx.description()) | |
1649 | |
1650 | |
1596 def getdiffmeta(diff): | 1651 def getdiffmeta(diff): |
1597 """get commit metadata (date, node, user, p1) from a diff object | 1652 """get commit metadata (date, node, user, p1) from a diff object |
1598 | 1653 |
1599 The metadata could be "hg:meta", sent by phabsend, like: | 1654 The metadata could be "hg:meta", sent by phabsend, like: |
1600 | 1655 |