mercurial/wireproto.py
changeset 37498 aacfca6f9767
parent 37490 3a91911c4343
child 37533 df4985497986
equal deleted inserted replaced
37497:1541e1a8e87d 37498:aacfca6f9767
   936     # only accept optional args from the known set
   936     # only accept optional args from the known set
   937     opts = options('debugwireargs', ['three', 'four'], others)
   937     opts = options('debugwireargs', ['three', 'four'], others)
   938     return wireprototypes.bytesresponse(repo.debugwireargs(
   938     return wireprototypes.bytesresponse(repo.debugwireargs(
   939         one, two, **pycompat.strkwargs(opts)))
   939         one, two, **pycompat.strkwargs(opts)))
   940 
   940 
       
   941 def find_pullbundle(repo, proto, opts, clheads, heads, common):
       
   942     """Return a file object for the first matching pullbundle.
       
   943 
       
   944     Pullbundles are specified in .hg/pullbundles.manifest similar to
       
   945     clonebundles.
       
   946     For each entry, the bundle specification is checked for compatibility:
       
   947     - Client features vs the BUNDLESPEC.
       
   948     - Revisions shared with the clients vs base revisions of the bundle.
       
   949       A bundle can be applied only if all its base revisions are known by
       
   950       the client.
       
   951     - At least one leaf of the bundle's DAG is missing on the client.
       
   952     - Every leaf of the bundle's DAG is part of node set the client wants.
       
   953       E.g. do not send a bundle of all changes if the client wants only
       
   954       one specific branch of many.
       
   955     """
       
   956     def decodehexstring(s):
       
   957         return set([h.decode('hex') for h in s.split(';')])
       
   958 
       
   959     manifest = repo.vfs.tryread('pullbundles.manifest')
       
   960     if not manifest:
       
   961         return None
       
   962     res = exchange.parseclonebundlesmanifest(repo, manifest)
       
   963     res = exchange.filterclonebundleentries(repo, res)
       
   964     if not res:
       
   965         return None
       
   966     cl = repo.changelog
       
   967     heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True)
       
   968     common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True)
       
   969     compformats = clientcompressionsupport(proto)
       
   970     for entry in res:
       
   971         if 'COMPRESSION' in entry and entry['COMPRESSION'] not in compformats:
       
   972             continue
       
   973         # No test yet for VERSION, since V2 is supported by any client
       
   974         # that advertises partial pulls
       
   975         if 'heads' in entry:
       
   976             try:
       
   977                 bundle_heads = decodehexstring(entry['heads'])
       
   978             except TypeError:
       
   979                 # Bad heads entry
       
   980                 continue
       
   981             if bundle_heads.issubset(common):
       
   982                 continue # Nothing new
       
   983             if all(cl.rev(rev) in common_anc for rev in bundle_heads):
       
   984                 continue # Still nothing new
       
   985             if any(cl.rev(rev) not in heads_anc and
       
   986                    cl.rev(rev) not in common_anc for rev in bundle_heads):
       
   987                 continue
       
   988         if 'bases' in entry:
       
   989             try:
       
   990                 bundle_bases = decodehexstring(entry['bases'])
       
   991             except TypeError:
       
   992                 # Bad bases entry
       
   993                 continue
       
   994             if not all(cl.rev(rev) in common_anc for rev in bundle_bases):
       
   995                 continue
       
   996         path = entry['URL']
       
   997         repo.ui.debug('sending pullbundle "%s"\n' % path)
       
   998         try:
       
   999             return repo.vfs.open(path)
       
  1000         except IOError:
       
  1001             repo.ui.debug('pullbundle "%s" not accessible\n' % path)
       
  1002             continue
       
  1003     return None
       
  1004 
   941 @wireprotocommand('getbundle', '*', permission='pull')
  1005 @wireprotocommand('getbundle', '*', permission='pull')
   942 def getbundle(repo, proto, others):
  1006 def getbundle(repo, proto, others):
   943     opts = options('getbundle', gboptsmap.keys(), others)
  1007     opts = options('getbundle', gboptsmap.keys(), others)
   944     for k, v in opts.iteritems():
  1008     for k, v in opts.iteritems():
   945         keytype = gboptsmap[k]
  1009         keytype = gboptsmap[k]
   968                               hint=bundle2requiredhint)
  1032                               hint=bundle2requiredhint)
   969 
  1033 
   970     prefercompressed = True
  1034     prefercompressed = True
   971 
  1035 
   972     try:
  1036     try:
       
  1037         clheads = set(repo.changelog.heads())
       
  1038         heads = set(opts.get('heads', set()))
       
  1039         common = set(opts.get('common', set()))
       
  1040         common.discard(nullid)
       
  1041         if (repo.ui.configbool('server', 'pullbundle') and
       
  1042             'partial-pull' in proto.getprotocaps()):
       
  1043             # Check if a pre-built bundle covers this request.
       
  1044             bundle = find_pullbundle(repo, proto, opts, clheads, heads, common)
       
  1045             if bundle:
       
  1046                 return wireprototypes.streamres(gen=util.filechunkiter(bundle),
       
  1047                                                 prefer_uncompressed=True)
       
  1048 
   973         if repo.ui.configbool('server', 'disablefullbundle'):
  1049         if repo.ui.configbool('server', 'disablefullbundle'):
   974             # Check to see if this is a full clone.
  1050             # Check to see if this is a full clone.
   975             clheads = set(repo.changelog.heads())
       
   976             changegroup = opts.get('cg', True)
  1051             changegroup = opts.get('cg', True)
   977             heads = set(opts.get('heads', set()))
       
   978             common = set(opts.get('common', set()))
       
   979             common.discard(nullid)
       
   980             if changegroup and not common and clheads == heads:
  1052             if changegroup and not common and clheads == heads:
   981                 raise error.Abort(
  1053                 raise error.Abort(
   982                     _('server has pull-based clones disabled'),
  1054                     _('server has pull-based clones disabled'),
   983                     hint=_('remove --pull if specified or upgrade Mercurial'))
  1055                     hint=_('remove --pull if specified or upgrade Mercurial'))
   984 
  1056