hgext3rd/topic/server.py
changeset 5139 19b8ffd23795
child 5140 c705c4069fb1
equal deleted inserted replaced
5136:bbf33d5f32ef 5139:19b8ffd23795
       
     1 # topic/server.py - server specific behavior with topic
       
     2 #
       
     3 # This software may be used and distributed according to the terms of the
       
     4 # GNU General Public License version 2 or any later version.
       
     5 from mercurial import (
       
     6     extensions,
       
     7     repoview,
       
     8     repoviewutil,
       
     9     wireprototypes,
       
    10     wireprotov1peer,
       
    11     wireprotov1server,
       
    12 )
       
    13 
       
    14 from . import (
       
    15     common,
       
    16     constants,
       
    17 )
       
    18 
       
    19 ### Visibility restriction
       
    20 #
       
    21 # Serving draft changesets with topics to clients without topic extension can
       
    22 # confuse them, because they won't see the topic label and will consider them
       
    23 # normal anonymous heads. Instead we have the option to not serve changesets
       
    24 # with topics to clients without topic support.
       
    25 #
       
    26 # To achieve this, we alter the behavior of the standard `heads` commands and
       
    27 # introduce a new `heads` command that only clients with topic will know about.
       
    28 
       
    29 # compat version of the wireprotocommand decorator, taken from evolve compat
       
    30 
       
    31 FILTERNAME = b'served-no-topic'
       
    32 
       
    33 def computeunservedtopic(repo, visibilityexceptions=None):
       
    34     assert not repo.changelog.filteredrevs
       
    35     filteredrevs = repoview.filtertable[b'served'](repo, visibilityexceptions).copy()
       
    36     mutable = repoview.filtertable[b'immutable'](repo, visibilityexceptions)
       
    37     consider = mutable - filteredrevs
       
    38     cl = repo.changelog
       
    39     extrafiltered = set()
       
    40     for r in consider:
       
    41         if cl.changelogrevision(r).extra.get(constants.extrakey, b''):
       
    42             extrafiltered.add(r)
       
    43     if extrafiltered:
       
    44         filteredrevs = frozenset(filteredrevs | extrafiltered)
       
    45     return filteredrevs
       
    46 
       
    47 def wireprotocommand(name, args=b'', permission=b'pull'):
       
    48     try:
       
    49         from mercurial.wireprotov1server import wireprotocommand
       
    50     except (ImportError, AttributeError):
       
    51         # hg <= 4.6 (b4d85bc122bd)
       
    52         from mercurial.wireproto import wireprotocommand
       
    53     return wireprotocommand(name, args, permission=permission)
       
    54 
       
    55 def wrapheads(orig, repo, proto):
       
    56     """wrap head to hide topic^W draft changeset to old client"""
       
    57     hidetopics = repo.ui.configbool(b'experimental', b'topic.server-gate-topic-changesets')
       
    58     if common.hastopicext(repo) and hidetopics:
       
    59         h = repo.filtered(FILTERNAME).heads()
       
    60         return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n')
       
    61     return orig(repo, proto)
       
    62 
       
    63 def topicheads(repo, proto):
       
    64     """Same as the normal wireprotocol command, but accessing with a different end point."""
       
    65     h = repo.heads()
       
    66     return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n')
       
    67 
       
    68 def wireprotocaps(orig, repo, proto):
       
    69     """advertise the new topic specific `head` command for client with topic"""
       
    70     caps = orig(repo, proto)
       
    71     if common.hastopicext(repo) and repo.peer().capable(b'topics'):
       
    72         caps.append(b'_exttopics_heads')
       
    73     return caps
       
    74 
       
    75 def setupserver(ui):
       
    76     extensions.wrapfunction(wireprotov1server, 'heads', wrapheads)
       
    77     wireprotov1server.commands.pop(b'heads')
       
    78     wireprotocommand(b'heads', permission=b'pull')(wireprotov1server.heads)
       
    79     wireprotocommand(b'_exttopics_heads', permission=b'pull')(topicheads)
       
    80     extensions.wrapfunction(wireprotov1server, '_capabilities', wireprotocaps)
       
    81 
       
    82     class topicpeerexecutor(wireprotov1peer.peerexecutor):
       
    83 
       
    84         def callcommand(self, command, args):
       
    85             if command == b'heads':
       
    86                 if self._peer.capable(b'_exttopics_heads'):
       
    87                     command = b'_exttopics_heads'
       
    88                     if getattr(self._peer, '_exttopics_heads', None) is None:
       
    89                         self._peer._exttopics_heads = self._peer.heads
       
    90             s = super(topicpeerexecutor, self)
       
    91             return s.callcommand(command, args)
       
    92 
       
    93     wireprotov1peer.peerexecutor = topicpeerexecutor
       
    94 
       
    95     if FILTERNAME not in repoview.filtertable:
       
    96         repoview.filtertable[FILTERNAME] = computeunservedtopic
       
    97         repoviewutil.subsettable[FILTERNAME] = b'immutable'
       
    98         repoviewutil.subsettable[b'served'] = FILTERNAME