mercurial/wireprotov2server.py
changeset 39641 aa7e312375cf
parent 39639 0e03e6a44dee
child 39810 0b61d21f05cc
equal deleted inserted replaced
39640:039bf1eddc2e 39641:aa7e312375cf
   413         caps['rawrepoformats'] = sorted(repo.requirements &
   413         caps['rawrepoformats'] = sorted(repo.requirements &
   414                                         repo.supportedformats)
   414                                         repo.supportedformats)
   415 
   415 
   416     return proto.addcapabilities(repo, caps)
   416     return proto.addcapabilities(repo, caps)
   417 
   417 
   418 def builddeltarequests(store, nodes):
   418 def builddeltarequests(store, nodes, haveparents):
   419     """Build a series of revision delta requests against a backend store.
   419     """Build a series of revision delta requests against a backend store.
   420 
   420 
   421     Returns a list of revision numbers in the order they should be sent
   421     Returns a list of revision numbers in the order they should be sent
   422     and a list of ``irevisiondeltarequest`` instances to be made against
   422     and a list of ``irevisiondeltarequest`` instances to be made against
   423     the backend store.
   423     the backend store.
   428     # a list of nodes and delta preconditions over a figurative wall and
   428     # a list of nodes and delta preconditions over a figurative wall and
   429     # have the storage backend figure it out for us.
   429     # have the storage backend figure it out for us.
   430     revs = dagop.linearize({store.rev(n) for n in nodes}, store.parentrevs)
   430     revs = dagop.linearize({store.rev(n) for n in nodes}, store.parentrevs)
   431 
   431 
   432     requests = []
   432     requests = []
       
   433     seenrevs = set()
   433 
   434 
   434     for rev in revs:
   435     for rev in revs:
   435         node = store.node(rev)
   436         node = store.node(rev)
   436         parents = store.parents(node)
   437         parentnodes = store.parents(node)
   437         deltaparent = store.node(store.deltaparent(rev))
   438         parentrevs = [store.rev(n) for n in parentnodes]
   438 
   439         deltabaserev = store.deltaparent(rev)
   439         # There is a delta in storage. That means we can send the delta
   440         deltabasenode = store.node(deltabaserev)
   440         # efficiently.
   441 
       
   442         # The choice of whether to send a fulltext revision or a delta and
       
   443         # what delta to send is governed by a few factors.
   441         #
   444         #
   442         # But, the delta may be against a revision the receiver doesn't
   445         # To send a delta, we need to ensure the receiver is capable of
   443         # have (e.g. shallow clone or when the delta isn't against a parent
   446         # decoding it. And that requires the receiver to have the base
   444         # revision). For now, we ignore the problem of shallow clone. As
   447         # revision the delta is against.
   445         # long as a delta exists against a parent, we send it.
       
   446         # TODO allow arguments to control this behavior, as the receiver
       
   447         # may not have the base revision in some scenarios.
       
   448         if deltaparent != nullid and deltaparent in parents:
       
   449             basenode = deltaparent
       
   450 
       
   451         # Else there is no delta parent in storage or the delta that is
       
   452         # # there isn't suitable. Let's use a delta against a parent
       
   453         # revision, if possible.
       
   454         #
   448         #
   455         # There is room to check if the delta parent is in the ancestry of
   449         # We can only guarantee the receiver has the base revision if
   456         # this node. But there isn't an API on the manifest storage object
   450         # a) we've already sent the revision as part of this group
   457         # for that. So ignore this case for now.
   451         # b) the receiver has indicated they already have the revision.
   458 
   452         # And the mechanism for "b" is the client indicating they have
   459         elif parents[0] != nullid:
   453         # parent revisions. So this means we can only send the delta if
   460             basenode = parents[0]
   454         # it is sent before or it is against a delta and the receiver says
   461         elif parents[1] != nullid:
   455         # they have a parent.
   462             basenode = parents[1]
   456 
   463 
   457         # We can send storage delta if it is against a revision we've sent
   464         # No potential bases to delta against. Send a full revision.
   458         # in this group.
       
   459         if deltabaserev != nullrev and deltabaserev in seenrevs:
       
   460             basenode = deltabasenode
       
   461 
       
   462         # We can send storage delta if it is against a parent revision and
       
   463         # the receiver indicates they have the parents.
       
   464         elif (deltabaserev != nullrev and deltabaserev in parentrevs
       
   465               and haveparents):
       
   466             basenode = deltabasenode
       
   467 
       
   468         # Otherwise the storage delta isn't appropriate. Fall back to
       
   469         # using another delta, if possible.
       
   470 
       
   471         # Use p1 if we've emitted it or receiver says they have it.
       
   472         elif parentrevs[0] != nullrev and (
       
   473             parentrevs[0] in seenrevs or haveparents):
       
   474             basenode = parentnodes[0]
       
   475 
       
   476         # Use p2 if we've emitted it or receiver says they have it.
       
   477         elif parentrevs[1] != nullrev and (
       
   478             parentrevs[1] in seenrevs or haveparents):
       
   479             basenode = parentnodes[1]
       
   480 
       
   481         # Nothing appropriate to delta against. Send the full revision.
   465         else:
   482         else:
   466             basenode = nullid
   483             basenode = nullid
   467 
   484 
   468         requests.append(changegroup.revisiondeltarequest(
   485         requests.append(changegroup.revisiondeltarequest(
   469             node=node,
   486             node=node,
   470             p1node=parents[0],
   487             p1node=parentnodes[0],
   471             p2node=parents[1],
   488             p2node=parentnodes[1],
   472             # Receiver deals with linknode resolution.
   489             # Receiver deals with linknode resolution.
   473             linknode=nullid,
   490             linknode=nullid,
   474             basenode=basenode,
   491             basenode=basenode,
   475         ))
   492         ))
       
   493 
       
   494         seenrevs.add(rev)
   476 
   495 
   477     return revs, requests
   496     return revs, requests
   478 
   497 
   479 def wireprotocommand(name, args=None, permission='push'):
   498 def wireprotocommand(name, args=None, permission='push'):
   480     """Decorator to declare a wire protocol command.
   499     """Decorator to declare a wire protocol command.
   672 
   691 
   673     return fl
   692     return fl
   674 
   693 
   675 @wireprotocommand('filedata',
   694 @wireprotocommand('filedata',
   676                   args={
   695                   args={
       
   696                       'haveparents': True,
   677                       'nodes': [b'0123456...'],
   697                       'nodes': [b'0123456...'],
   678                       'fields': [b'parents', b'revision'],
   698                       'fields': [b'parents', b'revision'],
   679                       'path': b'foo.txt',
   699                       'path': b'foo.txt',
   680                   },
   700                   },
   681                   permission='pull')
   701                   permission='pull')
   682 def filedata(repo, proto, nodes=None, fields=None, path=None):
   702 def filedata(repo, proto, haveparents=False, nodes=None, fields=None,
       
   703              path=None):
   683     fields = fields or set()
   704     fields = fields or set()
   684 
   705 
   685     if nodes is None:
   706     if nodes is None:
   686         raise error.WireprotoCommandError('nodes argument must be defined')
   707         raise error.WireprotoCommandError('nodes argument must be defined')
   687 
   708 
   700             store.rev(node)
   721             store.rev(node)
   701         except error.LookupError:
   722         except error.LookupError:
   702             raise error.WireprotoCommandError('unknown file node: %s',
   723             raise error.WireprotoCommandError('unknown file node: %s',
   703                                               (hex(node),))
   724                                               (hex(node),))
   704 
   725 
   705     revs, requests = builddeltarequests(store, nodes)
   726     revs, requests = builddeltarequests(store, nodes, haveparents)
   706 
   727 
   707     yield {
   728     yield {
   708         b'totalitems': len(revs),
   729         b'totalitems': len(revs),
   709     }
   730     }
   710 
   731 
   802     yield node
   823     yield node
   803 
   824 
   804 @wireprotocommand('manifestdata',
   825 @wireprotocommand('manifestdata',
   805                   args={
   826                   args={
   806                       'nodes': [b'0123456...'],
   827                       'nodes': [b'0123456...'],
       
   828                       'haveparents': True,
   807                       'fields': [b'parents', b'revision'],
   829                       'fields': [b'parents', b'revision'],
   808                       'tree': b'',
   830                       'tree': b'',
   809                   },
   831                   },
   810                   permission='pull')
   832                   permission='pull')
   811 def manifestdata(repo, proto, nodes=None, fields=None, tree=None):
   833 def manifestdata(repo, proto, haveparents=False, nodes=None, fields=None,
       
   834                  tree=None):
   812     fields = fields or set()
   835     fields = fields or set()
   813 
   836 
   814     if nodes is None:
   837     if nodes is None:
   815         raise error.WireprotoCommandError(
   838         raise error.WireprotoCommandError(
   816             'nodes argument must be defined')
   839             'nodes argument must be defined')
   827             store.rev(node)
   850             store.rev(node)
   828         except error.LookupError:
   851         except error.LookupError:
   829             raise error.WireprotoCommandError(
   852             raise error.WireprotoCommandError(
   830                 'unknown node: %s', (node,))
   853                 'unknown node: %s', (node,))
   831 
   854 
   832     revs, requests = builddeltarequests(store, nodes)
   855     revs, requests = builddeltarequests(store, nodes, haveparents)
   833 
   856 
   834     yield {
   857     yield {
   835         b'totalitems': len(revs),
   858         b'totalitems': len(revs),
   836     }
   859     }
   837 
   860