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