comparison mercurial/bundle2.py @ 24686:e0e28e910fa3

bundle2: rename format, parts and config to final names It is finally time to freeze the bundle2 format! To do so we: - rename HG2Y to HG20, - drop "b2x:" prefix from all part names, - rename capability to "bundle2-exp" to "bundle2" - rename the hook flag from 'bundle2-exp' to 'bundle2'
author Pierre-Yves David <pierre-yves.david@fb.com>
date Thu, 09 Apr 2015 16:25:48 -0400
parents 5cac3accdaa1
children c00e4338fa4b
comparison
equal deleted inserted replaced
24685:b3d78d82d84c 24686:e0e28e910fa3
356 handler(op, part) 356 handler(op, part)
357 finally: 357 finally:
358 if output is not None: 358 if output is not None:
359 output = op.ui.popbuffer() 359 output = op.ui.popbuffer()
360 if output: 360 if output:
361 outpart = op.reply.newpart('b2x:output', data=output, 361 outpart = op.reply.newpart('output', data=output,
362 mandatory=False) 362 mandatory=False)
363 outpart.addparam('in-reply-to', str(part.id), mandatory=False) 363 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
364 finally: 364 finally:
365 # consume the part content to not corrupt the stream. 365 # consume the part content to not corrupt the stream.
366 part.seek(0, 2) 366 part.seek(0, 2)
406 406
407 Use the `addparam` method to add stream level parameter. and `newpart` to 407 Use the `addparam` method to add stream level parameter. and `newpart` to
408 populate it. Then call `getchunks` to retrieve all the binary chunks of 408 populate it. Then call `getchunks` to retrieve all the binary chunks of
409 data that compose the bundle2 container.""" 409 data that compose the bundle2 container."""
410 410
411 _magicstring = 'HG2Y' 411 _magicstring = 'HG20'
412 412
413 def __init__(self, ui, capabilities=()): 413 def __init__(self, ui, capabilities=()):
414 self.ui = ui 414 self.ui = ui
415 self._params = [] 415 self._params = []
416 self._parts = [] 416 self._parts = []
614 return None 614 return None
615 615
616 def compressed(self): 616 def compressed(self):
617 return False 617 return False
618 618
619 formatmap = {'2Y': unbundle20} 619 formatmap = {'20': unbundle20}
620 620
621 class bundlepart(object): 621 class bundlepart(object):
622 """A bundle2 part contains application level payload 622 """A bundle2 part contains application level payload
623 623
624 The part `type` is used to route the part to the application level 624 The part `type` is used to route the part to the application level
732 yield chunk 732 yield chunk
733 except Exception, exc: 733 except Exception, exc:
734 # backup exception data for later 734 # backup exception data for later
735 exc_info = sys.exc_info() 735 exc_info = sys.exc_info()
736 msg = 'unexpected error: %s' % exc 736 msg = 'unexpected error: %s' % exc
737 interpart = bundlepart('b2x:error:abort', [('message', msg)], 737 interpart = bundlepart('error:abort', [('message', msg)],
738 mandatory=False) 738 mandatory=False)
739 interpart.id = 0 739 interpart.id = 0
740 yield _pack(_fpayloadsize, -1) 740 yield _pack(_fpayloadsize, -1)
741 for chunk in interpart.getchunks(): 741 for chunk in interpart.getchunks():
742 yield chunk 742 yield chunk
980 adjust = self.read(internaloffset) 980 adjust = self.read(internaloffset)
981 if len(adjust) != internaloffset: 981 if len(adjust) != internaloffset:
982 raise util.Abort(_('Seek failed\n')) 982 raise util.Abort(_('Seek failed\n'))
983 self._pos = newpos 983 self._pos = newpos
984 984
985 capabilities = {'HG2Y': (), 985 capabilities = {'HG20': (),
986 'b2x:listkeys': (), 986 'listkeys': (),
987 'b2x:pushkey': (), 987 'pushkey': (),
988 'digests': tuple(sorted(util.DIGESTS.keys())), 988 'digests': tuple(sorted(util.DIGESTS.keys())),
989 'b2x:remote-changegroup': ('http', 'https'), 989 'remote-changegroup': ('http', 'https'),
990 } 990 }
991 991
992 def getrepocaps(repo, allowpushback=False): 992 def getrepocaps(repo, allowpushback=False):
993 """return the bundle2 capabilities for a given repo 993 """return the bundle2 capabilities for a given repo
994 994
995 Exists to allow extensions (like evolution) to mutate the capabilities. 995 Exists to allow extensions (like evolution) to mutate the capabilities.
996 """ 996 """
997 caps = capabilities.copy() 997 caps = capabilities.copy()
998 caps['b2x:changegroup'] = tuple(sorted(changegroup.packermap.keys())) 998 caps['changegroup'] = tuple(sorted(changegroup.packermap.keys()))
999 if obsolete.isenabled(repo, obsolete.exchangeopt): 999 if obsolete.isenabled(repo, obsolete.exchangeopt):
1000 supportedformat = tuple('V%i' % v for v in obsolete.formats) 1000 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1001 caps['b2x:obsmarkers'] = supportedformat 1001 caps['obsmarkers'] = supportedformat
1002 if allowpushback: 1002 if allowpushback:
1003 caps['b2x:pushback'] = () 1003 caps['pushback'] = ()
1004 return caps 1004 return caps
1005 1005
1006 def bundle2caps(remote): 1006 def bundle2caps(remote):
1007 """return the bundle capabilities of a peer as dict""" 1007 """return the bundle capabilities of a peer as dict"""
1008 raw = remote.capable('bundle2-exp') 1008 raw = remote.capable('bundle2')
1009 if not raw and raw != '': 1009 if not raw and raw != '':
1010 return {} 1010 return {}
1011 capsblob = urllib.unquote(remote.capable('bundle2-exp')) 1011 capsblob = urllib.unquote(remote.capable('bundle2'))
1012 return decodecaps(capsblob) 1012 return decodecaps(capsblob)
1013 1013
1014 def obsmarkersversion(caps): 1014 def obsmarkersversion(caps):
1015 """extract the list of supported obsmarkers versions from a bundle2caps dict 1015 """extract the list of supported obsmarkers versions from a bundle2caps dict
1016 """ 1016 """
1017 obscaps = caps.get('b2x:obsmarkers', ()) 1017 obscaps = caps.get('obsmarkers', ())
1018 return [int(c[1:]) for c in obscaps if c.startswith('V')] 1018 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1019 1019
1020 @parthandler('b2x:changegroup', ('version',)) 1020 @parthandler('changegroup', ('version',))
1021 def handlechangegroup(op, inpart): 1021 def handlechangegroup(op, inpart):
1022 """apply a changegroup part on the repo 1022 """apply a changegroup part on the repo
1023 1023
1024 This is a very early implementation that will massive rework before being 1024 This is a very early implementation that will massive rework before being
1025 inflicted to any end-user. 1025 inflicted to any end-user.
1039 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2') 1039 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1040 op.records.add('changegroup', {'return': ret}) 1040 op.records.add('changegroup', {'return': ret})
1041 if op.reply is not None: 1041 if op.reply is not None:
1042 # This is definitely not the final form of this 1042 # This is definitely not the final form of this
1043 # return. But one need to start somewhere. 1043 # return. But one need to start somewhere.
1044 part = op.reply.newpart('b2x:reply:changegroup', mandatory=False) 1044 part = op.reply.newpart('reply:changegroup', mandatory=False)
1045 part.addparam('in-reply-to', str(inpart.id), mandatory=False) 1045 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1046 part.addparam('return', '%i' % ret, mandatory=False) 1046 part.addparam('return', '%i' % ret, mandatory=False)
1047 assert not inpart.read() 1047 assert not inpart.read()
1048 1048
1049 _remotechangegroupparams = tuple(['url', 'size', 'digests'] + 1049 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1050 ['digest:%s' % k for k in util.DIGESTS.keys()]) 1050 ['digest:%s' % k for k in util.DIGESTS.keys()])
1051 @parthandler('b2x:remote-changegroup', _remotechangegroupparams) 1051 @parthandler('remote-changegroup', _remotechangegroupparams)
1052 def handleremotechangegroup(op, inpart): 1052 def handleremotechangegroup(op, inpart):
1053 """apply a bundle10 on the repo, given an url and validation information 1053 """apply a bundle10 on the repo, given an url and validation information
1054 1054
1055 All the information about the remote bundle to import are given as 1055 All the information about the remote bundle to import are given as
1056 parameters. The parameters include: 1056 parameters. The parameters include:
1068 try: 1068 try:
1069 raw_url = inpart.params['url'] 1069 raw_url = inpart.params['url']
1070 except KeyError: 1070 except KeyError:
1071 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url') 1071 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1072 parsed_url = util.url(raw_url) 1072 parsed_url = util.url(raw_url)
1073 if parsed_url.scheme not in capabilities['b2x:remote-changegroup']: 1073 if parsed_url.scheme not in capabilities['remote-changegroup']:
1074 raise util.Abort(_('remote-changegroup does not support %s urls') % 1074 raise util.Abort(_('remote-changegroup does not support %s urls') %
1075 parsed_url.scheme) 1075 parsed_url.scheme)
1076 1076
1077 try: 1077 try:
1078 size = int(inpart.params['size']) 1078 size = int(inpart.params['size'])
1108 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2') 1108 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1109 op.records.add('changegroup', {'return': ret}) 1109 op.records.add('changegroup', {'return': ret})
1110 if op.reply is not None: 1110 if op.reply is not None:
1111 # This is definitely not the final form of this 1111 # This is definitely not the final form of this
1112 # return. But one need to start somewhere. 1112 # return. But one need to start somewhere.
1113 part = op.reply.newpart('b2x:reply:changegroup') 1113 part = op.reply.newpart('reply:changegroup')
1114 part.addparam('in-reply-to', str(inpart.id), mandatory=False) 1114 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1115 part.addparam('return', '%i' % ret, mandatory=False) 1115 part.addparam('return', '%i' % ret, mandatory=False)
1116 try: 1116 try:
1117 real_part.validate() 1117 real_part.validate()
1118 except util.Abort, e: 1118 except util.Abort, e:
1119 raise util.Abort(_('bundle at %s is corrupted:\n%s') % 1119 raise util.Abort(_('bundle at %s is corrupted:\n%s') %
1120 (util.hidepassword(raw_url), str(e))) 1120 (util.hidepassword(raw_url), str(e)))
1121 assert not inpart.read() 1121 assert not inpart.read()
1122 1122
1123 @parthandler('b2x:reply:changegroup', ('return', 'in-reply-to')) 1123 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1124 def handlereplychangegroup(op, inpart): 1124 def handlereplychangegroup(op, inpart):
1125 ret = int(inpart.params['return']) 1125 ret = int(inpart.params['return'])
1126 replyto = int(inpart.params['in-reply-to']) 1126 replyto = int(inpart.params['in-reply-to'])
1127 op.records.add('changegroup', {'return': ret}, replyto) 1127 op.records.add('changegroup', {'return': ret}, replyto)
1128 1128
1129 @parthandler('b2x:check:heads') 1129 @parthandler('check:heads')
1130 def handlecheckheads(op, inpart): 1130 def handlecheckheads(op, inpart):
1131 """check that head of the repo did not change 1131 """check that head of the repo did not change
1132 1132
1133 This is used to detect a push race when using unbundle. 1133 This is used to detect a push race when using unbundle.
1134 This replaces the "heads" argument of unbundle.""" 1134 This replaces the "heads" argument of unbundle."""
1140 assert not h 1140 assert not h
1141 if heads != op.repo.heads(): 1141 if heads != op.repo.heads():
1142 raise error.PushRaced('repository changed while pushing - ' 1142 raise error.PushRaced('repository changed while pushing - '
1143 'please try again') 1143 'please try again')
1144 1144
1145 @parthandler('b2x:output') 1145 @parthandler('output')
1146 def handleoutput(op, inpart): 1146 def handleoutput(op, inpart):
1147 """forward output captured on the server to the client""" 1147 """forward output captured on the server to the client"""
1148 for line in inpart.read().splitlines(): 1148 for line in inpart.read().splitlines():
1149 op.ui.write(('remote: %s\n' % line)) 1149 op.ui.write(('remote: %s\n' % line))
1150 1150
1151 @parthandler('b2x:replycaps') 1151 @parthandler('replycaps')
1152 def handlereplycaps(op, inpart): 1152 def handlereplycaps(op, inpart):
1153 """Notify that a reply bundle should be created 1153 """Notify that a reply bundle should be created
1154 1154
1155 The payload contains the capabilities information for the reply""" 1155 The payload contains the capabilities information for the reply"""
1156 caps = decodecaps(inpart.read()) 1156 caps = decodecaps(inpart.read())
1157 if op.reply is None: 1157 if op.reply is None:
1158 op.reply = bundle20(op.ui, caps) 1158 op.reply = bundle20(op.ui, caps)
1159 1159
1160 @parthandler('b2x:error:abort', ('message', 'hint')) 1160 @parthandler('error:abort', ('message', 'hint'))
1161 def handlereplycaps(op, inpart): 1161 def handlereplycaps(op, inpart):
1162 """Used to transmit abort error over the wire""" 1162 """Used to transmit abort error over the wire"""
1163 raise util.Abort(inpart.params['message'], hint=inpart.params.get('hint')) 1163 raise util.Abort(inpart.params['message'], hint=inpart.params.get('hint'))
1164 1164
1165 @parthandler('b2x:error:unsupportedcontent', ('parttype', 'params')) 1165 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1166 def handlereplycaps(op, inpart): 1166 def handlereplycaps(op, inpart):
1167 """Used to transmit unknown content error over the wire""" 1167 """Used to transmit unknown content error over the wire"""
1168 kwargs = {} 1168 kwargs = {}
1169 parttype = inpart.params.get('parttype') 1169 parttype = inpart.params.get('parttype')
1170 if parttype is not None: 1170 if parttype is not None:
1173 if params is not None: 1173 if params is not None:
1174 kwargs['params'] = params.split('\0') 1174 kwargs['params'] = params.split('\0')
1175 1175
1176 raise error.UnsupportedPartError(**kwargs) 1176 raise error.UnsupportedPartError(**kwargs)
1177 1177
1178 @parthandler('b2x:error:pushraced', ('message',)) 1178 @parthandler('error:pushraced', ('message',))
1179 def handlereplycaps(op, inpart): 1179 def handlereplycaps(op, inpart):
1180 """Used to transmit push race error over the wire""" 1180 """Used to transmit push race error over the wire"""
1181 raise error.ResponseError(_('push failed:'), inpart.params['message']) 1181 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1182 1182
1183 @parthandler('b2x:listkeys', ('namespace',)) 1183 @parthandler('listkeys', ('namespace',))
1184 def handlelistkeys(op, inpart): 1184 def handlelistkeys(op, inpart):
1185 """retrieve pushkey namespace content stored in a bundle2""" 1185 """retrieve pushkey namespace content stored in a bundle2"""
1186 namespace = inpart.params['namespace'] 1186 namespace = inpart.params['namespace']
1187 r = pushkey.decodekeys(inpart.read()) 1187 r = pushkey.decodekeys(inpart.read())
1188 op.records.add('listkeys', (namespace, r)) 1188 op.records.add('listkeys', (namespace, r))
1189 1189
1190 @parthandler('b2x:pushkey', ('namespace', 'key', 'old', 'new')) 1190 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1191 def handlepushkey(op, inpart): 1191 def handlepushkey(op, inpart):
1192 """process a pushkey request""" 1192 """process a pushkey request"""
1193 dec = pushkey.decode 1193 dec = pushkey.decode
1194 namespace = dec(inpart.params['namespace']) 1194 namespace = dec(inpart.params['namespace'])
1195 key = dec(inpart.params['key']) 1195 key = dec(inpart.params['key'])
1200 'key': key, 1200 'key': key,
1201 'old': old, 1201 'old': old,
1202 'new': new} 1202 'new': new}
1203 op.records.add('pushkey', record) 1203 op.records.add('pushkey', record)
1204 if op.reply is not None: 1204 if op.reply is not None:
1205 rpart = op.reply.newpart('b2x:reply:pushkey') 1205 rpart = op.reply.newpart('reply:pushkey')
1206 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False) 1206 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1207 rpart.addparam('return', '%i' % ret, mandatory=False) 1207 rpart.addparam('return', '%i' % ret, mandatory=False)
1208 1208
1209 @parthandler('b2x:reply:pushkey', ('return', 'in-reply-to')) 1209 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1210 def handlepushkeyreply(op, inpart): 1210 def handlepushkeyreply(op, inpart):
1211 """retrieve the result of a pushkey request""" 1211 """retrieve the result of a pushkey request"""
1212 ret = int(inpart.params['return']) 1212 ret = int(inpart.params['return'])
1213 partid = int(inpart.params['in-reply-to']) 1213 partid = int(inpart.params['in-reply-to'])
1214 op.records.add('pushkey', {'return': ret}, partid) 1214 op.records.add('pushkey', {'return': ret}, partid)
1215 1215
1216 @parthandler('b2x:obsmarkers') 1216 @parthandler('obsmarkers')
1217 def handleobsmarker(op, inpart): 1217 def handleobsmarker(op, inpart):
1218 """add a stream of obsmarkers to the repo""" 1218 """add a stream of obsmarkers to the repo"""
1219 tr = op.gettransaction() 1219 tr = op.gettransaction()
1220 new = op.repo.obsstore.mergemarkers(tr, inpart.read()) 1220 new = op.repo.obsstore.mergemarkers(tr, inpart.read())
1221 if new: 1221 if new:
1222 op.repo.ui.status(_('%i new obsolescence markers\n') % new) 1222 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1223 op.records.add('obsmarkers', {'new': new}) 1223 op.records.add('obsmarkers', {'new': new})
1224 if op.reply is not None: 1224 if op.reply is not None:
1225 rpart = op.reply.newpart('b2x:reply:obsmarkers') 1225 rpart = op.reply.newpart('reply:obsmarkers')
1226 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False) 1226 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1227 rpart.addparam('new', '%i' % new, mandatory=False) 1227 rpart.addparam('new', '%i' % new, mandatory=False)
1228 1228
1229 1229
1230 @parthandler('b2x:reply:obsmarkers', ('new', 'in-reply-to')) 1230 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1231 def handlepushkeyreply(op, inpart): 1231 def handlepushkeyreply(op, inpart):
1232 """retrieve the result of a pushkey request""" 1232 """retrieve the result of a pushkey request"""
1233 ret = int(inpart.params['new']) 1233 ret = int(inpart.params['new'])
1234 partid = int(inpart.params['in-reply-to']) 1234 partid = int(inpart.params['in-reply-to'])
1235 op.records.add('obsmarkers', {'new': ret}, partid) 1235 op.records.add('obsmarkers', {'new': ret}, partid)