mercurial/branchmap.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43104 74802979dd9d
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
   108                 closednodes=closed,
   108                 closednodes=closed,
   109             )
   109             )
   110 
   110 
   111             # Try to stick it as low as possible
   111             # Try to stick it as low as possible
   112             # filter above served are unlikely to be fetch from a clone
   112             # filter above served are unlikely to be fetch from a clone
   113             for candidate in ('base', 'immutable', 'served'):
   113             for candidate in (b'base', b'immutable', b'served'):
   114                 rview = repo.filtered(candidate)
   114                 rview = repo.filtered(candidate)
   115                 if cache.validfor(rview):
   115                 if cache.validfor(rview):
   116                     self._per_filter[candidate] = cache
   116                     self._per_filter[candidate] = cache
   117                     cache.write(rview)
   117                     cache.write(rview)
   118                     return
   118                     return
   127     raise ValueError(r'node %s does not exist' % pycompat.sysstr(hex(node)))
   127     raise ValueError(r'node %s does not exist' % pycompat.sysstr(hex(node)))
   128 
   128 
   129 
   129 
   130 def _branchcachedesc(repo):
   130 def _branchcachedesc(repo):
   131     if repo.filtername is not None:
   131     if repo.filtername is not None:
   132         return 'branch cache (%s)' % repo.filtername
   132         return b'branch cache (%s)' % repo.filtername
   133     else:
   133     else:
   134         return 'branch cache'
   134         return b'branch cache'
   135 
   135 
   136 
   136 
   137 class branchcache(object):
   137 class branchcache(object):
   138     """A dict like object that hold branches heads cache.
   138     """A dict like object that hold branches heads cache.
   139 
   139 
   243     def fromfile(cls, repo):
   243     def fromfile(cls, repo):
   244         f = None
   244         f = None
   245         try:
   245         try:
   246             f = repo.cachevfs(cls._filename(repo))
   246             f = repo.cachevfs(cls._filename(repo))
   247             lineiter = iter(f)
   247             lineiter = iter(f)
   248             cachekey = next(lineiter).rstrip('\n').split(" ", 2)
   248             cachekey = next(lineiter).rstrip(b'\n').split(b" ", 2)
   249             last, lrev = cachekey[:2]
   249             last, lrev = cachekey[:2]
   250             last, lrev = bin(last), int(lrev)
   250             last, lrev = bin(last), int(lrev)
   251             filteredhash = None
   251             filteredhash = None
   252             hasnode = repo.changelog.hasnode
   252             hasnode = repo.changelog.hasnode
   253             if len(cachekey) > 2:
   253             if len(cachekey) > 2:
   265         except (IOError, OSError):
   265         except (IOError, OSError):
   266             return None
   266             return None
   267 
   267 
   268         except Exception as inst:
   268         except Exception as inst:
   269             if repo.ui.debugflag:
   269             if repo.ui.debugflag:
   270                 msg = 'invalid %s: %s\n'
   270                 msg = b'invalid %s: %s\n'
   271                 repo.ui.debug(
   271                 repo.ui.debug(
   272                     msg % (_branchcachedesc(repo), pycompat.bytestr(inst))
   272                     msg % (_branchcachedesc(repo), pycompat.bytestr(inst))
   273                 )
   273                 )
   274             bcache = None
   274             bcache = None
   275 
   275 
   281 
   281 
   282     def load(self, repo, lineiter):
   282     def load(self, repo, lineiter):
   283         """ fully loads the branchcache by reading from the file using the line
   283         """ fully loads the branchcache by reading from the file using the line
   284         iterator passed"""
   284         iterator passed"""
   285         for line in lineiter:
   285         for line in lineiter:
   286             line = line.rstrip('\n')
   286             line = line.rstrip(b'\n')
   287             if not line:
   287             if not line:
   288                 continue
   288                 continue
   289             node, state, label = line.split(" ", 2)
   289             node, state, label = line.split(b" ", 2)
   290             if state not in 'oc':
   290             if state not in b'oc':
   291                 raise ValueError(r'invalid branch state')
   291                 raise ValueError(r'invalid branch state')
   292             label = encoding.tolocal(label.strip())
   292             label = encoding.tolocal(label.strip())
   293             node = bin(node)
   293             node = bin(node)
   294             self._entries.setdefault(label, []).append(node)
   294             self._entries.setdefault(label, []).append(node)
   295             if state == 'c':
   295             if state == b'c':
   296                 self._closednodes.add(node)
   296                 self._closednodes.add(node)
   297 
   297 
   298     @staticmethod
   298     @staticmethod
   299     def _filename(repo):
   299     def _filename(repo):
   300         """name of a branchcache file for a given repo or repoview"""
   300         """name of a branchcache file for a given repo or repoview"""
   301         filename = "branch2"
   301         filename = b"branch2"
   302         if repo.filtername:
   302         if repo.filtername:
   303             filename = '%s-%s' % (filename, repo.filtername)
   303             filename = b'%s-%s' % (filename, repo.filtername)
   304         return filename
   304         return filename
   305 
   305 
   306     def validfor(self, repo):
   306     def validfor(self, repo):
   307         """Is the cache content valid regarding a repo
   307         """Is the cache content valid regarding a repo
   308 
   308 
   362             self._closednodes,
   362             self._closednodes,
   363         )
   363         )
   364 
   364 
   365     def write(self, repo):
   365     def write(self, repo):
   366         try:
   366         try:
   367             f = repo.cachevfs(self._filename(repo), "w", atomictemp=True)
   367             f = repo.cachevfs(self._filename(repo), b"w", atomictemp=True)
   368             cachekey = [hex(self.tipnode), '%d' % self.tiprev]
   368             cachekey = [hex(self.tipnode), b'%d' % self.tiprev]
   369             if self.filteredhash is not None:
   369             if self.filteredhash is not None:
   370                 cachekey.append(hex(self.filteredhash))
   370                 cachekey.append(hex(self.filteredhash))
   371             f.write(" ".join(cachekey) + '\n')
   371             f.write(b" ".join(cachekey) + b'\n')
   372             nodecount = 0
   372             nodecount = 0
   373             for label, nodes in sorted(self._entries.iteritems()):
   373             for label, nodes in sorted(self._entries.iteritems()):
   374                 label = encoding.fromlocal(label)
   374                 label = encoding.fromlocal(label)
   375                 for node in nodes:
   375                 for node in nodes:
   376                     nodecount += 1
   376                     nodecount += 1
   377                     if node in self._closednodes:
   377                     if node in self._closednodes:
   378                         state = 'c'
   378                         state = b'c'
   379                     else:
   379                     else:
   380                         state = 'o'
   380                         state = b'o'
   381                     f.write("%s %s %s\n" % (hex(node), state, label))
   381                     f.write(b"%s %s %s\n" % (hex(node), state, label))
   382             f.close()
   382             f.close()
   383             repo.ui.log(
   383             repo.ui.log(
   384                 'branchcache',
   384                 b'branchcache',
   385                 'wrote %s with %d labels and %d nodes\n',
   385                 b'wrote %s with %d labels and %d nodes\n',
   386                 _branchcachedesc(repo),
   386                 _branchcachedesc(repo),
   387                 len(self._entries),
   387                 len(self._entries),
   388                 nodecount,
   388                 nodecount,
   389             )
   389             )
   390         except (IOError, OSError, error.Abort) as inst:
   390         except (IOError, OSError, error.Abort) as inst:
   391             # Abort may be raised by read only opener, so log and continue
   391             # Abort may be raised by read only opener, so log and continue
   392             repo.ui.debug(
   392             repo.ui.debug(
   393                 "couldn't write branch cache: %s\n"
   393                 b"couldn't write branch cache: %s\n"
   394                 % stringutil.forcebytestr(inst)
   394                 % stringutil.forcebytestr(inst)
   395             )
   395             )
   396 
   396 
   397     def update(self, repo, revgen):
   397     def update(self, repo, revgen):
   398         """Given a branchhead cache, self, that may have extra nodes or be
   398         """Given a branchhead cache, self, that may have extra nodes or be
   458                     self.tiprev = tiprev
   458                     self.tiprev = tiprev
   459         self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
   459         self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
   460 
   460 
   461         duration = util.timer() - starttime
   461         duration = util.timer() - starttime
   462         repo.ui.log(
   462         repo.ui.log(
   463             'branchcache',
   463             b'branchcache',
   464             'updated %s in %.4f seconds\n',
   464             b'updated %s in %.4f seconds\n',
   465             _branchcachedesc(repo),
   465             _branchcachedesc(repo),
   466             duration,
   466             duration,
   467         )
   467         )
   468 
   468 
   469         self.write(repo)
   469         self.write(repo)
   476         pass
   476         pass
   477 
   477 
   478 
   478 
   479 # Revision branch info cache
   479 # Revision branch info cache
   480 
   480 
   481 _rbcversion = '-v1'
   481 _rbcversion = b'-v1'
   482 _rbcnames = 'rbc-names' + _rbcversion
   482 _rbcnames = b'rbc-names' + _rbcversion
   483 _rbcrevs = 'rbc-revs' + _rbcversion
   483 _rbcrevs = b'rbc-revs' + _rbcversion
   484 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
   484 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
   485 _rbcrecfmt = '>4sI'
   485 _rbcrecfmt = b'>4sI'
   486 _rbcrecsize = calcsize(_rbcrecfmt)
   486 _rbcrecsize = calcsize(_rbcrecfmt)
   487 _rbcnodelen = 4
   487 _rbcnodelen = 4
   488 _rbcbranchidxmask = 0x7FFFFFFF
   488 _rbcbranchidxmask = 0x7FFFFFFF
   489 _rbccloseflag = 0x80000000
   489 _rbccloseflag = 0x80000000
   490 
   490 
   521         try:
   521         try:
   522             bndata = repo.cachevfs.read(_rbcnames)
   522             bndata = repo.cachevfs.read(_rbcnames)
   523             self._rbcsnameslen = len(bndata)  # for verification before writing
   523             self._rbcsnameslen = len(bndata)  # for verification before writing
   524             if bndata:
   524             if bndata:
   525                 self._names = [
   525                 self._names = [
   526                     encoding.tolocal(bn) for bn in bndata.split('\0')
   526                     encoding.tolocal(bn) for bn in bndata.split(b'\0')
   527                 ]
   527                 ]
   528         except (IOError, OSError):
   528         except (IOError, OSError):
   529             if readonly:
   529             if readonly:
   530                 # don't try to use cache - fall back to the slow path
   530                 # don't try to use cache - fall back to the slow path
   531                 self.branchinfo = self._branchinfo
   531                 self.branchinfo = self._branchinfo
   534             try:
   534             try:
   535                 data = repo.cachevfs.read(_rbcrevs)
   535                 data = repo.cachevfs.read(_rbcrevs)
   536                 self._rbcrevs[:] = data
   536                 self._rbcrevs[:] = data
   537             except (IOError, OSError) as inst:
   537             except (IOError, OSError) as inst:
   538                 repo.ui.debug(
   538                 repo.ui.debug(
   539                     "couldn't read revision branch cache: %s\n"
   539                     b"couldn't read revision branch cache: %s\n"
   540                     % stringutil.forcebytestr(inst)
   540                     % stringutil.forcebytestr(inst)
   541                 )
   541                 )
   542         # remember number of good records on disk
   542         # remember number of good records on disk
   543         self._rbcrevslen = min(
   543         self._rbcrevslen = min(
   544             len(self._rbcrevs) // _rbcrecsize, len(repo.changelog)
   544             len(self._rbcrevs) // _rbcrecsize, len(repo.changelog)
   552         self._rbcsnameslen = 0
   552         self._rbcsnameslen = 0
   553         del self._names[:]
   553         del self._names[:]
   554         self._rbcnamescount = 0
   554         self._rbcnamescount = 0
   555         self._rbcrevslen = len(self._repo.changelog)
   555         self._rbcrevslen = len(self._repo.changelog)
   556         self._rbcrevs = bytearray(self._rbcrevslen * _rbcrecsize)
   556         self._rbcrevs = bytearray(self._rbcrevslen * _rbcrecsize)
   557         util.clearcachedproperty(self, '_namesreverse')
   557         util.clearcachedproperty(self, b'_namesreverse')
   558 
   558 
   559     @util.propertycache
   559     @util.propertycache
   560     def _namesreverse(self):
   560     def _namesreverse(self):
   561         return dict((b, r) for r, b in enumerate(self._names))
   561         return dict((b, r) for r, b in enumerate(self._names))
   562 
   562 
   580             _rbcrecfmt, util.buffer(self._rbcrevs), rbcrevidx
   580             _rbcrecfmt, util.buffer(self._rbcrevs), rbcrevidx
   581         )
   581         )
   582         close = bool(branchidx & _rbccloseflag)
   582         close = bool(branchidx & _rbccloseflag)
   583         if close:
   583         if close:
   584             branchidx &= _rbcbranchidxmask
   584             branchidx &= _rbcbranchidxmask
   585         if cachenode == '\0\0\0\0':
   585         if cachenode == b'\0\0\0\0':
   586             pass
   586             pass
   587         elif cachenode == reponode:
   587         elif cachenode == reponode:
   588             try:
   588             try:
   589                 return self._names[branchidx], close
   589                 return self._names[branchidx], close
   590             except IndexError:
   590             except IndexError:
   591                 # recover from invalid reference to unknown branch
   591                 # recover from invalid reference to unknown branch
   592                 self._repo.ui.debug(
   592                 self._repo.ui.debug(
   593                     "referenced branch names not found"
   593                     b"referenced branch names not found"
   594                     " - rebuilding revision branch cache from scratch\n"
   594                     b" - rebuilding revision branch cache from scratch\n"
   595                 )
   595                 )
   596                 self._clear()
   596                 self._clear()
   597         else:
   597         else:
   598             # rev/node map has changed, invalidate the cache from here up
   598             # rev/node map has changed, invalidate the cache from here up
   599             self._repo.ui.debug(
   599             self._repo.ui.debug(
   600                 "history modification detected - truncating "
   600                 b"history modification detected - truncating "
   601                 "revision branch cache to revision %d\n" % rev
   601                 b"revision branch cache to revision %d\n" % rev
   602             )
   602             )
   603             truncate = rbcrevidx + _rbcrecsize
   603             truncate = rbcrevidx + _rbcrecsize
   604             del self._rbcrevs[truncate:]
   604             del self._rbcrevs[truncate:]
   605             self._rbcrevslen = min(self._rbcrevslen, truncate)
   605             self._rbcrevslen = min(self._rbcrevslen, truncate)
   606 
   606 
   648         if rev == nullrev:
   648         if rev == nullrev:
   649             return
   649             return
   650         rbcrevidx = rev * _rbcrecsize
   650         rbcrevidx = rev * _rbcrecsize
   651         if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
   651         if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
   652             self._rbcrevs.extend(
   652             self._rbcrevs.extend(
   653                 '\0'
   653                 b'\0'
   654                 * (len(self._repo.changelog) * _rbcrecsize - len(self._rbcrevs))
   654                 * (len(self._repo.changelog) * _rbcrecsize - len(self._rbcrevs))
   655             )
   655             )
   656         pack_into(_rbcrecfmt, self._rbcrevs, rbcrevidx, node, branchidx)
   656         pack_into(_rbcrecfmt, self._rbcrevs, rbcrevidx, node, branchidx)
   657         self._rbcrevslen = min(self._rbcrevslen, rev)
   657         self._rbcrevslen = min(self._rbcrevslen, rev)
   658 
   658 
   659         tr = self._repo.currenttransaction()
   659         tr = self._repo.currenttransaction()
   660         if tr:
   660         if tr:
   661             tr.addfinalize('write-revbranchcache', self.write)
   661             tr.addfinalize(b'write-revbranchcache', self.write)
   662 
   662 
   663     def write(self, tr=None):
   663     def write(self, tr=None):
   664         """Save branch cache if it is dirty."""
   664         """Save branch cache if it is dirty."""
   665         repo = self._repo
   665         repo = self._repo
   666         wlock = None
   666         wlock = None
   667         step = ''
   667         step = b''
   668         try:
   668         try:
   669             # write the new names
   669             # write the new names
   670             if self._rbcnamescount < len(self._names):
   670             if self._rbcnamescount < len(self._names):
   671                 wlock = repo.wlock(wait=False)
   671                 wlock = repo.wlock(wait=False)
   672                 step = ' names'
   672                 step = b' names'
   673                 self._writenames(repo)
   673                 self._writenames(repo)
   674 
   674 
   675             # write the new revs
   675             # write the new revs
   676             start = self._rbcrevslen * _rbcrecsize
   676             start = self._rbcrevslen * _rbcrecsize
   677             if start != len(self._rbcrevs):
   677             if start != len(self._rbcrevs):
   678                 step = ''
   678                 step = b''
   679                 if wlock is None:
   679                 if wlock is None:
   680                     wlock = repo.wlock(wait=False)
   680                     wlock = repo.wlock(wait=False)
   681                 self._writerevs(repo, start)
   681                 self._writerevs(repo, start)
   682 
   682 
   683         except (IOError, OSError, error.Abort, error.LockError) as inst:
   683         except (IOError, OSError, error.Abort, error.LockError) as inst:
   684             repo.ui.debug(
   684             repo.ui.debug(
   685                 "couldn't write revision branch cache%s: %s\n"
   685                 b"couldn't write revision branch cache%s: %s\n"
   686                 % (step, stringutil.forcebytestr(inst))
   686                 % (step, stringutil.forcebytestr(inst))
   687             )
   687             )
   688         finally:
   688         finally:
   689             if wlock is not None:
   689             if wlock is not None:
   690                 wlock.release()
   690                 wlock.release()
   691 
   691 
   692     def _writenames(self, repo):
   692     def _writenames(self, repo):
   693         """ write the new branch names to revbranchcache """
   693         """ write the new branch names to revbranchcache """
   694         if self._rbcnamescount != 0:
   694         if self._rbcnamescount != 0:
   695             f = repo.cachevfs.open(_rbcnames, 'ab')
   695             f = repo.cachevfs.open(_rbcnames, b'ab')
   696             if f.tell() == self._rbcsnameslen:
   696             if f.tell() == self._rbcsnameslen:
   697                 f.write('\0')
   697                 f.write(b'\0')
   698             else:
   698             else:
   699                 f.close()
   699                 f.close()
   700                 repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
   700                 repo.ui.debug(b"%s changed - rewriting it\n" % _rbcnames)
   701                 self._rbcnamescount = 0
   701                 self._rbcnamescount = 0
   702                 self._rbcrevslen = 0
   702                 self._rbcrevslen = 0
   703         if self._rbcnamescount == 0:
   703         if self._rbcnamescount == 0:
   704             # before rewriting names, make sure references are removed
   704             # before rewriting names, make sure references are removed
   705             repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
   705             repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
   706             f = repo.cachevfs.open(_rbcnames, 'wb')
   706             f = repo.cachevfs.open(_rbcnames, b'wb')
   707         f.write(
   707         f.write(
   708             '\0'.join(
   708             b'\0'.join(
   709                 encoding.fromlocal(b)
   709                 encoding.fromlocal(b)
   710                 for b in self._names[self._rbcnamescount :]
   710                 for b in self._names[self._rbcnamescount :]
   711             )
   711             )
   712         )
   712         )
   713         self._rbcsnameslen = f.tell()
   713         self._rbcsnameslen = f.tell()
   715         self._rbcnamescount = len(self._names)
   715         self._rbcnamescount = len(self._names)
   716 
   716 
   717     def _writerevs(self, repo, start):
   717     def _writerevs(self, repo, start):
   718         """ write the new revs to revbranchcache """
   718         """ write the new revs to revbranchcache """
   719         revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
   719         revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
   720         with repo.cachevfs.open(_rbcrevs, 'ab') as f:
   720         with repo.cachevfs.open(_rbcrevs, b'ab') as f:
   721             if f.tell() != start:
   721             if f.tell() != start:
   722                 repo.ui.debug("truncating cache/%s to %d\n" % (_rbcrevs, start))
   722                 repo.ui.debug(
       
   723                     b"truncating cache/%s to %d\n" % (_rbcrevs, start)
       
   724                 )
   723                 f.seek(start)
   725                 f.seek(start)
   724                 if f.tell() != start:
   726                 if f.tell() != start:
   725                     start = 0
   727                     start = 0
   726                     f.seek(start)
   728                     f.seek(start)
   727                 f.truncate()
   729                 f.truncate()