hgext/infinitepush/bundleparts.py
changeset 37187 03ff17a4bf53
child 37193 5a9692d0d6fc
equal deleted inserted replaced
37186:6d43b39fbaa0 37187:03ff17a4bf53
       
     1 # Copyright 2017 Facebook, Inc.
       
     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 
       
     6 from __future__ import absolute_import
       
     7 
       
     8 from mercurial.i18n import _
       
     9 
       
    10 from mercurial import (
       
    11     bundle2,
       
    12     changegroup,
       
    13     error,
       
    14     extensions,
       
    15     revsetlang,
       
    16     util,
       
    17 )
       
    18 
       
    19 from . import common
       
    20 
       
    21 encodebookmarks = common.encodebookmarks
       
    22 isremotebooksenabled = common.isremotebooksenabled
       
    23 
       
    24 scratchbranchparttype = 'b2x:infinitepush'
       
    25 scratchbookmarksparttype = 'b2x:infinitepushscratchbookmarks'
       
    26 
       
    27 def getscratchbranchparts(repo, peer, outgoing, confignonforwardmove,
       
    28                          ui, bookmark, create):
       
    29     if not outgoing.missing:
       
    30         raise error.Abort(_('no commits to push'))
       
    31 
       
    32     if scratchbranchparttype not in bundle2.bundle2caps(peer):
       
    33         raise error.Abort(_('no server support for %r') % scratchbranchparttype)
       
    34 
       
    35     _validaterevset(repo, revsetlang.formatspec('%ln', outgoing.missing),
       
    36                     bookmark)
       
    37 
       
    38     supportedversions = changegroup.supportedoutgoingversions(repo)
       
    39     # Explicitly avoid using '01' changegroup version in infinitepush to
       
    40     # support general delta
       
    41     supportedversions.discard('01')
       
    42     cgversion = min(supportedversions)
       
    43     _handlelfs(repo, outgoing.missing)
       
    44     cg = changegroup.makestream(repo, outgoing, cgversion, 'push')
       
    45 
       
    46     params = {}
       
    47     params['cgversion'] = cgversion
       
    48     if bookmark:
       
    49         params['bookmark'] = bookmark
       
    50         # 'prevbooknode' is necessary for pushkey reply part
       
    51         params['bookprevnode'] = ''
       
    52         if bookmark in repo:
       
    53             params['bookprevnode'] = repo[bookmark].hex()
       
    54         if create:
       
    55             params['create'] = '1'
       
    56     if confignonforwardmove:
       
    57         params['force'] = '1'
       
    58 
       
    59     # Do not send pushback bundle2 part with bookmarks if remotenames extension
       
    60     # is enabled. It will be handled manually in `_push()`
       
    61     if not isremotebooksenabled(ui):
       
    62         params['pushbackbookmarks'] = '1'
       
    63 
       
    64     parts = []
       
    65 
       
    66     # .upper() marks this as a mandatory part: server will abort if there's no
       
    67     #  handler
       
    68     parts.append(bundle2.bundlepart(
       
    69         scratchbranchparttype.upper(),
       
    70         advisoryparams=params.iteritems(),
       
    71         data=cg))
       
    72 
       
    73     try:
       
    74         treemod = extensions.find('treemanifest')
       
    75         mfnodes = []
       
    76         for node in outgoing.missing:
       
    77             mfnodes.append(('', repo[node].manifestnode()))
       
    78 
       
    79         # Only include the tree parts if they all exist
       
    80         if not repo.manifestlog.datastore.getmissing(mfnodes):
       
    81             parts.append(treemod.createtreepackpart(
       
    82                 repo, outgoing, treemod.TREEGROUP_PARTTYPE2))
       
    83     except KeyError:
       
    84         pass
       
    85 
       
    86     return parts
       
    87 
       
    88 def getscratchbookmarkspart(peer, bookmarks):
       
    89     if scratchbookmarksparttype not in bundle2.bundle2caps(peer):
       
    90         raise error.Abort(
       
    91             _('no server support for %r') % scratchbookmarksparttype)
       
    92 
       
    93     return bundle2.bundlepart(
       
    94         scratchbookmarksparttype.upper(),
       
    95         data=encodebookmarks(bookmarks))
       
    96 
       
    97 def _validaterevset(repo, revset, bookmark):
       
    98     """Abort if the revs to be pushed aren't valid for a scratch branch."""
       
    99     if not repo.revs(revset):
       
   100         raise error.Abort(_('nothing to push'))
       
   101     if bookmark:
       
   102         # Allow bundle with many heads only if no bookmark is specified
       
   103         heads = repo.revs('heads(%r)', revset)
       
   104         if len(heads) > 1:
       
   105             raise error.Abort(
       
   106                 _('cannot push more than one head to a scratch branch'))
       
   107 
       
   108 def _handlelfs(repo, missing):
       
   109     '''Special case if lfs is enabled
       
   110 
       
   111     If lfs is enabled then we need to call prepush hook
       
   112     to make sure large files are uploaded to lfs
       
   113     '''
       
   114     try:
       
   115         lfsmod = extensions.find('lfs')
       
   116         lfsmod.wrapper.uploadblobsfromrevs(repo, missing)
       
   117     except KeyError:
       
   118         # Ignore if lfs extension is not enabled
       
   119         return
       
   120 
       
   121 class copiedpart(object):
       
   122     """a copy of unbundlepart content that can be consumed later"""
       
   123 
       
   124     def __init__(self, part):
       
   125         # copy "public properties"
       
   126         self.type = part.type
       
   127         self.id = part.id
       
   128         self.mandatory = part.mandatory
       
   129         self.mandatoryparams = part.mandatoryparams
       
   130         self.advisoryparams = part.advisoryparams
       
   131         self.params = part.params
       
   132         self.mandatorykeys = part.mandatorykeys
       
   133         # copy the buffer
       
   134         self._io = util.stringio(part.read())
       
   135 
       
   136     def consume(self):
       
   137         return
       
   138 
       
   139     def read(self, size=None):
       
   140         if size is None:
       
   141             return self._io.read()
       
   142         else:
       
   143             return self._io.read(size)