hgext/narrow/narrowbundle2.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43115 4aa72cdf616f
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    27     wireprototypes,
    27     wireprototypes,
    28 )
    28 )
    29 from mercurial.interfaces import repository
    29 from mercurial.interfaces import repository
    30 from mercurial.utils import stringutil
    30 from mercurial.utils import stringutil
    31 
    31 
    32 _NARROWACL_SECTION = 'narrowacl'
    32 _NARROWACL_SECTION = b'narrowacl'
    33 _CHANGESPECPART = 'narrow:changespec'
    33 _CHANGESPECPART = b'narrow:changespec'
    34 _RESSPECS = 'narrow:responsespec'
    34 _RESSPECS = b'narrow:responsespec'
    35 _SPECPART = 'narrow:spec'
    35 _SPECPART = b'narrow:spec'
    36 _SPECPART_INCLUDE = 'include'
    36 _SPECPART_INCLUDE = b'include'
    37 _SPECPART_EXCLUDE = 'exclude'
    37 _SPECPART_EXCLUDE = b'exclude'
    38 _KILLNODESIGNAL = 'KILL'
    38 _KILLNODESIGNAL = b'KILL'
    39 _DONESIGNAL = 'DONE'
    39 _DONESIGNAL = b'DONE'
    40 _ELIDEDCSHEADER = '>20s20s20sl'  # cset id, p1, p2, len(text)
    40 _ELIDEDCSHEADER = b'>20s20s20sl'  # cset id, p1, p2, len(text)
    41 _ELIDEDMFHEADER = '>20s20s20s20sl'  # manifest id, p1, p2, link id, len(text)
    41 _ELIDEDMFHEADER = b'>20s20s20s20sl'  # manifest id, p1, p2, link id, len(text)
    42 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
    42 _CSHEADERSIZE = struct.calcsize(_ELIDEDCSHEADER)
    43 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
    43 _MFHEADERSIZE = struct.calcsize(_ELIDEDMFHEADER)
    44 
    44 
    45 # Serve a changegroup for a client with a narrow clone.
    45 # Serve a changegroup for a client with a narrow clone.
    46 def getbundlechangegrouppart_narrow(
    46 def getbundlechangegrouppart_narrow(
    51     b2caps=None,
    51     b2caps=None,
    52     heads=None,
    52     heads=None,
    53     common=None,
    53     common=None,
    54     **kwargs
    54     **kwargs
    55 ):
    55 ):
    56     assert repo.ui.configbool('experimental', 'narrowservebrokenellipses')
    56     assert repo.ui.configbool(b'experimental', b'narrowservebrokenellipses')
    57 
    57 
    58     cgversions = b2caps.get('changegroup')
    58     cgversions = b2caps.get(b'changegroup')
    59     cgversions = [
    59     cgversions = [
    60         v
    60         v
    61         for v in cgversions
    61         for v in cgversions
    62         if v in changegroup.supportedoutgoingversions(repo)
    62         if v in changegroup.supportedoutgoingversions(repo)
    63     ]
    63     ]
    64     if not cgversions:
    64     if not cgversions:
    65         raise ValueError(_('no common changegroup version'))
    65         raise ValueError(_(b'no common changegroup version'))
    66     version = max(cgversions)
    66     version = max(cgversions)
    67 
    67 
    68     oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
    68     oldinclude = sorted(filter(bool, kwargs.get(r'oldincludepats', [])))
    69     oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
    69     oldexclude = sorted(filter(bool, kwargs.get(r'oldexcludepats', [])))
    70     newinclude = sorted(filter(bool, kwargs.get(r'includepats', [])))
    70     newinclude = sorted(filter(bool, kwargs.get(r'includepats', [])))
   102         repo.root, include=newinclude, exclude=newexclude
   102         repo.root, include=newinclude, exclude=newexclude
   103     )
   103     )
   104     if depth is not None:
   104     if depth is not None:
   105         depth = int(depth)
   105         depth = int(depth)
   106         if depth < 1:
   106         if depth < 1:
   107             raise error.Abort(_('depth must be positive, got %d') % depth)
   107             raise error.Abort(_(b'depth must be positive, got %d') % depth)
   108 
   108 
   109     heads = set(heads or repo.heads())
   109     heads = set(heads or repo.heads())
   110     common = set(common or [nullid])
   110     common = set(common or [nullid])
   111     if known and (oldinclude != newinclude or oldexclude != newexclude):
   111     if known and (oldinclude != newinclude or oldexclude != newexclude):
   112         # Steps:
   112         # Steps:
   125         # c) goto a
   125         # c) goto a
   126         #
   126         #
   127         # until they've built up the full new state.
   127         # until they've built up the full new state.
   128         # Convert to revnums and intersect with "common". The client should
   128         # Convert to revnums and intersect with "common". The client should
   129         # have made it a subset of "common" already, but let's be safe.
   129         # have made it a subset of "common" already, but let's be safe.
   130         known = set(repo.revs("%ln & ::%ln", known, common))
   130         known = set(repo.revs(b"%ln & ::%ln", known, common))
   131         # TODO: we could send only roots() of this set, and the
   131         # TODO: we could send only roots() of this set, and the
   132         # list of nodes in common, and the client could work out
   132         # list of nodes in common, and the client could work out
   133         # what to strip, instead of us explicitly sending every
   133         # what to strip, instead of us explicitly sending every
   134         # single node.
   134         # single node.
   135         deadrevs = known
   135         deadrevs = known
   152                 ellipses=True,
   152                 ellipses=True,
   153                 shallow=depth is not None,
   153                 shallow=depth is not None,
   154                 ellipsisroots=newellipsis,
   154                 ellipsisroots=newellipsis,
   155                 fullnodes=newfull,
   155                 fullnodes=newfull,
   156             )
   156             )
   157             cgdata = packer.generate(common, newvisit, False, 'narrow_widen')
   157             cgdata = packer.generate(common, newvisit, False, b'narrow_widen')
   158 
   158 
   159             part = bundler.newpart('changegroup', data=cgdata)
   159             part = bundler.newpart(b'changegroup', data=cgdata)
   160             part.addparam('version', version)
   160             part.addparam(b'version', version)
   161             if 'treemanifest' in repo.requirements:
   161             if b'treemanifest' in repo.requirements:
   162                 part.addparam('treemanifest', '1')
   162                 part.addparam(b'treemanifest', b'1')
   163 
   163 
   164     visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
   164     visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
   165         repo, common, heads, set(), newmatch, depth=depth
   165         repo, common, heads, set(), newmatch, depth=depth
   166     )
   166     )
   167 
   167 
   168     repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
   168     repo.ui.debug(b'Found %d relevant revs\n' % len(relevant_nodes))
   169     if visitnodes:
   169     if visitnodes:
   170         packer = changegroup.getbundler(
   170         packer = changegroup.getbundler(
   171             version,
   171             version,
   172             repo,
   172             repo,
   173             matcher=newmatch,
   173             matcher=newmatch,
   174             ellipses=True,
   174             ellipses=True,
   175             shallow=depth is not None,
   175             shallow=depth is not None,
   176             ellipsisroots=ellipsisroots,
   176             ellipsisroots=ellipsisroots,
   177             fullnodes=relevant_nodes,
   177             fullnodes=relevant_nodes,
   178         )
   178         )
   179         cgdata = packer.generate(common, visitnodes, False, 'narrow_widen')
   179         cgdata = packer.generate(common, visitnodes, False, b'narrow_widen')
   180 
   180 
   181         part = bundler.newpart('changegroup', data=cgdata)
   181         part = bundler.newpart(b'changegroup', data=cgdata)
   182         part.addparam('version', version)
   182         part.addparam(b'version', version)
   183         if 'treemanifest' in repo.requirements:
   183         if b'treemanifest' in repo.requirements:
   184             part.addparam('treemanifest', '1')
   184             part.addparam(b'treemanifest', b'1')
   185 
   185 
   186 
   186 
   187 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
   187 @bundle2.parthandler(_SPECPART, (_SPECPART_INCLUDE, _SPECPART_EXCLUDE))
   188 def _handlechangespec_2(op, inpart):
   188 def _handlechangespec_2(op, inpart):
   189     # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
   189     # XXX: This bundle2 handling is buggy and should be removed after hg5.2 is
   190     # released. New servers will send a mandatory bundle2 part named
   190     # released. New servers will send a mandatory bundle2 part named
   191     # 'Narrowspec' and will send specs as data instead of params.
   191     # 'Narrowspec' and will send specs as data instead of params.
   192     # Refer to issue5952 and 6019
   192     # Refer to issue5952 and 6019
   193     includepats = set(inpart.params.get(_SPECPART_INCLUDE, '').splitlines())
   193     includepats = set(inpart.params.get(_SPECPART_INCLUDE, b'').splitlines())
   194     excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, '').splitlines())
   194     excludepats = set(inpart.params.get(_SPECPART_EXCLUDE, b'').splitlines())
   195     narrowspec.validatepatterns(includepats)
   195     narrowspec.validatepatterns(includepats)
   196     narrowspec.validatepatterns(excludepats)
   196     narrowspec.validatepatterns(excludepats)
   197 
   197 
   198     if not repository.NARROW_REQUIREMENT in op.repo.requirements:
   198     if not repository.NARROW_REQUIREMENT in op.repo.requirements:
   199         op.repo.requirements.add(repository.NARROW_REQUIREMENT)
   199         op.repo.requirements.add(repository.NARROW_REQUIREMENT)
   203 
   203 
   204 
   204 
   205 @bundle2.parthandler(_RESSPECS)
   205 @bundle2.parthandler(_RESSPECS)
   206 def _handlenarrowspecs(op, inpart):
   206 def _handlenarrowspecs(op, inpart):
   207     data = inpart.read()
   207     data = inpart.read()
   208     inc, exc = data.split('\0')
   208     inc, exc = data.split(b'\0')
   209     includepats = set(inc.splitlines())
   209     includepats = set(inc.splitlines())
   210     excludepats = set(exc.splitlines())
   210     excludepats = set(exc.splitlines())
   211     narrowspec.validatepatterns(includepats)
   211     narrowspec.validatepatterns(includepats)
   212     narrowspec.validatepatterns(excludepats)
   212     narrowspec.validatepatterns(excludepats)
   213 
   213 
   239             ck = changegroup.readexactly(inpart, 20)
   239             ck = changegroup.readexactly(inpart, 20)
   240             if cl.hasnode(ck):
   240             if cl.hasnode(ck):
   241                 clkills.add(ck)
   241                 clkills.add(ck)
   242         else:
   242         else:
   243             raise error.Abort(
   243             raise error.Abort(
   244                 _('unexpected changespec node chunk type: %s') % chunksignal
   244                 _(b'unexpected changespec node chunk type: %s') % chunksignal
   245             )
   245             )
   246         chunksignal = changegroup.readexactly(inpart, 4)
   246         chunksignal = changegroup.readexactly(inpart, 4)
   247 
   247 
   248     if clkills:
   248     if clkills:
   249         # preserve bookmarks that repair.strip() would otherwise strip
   249         # preserve bookmarks that repair.strip() would otherwise strip
   253             def applychanges(self, repo, tr, changes):
   253             def applychanges(self, repo, tr, changes):
   254                 pass
   254                 pass
   255 
   255 
   256         localrepo.localrepository._bookmarks.set(repo, dummybmstore())
   256         localrepo.localrepository._bookmarks.set(repo, dummybmstore())
   257         chgrpfile = repair.strip(
   257         chgrpfile = repair.strip(
   258             op.ui, repo, list(clkills), backup=True, topic='widen'
   258             op.ui, repo, list(clkills), backup=True, topic=b'widen'
   259         )
   259         )
   260         if chgrpfile:
   260         if chgrpfile:
   261             op._widen_uninterr = repo.ui.uninterruptible()
   261             op._widen_uninterr = repo.ui.uninterruptible()
   262             op._widen_uninterr.__enter__()
   262             op._widen_uninterr.__enter__()
   263             # presence of _widen_bundle attribute activates widen handler later
   263             # presence of _widen_bundle attribute activates widen handler later
   264             op._widen_bundle = chgrpfile
   264             op._widen_bundle = chgrpfile
   265     # Set the new narrowspec if we're widening. The setnewnarrowpats() method
   265     # Set the new narrowspec if we're widening. The setnewnarrowpats() method
   266     # will currently always be there when using the core+narrowhg server, but
   266     # will currently always be there when using the core+narrowhg server, but
   267     # other servers may include a changespec part even when not widening (e.g.
   267     # other servers may include a changespec part even when not widening (e.g.
   268     # because we're deepening a shallow repo).
   268     # because we're deepening a shallow repo).
   269     if util.safehasattr(repo, 'setnewnarrowpats'):
   269     if util.safehasattr(repo, b'setnewnarrowpats'):
   270         repo.setnewnarrowpats()
   270         repo.setnewnarrowpats()
   271 
   271 
   272 
   272 
   273 def handlechangegroup_widen(op, inpart):
   273 def handlechangegroup_widen(op, inpart):
   274     """Changegroup exchange handler which restores temporarily-stripped nodes"""
   274     """Changegroup exchange handler which restores temporarily-stripped nodes"""
   279 
   279 
   280     chgrpfile = op._widen_bundle
   280     chgrpfile = op._widen_bundle
   281     del op._widen_bundle
   281     del op._widen_bundle
   282     vfs = repo.vfs
   282     vfs = repo.vfs
   283 
   283 
   284     ui.note(_("adding branch\n"))
   284     ui.note(_(b"adding branch\n"))
   285     f = vfs.open(chgrpfile, "rb")
   285     f = vfs.open(chgrpfile, b"rb")
   286     try:
   286     try:
   287         gen = exchange.readbundle(ui, f, chgrpfile, vfs)
   287         gen = exchange.readbundle(ui, f, chgrpfile, vfs)
   288         # silence internal shuffling chatter
   288         # silence internal shuffling chatter
   289         override = {('ui', 'quiet'): True}
   289         override = {(b'ui', b'quiet'): True}
   290         if ui.verbose:
   290         if ui.verbose:
   291             override = {}
   291             override = {}
   292         with ui.configoverride(override):
   292         with ui.configoverride(override):
   293             if isinstance(gen, bundle2.unbundle20):
   293             if isinstance(gen, bundle2.unbundle20):
   294                 with repo.transaction('strip') as tr:
   294                 with repo.transaction(b'strip') as tr:
   295                     bundle2.processbundle(repo, gen, lambda: tr)
   295                     bundle2.processbundle(repo, gen, lambda: tr)
   296             else:
   296             else:
   297                 gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True)
   297                 gen.apply(
       
   298                     repo, b'strip', b'bundle:' + vfs.join(chgrpfile), True
       
   299                 )
   298     finally:
   300     finally:
   299         f.close()
   301         f.close()
   300 
   302 
   301     # remove undo files
   303     # remove undo files
   302     for undovfs, undofile in repo.undofiles():
   304     for undovfs, undofile in repo.undofiles():
   303         try:
   305         try:
   304             undovfs.unlink(undofile)
   306             undovfs.unlink(undofile)
   305         except OSError as e:
   307         except OSError as e:
   306             if e.errno != errno.ENOENT:
   308             if e.errno != errno.ENOENT:
   307                 ui.warn(
   309                 ui.warn(
   308                     _('error removing %s: %s\n')
   310                     _(b'error removing %s: %s\n')
   309                     % (undovfs.join(undofile), stringutil.forcebytestr(e))
   311                     % (undovfs.join(undofile), stringutil.forcebytestr(e))
   310                 )
   312                 )
   311 
   313 
   312     # Remove partial backup only if there were no exceptions
   314     # Remove partial backup only if there were no exceptions
   313     op._widen_uninterr.__exit__(None, None, None)
   315     op._widen_uninterr.__exit__(None, None, None)
   316 
   318 
   317 def setup():
   319 def setup():
   318     """Enable narrow repo support in bundle2-related extension points."""
   320     """Enable narrow repo support in bundle2-related extension points."""
   319     getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
   321     getbundleargs = wireprototypes.GETBUNDLE_ARGUMENTS
   320 
   322 
   321     getbundleargs['narrow'] = 'boolean'
   323     getbundleargs[b'narrow'] = b'boolean'
   322     getbundleargs['depth'] = 'plain'
   324     getbundleargs[b'depth'] = b'plain'
   323     getbundleargs['oldincludepats'] = 'csv'
   325     getbundleargs[b'oldincludepats'] = b'csv'
   324     getbundleargs['oldexcludepats'] = 'csv'
   326     getbundleargs[b'oldexcludepats'] = b'csv'
   325     getbundleargs['known'] = 'csv'
   327     getbundleargs[b'known'] = b'csv'
   326 
   328 
   327     # Extend changegroup serving to handle requests from narrow clients.
   329     # Extend changegroup serving to handle requests from narrow clients.
   328     origcgfn = exchange.getbundle2partsmapping['changegroup']
   330     origcgfn = exchange.getbundle2partsmapping[b'changegroup']
   329 
   331 
   330     def wrappedcgfn(*args, **kwargs):
   332     def wrappedcgfn(*args, **kwargs):
   331         repo = args[1]
   333         repo = args[1]
   332         if repo.ui.has_section(_NARROWACL_SECTION):
   334         if repo.ui.has_section(_NARROWACL_SECTION):
   333             kwargs = exchange.applynarrowacl(repo, kwargs)
   335             kwargs = exchange.applynarrowacl(repo, kwargs)
   334 
   336 
   335         if kwargs.get(r'narrow', False) and repo.ui.configbool(
   337         if kwargs.get(r'narrow', False) and repo.ui.configbool(
   336             'experimental', 'narrowservebrokenellipses'
   338             b'experimental', b'narrowservebrokenellipses'
   337         ):
   339         ):
   338             getbundlechangegrouppart_narrow(*args, **kwargs)
   340             getbundlechangegrouppart_narrow(*args, **kwargs)
   339         else:
   341         else:
   340             origcgfn(*args, **kwargs)
   342             origcgfn(*args, **kwargs)
   341 
   343 
   342     exchange.getbundle2partsmapping['changegroup'] = wrappedcgfn
   344     exchange.getbundle2partsmapping[b'changegroup'] = wrappedcgfn
   343 
   345 
   344     # Extend changegroup receiver so client can fixup after widen requests.
   346     # Extend changegroup receiver so client can fixup after widen requests.
   345     origcghandler = bundle2.parthandlermapping['changegroup']
   347     origcghandler = bundle2.parthandlermapping[b'changegroup']
   346 
   348 
   347     def wrappedcghandler(op, inpart):
   349     def wrappedcghandler(op, inpart):
   348         origcghandler(op, inpart)
   350         origcghandler(op, inpart)
   349         if util.safehasattr(op, '_widen_bundle'):
   351         if util.safehasattr(op, b'_widen_bundle'):
   350             handlechangegroup_widen(op, inpart)
   352             handlechangegroup_widen(op, inpart)
   351         if util.safehasattr(op, '_bookmarksbackup'):
   353         if util.safehasattr(op, b'_bookmarksbackup'):
   352             localrepo.localrepository._bookmarks.set(
   354             localrepo.localrepository._bookmarks.set(
   353                 op.repo, op._bookmarksbackup
   355                 op.repo, op._bookmarksbackup
   354             )
   356             )
   355             del op._bookmarksbackup
   357             del op._bookmarksbackup
   356 
   358 
   357     wrappedcghandler.params = origcghandler.params
   359     wrappedcghandler.params = origcghandler.params
   358     bundle2.parthandlermapping['changegroup'] = wrappedcghandler
   360     bundle2.parthandlermapping[b'changegroup'] = wrappedcghandler