comparison mercurial/store.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children c59eb1560c44
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
38 If matcher is None, returns True""" 38 If matcher is None, returns True"""
39 39
40 if matcher is None: 40 if matcher is None:
41 return True 41 return True
42 path = decodedir(path) 42 path = decodedir(path)
43 if path.startswith('data/'): 43 if path.startswith(b'data/'):
44 return matcher(path[len('data/') : -len('.i')]) 44 return matcher(path[len(b'data/') : -len(b'.i')])
45 elif path.startswith('meta/'): 45 elif path.startswith(b'meta/'):
46 return matcher.visitdir(path[len('meta/') : -len('/00manifest.i')]) 46 return matcher.visitdir(path[len(b'meta/') : -len(b'/00manifest.i')])
47 47
48 raise error.ProgrammingError("cannot decode path %s" % path) 48 raise error.ProgrammingError(b"cannot decode path %s" % path)
49 49
50 50
51 # This avoids a collision between a file named foo and a dir named 51 # This avoids a collision between a file named foo and a dir named
52 # foo.i or foo.d 52 # foo.i or foo.d
53 def _encodedir(path): 53 def _encodedir(path):
60 'data/foo.i.hg.hg/bla.i' 60 'data/foo.i.hg.hg/bla.i'
61 >>> _encodedir(b'data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n') 61 >>> _encodedir(b'data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
62 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n' 62 'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
63 ''' 63 '''
64 return ( 64 return (
65 path.replace(".hg/", ".hg.hg/") 65 path.replace(b".hg/", b".hg.hg/")
66 .replace(".i/", ".i.hg/") 66 .replace(b".i/", b".i.hg/")
67 .replace(".d/", ".d.hg/") 67 .replace(b".d/", b".d.hg/")
68 ) 68 )
69 69
70 70
71 encodedir = getattr(parsers, 'encodedir', _encodedir) 71 encodedir = getattr(parsers, 'encodedir', _encodedir)
72 72
78 >>> decodedir(b'data/foo.i.hg/bla.i') 78 >>> decodedir(b'data/foo.i.hg/bla.i')
79 'data/foo.i/bla.i' 79 'data/foo.i/bla.i'
80 >>> decodedir(b'data/foo.i.hg.hg/bla.i') 80 >>> decodedir(b'data/foo.i.hg.hg/bla.i')
81 'data/foo.i.hg/bla.i' 81 'data/foo.i.hg/bla.i'
82 ''' 82 '''
83 if ".hg/" not in path: 83 if b".hg/" not in path:
84 return path 84 return path
85 return ( 85 return (
86 path.replace(".d.hg/", ".d/") 86 path.replace(b".d.hg/", b".d/")
87 .replace(".i.hg/", ".i/") 87 .replace(b".i.hg/", b".i/")
88 .replace(".hg.hg/", ".hg/") 88 .replace(b".hg.hg/", b".hg/")
89 ) 89 )
90 90
91 91
92 def _reserved(): 92 def _reserved():
93 ''' characters that are problematic for filesystems 93 ''' characters that are problematic for filesystems
129 >>> enc(b'the\\x07quick\\xADshot') 129 >>> enc(b'the\\x07quick\\xADshot')
130 'the~07quick~adshot' 130 'the~07quick~adshot'
131 >>> dec(b'the~07quick~adshot') 131 >>> dec(b'the~07quick~adshot')
132 'the\\x07quick\\xadshot' 132 'the\\x07quick\\xadshot'
133 ''' 133 '''
134 e = '_' 134 e = b'_'
135 xchr = pycompat.bytechr 135 xchr = pycompat.bytechr
136 asciistr = list(map(xchr, range(127))) 136 asciistr = list(map(xchr, range(127)))
137 capitals = list(range(ord("A"), ord("Z") + 1)) 137 capitals = list(range(ord(b"A"), ord(b"Z") + 1))
138 138
139 cmap = dict((x, x) for x in asciistr) 139 cmap = dict((x, x) for x in asciistr)
140 for x in _reserved(): 140 for x in _reserved():
141 cmap[xchr(x)] = "~%02x" % x 141 cmap[xchr(x)] = b"~%02x" % x
142 for x in capitals + [ord(e)]: 142 for x in capitals + [ord(e)]:
143 cmap[xchr(x)] = e + xchr(x).lower() 143 cmap[xchr(x)] = e + xchr(x).lower()
144 144
145 dmap = {} 145 dmap = {}
146 for k, v in cmap.iteritems(): 146 for k, v in cmap.iteritems():
158 pass 158 pass
159 else: 159 else:
160 raise KeyError 160 raise KeyError
161 161
162 return ( 162 return (
163 lambda s: ''.join( 163 lambda s: b''.join(
164 [cmap[s[c : c + 1]] for c in pycompat.xrange(len(s))] 164 [cmap[s[c : c + 1]] for c in pycompat.xrange(len(s))]
165 ), 165 ),
166 lambda s: ''.join(list(decode(s))), 166 lambda s: b''.join(list(decode(s))),
167 ) 167 )
168 168
169 169
170 _encodefname, _decodefname = _buildencodefun() 170 _encodefname, _decodefname = _buildencodefun()
171 171
199 'the~07quick~adshot' 199 'the~07quick~adshot'
200 ''' 200 '''
201 xchr = pycompat.bytechr 201 xchr = pycompat.bytechr
202 cmap = dict([(xchr(x), xchr(x)) for x in pycompat.xrange(127)]) 202 cmap = dict([(xchr(x), xchr(x)) for x in pycompat.xrange(127)])
203 for x in _reserved(): 203 for x in _reserved():
204 cmap[xchr(x)] = "~%02x" % x 204 cmap[xchr(x)] = b"~%02x" % x
205 for x in range(ord("A"), ord("Z") + 1): 205 for x in range(ord(b"A"), ord(b"Z") + 1):
206 cmap[xchr(x)] = xchr(x).lower() 206 cmap[xchr(x)] = xchr(x).lower()
207 207
208 def lowerencode(s): 208 def lowerencode(s):
209 return "".join([cmap[c] for c in pycompat.iterbytestr(s)]) 209 return b"".join([cmap[c] for c in pycompat.iterbytestr(s)])
210 210
211 return lowerencode 211 return lowerencode
212 212
213 213
214 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun() 214 lowerencode = getattr(parsers, 'lowerencode', None) or _buildlowerencodefun()
215 215
216 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9 216 # Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
217 _winres3 = ('aux', 'con', 'prn', 'nul') # length 3 217 _winres3 = (b'aux', b'con', b'prn', b'nul') # length 3
218 _winres4 = ('com', 'lpt') # length 4 (with trailing 1..9) 218 _winres4 = (b'com', b'lpt') # length 4 (with trailing 1..9)
219 219
220 220
221 def _auxencode(path, dotencode): 221 def _auxencode(path, dotencode):
222 ''' 222 '''
223 Encodes filenames containing names reserved by Windows or which end in 223 Encodes filenames containing names reserved by Windows or which end in
241 ['~20.foo'] 241 ['~20.foo']
242 ''' 242 '''
243 for i, n in enumerate(path): 243 for i, n in enumerate(path):
244 if not n: 244 if not n:
245 continue 245 continue
246 if dotencode and n[0] in '. ': 246 if dotencode and n[0] in b'. ':
247 n = "~%02x" % ord(n[0:1]) + n[1:] 247 n = b"~%02x" % ord(n[0:1]) + n[1:]
248 path[i] = n 248 path[i] = n
249 else: 249 else:
250 l = n.find('.') 250 l = n.find(b'.')
251 if l == -1: 251 if l == -1:
252 l = len(n) 252 l = len(n)
253 if (l == 3 and n[:3] in _winres3) or ( 253 if (l == 3 and n[:3] in _winres3) or (
254 l == 4 and n[3:4] <= '9' and n[3:4] >= '1' and n[:3] in _winres4 254 l == 4
255 and n[3:4] <= b'9'
256 and n[3:4] >= b'1'
257 and n[:3] in _winres4
255 ): 258 ):
256 # encode third letter ('aux' -> 'au~78') 259 # encode third letter ('aux' -> 'au~78')
257 ec = "~%02x" % ord(n[2:3]) 260 ec = b"~%02x" % ord(n[2:3])
258 n = n[0:2] + ec + n[3:] 261 n = n[0:2] + ec + n[3:]
259 path[i] = n 262 path[i] = n
260 if n[-1] in '. ': 263 if n[-1] in b'. ':
261 # encode last period or space ('foo...' -> 'foo..~2e') 264 # encode last period or space ('foo...' -> 'foo..~2e')
262 path[i] = n[:-1] + "~%02x" % ord(n[-1:]) 265 path[i] = n[:-1] + b"~%02x" % ord(n[-1:])
263 return path 266 return path
264 267
265 268
266 _maxstorepathlen = 120 269 _maxstorepathlen = 120
267 _dirprefixlen = 8 270 _dirprefixlen = 8
268 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4 271 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
269 272
270 273
271 def _hashencode(path, dotencode): 274 def _hashencode(path, dotencode):
272 digest = node.hex(hashlib.sha1(path).digest()) 275 digest = node.hex(hashlib.sha1(path).digest())
273 le = lowerencode(path[5:]).split('/') # skips prefix 'data/' or 'meta/' 276 le = lowerencode(path[5:]).split(b'/') # skips prefix 'data/' or 'meta/'
274 parts = _auxencode(le, dotencode) 277 parts = _auxencode(le, dotencode)
275 basename = parts[-1] 278 basename = parts[-1]
276 _root, ext = os.path.splitext(basename) 279 _root, ext = os.path.splitext(basename)
277 sdirs = [] 280 sdirs = []
278 sdirslen = 0 281 sdirslen = 0
279 for p in parts[:-1]: 282 for p in parts[:-1]:
280 d = p[:_dirprefixlen] 283 d = p[:_dirprefixlen]
281 if d[-1] in '. ': 284 if d[-1] in b'. ':
282 # Windows can't access dirs ending in period or space 285 # Windows can't access dirs ending in period or space
283 d = d[:-1] + '_' 286 d = d[:-1] + b'_'
284 if sdirslen == 0: 287 if sdirslen == 0:
285 t = len(d) 288 t = len(d)
286 else: 289 else:
287 t = sdirslen + 1 + len(d) 290 t = sdirslen + 1 + len(d)
288 if t > _maxshortdirslen: 291 if t > _maxshortdirslen:
289 break 292 break
290 sdirs.append(d) 293 sdirs.append(d)
291 sdirslen = t 294 sdirslen = t
292 dirs = '/'.join(sdirs) 295 dirs = b'/'.join(sdirs)
293 if len(dirs) > 0: 296 if len(dirs) > 0:
294 dirs += '/' 297 dirs += b'/'
295 res = 'dh/' + dirs + digest + ext 298 res = b'dh/' + dirs + digest + ext
296 spaceleft = _maxstorepathlen - len(res) 299 spaceleft = _maxstorepathlen - len(res)
297 if spaceleft > 0: 300 if spaceleft > 0:
298 filler = basename[:spaceleft] 301 filler = basename[:spaceleft]
299 res = 'dh/' + dirs + filler + digest + ext 302 res = b'dh/' + dirs + filler + digest + ext
300 return res 303 return res
301 304
302 305
303 def _hybridencode(path, dotencode): 306 def _hybridencode(path, dotencode):
304 '''encodes path with a length limit 307 '''encodes path with a length limit
330 333
331 The string 'data/' at the beginning is replaced with 'dh/', if the hashed 334 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
332 encoding was used. 335 encoding was used.
333 ''' 336 '''
334 path = encodedir(path) 337 path = encodedir(path)
335 ef = _encodefname(path).split('/') 338 ef = _encodefname(path).split(b'/')
336 res = '/'.join(_auxencode(ef, dotencode)) 339 res = b'/'.join(_auxencode(ef, dotencode))
337 if len(res) > _maxstorepathlen: 340 if len(res) > _maxstorepathlen:
338 res = _hashencode(path, dotencode) 341 res = _hashencode(path, dotencode)
339 return res 342 return res
340 343
341 344
342 def _pathencode(path): 345 def _pathencode(path):
343 de = encodedir(path) 346 de = encodedir(path)
344 if len(path) > _maxstorepathlen: 347 if len(path) > _maxstorepathlen:
345 return _hashencode(de, True) 348 return _hashencode(de, True)
346 ef = _encodefname(de).split('/') 349 ef = _encodefname(de).split(b'/')
347 res = '/'.join(_auxencode(ef, True)) 350 res = b'/'.join(_auxencode(ef, True))
348 if len(res) > _maxstorepathlen: 351 if len(res) > _maxstorepathlen:
349 return _hashencode(de, True) 352 return _hashencode(de, True)
350 return res 353 return res
351 354
352 355
368 mode = None 371 mode = None
369 return mode 372 return mode
370 373
371 374
372 _data = ( 375 _data = (
373 'bookmarks narrowspec data meta 00manifest.d 00manifest.i' 376 b'bookmarks narrowspec data meta 00manifest.d 00manifest.i'
374 ' 00changelog.d 00changelog.i phaseroots obsstore' 377 b' 00changelog.d 00changelog.i phaseroots obsstore'
375 ) 378 )
376 379
377 380
378 def isrevlog(f, kind, st): 381 def isrevlog(f, kind, st):
379 return kind == stat.S_IFREG and f[-2:] in ('.i', '.d') 382 return kind == stat.S_IFREG and f[-2:] in (b'.i', b'.d')
380 383
381 384
382 class basicstore(object): 385 class basicstore(object):
383 '''base class for local repository stores''' 386 '''base class for local repository stores'''
384 387
390 self.rawvfs = vfs 393 self.rawvfs = vfs
391 self.vfs = vfsmod.filtervfs(vfs, encodedir) 394 self.vfs = vfsmod.filtervfs(vfs, encodedir)
392 self.opener = self.vfs 395 self.opener = self.vfs
393 396
394 def join(self, f): 397 def join(self, f):
395 return self.path + '/' + encodedir(f) 398 return self.path + b'/' + encodedir(f)
396 399
397 def _walk(self, relpath, recurse, filefilter=isrevlog): 400 def _walk(self, relpath, recurse, filefilter=isrevlog):
398 '''yields (unencoded, encoded, size)''' 401 '''yields (unencoded, encoded, size)'''
399 path = self.path 402 path = self.path
400 if relpath: 403 if relpath:
401 path += '/' + relpath 404 path += b'/' + relpath
402 striplen = len(self.path) + 1 405 striplen = len(self.path) + 1
403 l = [] 406 l = []
404 if self.rawvfs.isdir(path): 407 if self.rawvfs.isdir(path):
405 visit = [path] 408 visit = [path]
406 readdir = self.rawvfs.readdir 409 readdir = self.rawvfs.readdir
407 while visit: 410 while visit:
408 p = visit.pop() 411 p = visit.pop()
409 for f, kind, st in readdir(p, stat=True): 412 for f, kind, st in readdir(p, stat=True):
410 fp = p + '/' + f 413 fp = p + b'/' + f
411 if filefilter(f, kind, st): 414 if filefilter(f, kind, st):
412 n = util.pconvert(fp[striplen:]) 415 n = util.pconvert(fp[striplen:])
413 l.append((decodedir(n), n, st.st_size)) 416 l.append((decodedir(n), n, st.st_size))
414 elif kind == stat.S_IFDIR and recurse: 417 elif kind == stat.S_IFDIR and recurse:
415 visit.append(fp) 418 visit.append(fp)
422 def manifestlog(self, repo, storenarrowmatch): 425 def manifestlog(self, repo, storenarrowmatch):
423 rootstore = manifest.manifestrevlog(self.vfs) 426 rootstore = manifest.manifestrevlog(self.vfs)
424 return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch) 427 return manifest.manifestlog(self.vfs, repo, rootstore, storenarrowmatch)
425 428
426 def datafiles(self, matcher=None): 429 def datafiles(self, matcher=None):
427 return self._walk('data', True) + self._walk('meta', True) 430 return self._walk(b'data', True) + self._walk(b'meta', True)
428 431
429 def topfiles(self): 432 def topfiles(self):
430 # yield manifest before changelog 433 # yield manifest before changelog
431 return reversed(self._walk('', False)) 434 return reversed(self._walk(b'', False))
432 435
433 def walk(self, matcher=None): 436 def walk(self, matcher=None):
434 '''yields (unencoded, encoded, size) 437 '''yields (unencoded, encoded, size)
435 438
436 if a matcher is passed, storage files of only those tracked paths 439 if a matcher is passed, storage files of only those tracked paths
441 yield x 444 yield x
442 for x in self.topfiles(): 445 for x in self.topfiles():
443 yield x 446 yield x
444 447
445 def copylist(self): 448 def copylist(self):
446 return ['requires'] + _data.split() 449 return [b'requires'] + _data.split()
447 450
448 def write(self, tr): 451 def write(self, tr):
449 pass 452 pass
450 453
451 def invalidatecaches(self): 454 def invalidatecaches(self):
454 def markremoved(self, fn): 457 def markremoved(self, fn):
455 pass 458 pass
456 459
457 def __contains__(self, path): 460 def __contains__(self, path):
458 '''Checks if the store contains path''' 461 '''Checks if the store contains path'''
459 path = "/".join(("data", path)) 462 path = b"/".join((b"data", path))
460 # file? 463 # file?
461 if self.vfs.exists(path + ".i"): 464 if self.vfs.exists(path + b".i"):
462 return True 465 return True
463 # dir? 466 # dir?
464 if not path.endswith("/"): 467 if not path.endswith(b"/"):
465 path = path + "/" 468 path = path + b"/"
466 return self.vfs.exists(path) 469 return self.vfs.exists(path)
467 470
468 471
469 class encodedstore(basicstore): 472 class encodedstore(basicstore):
470 def __init__(self, path, vfstype): 473 def __init__(self, path, vfstype):
471 vfs = vfstype(path + '/store') 474 vfs = vfstype(path + b'/store')
472 self.path = vfs.base 475 self.path = vfs.base
473 self.createmode = _calcmode(vfs) 476 self.createmode = _calcmode(vfs)
474 vfs.createmode = self.createmode 477 vfs.createmode = self.createmode
475 self.rawvfs = vfs 478 self.rawvfs = vfs
476 self.vfs = vfsmod.filtervfs(vfs, encodefilename) 479 self.vfs = vfsmod.filtervfs(vfs, encodefilename)
485 if a is not None and not _matchtrackedpath(a, matcher): 488 if a is not None and not _matchtrackedpath(a, matcher):
486 continue 489 continue
487 yield a, b, size 490 yield a, b, size
488 491
489 def join(self, f): 492 def join(self, f):
490 return self.path + '/' + encodefilename(f) 493 return self.path + b'/' + encodefilename(f)
491 494
492 def copylist(self): 495 def copylist(self):
493 return ['requires', '00changelog.i'] + [ 496 return [b'requires', b'00changelog.i'] + [
494 'store/' + f for f in _data.split() 497 b'store/' + f for f in _data.split()
495 ] 498 ]
496 499
497 500
498 class fncache(object): 501 class fncache(object):
499 # the filename used to be partially encoded 502 # the filename used to be partially encoded
515 518
516 def _load(self, warn=None): 519 def _load(self, warn=None):
517 '''fill the entries from the fncache file''' 520 '''fill the entries from the fncache file'''
518 self._dirty = False 521 self._dirty = False
519 try: 522 try:
520 fp = self.vfs('fncache', mode='rb') 523 fp = self.vfs(b'fncache', mode=b'rb')
521 except IOError: 524 except IOError:
522 # skip nonexistent file 525 # skip nonexistent file
523 self.entries = set() 526 self.entries = set()
524 return 527 return
525 528
535 # substring '\n' not found, maybe the entry is bigger than the 538 # substring '\n' not found, maybe the entry is bigger than the
536 # chunksize, so let's keep iterating 539 # chunksize, so let's keep iterating
537 pass 540 pass
538 541
539 if chunk: 542 if chunk:
540 msg = _("fncache does not ends with a newline") 543 msg = _(b"fncache does not ends with a newline")
541 if warn: 544 if warn:
542 warn(msg + '\n') 545 warn(msg + b'\n')
543 else: 546 else:
544 raise error.Abort( 547 raise error.Abort(
545 msg, 548 msg,
546 hint=_( 549 hint=_(
547 "use 'hg debugrebuildfncache' to " "rebuild the fncache" 550 b"use 'hg debugrebuildfncache' to "
551 b"rebuild the fncache"
548 ), 552 ),
549 ) 553 )
550 self._checkentries(fp, warn) 554 self._checkentries(fp, warn)
551 fp.close() 555 fp.close()
552 556
553 def _checkentries(self, fp, warn): 557 def _checkentries(self, fp, warn):
554 """ make sure there is no empty string in entries """ 558 """ make sure there is no empty string in entries """
555 if '' in self.entries: 559 if b'' in self.entries:
556 fp.seek(0) 560 fp.seek(0)
557 for n, line in enumerate(util.iterfile(fp)): 561 for n, line in enumerate(util.iterfile(fp)):
558 if not line.rstrip('\n'): 562 if not line.rstrip(b'\n'):
559 t = _('invalid entry in fncache, line %d') % (n + 1) 563 t = _(b'invalid entry in fncache, line %d') % (n + 1)
560 if warn: 564 if warn:
561 warn(t + '\n') 565 warn(t + b'\n')
562 else: 566 else:
563 raise error.Abort(t) 567 raise error.Abort(t)
564 568
565 def write(self, tr): 569 def write(self, tr):
566 if self._dirty: 570 if self._dirty:
567 assert self.entries is not None 571 assert self.entries is not None
568 self.entries = self.entries | self.addls 572 self.entries = self.entries | self.addls
569 self.addls = set() 573 self.addls = set()
570 tr.addbackup('fncache') 574 tr.addbackup(b'fncache')
571 fp = self.vfs('fncache', mode='wb', atomictemp=True) 575 fp = self.vfs(b'fncache', mode=b'wb', atomictemp=True)
572 if self.entries: 576 if self.entries:
573 fp.write(encodedir('\n'.join(self.entries) + '\n')) 577 fp.write(encodedir(b'\n'.join(self.entries) + b'\n'))
574 fp.close() 578 fp.close()
575 self._dirty = False 579 self._dirty = False
576 if self.addls: 580 if self.addls:
577 # if we have just new entries, let's append them to the fncache 581 # if we have just new entries, let's append them to the fncache
578 tr.addbackup('fncache') 582 tr.addbackup(b'fncache')
579 fp = self.vfs('fncache', mode='ab', atomictemp=True) 583 fp = self.vfs(b'fncache', mode=b'ab', atomictemp=True)
580 if self.addls: 584 if self.addls:
581 fp.write(encodedir('\n'.join(self.addls) + '\n')) 585 fp.write(encodedir(b'\n'.join(self.addls) + b'\n'))
582 fp.close() 586 fp.close()
583 self.entries = None 587 self.entries = None
584 self.addls = set() 588 self.addls = set()
585 589
586 def add(self, fn): 590 def add(self, fn):
618 def __init__(self, vfs, fnc, encode): 622 def __init__(self, vfs, fnc, encode):
619 vfsmod.proxyvfs.__init__(self, vfs) 623 vfsmod.proxyvfs.__init__(self, vfs)
620 self.fncache = fnc 624 self.fncache = fnc
621 self.encode = encode 625 self.encode = encode
622 626
623 def __call__(self, path, mode='r', *args, **kw): 627 def __call__(self, path, mode=b'r', *args, **kw):
624 encoded = self.encode(path) 628 encoded = self.encode(path)
625 if mode not in ('r', 'rb') and ( 629 if mode not in (b'r', b'rb') and (
626 path.startswith('data/') or path.startswith('meta/') 630 path.startswith(b'data/') or path.startswith(b'meta/')
627 ): 631 ):
628 # do not trigger a fncache load when adding a file that already is 632 # do not trigger a fncache load when adding a file that already is
629 # known to exist. 633 # known to exist.
630 notload = self.fncache.entries is None and self.vfs.exists(encoded) 634 notload = self.fncache.entries is None and self.vfs.exists(encoded)
631 if notload and 'a' in mode and not self.vfs.stat(encoded).st_size: 635 if notload and b'a' in mode and not self.vfs.stat(encoded).st_size:
632 # when appending to an existing file, if the file has size zero, 636 # when appending to an existing file, if the file has size zero,
633 # it should be considered as missing. Such zero-size files are 637 # it should be considered as missing. Such zero-size files are
634 # the result of truncation when a transaction is aborted. 638 # the result of truncation when a transaction is aborted.
635 notload = False 639 notload = False
636 if not notload: 640 if not notload:
649 if dotencode: 653 if dotencode:
650 encode = _pathencode 654 encode = _pathencode
651 else: 655 else:
652 encode = _plainhybridencode 656 encode = _plainhybridencode
653 self.encode = encode 657 self.encode = encode
654 vfs = vfstype(path + '/store') 658 vfs = vfstype(path + b'/store')
655 self.path = vfs.base 659 self.path = vfs.base
656 self.pathsep = self.path + '/' 660 self.pathsep = self.path + b'/'
657 self.createmode = _calcmode(vfs) 661 self.createmode = _calcmode(vfs)
658 vfs.createmode = self.createmode 662 vfs.createmode = self.createmode
659 self.rawvfs = vfs 663 self.rawvfs = vfs
660 fnc = fncache(vfs) 664 fnc = fncache(vfs)
661 self.fncache = fnc 665 self.fncache = fnc
679 if err.errno != errno.ENOENT: 683 if err.errno != errno.ENOENT:
680 raise 684 raise
681 685
682 def copylist(self): 686 def copylist(self):
683 d = ( 687 d = (
684 'bookmarks narrowspec data meta dh fncache phaseroots obsstore' 688 b'bookmarks narrowspec data meta dh fncache phaseroots obsstore'
685 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i' 689 b' 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
686 ) 690 )
687 return ['requires', '00changelog.i'] + ['store/' + f for f in d.split()] 691 return [b'requires', b'00changelog.i'] + [
692 b'store/' + f for f in d.split()
693 ]
688 694
689 def write(self, tr): 695 def write(self, tr):
690 self.fncache.write(tr) 696 self.fncache.write(tr)
691 697
692 def invalidatecaches(self): 698 def invalidatecaches(self):
707 # nonexistent entry 713 # nonexistent entry
708 return False 714 return False
709 715
710 def __contains__(self, path): 716 def __contains__(self, path):
711 '''Checks if the store contains path''' 717 '''Checks if the store contains path'''
712 path = "/".join(("data", path)) 718 path = b"/".join((b"data", path))
713 # check for files (exact match) 719 # check for files (exact match)
714 e = path + '.i' 720 e = path + b'.i'
715 if e in self.fncache and self._exists(e): 721 if e in self.fncache and self._exists(e):
716 return True 722 return True
717 # now check for directories (prefix match) 723 # now check for directories (prefix match)
718 if not path.endswith('/'): 724 if not path.endswith(b'/'):
719 path += '/' 725 path += b'/'
720 for e in self.fncache: 726 for e in self.fncache:
721 if e.startswith(path) and self._exists(e): 727 if e.startswith(path) and self._exists(e):
722 return True 728 return True
723 return False 729 return False