hgext3rd/evolve/headchecking.py
branchstable
changeset 5268 11c359b4071d
child 5272 b20d04641c0f
equal deleted inserted replaced
5267:ba53591d4aab 5268:11c359b4071d
       
     1 from __future__ import absolute_import
       
     2 
       
     3 import functools
       
     4 
       
     5 from mercurial import (
       
     6     discovery,
       
     7     extensions,
       
     8     phases,
       
     9 )
       
    10 
       
    11 
       
    12 from . import (
       
    13     compat,
       
    14     exthelper,
       
    15 )
       
    16 
       
    17 eh = exthelper.exthelper()
       
    18 
       
    19 
       
    20 @eh.uisetup
       
    21 def uisetup(ui):
       
    22     extensions.wrapfunction(discovery, '_postprocessobsolete', _postprocessobsolete)
       
    23 
       
    24 def branchinfo(pushop, repo, node):
       
    25     return repo[node].branch()
       
    26 
       
    27 # taken from 7d5455b988ec + branchinfo abstraction.
       
    28 def _postprocessobsolete(orig, pushop, futurecommon, candidate_newhs):
       
    29     """post process the list of new heads with obsolescence information
       
    30 
       
    31     Exists as a sub-function to contain the complexity and allow extensions to
       
    32     experiment with smarter logic.
       
    33 
       
    34     Returns (newheads, discarded_heads) tuple
       
    35     """
       
    36     pushingmarkerfor = discovery.pushingmarkerfor
       
    37     # known issue
       
    38     #
       
    39     # * We "silently" skip processing on all changeset unknown locally
       
    40     #
       
    41     # * if <nh> is public on the remote, it won't be affected by obsolete
       
    42     #     marker and a new is created
       
    43 
       
    44     # define various utilities and containers
       
    45     repo = pushop.repo
       
    46     unfi = repo.unfiltered()
       
    47     torev = compat.getgetrev(unfi.changelog)
       
    48     public = phases.public
       
    49     getphase = unfi._phasecache.phase
       
    50     ispublic = lambda r: getphase(unfi, r) == public
       
    51     ispushed = lambda n: torev(n) in futurecommon
       
    52     hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, ispushed)
       
    53     successorsmarkers = unfi.obsstore.successors
       
    54     newhs = set()  # final set of new heads
       
    55     discarded = set()  # new head of fully replaced branch
       
    56 
       
    57     localcandidate = set()  # candidate heads known locally
       
    58     unknownheads = set()  # candidate heads unknown locally
       
    59     for h in candidate_newhs:
       
    60         if h in unfi:
       
    61             localcandidate.add(h)
       
    62         else:
       
    63             if successorsmarkers.get(h) is not None:
       
    64                 msg = (
       
    65                     b'checkheads: remote head unknown locally has'
       
    66                     b' local marker: %s\n'
       
    67                 )
       
    68                 repo.ui.debug(msg % hex(h))
       
    69             unknownheads.add(h)
       
    70 
       
    71     # fast path the simple case
       
    72     if len(localcandidate) == 1:
       
    73         return unknownheads | set(candidate_newhs), set()
       
    74 
       
    75     # actually process branch replacement
       
    76     while localcandidate:
       
    77         nh = localcandidate.pop()
       
    78         current_branch = branchinfo(pushop, unfi, nh)
       
    79         # run this check early to skip the evaluation of the whole branch
       
    80         if torev(nh) in futurecommon or ispublic(torev(nh)):
       
    81             newhs.add(nh)
       
    82             continue
       
    83 
       
    84         # Get all revs/nodes on the branch exclusive to this head
       
    85         # (already filtered heads are "ignored"))
       
    86         branchrevs = unfi.revs(
       
    87             b'only(%n, (%ln+%ln))', nh, localcandidate, newhs
       
    88         )
       
    89 
       
    90         branchnodes = []
       
    91         for r in branchrevs:
       
    92             ctx = unfi[r]
       
    93             if ctx.branch() == current_branch:
       
    94                 branchnodes.append(ctx.node())
       
    95 
       
    96         # The branch won't be hidden on the remote if
       
    97         # * any part of it is public,
       
    98         # * any part of it is considered part of the result by previous logic,
       
    99         # * if we have no markers to push to obsolete it.
       
   100         if (
       
   101             any(ispublic(torev(n)) for n in branchnodes)
       
   102             or any(torev(n) in futurecommon for n in branchnodes)
       
   103             or any(not hasoutmarker(n) for n in branchnodes)
       
   104         ):
       
   105             newhs.add(nh)
       
   106         else:
       
   107             # note: there is a corner case if there is a merge in the branch.
       
   108             # we might end up with -more- heads.  However, these heads are not
       
   109             # "added" by the push, but more by the "removal" on the remote so I
       
   110             # think is a okay to ignore them,
       
   111             discarded.add(nh)
       
   112     newhs |= unknownheads
       
   113     return newhs, discarded