Mercurial > hg
comparison hgext/phabricator.py @ 44646:5f9c917e3b50
phabricator: teach `getoldnodedrevmap()` to handle folded reviews
The tricky part here is reasoning through all of the possible predecessor
scenarios. In the typical case of submitting a folded range and then
resubmitting it (also folded), filtering the list of commits for the diff stored
on Phabricator through the local predecessor list for each single node will
result in the typical 1:1 mapping to the old node.
There are edge cases like using `hg fold` within the range prior to
resubmitting, that will result in mapping to multiple old nodes. In that case,
the first direct predecessor is needed for the base of the diff, and the last
direct predecessor is needed for the head of the diff in order to make sure that
the entire range is included in the diff content. And none of this matters for
commits in the middle of the range, as they are never used.
Fortunately the only crucial thing here is the `drev` number for each node. For
these complicated cases where there are multiple old nodes, simply ignore them
all. This will cause `createdifferentialrevision()` to generate a new diff
(within the same Differential), and avoids complicating the code.
Differential Revision: https://phab.mercurial-scm.org/D8311
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Mon, 16 Mar 2020 13:36:12 -0400 |
parents | 419fec8237b7 |
children | 99fa161a883c |
comparison
equal
deleted
inserted
replaced
44645:419fec8237b7 | 44646:5f9c917e3b50 |
---|---|
481 if toconfirm: | 481 if toconfirm: |
482 drevs = [drev for force, precs, drev in toconfirm.values()] | 482 drevs = [drev for force, precs, drev in toconfirm.values()] |
483 alldiffs = callconduit( | 483 alldiffs = callconduit( |
484 unfi.ui, b'differential.querydiffs', {b'revisionIDs': drevs} | 484 unfi.ui, b'differential.querydiffs', {b'revisionIDs': drevs} |
485 ) | 485 ) |
486 getnode = lambda d: bin(getdiffmeta(d).get(b'node', b'')) or None | 486 |
487 def getnodes(d, precset): | |
488 # Ignore other nodes that were combined into the Differential | |
489 # that aren't predecessors of the current local node. | |
490 return [n for n in getlocalcommits(d) if n in precset] | |
491 | |
487 for newnode, (force, precset, drev) in toconfirm.items(): | 492 for newnode, (force, precset, drev) in toconfirm.items(): |
488 diffs = [ | 493 diffs = [ |
489 d for d in alldiffs.values() if int(d[b'revisionID']) == drev | 494 d for d in alldiffs.values() if int(d[b'revisionID']) == drev |
490 ] | 495 ] |
491 | 496 |
492 # "precursors" as known by Phabricator | 497 # local predecessors known by Phabricator |
493 phprecset = {getnode(d) for d in diffs} | 498 phprecset = {n for d in diffs for n in getnodes(d, precset)} |
494 | 499 |
495 # Ignore if precursors (Phabricator and local repo) do not overlap, | 500 # Ignore if precursors (Phabricator and local repo) do not overlap, |
496 # and force is not set (when commit message says nothing) | 501 # and force is not set (when commit message says nothing) |
497 if not force and not bool(phprecset & precset): | 502 if not force and not phprecset: |
498 tagname = b'D%d' % drev | 503 tagname = b'D%d' % drev |
499 tags.tag( | 504 tags.tag( |
500 repo, | 505 repo, |
501 tagname, | 506 tagname, |
502 nullid, | 507 nullid, |
517 # Find the last node using Phabricator metadata, and make sure it | 522 # Find the last node using Phabricator metadata, and make sure it |
518 # exists in the repo | 523 # exists in the repo |
519 oldnode = lastdiff = None | 524 oldnode = lastdiff = None |
520 if diffs: | 525 if diffs: |
521 lastdiff = max(diffs, key=lambda d: int(d[b'id'])) | 526 lastdiff = max(diffs, key=lambda d: int(d[b'id'])) |
522 oldnode = getnode(lastdiff) | 527 oldnodes = getnodes(lastdiff, precset) |
528 | |
529 # If this commit was the result of `hg fold` after submission, | |
530 # and now resubmitted with --fold, the easiest thing to do is | |
531 # to leave the node clear. This only results in creating a new | |
532 # diff for the _same_ Differential Revision if this commit is | |
533 # the first or last in the selected range. | |
534 # If this commit is the result of `hg split` in the same | |
535 # scenario, there is a single oldnode here (and multiple | |
536 # newnodes mapped to it). That makes it the same as the normal | |
537 # case, as the edges of the newnode range cleanly maps to one | |
538 # oldnode each. | |
539 if len(oldnodes) == 1: | |
540 oldnode = oldnodes[0] | |
523 if oldnode and not has_node(oldnode): | 541 if oldnode and not has_node(oldnode): |
524 oldnode = None | 542 oldnode = None |
525 | 543 |
526 result[newnode] = (oldnode, lastdiff, drev) | 544 result[newnode] = (oldnode, lastdiff, drev) |
527 | 545 |
1665 return b'\n\n'.join([ctx.description(), uri]) | 1683 return b'\n\n'.join([ctx.description(), uri]) |
1666 | 1684 |
1667 return _differentialrevisiondescre.sub(uri, ctx.description()) | 1685 return _differentialrevisiondescre.sub(uri, ctx.description()) |
1668 | 1686 |
1669 | 1687 |
1688 def getlocalcommits(diff): | |
1689 """get the set of local commits from a diff object | |
1690 | |
1691 See ``getdiffmeta()`` for an example diff object. | |
1692 """ | |
1693 props = diff.get(b'properties') or {} | |
1694 commits = props.get(b'local:commits') or {} | |
1695 if len(commits) > 1: | |
1696 return {bin(c) for c in commits.keys()} | |
1697 | |
1698 # Storing the diff metadata predates storing `local:commits`, so continue | |
1699 # to use that in the --no-fold case. | |
1700 return {bin(getdiffmeta(diff).get(b'node', b'')) or None} | |
1701 | |
1702 | |
1670 def getdiffmeta(diff): | 1703 def getdiffmeta(diff): |
1671 """get commit metadata (date, node, user, p1) from a diff object | 1704 """get commit metadata (date, node, user, p1) from a diff object |
1672 | 1705 |
1673 The metadata could be "hg:meta", sent by phabsend, like: | 1706 The metadata could be "hg:meta", sent by phabsend, like: |
1674 | 1707 |