##// END OF EJS Templates
py3: byteify the LFS blobstore module...
Matt Harbison -
r41471:02d0a777 default
parent child Browse files
Show More
@@ -42,7 +42,7 b' class lfsvfs(vfsmod.vfs):'
42 def join(self, path):
42 def join(self, path):
43 """split the path at first two characters, like: XX/XXXXX..."""
43 """split the path at first two characters, like: XX/XXXXX..."""
44 if not _lfsre.match(path):
44 if not _lfsre.match(path):
45 raise error.ProgrammingError('unexpected lfs path: %s' % path)
45 raise error.ProgrammingError(b'unexpected lfs path: %s' % path)
46 return super(lfsvfs, self).join(path[0:2], path[2:])
46 return super(lfsvfs, self).join(path[0:2], path[2:])
47
47
48 def walk(self, path=None, onerror=None):
48 def walk(self, path=None, onerror=None):
@@ -56,7 +56,8 b' class lfsvfs(vfsmod.vfs):'
56 prefixlen = len(pathutil.normasprefix(root))
56 prefixlen = len(pathutil.normasprefix(root))
57 oids = []
57 oids = []
58
58
59 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path or ''),
59 for dirpath, dirs, files in os.walk(self.reljoin(self.base, path
60 or b''),
60 onerror=onerror):
61 onerror=onerror):
61 dirpath = dirpath[prefixlen:]
62 dirpath = dirpath[prefixlen:]
62
63
@@ -79,10 +80,11 b' class nullvfs(lfsvfs):'
79 # self.vfs. Raise the same error as a normal vfs when asked to read a
80 # self.vfs. Raise the same error as a normal vfs when asked to read a
80 # file that doesn't exist. The only difference is the full file path
81 # file that doesn't exist. The only difference is the full file path
81 # isn't available in the error.
82 # isn't available in the error.
82 raise IOError(errno.ENOENT, '%s: No such file or directory' % oid)
83 raise IOError(errno.ENOENT,
84 pycompat.sysstr(b'%s: No such file or directory' % oid))
83
85
84 def walk(self, path=None, onerror=None):
86 def walk(self, path=None, onerror=None):
85 return ('', [], [])
87 return (b'', [], [])
86
88
87 def write(self, oid, data):
89 def write(self, oid, data):
88 pass
90 pass
@@ -123,13 +125,13 b' class local(object):'
123 """
125 """
124
126
125 def __init__(self, repo):
127 def __init__(self, repo):
126 fullpath = repo.svfs.join('lfs/objects')
128 fullpath = repo.svfs.join(b'lfs/objects')
127 self.vfs = lfsvfs(fullpath)
129 self.vfs = lfsvfs(fullpath)
128
130
129 if repo.ui.configbool('experimental', 'lfs.disableusercache'):
131 if repo.ui.configbool(b'experimental', b'lfs.disableusercache'):
130 self.cachevfs = nullvfs()
132 self.cachevfs = nullvfs()
131 else:
133 else:
132 usercache = lfutil._usercachedir(repo.ui, 'lfs')
134 usercache = lfutil._usercachedir(repo.ui, b'lfs')
133 self.cachevfs = lfsvfs(usercache)
135 self.cachevfs = lfsvfs(usercache)
134 self.ui = repo.ui
136 self.ui = repo.ui
135
137
@@ -143,23 +145,23 b' class local(object):'
143 # the usercache is the only place it _could_ be. If not present, the
145 # the usercache is the only place it _could_ be. If not present, the
144 # missing file msg here will indicate the local repo, not the usercache.
146 # missing file msg here will indicate the local repo, not the usercache.
145 if self.cachevfs.exists(oid):
147 if self.cachevfs.exists(oid):
146 return self.cachevfs(oid, 'rb')
148 return self.cachevfs(oid, b'rb')
147
149
148 return self.vfs(oid, 'rb')
150 return self.vfs(oid, b'rb')
149
151
150 def download(self, oid, src):
152 def download(self, oid, src):
151 """Read the blob from the remote source in chunks, verify the content,
153 """Read the blob from the remote source in chunks, verify the content,
152 and write to this local blobstore."""
154 and write to this local blobstore."""
153 sha256 = hashlib.sha256()
155 sha256 = hashlib.sha256()
154
156
155 with self.vfs(oid, 'wb', atomictemp=True) as fp:
157 with self.vfs(oid, b'wb', atomictemp=True) as fp:
156 for chunk in util.filechunkiter(src, size=1048576):
158 for chunk in util.filechunkiter(src, size=1048576):
157 fp.write(chunk)
159 fp.write(chunk)
158 sha256.update(chunk)
160 sha256.update(chunk)
159
161
160 realoid = node.hex(sha256.digest())
162 realoid = node.hex(sha256.digest())
161 if realoid != oid:
163 if realoid != oid:
162 raise LfsCorruptionError(_('corrupt remote lfs object: %s')
164 raise LfsCorruptionError(_(b'corrupt remote lfs object: %s')
163 % oid)
165 % oid)
164
166
165 self._linktousercache(oid)
167 self._linktousercache(oid)
@@ -170,7 +172,7 b' class local(object):'
170 This should only be called from the filelog during a commit or similar.
172 This should only be called from the filelog during a commit or similar.
171 As such, there is no need to verify the data. Imports from a remote
173 As such, there is no need to verify the data. Imports from a remote
172 store must use ``download()`` instead."""
174 store must use ``download()`` instead."""
173 with self.vfs(oid, 'wb', atomictemp=True) as fp:
175 with self.vfs(oid, b'wb', atomictemp=True) as fp:
174 fp.write(data)
176 fp.write(data)
175
177
176 self._linktousercache(oid)
178 self._linktousercache(oid)
@@ -186,7 +188,7 b' class local(object):'
186 """
188 """
187 if (not isinstance(self.cachevfs, nullvfs)
189 if (not isinstance(self.cachevfs, nullvfs)
188 and not self.vfs.exists(oid)):
190 and not self.vfs.exists(oid)):
189 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
191 self.ui.note(_(b'lfs: found %s in the usercache\n') % oid)
190 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
192 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
191
193
192 def _linktousercache(self, oid):
194 def _linktousercache(self, oid):
@@ -194,7 +196,7 b' class local(object):'
194 # the local store on success, but truncate, write and link on failure?
196 # the local store on success, but truncate, write and link on failure?
195 if (not self.cachevfs.exists(oid)
197 if (not self.cachevfs.exists(oid)
196 and not isinstance(self.cachevfs, nullvfs)):
198 and not isinstance(self.cachevfs, nullvfs)):
197 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
199 self.ui.note(_(b'lfs: adding %s to the usercache\n') % oid)
198 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
200 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
199
201
200 def read(self, oid, verify=True):
202 def read(self, oid, verify=True):
@@ -208,10 +210,10 b' class local(object):'
208 # give more useful info about the corruption- simply don't add the
210 # give more useful info about the corruption- simply don't add the
209 # hardlink.
211 # hardlink.
210 if verify or node.hex(hashlib.sha256(blob).digest()) == oid:
212 if verify or node.hex(hashlib.sha256(blob).digest()) == oid:
211 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
213 self.ui.note(_(b'lfs: found %s in the usercache\n') % oid)
212 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
214 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
213 else:
215 else:
214 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
216 self.ui.note(_(b'lfs: found %s in the local lfs store\n') % oid)
215 blob = self._read(self.vfs, oid, verify)
217 blob = self._read(self.vfs, oid, verify)
216 return blob
218 return blob
217
219
@@ -268,20 +270,20 b' class _gitlfsremote(object):'
268 ui = repo.ui
270 ui = repo.ui
269 self.ui = ui
271 self.ui = ui
270 baseurl, authinfo = url.authinfo()
272 baseurl, authinfo = url.authinfo()
271 self.baseurl = baseurl.rstrip('/')
273 self.baseurl = baseurl.rstrip(b'/')
272 useragent = repo.ui.config('experimental', 'lfs.user-agent')
274 useragent = repo.ui.config(b'experimental', b'lfs.user-agent')
273 if not useragent:
275 if not useragent:
274 useragent = 'git-lfs/2.3.4 (Mercurial %s)' % util.version()
276 useragent = b'git-lfs/2.3.4 (Mercurial %s)' % util.version()
275 self.urlopener = urlmod.opener(ui, authinfo, useragent)
277 self.urlopener = urlmod.opener(ui, authinfo, useragent)
276 self.retry = ui.configint('lfs', 'retry')
278 self.retry = ui.configint(b'lfs', b'retry')
277
279
278 def writebatch(self, pointers, fromstore):
280 def writebatch(self, pointers, fromstore):
279 """Batch upload from local to remote blobstore."""
281 """Batch upload from local to remote blobstore."""
280 self._batch(_deduplicate(pointers), fromstore, 'upload')
282 self._batch(_deduplicate(pointers), fromstore, b'upload')
281
283
282 def readbatch(self, pointers, tostore):
284 def readbatch(self, pointers, tostore):
283 """Batch download from remote to local blostore."""
285 """Batch download from remote to local blostore."""
284 self._batch(_deduplicate(pointers), tostore, 'download')
286 self._batch(_deduplicate(pointers), tostore, b'download')
285
287
286 def _batchrequest(self, pointers, action):
288 def _batchrequest(self, pointers, action):
287 """Get metadata about objects pointed by pointers for given action
289 """Get metadata about objects pointed by pointers for given action
@@ -294,8 +296,8 b' class _gitlfsremote(object):'
294 'objects': objects,
296 'objects': objects,
295 'operation': action,
297 'operation': action,
296 })
298 })
297 url = '%s/objects/batch' % self.baseurl
299 url = b'%s/objects/batch' % self.baseurl
298 batchreq = util.urlreq.request(url, data=requestdata)
300 batchreq = util.urlreq.request(pycompat.strurl(url), data=requestdata)
299 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
301 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
300 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
302 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
301 try:
303 try:
@@ -303,29 +305,32 b' class _gitlfsremote(object):'
303 rawjson = rsp.read()
305 rawjson = rsp.read()
304 except util.urlerr.httperror as ex:
306 except util.urlerr.httperror as ex:
305 hints = {
307 hints = {
306 400: _('check that lfs serving is enabled on %s and "%s" is '
308 400: _(b'check that lfs serving is enabled on %s and "%s" is '
307 'supported') % (self.baseurl, action),
309 'supported') % (self.baseurl, action),
308 404: _('the "lfs.url" config may be used to override %s')
310 404: _(b'the "lfs.url" config may be used to override %s')
309 % self.baseurl,
311 % self.baseurl,
310 }
312 }
311 hint = hints.get(ex.code, _('api=%s, action=%s') % (url, action))
313 hint = hints.get(ex.code, _(b'api=%s, action=%s') % (url, action))
312 raise LfsRemoteError(_('LFS HTTP error: %s') % ex, hint=hint)
314 raise LfsRemoteError(
315 _(b'LFS HTTP error: %s') % stringutil.forcebytestr(ex),
316 hint=hint)
313 except util.urlerr.urlerror as ex:
317 except util.urlerr.urlerror as ex:
314 hint = (_('the "lfs.url" config may be used to override %s')
318 hint = (_(b'the "lfs.url" config may be used to override %s')
315 % self.baseurl)
319 % self.baseurl)
316 raise LfsRemoteError(_('LFS error: %s') % _urlerrorreason(ex),
320 raise LfsRemoteError(_(b'LFS error: %s') % _urlerrorreason(ex),
317 hint=hint)
321 hint=hint)
318 try:
322 try:
319 response = json.loads(rawjson)
323 response = json.loads(rawjson)
320 except ValueError:
324 except ValueError:
321 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
325 raise LfsRemoteError(_(b'LFS server returns invalid JSON: %s')
322 % rawjson)
326 % rawjson.encode("utf-8"))
323
327
324 if self.ui.debugflag:
328 if self.ui.debugflag:
325 self.ui.debug('Status: %d\n' % rsp.status)
329 self.ui.debug(b'Status: %d\n' % rsp.status)
326 # lfs-test-server and hg serve return headers in different order
330 # lfs-test-server and hg serve return headers in different order
327 self.ui.debug('%s\n'
331 headers = pycompat.bytestr(rsp.info())
328 % '\n'.join(sorted(str(rsp.info()).splitlines())))
332 self.ui.debug(b'%s\n'
333 % b'\n'.join(sorted(headers.splitlines())))
329
334
330 if 'objects' in response:
335 if 'objects' in response:
331 response['objects'] = sorted(response['objects'],
336 response['objects'] = sorted(response['objects'],
@@ -345,34 +350,34 b' class _gitlfsremote(object):'
345 # server implementation (ex. lfs-test-server) does not set "error"
350 # server implementation (ex. lfs-test-server) does not set "error"
346 # but just removes "download" from "actions". Treat that case
351 # but just removes "download" from "actions". Treat that case
347 # as the same as 404 error.
352 # as the same as 404 error.
348 if 'error' not in response:
353 if b'error' not in response:
349 if (action == 'download'
354 if (action == b'download'
350 and action not in response.get('actions', [])):
355 and action not in response.get(b'actions', [])):
351 code = 404
356 code = 404
352 else:
357 else:
353 continue
358 continue
354 else:
359 else:
355 # An error dict without a code doesn't make much sense, so
360 # An error dict without a code doesn't make much sense, so
356 # treat as a server error.
361 # treat as a server error.
357 code = response.get('error').get('code', 500)
362 code = response.get(b'error').get(b'code', 500)
358
363
359 ptrmap = {p.oid(): p for p in pointers}
364 ptrmap = {p.oid(): p for p in pointers}
360 p = ptrmap.get(response['oid'], None)
365 p = ptrmap.get(response[b'oid'], None)
361 if p:
366 if p:
362 filename = getattr(p, 'filename', 'unknown')
367 filename = getattr(p, 'filename', b'unknown')
363 errors = {
368 errors = {
364 404: 'The object does not exist',
369 404: b'The object does not exist',
365 410: 'The object was removed by the owner',
370 410: b'The object was removed by the owner',
366 422: 'Validation error',
371 422: b'Validation error',
367 500: 'Internal server error',
372 500: b'Internal server error',
368 }
373 }
369 msg = errors.get(code, 'status code %d' % code)
374 msg = errors.get(code, b'status code %d' % code)
370 raise LfsRemoteError(_('LFS server error for "%s": %s')
375 raise LfsRemoteError(_(b'LFS server error for "%s": %s')
371 % (filename, msg))
376 % (filename, msg))
372 else:
377 else:
373 raise LfsRemoteError(
378 raise LfsRemoteError(
374 _('LFS server error. Unsolicited response for oid %s')
379 _(b'LFS server error. Unsolicited response for oid %s')
375 % response['oid'])
380 % response[b'oid'])
376
381
377 def _extractobjects(self, response, pointers, action):
382 def _extractobjects(self, response, pointers, action):
378 """extract objects from response of the batch API
383 """extract objects from response of the batch API
@@ -382,12 +387,13 b' class _gitlfsremote(object):'
382 raise if any object has an error
387 raise if any object has an error
383 """
388 """
384 # Scan errors from objects - fail early
389 # Scan errors from objects - fail early
385 objects = response.get('objects', [])
390 objects = response.get(b'objects', [])
386 self._checkforservererror(pointers, objects, action)
391 self._checkforservererror(pointers, objects, action)
387
392
388 # Filter objects with given action. Practically, this skips uploading
393 # Filter objects with given action. Practically, this skips uploading
389 # objects which exist in the server.
394 # objects which exist in the server.
390 filteredobjects = [o for o in objects if action in o.get('actions', [])]
395 filteredobjects = [o for o in objects
396 if action in o.get(b'actions', [])]
391
397
392 return filteredobjects
398 return filteredobjects
393
399
@@ -407,11 +413,11 b' class _gitlfsremote(object):'
407 headers = obj['actions'][action].get('header', {}).items()
413 headers = obj['actions'][action].get('header', {}).items()
408
414
409 request = util.urlreq.request(href)
415 request = util.urlreq.request(href)
410 if action == 'upload':
416 if action == b'upload':
411 # If uploading blobs, read data from local blobstore.
417 # If uploading blobs, read data from local blobstore.
412 if not localstore.verify(oid):
418 if not localstore.verify(oid):
413 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
419 raise error.Abort(_(b'detected corrupt lfs object: %s') % oid,
414 hint=_('run hg verify'))
420 hint=_(b'run hg verify'))
415 request.data = filewithprogress(localstore.open(oid), None)
421 request.data = filewithprogress(localstore.open(oid), None)
416 request.get_method = lambda: 'PUT'
422 request.get_method = lambda: 'PUT'
417 request.add_header('Content-Type', 'application/octet-stream')
423 request.add_header('Content-Type', 'application/octet-stream')
@@ -424,13 +430,14 b' class _gitlfsremote(object):'
424 with contextlib.closing(self.urlopener.open(request)) as req:
430 with contextlib.closing(self.urlopener.open(request)) as req:
425 ui = self.ui # Shorten debug lines
431 ui = self.ui # Shorten debug lines
426 if self.ui.debugflag:
432 if self.ui.debugflag:
427 ui.debug('Status: %d\n' % req.status)
433 ui.debug(b'Status: %d\n' % req.status)
428 # lfs-test-server and hg serve return headers in different
434 # lfs-test-server and hg serve return headers in different
429 # order
435 # order
430 ui.debug('%s\n'
436 headers = pycompat.bytestr(req.info())
431 % '\n'.join(sorted(str(req.info()).splitlines())))
437 ui.debug(b'%s\n'
438 % b'\n'.join(sorted(headers.splitlines())))
432
439
433 if action == 'download':
440 if action == b'download':
434 # If downloading blobs, store downloaded data to local
441 # If downloading blobs, store downloaded data to local
435 # blobstore
442 # blobstore
436 localstore.download(oid, req)
443 localstore.download(oid, req)
@@ -441,65 +448,65 b' class _gitlfsremote(object):'
441 break
448 break
442 response += data
449 response += data
443 if response:
450 if response:
444 ui.debug('lfs %s response: %s' % (action, response))
451 ui.debug(b'lfs %s response: %s' % (action, response))
445 except util.urlerr.httperror as ex:
452 except util.urlerr.httperror as ex:
446 if self.ui.debugflag:
453 if self.ui.debugflag:
447 self.ui.debug('%s: %s\n' % (oid, ex.read()))
454 self.ui.debug(b'%s: %s\n' % (oid, ex.read())) # XXX: also bytes?
448 raise LfsRemoteError(_('LFS HTTP error: %s (oid=%s, action=%s)')
455 raise LfsRemoteError(_(b'LFS HTTP error: %s (oid=%s, action=%s)')
449 % (ex, oid, action))
456 % (stringutil.forcebytestr(ex), oid, action))
450 except util.urlerr.urlerror as ex:
457 except util.urlerr.urlerror as ex:
451 hint = (_('attempted connection to %s')
458 hint = (_(b'attempted connection to %s')
452 % util.urllibcompat.getfullurl(request))
459 % pycompat.bytesurl(util.urllibcompat.getfullurl(request)))
453 raise LfsRemoteError(_('LFS error: %s') % _urlerrorreason(ex),
460 raise LfsRemoteError(_(b'LFS error: %s') % _urlerrorreason(ex),
454 hint=hint)
461 hint=hint)
455
462
456 def _batch(self, pointers, localstore, action):
463 def _batch(self, pointers, localstore, action):
457 if action not in ['upload', 'download']:
464 if action not in [b'upload', b'download']:
458 raise error.ProgrammingError('invalid Git-LFS action: %s' % action)
465 raise error.ProgrammingError(b'invalid Git-LFS action: %s' % action)
459
466
460 response = self._batchrequest(pointers, action)
467 response = self._batchrequest(pointers, action)
461 objects = self._extractobjects(response, pointers, action)
468 objects = self._extractobjects(response, pointers, action)
462 total = sum(x.get('size', 0) for x in objects)
469 total = sum(x.get(b'size', 0) for x in objects)
463 sizes = {}
470 sizes = {}
464 for obj in objects:
471 for obj in objects:
465 sizes[obj.get('oid')] = obj.get('size', 0)
472 sizes[obj.get(b'oid')] = obj.get(b'size', 0)
466 topic = {'upload': _('lfs uploading'),
473 topic = {b'upload': _(b'lfs uploading'),
467 'download': _('lfs downloading')}[action]
474 b'download': _(b'lfs downloading')}[action]
468 if len(objects) > 1:
475 if len(objects) > 1:
469 self.ui.note(_('lfs: need to transfer %d objects (%s)\n')
476 self.ui.note(_(b'lfs: need to transfer %d objects (%s)\n')
470 % (len(objects), util.bytecount(total)))
477 % (len(objects), util.bytecount(total)))
471
478
472 def transfer(chunk):
479 def transfer(chunk):
473 for obj in chunk:
480 for obj in chunk:
474 objsize = obj.get('size', 0)
481 objsize = obj.get(b'size', 0)
475 if self.ui.verbose:
482 if self.ui.verbose:
476 if action == 'download':
483 if action == b'download':
477 msg = _('lfs: downloading %s (%s)\n')
484 msg = _(b'lfs: downloading %s (%s)\n')
478 elif action == 'upload':
485 elif action == b'upload':
479 msg = _('lfs: uploading %s (%s)\n')
486 msg = _(b'lfs: uploading %s (%s)\n')
480 self.ui.note(msg % (obj.get('oid'),
487 self.ui.note(msg % (obj.get(b'oid'),
481 util.bytecount(objsize)))
488 util.bytecount(objsize)))
482 retry = self.retry
489 retry = self.retry
483 while True:
490 while True:
484 try:
491 try:
485 self._basictransfer(obj, action, localstore)
492 self._basictransfer(obj, action, localstore)
486 yield 1, obj.get('oid')
493 yield 1, obj.get(b'oid')
487 break
494 break
488 except socket.error as ex:
495 except socket.error as ex:
489 if retry > 0:
496 if retry > 0:
490 self.ui.note(
497 self.ui.note(
491 _('lfs: failed: %r (remaining retry %d)\n')
498 _(b'lfs: failed: %r (remaining retry %d)\n')
492 % (ex, retry))
499 % (stringutil.forcebytestr(ex), retry))
493 retry -= 1
500 retry -= 1
494 continue
501 continue
495 raise
502 raise
496
503
497 # Until https multiplexing gets sorted out
504 # Until https multiplexing gets sorted out
498 if self.ui.configbool('experimental', 'lfs.worker-enable'):
505 if self.ui.configbool(b'experimental', b'lfs.worker-enable'):
499 oids = worker.worker(self.ui, 0.1, transfer, (),
506 oids = worker.worker(self.ui, 0.1, transfer, (),
500 sorted(objects, key=lambda o: o.get('oid')))
507 sorted(objects, key=lambda o: o.get(b'oid')))
501 else:
508 else:
502 oids = transfer(sorted(objects, key=lambda o: o.get('oid')))
509 oids = transfer(sorted(objects, key=lambda o: o.get(b'oid')))
503
510
504 with self.ui.makeprogress(topic, total=total) as progress:
511 with self.ui.makeprogress(topic, total=total) as progress:
505 progress.update(0)
512 progress.update(0)
@@ -509,14 +516,14 b' class _gitlfsremote(object):'
509 processed += sizes[oid]
516 processed += sizes[oid]
510 blobs += 1
517 blobs += 1
511 progress.update(processed)
518 progress.update(processed)
512 self.ui.note(_('lfs: processed: %s\n') % oid)
519 self.ui.note(_(b'lfs: processed: %s\n') % oid)
513
520
514 if blobs > 0:
521 if blobs > 0:
515 if action == 'upload':
522 if action == b'upload':
516 self.ui.status(_('lfs: uploaded %d files (%s)\n')
523 self.ui.status(_(b'lfs: uploaded %d files (%s)\n')
517 % (blobs, util.bytecount(processed)))
524 % (blobs, util.bytecount(processed)))
518 elif action == 'download':
525 elif action == b'download':
519 self.ui.status(_('lfs: downloaded %d files (%s)\n')
526 self.ui.status(_(b'lfs: downloaded %d files (%s)\n')
520 % (blobs, util.bytecount(processed)))
527 % (blobs, util.bytecount(processed)))
521
528
522 def __del__(self):
529 def __del__(self):
@@ -531,18 +538,18 b' class _dummyremote(object):'
531 """Dummy store storing blobs to temp directory."""
538 """Dummy store storing blobs to temp directory."""
532
539
533 def __init__(self, repo, url):
540 def __init__(self, repo, url):
534 fullpath = repo.vfs.join('lfs', url.path)
541 fullpath = repo.vfs.join(b'lfs', url.path)
535 self.vfs = lfsvfs(fullpath)
542 self.vfs = lfsvfs(fullpath)
536
543
537 def writebatch(self, pointers, fromstore):
544 def writebatch(self, pointers, fromstore):
538 for p in _deduplicate(pointers):
545 for p in _deduplicate(pointers):
539 content = fromstore.read(p.oid(), verify=True)
546 content = fromstore.read(p.oid(), verify=True)
540 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
547 with self.vfs(p.oid(), b'wb', atomictemp=True) as fp:
541 fp.write(content)
548 fp.write(content)
542
549
543 def readbatch(self, pointers, tostore):
550 def readbatch(self, pointers, tostore):
544 for p in _deduplicate(pointers):
551 for p in _deduplicate(pointers):
545 with self.vfs(p.oid(), 'rb') as fp:
552 with self.vfs(p.oid(), b'rb') as fp:
546 tostore.download(p.oid(), fp)
553 tostore.download(p.oid(), fp)
547
554
548 class _nullremote(object):
555 class _nullremote(object):
@@ -570,13 +577,13 b' class _promptremote(object):'
570 self._prompt()
577 self._prompt()
571
578
572 def _prompt(self):
579 def _prompt(self):
573 raise error.Abort(_('lfs.url needs to be configured'))
580 raise error.Abort(_(b'lfs.url needs to be configured'))
574
581
575 _storemap = {
582 _storemap = {
576 'https': _gitlfsremote,
583 b'https': _gitlfsremote,
577 'http': _gitlfsremote,
584 b'http': _gitlfsremote,
578 'file': _dummyremote,
585 b'file': _dummyremote,
579 'null': _nullremote,
586 b'null': _nullremote,
580 None: _promptremote,
587 None: _promptremote,
581 }
588 }
582
589
@@ -590,8 +597,8 b' def _deduplicate(pointers):'
590 def _verify(oid, content):
597 def _verify(oid, content):
591 realoid = node.hex(hashlib.sha256(content).digest())
598 realoid = node.hex(hashlib.sha256(content).digest())
592 if realoid != oid:
599 if realoid != oid:
593 raise LfsCorruptionError(_('detected corrupt lfs object: %s') % oid,
600 raise LfsCorruptionError(_(b'detected corrupt lfs object: %s') % oid,
594 hint=_('run hg verify'))
601 hint=_(b'run hg verify'))
595
602
596 def remote(repo, remote=None):
603 def remote(repo, remote=None):
597 """remotestore factory. return a store in _storemap depending on config
604 """remotestore factory. return a store in _storemap depending on config
@@ -603,7 +610,7 b' def remote(repo, remote=None):'
603
610
604 https://github.com/git-lfs/git-lfs/blob/master/docs/api/server-discovery.md
611 https://github.com/git-lfs/git-lfs/blob/master/docs/api/server-discovery.md
605 """
612 """
606 lfsurl = repo.ui.config('lfs', 'url')
613 lfsurl = repo.ui.config(b'lfs', b'url')
607 url = util.url(lfsurl or '')
614 url = util.url(lfsurl or '')
608 if lfsurl is None:
615 if lfsurl is None:
609 if remote:
616 if remote:
@@ -616,7 +623,7 b' def remote(repo, remote=None):'
616 else:
623 else:
617 # TODO: investigate 'paths.remote:lfsurl' style path customization,
624 # TODO: investigate 'paths.remote:lfsurl' style path customization,
618 # and fall back to inferring from 'paths.remote' if unspecified.
625 # and fall back to inferring from 'paths.remote' if unspecified.
619 path = repo.ui.config('paths', 'default') or ''
626 path = repo.ui.config(b'paths', b'default') or b''
620
627
621 defaulturl = util.url(path)
628 defaulturl = util.url(path)
622
629
@@ -628,11 +635,11 b' def remote(repo, remote=None):'
628 defaulturl.path = (defaulturl.path or b'') + b'.git/info/lfs'
635 defaulturl.path = (defaulturl.path or b'') + b'.git/info/lfs'
629
636
630 url = util.url(bytes(defaulturl))
637 url = util.url(bytes(defaulturl))
631 repo.ui.note(_('lfs: assuming remote store: %s\n') % url)
638 repo.ui.note(_(b'lfs: assuming remote store: %s\n') % url)
632
639
633 scheme = url.scheme
640 scheme = url.scheme
634 if scheme not in _storemap:
641 if scheme not in _storemap:
635 raise error.Abort(_('lfs: unknown url scheme: %s') % scheme)
642 raise error.Abort(_(b'lfs: unknown url scheme: %s') % scheme)
636 return _storemap[scheme](repo, url)
643 return _storemap[scheme](repo, url)
637
644
638 class LfsRemoteError(error.StorageError):
645 class LfsRemoteError(error.StorageError):
@@ -374,7 +374,7 b' Test a checksum failure during the proce'
374 $LOCALIP - - [$ERRDATE$] HG error: res.setbodybytes(localstore.read(oid)) (glob)
374 $LOCALIP - - [$ERRDATE$] HG error: res.setbodybytes(localstore.read(oid)) (glob)
375 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
375 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
376 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, b'dummy content') (glob)
376 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, b'dummy content') (glob)
377 $LOCALIP - - [$ERRDATE$] HG error: hint=_('run hg verify')) (glob)
377 $LOCALIP - - [$ERRDATE$] HG error: hint=_(b'run hg verify')) (glob)
378 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
378 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
379 $LOCALIP - - [$ERRDATE$] HG error: (glob)
379 $LOCALIP - - [$ERRDATE$] HG error: (glob)
380
380
General Comments 0
You need to be logged in to leave comments. Login now