##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r29423:d2c6f3a9 default
r29459:fd93b15b merge default
Show More
branchmap.py
509 lines | 19.3 KiB | text/x-python | PythonLexer
Pierre-Yves David
branchmap: create a mercurial.branchmap module...
r18116 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Pierre-Yves David
branchmap: extract write logic from localrepo
r18117
Gregory Szorc
branchmap: use absolute_import
r25918 from __future__ import absolute_import
import array
import struct
Gregory Szorc
branchmap: log events related to branch cache...
r21031 import time
Gregory Szorc
branchmap: use absolute_import
r25918
from .node import (
bin,
hex,
nullid,
nullrev,
)
from . import (
encoding,
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 error,
Gregory Szorc
branchmap: use absolute_import
r25918 scmutil,
)
array = array.array
calcsize = struct.calcsize
pack = struct.pack
unpack = struct.unpack
Pierre-Yves David
branchmap: extract write logic from localrepo
r18117
Pierre-Yves David
branchmap: move the cache file name into a dedicated function...
r18185 def _filename(repo):
Pierre-Yves David
branchmap: use a different file name for filtered view of repo
r18187 """name of a branchcache file for a given repo or repoview"""
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 filename = "cache/branch2"
Pierre-Yves David
branchmap: use a different file name for filtered view of repo
r18187 if repo.filtername:
filename = '%s-%s' % (filename, repo.filtername)
return filename
Pierre-Yves David
branchmap: move the cache file name into a dedicated function...
r18185
Pierre-Yves David
branchmap: extract read logic from repo
r18118 def read(repo):
try:
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = repo.vfs(_filename(repo))
Pierre-Yves David
branchmap: extract read logic from repo
r18118 lines = f.read().split('\n')
f.close()
except (IOError, OSError):
Pierre-Yves David
branchmap: read return None in case of failure...
r18212 return None
Pierre-Yves David
branchmap: extract read logic from repo
r18118
try:
Pierre-Yves David
branchmap: read and write key part related to filtered revision...
r18184 cachekey = lines.pop(0).split(" ", 2)
last, lrev = cachekey[:2]
Pierre-Yves David
branchmap: extract read logic from repo
r18118 last, lrev = bin(last), int(lrev)
Pierre-Yves David
branchmap: read and write key part related to filtered revision...
r18184 filteredhash = None
if len(cachekey) > 2:
filteredhash = bin(cachekey[2])
partial = branchcache(tipnode=last, tiprev=lrev,
filteredhash=filteredhash)
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 if not partial.validfor(repo):
Pierre-Yves David
branchmap: extract read logic from repo
r18118 # invalidate the cache
Pierre-Yves David
branchmap: improve invalid cache message when reading...
r18166 raise ValueError('tip differs')
Durham Goode
branchmap: check node against changelog instead of repo...
r28364 cl = repo.changelog
Pierre-Yves David
branchmap: extract read logic from repo
r18118 for l in lines:
if not l:
continue
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 node, state, label = l.split(" ", 2)
if state not in 'oc':
raise ValueError('invalid branch state')
Pierre-Yves David
branchmap: extract read logic from repo
r18118 label = encoding.tolocal(label.strip())
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 node = bin(node)
Durham Goode
branchmap: check node against changelog instead of repo...
r28364 if not cl.hasnode(node):
raise ValueError('node %s does not exist' % hex(node))
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 partial.setdefault(label, []).append(node)
if state == 'c':
partial._closednodes.add(node)
Pierre-Yves David
branchmap: extract read logic from repo
r18118 except KeyboardInterrupt:
raise
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except Exception as inst:
Pierre-Yves David
branchmap: extract read logic from repo
r18118 if repo.ui.debugflag:
Pierre-Yves David
branchmap: report filtername when read fails...
r18188 msg = 'invalid branchheads cache'
if repo.filtername is not None:
msg += ' (%s)' % repo.filtername
msg += ': %s\n'
Matt Mackall
branchmap: don't use ui.warn for debug message
r21789 repo.ui.debug(msg % inst)
Pierre-Yves David
branchmap: read return None in case of failure...
r18212 partial = None
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 return partial
Pierre-Yves David
branchmap: extract read logic from repo
r18118
Augie Fackler
subsettable: move from repoview to branchmap, the only place it's used...
r20032 ### Nearest subset relation
# Nearest subset of filter X is a filter Y so that:
# * Y is included in X,
# * X - Y is as small as possible.
# This create and ordering used for branchmap purpose.
# the ordering may be partial
subsettable = {None: 'visible',
'visible': 'served',
'served': 'immutable',
'immutable': 'base'}
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121 def updatecache(repo):
cl = repo.changelog
Pierre-Yves David
branchmap: enable caching for filtered version too...
r18189 filtername = repo.filtername
partial = repo._branchcaches.get(filtername)
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121
Pierre-Yves David
branchmap: allow to use cache of subset...
r18234 revs = []
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 if partial is None or not partial.validfor(repo):
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 partial = read(repo)
Pierre-Yves David
branchmap: read return None in case of failure...
r18212 if partial is None:
Augie Fackler
subsettable: move from repoview to branchmap, the only place it's used...
r20032 subsetname = subsettable.get(filtername)
Pierre-Yves David
branchmap: allow to use cache of subset...
r18234 if subsetname is None:
partial = branchcache()
else:
subset = repo.filtered(subsetname)
partial = subset.branchmap().copy()
extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
revs.extend(r for r in extrarevs if r <= partial.tiprev)
revs.extend(cl.revs(start=partial.tiprev + 1))
Pierre-Yves David
branchmap: drop `_cacheabletip` usage in `updatecache`...
r18218 if revs:
Pierre-Yves David
branchmap: pass revision insteads of changectx to the update function...
r18305 partial.update(repo, revs)
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 partial.write(repo)
Durham Goode
revbranchcache: move out of branchmap onto localrepo...
r24373
Pierre-Yves David
branchmap: display filtername when `updatebranch` fails to do its jobs...
r18451 assert partial.validfor(repo), filtername
Pierre-Yves David
branchmap: enable caching for filtered version too...
r18189 repo._branchcaches[repo.filtername] = partial
Pierre-Yves David
branchmap: store branchcache in a dedicated object...
r18124
Gregory Szorc
branchmap: move branch cache code out of streamclone.py...
r26460 def replacecache(repo, bm):
"""Replace the branchmap cache for a repo with a branch mapping.
This is likely only called during clone with a branch map from a remote.
"""
rbheads = []
closed = []
for bheads in bm.itervalues():
rbheads.extend(bheads)
for h in bheads:
r = repo.changelog.rev(h)
b, c = repo.changelog.branchinfo(r)
if c:
closed.append(h)
if rbheads:
rtiprev = max((int(repo.changelog.rev(node))
for node in rbheads))
cache = branchcache(bm,
repo[rtiprev].node(),
rtiprev,
closednodes=closed)
# Try to stick it as low as possible
# filter above served are unlikely to be fetch from a clone
for candidate in ('base', 'immutable', 'served'):
rview = repo.filtered(candidate)
if cache.validfor(rview):
repo._branchcaches[candidate] = cache
cache.write(rview)
break
Pierre-Yves David
branchmap: store branchcache in a dedicated object...
r18124 class branchcache(dict):
Brodie Rao
branchmap: add documentation on the branchcache on-disk format
r20181 """A dict like object that hold branches heads cache.
This cache is used to avoid costly computations to determine all the
branch heads of a repo.
The cache is serialized on disk in the following format:
<tip hex node> <tip rev number> [optional filtered repo hex hash]
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 <branch head hex node> <open/closed state> <branch name>
<branch head hex node> <open/closed state> <branch name>
Brodie Rao
branchmap: add documentation on the branchcache on-disk format
r20181 ...
The first line is used to check if the cache is still valid. If the
branch cache is for a filtered repo view, an optional third hash is
included that hashes the hashes of all filtered revisions.
Brodie Rao
branchmap: cache open/closed branch head information...
r20185
The open/closed state is represented by a single letter 'o' or 'c'.
This field can be used to avoid changelog reads when determining if a
branch head closes a branch or not.
Brodie Rao
branchmap: add documentation on the branchcache on-disk format
r20181 """
Pierre-Yves David
branchmap: store branchcache in a dedicated object...
r18124
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 filteredhash=None, closednodes=None):
Pierre-Yves David
branchmap: add the tipnode (cache key) on the branchcache object...
r18125 super(branchcache, self).__init__(entries)
self.tipnode = tipnode
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 self.tiprev = tiprev
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 self.filteredhash = filteredhash
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 # closednodes is a set of nodes that close their branch. If the branch
# cache has been updated, it may contain nodes that are no longer
# heads.
if closednodes is None:
self._closednodes = set()
else:
self._closednodes = closednodes
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 def validfor(self, repo):
Mads Kiilerich
spelling: fix some minor issues found by spell checker
r18644 """Is the cache content valid regarding a repo
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132
Mads Kiilerich
spelling: fix some minor issues found by spell checker
r18644 - False when cached tipnode is unknown or if we detect a strip.
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 - True when cache is up to date or a subset of current repo."""
try:
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 return ((self.tipnode == repo.changelog.node(self.tiprev))
Gregory Szorc
repoview: move function for computing filtered hash...
r24723 and (self.filteredhash == \
scmutil.filteredhash(repo, self.tiprev)))
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 except IndexError:
return False
Brodie Rao
branchmap: introduce branchtip() method
r20186 def _branchtip(self, heads):
Mads Kiilerich
help: branch names primarily denote the tipmost unclosed branch head...
r20245 '''Return tuple with last open head in heads and false,
otherwise return last closed head and true.'''
Brodie Rao
branchmap: introduce branchtip() method
r20186 tip = heads[-1]
closed = True
for h in reversed(heads):
if h not in self._closednodes:
tip = h
closed = False
break
return tip, closed
def branchtip(self, branch):
Mads Kiilerich
help: branch names primarily denote the tipmost unclosed branch head...
r20245 '''Return the tipmost open head on branch head, otherwise return the
tipmost closed head on branch.
Raise KeyError for unknown branch.'''
Brodie Rao
branchmap: introduce branchtip() method
r20186 return self._branchtip(self[branch])[0]
Brodie Rao
branchmap: introduce branchheads() method
r20188 def branchheads(self, branch, closed=False):
heads = self[branch]
if not closed:
heads = [h for h in heads if h not in self._closednodes]
return heads
Brodie Rao
branchmap: introduce iterbranches() method
r20190 def iterbranches(self):
for bn, heads in self.iteritems():
yield (bn, heads) + self._branchtip(heads)
Pierre-Yves David
branchmap: add a copy method...
r18232 def copy(self):
"""return an deep copy of the branchcache object"""
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
self._closednodes)
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 def write(self, repo):
try:
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = repo.vfs(_filename(repo), "w", atomictemp=True)
Pierre-Yves David
branchmap: read and write key part related to filtered revision...
r18184 cachekey = [hex(self.tipnode), str(self.tiprev)]
if self.filteredhash is not None:
cachekey.append(hex(self.filteredhash))
f.write(" ".join(cachekey) + '\n')
Gregory Szorc
branchmap: log events related to branch cache...
r21031 nodecount = 0
Mads Kiilerich
localrepo: store branchheads sorted
r18357 for label, nodes in sorted(self.iteritems()):
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 for node in nodes:
Gregory Szorc
branchmap: log events related to branch cache...
r21031 nodecount += 1
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 if node in self._closednodes:
state = 'c'
else:
state = 'o'
f.write("%s %s %s\n" % (hex(node), state,
encoding.fromlocal(label)))
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 f.close()
Gregory Szorc
branchmap: log events related to branch cache...
r21031 repo.ui.log('branchcache',
'wrote %s branch cache with %d labels and %d nodes\n',
repo.filtername, len(self), nodecount)
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except (IOError, OSError, error.Abort) as inst:
Matt Mackall
branch: add debug message for branch cache write failure
r21788 repo.ui.debug("couldn't write branch cache: %s\n" % inst)
Pierre-Yves David
branchmap: ignore Abort error while writing cache...
r18214 # Abort may be raise by read only opener
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 pass
Pierre-Yves David
branchmap: make update a method
r18131
Pierre-Yves David
branchmap: pass revision insteads of changectx to the update function...
r18305 def update(self, repo, revgen):
Pierre-Yves David
branchmap: make update a method
r18131 """Given a branchhead cache, self, that may have extra nodes or be
Pierre-Yves David
branchmap: simplify update code...
r20263 missing heads, and a generator of nodes that are strictly a superset of
Pierre-Yves David
branchmap: make update a method
r18131 heads missing, this function updates self to be correct.
"""
Gregory Szorc
branchmap: log events related to branch cache...
r21031 starttime = time.time()
Pierre-Yves David
branchmap: make update a method
r18131 cl = repo.changelog
# collect new branch entries
newbranches = {}
Durham Goode
revbranchcache: move out of branchmap onto localrepo...
r24373 getbranchinfo = repo.revbranchcache().branchinfo
Pierre-Yves David
branchmap: Save changectx creation during update...
r18307 for r in revgen:
Durham Goode
revbranchcache: store repo on the object...
r24374 branch, closesbranch = getbranchinfo(r)
Pierre-Yves David
branchmap: stop useless rev -> node -> rev round trip...
r20262 newbranches.setdefault(branch, []).append(r)
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 if closesbranch:
Pierre-Yves David
branchmap: stop useless rev -> node -> rev round trip...
r20262 self._closednodes.add(cl.node(r))
Pierre-Yves David
branchmap: pre-filter topological heads before ancestors based filtering...
r22357
# fetch current topological heads to speed up filtering
topoheads = set(cl.headrevs())
Pierre-Yves David
branchmap: make update a method
r18131 # if older branchheads are reachable from new ones, they aren't
# really branchheads. Note checking parents is insufficient:
# 1 (branch a) -> 2 (branch b) -> 3 (branch a)
Pierre-Yves David
branchmap: stop useless rev -> node -> rev round trip...
r20262 for branch, newheadrevs in newbranches.iteritems():
Pierre-Yves David
branchmap: make update a method
r18131 bheads = self.setdefault(branch, [])
Pierre-Yves David
branchmap: use set for update code...
r20264 bheadset = set(cl.rev(node) for node in bheads)
Pierre-Yves David
branchmap: make update a method
r18131
Pierre-Yves David
branchmap: simplify update code...
r20263 # This have been tested True on all internal usage of this function.
# run it again in case of doubt
# assert not (set(bheadrevs) & set(newheadrevs))
newheadrevs.sort()
Pierre-Yves David
branchmap: use set for update code...
r20264 bheadset.update(newheadrevs)
Pierre-Yves David
branchmap: make update a method
r18131
Pierre-Yves David
branchmap: issue a single call to `ancestors` for all heads...
r22356 # This prunes out two kinds of heads - heads that are superseded by
# a head in newheadrevs, and newheadrevs that are not heads because
# an existing head is their descendant.
Pierre-Yves David
branchmap: pre-filter topological heads before ancestors based filtering...
r22357 uncertain = bheadset - topoheads
if uncertain:
floorrev = min(uncertain)
ancestors = set(cl.ancestors(newheadrevs, floorrev))
bheadset -= ancestors
Pierre-Yves David
branchmap: use set for update code...
r20264 bheadrevs = sorted(bheadset)
Pierre-Yves David
branchmap: make update a method
r18131 self[branch] = [cl.node(rev) for rev in bheadrevs]
Pierre-Yves David
branchmap: simplify update code...
r20263 tiprev = bheadrevs[-1]
Pierre-Yves David
branchmap: make update a method
r18131 if tiprev > self.tiprev:
self.tipnode = cl.node(tiprev)
self.tiprev = tiprev
Pierre-Yves David
branchmap: remove the droppednodes logic...
r19838 if not self.validfor(repo):
Pierre-Yves David
branchmap: make update a method
r18131 # cache key are not valid anymore
self.tipnode = nullid
self.tiprev = nullrev
for heads in self.values():
tiprev = max(cl.rev(node) for node in heads)
if tiprev > self.tiprev:
self.tipnode = cl.node(tiprev)
self.tiprev = tiprev
Gregory Szorc
repoview: move function for computing filtered hash...
r24723 self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
Gregory Szorc
branchmap: log events related to branch cache...
r21031
duration = time.time() - starttime
repo.ui.log('branchcache', 'updated %s branch cache in %.4f seconds\n',
repo.filtername, duration)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785
# Revision branch info cache
_rbcversion = '-v1'
_rbcnames = 'cache/rbc-names' + _rbcversion
_rbcrevs = 'cache/rbc-revs' + _rbcversion
# [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
_rbcrecfmt = '>4sI'
_rbcrecsize = calcsize(_rbcrecfmt)
_rbcnodelen = 4
_rbcbranchidxmask = 0x7fffffff
_rbccloseflag = 0x80000000
class revbranchcache(object):
"""Persistent cache, mapping from revision number to branch name and close.
This is a low level cache, independent of filtering.
Branch names are stored in rbc-names in internal encoding separated by 0.
rbc-names is append-only, and each branch name is only stored once and will
thus have a unique index.
The branch info for each revision is stored in rbc-revs as constant size
records. The whole file is read into memory, but it is only 'parsed' on
demand. The file is usually append-only but will be truncated if repo
modification is detected.
The record for each revision contains the first 4 bytes of the
corresponding node hash, and the record is only used if it still matches.
Even a completely trashed rbc-revs fill thus still give the right result
while converging towards full recovery ... assuming no incorrectly matching
node hashes.
The record also contains 4 bytes where 31 bits contains the index of the
branch and the last bit indicate that it is a branch close commit.
The usage pattern for rbc-revs is thus somewhat similar to 00changelog.i
and will grow with it but be 1/8th of its size.
"""
Mads Kiilerich
revisionbranchcache: fall back to slow path if starting readonly (issue4531)...
r24159 def __init__(self, repo, readonly=True):
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 assert repo.filtername is None
Durham Goode
revbranchcache: store repo on the object...
r24374 self._repo = repo
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 self._names = [] # branch names in local encoding with static index
self._rbcrevs = array('c') # structs of type _rbcrecfmt
self._rbcsnameslen = 0
try:
bndata = repo.vfs.read(_rbcnames)
self._rbcsnameslen = len(bndata) # for verification before writing
self._names = [encoding.tolocal(bn) for bn in bndata.split('\0')]
Gregory Szorc
branchmap: remove unused exception variable
r29423 except (IOError, OSError):
Mads Kiilerich
revisionbranchcache: fall back to slow path if starting readonly (issue4531)...
r24159 if readonly:
# don't try to use cache - fall back to the slow path
self.branchinfo = self._branchinfo
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 if self._names:
try:
data = repo.vfs.read(_rbcrevs)
self._rbcrevs.fromstring(data)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except (IOError, OSError) as inst:
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 repo.ui.debug("couldn't read revision branch cache: %s\n" %
inst)
# remember number of good records on disk
self._rbcrevslen = min(len(self._rbcrevs) // _rbcrecsize,
len(repo.changelog))
if self._rbcrevslen == 0:
self._names = []
self._rbcnamescount = len(self._names) # number of good names on disk
self._namesreverse = dict((b, r) for r, b in enumerate(self._names))
Mads Kiilerich
cache: rebuild branch cache from scratch when inconsistencies are detected...
r28558 def _clear(self):
self._rbcsnameslen = 0
del self._names[:]
self._rbcnamescount = 0
self._namesreverse.clear()
self._rbcrevslen = len(self._repo.changelog)
self._rbcrevs = array('c')
self._rbcrevs.fromstring('\0' * (self._rbcrevslen * _rbcrecsize))
Durham Goode
revbranchcache: store repo on the object...
r24374 def branchinfo(self, rev):
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 """Return branch name and close flag for rev, using and updating
persistent cache."""
Durham Goode
revbranchcache: store repo on the object...
r24374 changelog = self._repo.changelog
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 rbcrevidx = rev * _rbcrecsize
Yuya Nishihara
revbranchcache: return uncached branchinfo for nullrev (issue4683)...
r25266 # avoid negative index, changelog.read(nullrev) is fast without cache
if rev == nullrev:
return changelog.branchinfo(rev)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 # if requested rev is missing, add and populate all missing revs
if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
self._rbcrevs.extend('\0' * (len(changelog) * _rbcrecsize -
len(self._rbcrevs)))
# fast path: extract data from cache, use it if node is matching
reponode = changelog.node(rev)[:_rbcnodelen]
cachenode, branchidx = unpack(
_rbcrecfmt, buffer(self._rbcrevs, rbcrevidx, _rbcrecsize))
close = bool(branchidx & _rbccloseflag)
if close:
branchidx &= _rbcbranchidxmask
Durham Goode
revbranchcache: populate cache incrementally...
r24376 if cachenode == '\0\0\0\0':
pass
elif cachenode == reponode:
Mads Kiilerich
cache: rebuild branch cache from scratch when inconsistencies are detected...
r28558 if branchidx < self._rbcnamescount:
return self._names[branchidx], close
# referenced branch doesn't exist - rebuild is expensive but needed
self._repo.ui.debug("rebuilding corrupted revision branch cache\n")
self._clear()
Durham Goode
revbranchcache: populate cache incrementally...
r24376 else:
# rev/node map has changed, invalidate the cache from here up
truncate = rbcrevidx + _rbcrecsize
del self._rbcrevs[truncate:]
self._rbcrevslen = min(self._rbcrevslen, truncate)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 # fall back to slow path and make sure it will be written to disk
Durham Goode
revbranchcache: store repo on the object...
r24374 return self._branchinfo(rev)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785
Durham Goode
revbranchcache: store repo on the object...
r24374 def _branchinfo(self, rev):
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 """Retrieve branch info from changelog and update _rbcrevs"""
Durham Goode
revbranchcache: store repo on the object...
r24374 changelog = self._repo.changelog
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 b, close = changelog.branchinfo(rev)
if b in self._namesreverse:
branchidx = self._namesreverse[b]
else:
branchidx = len(self._names)
self._names.append(b)
self._namesreverse[b] = branchidx
reponode = changelog.node(rev)
if close:
branchidx |= _rbccloseflag
Durham Goode
revbranchcache: move entry writing to a separate function...
r24375 self._setcachedata(rev, reponode, branchidx)
return b, close
def _setcachedata(self, rev, node, branchidx):
"""Writes the node's branch data to the in-memory cache data."""
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 rbcrevidx = rev * _rbcrecsize
rec = array('c')
Durham Goode
revbranchcache: move entry writing to a separate function...
r24375 rec.fromstring(pack(_rbcrecfmt, node, branchidx))
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 self._rbcrevs[rbcrevidx:rbcrevidx + _rbcrecsize] = rec
Durham Goode
revbranchcache: populate cache incrementally...
r24376 self._rbcrevslen = min(self._rbcrevslen, rev)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785
Durham Goode
revbranchcache: move cache writing to the transaction finalizer...
r24377 tr = self._repo.currenttransaction()
if tr:
tr.addfinalize('write-revbranchcache', self.write)
def write(self, tr=None):
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 """Save branch cache if it is dirty."""
Durham Goode
revbranchcache: store repo on the object...
r24374 repo = self._repo
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 if self._rbcnamescount < len(self._names):
try:
if self._rbcnamescount != 0:
f = repo.vfs.open(_rbcnames, 'ab')
if f.tell() == self._rbcsnameslen:
f.write('\0')
else:
f.close()
Mads Kiilerich
branchcache: add debug output whenever cache files use truncate...
r23862 repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 self._rbcnamescount = 0
self._rbcrevslen = 0
if self._rbcnamescount == 0:
Mads Kiilerich
cache: remove branch revision file before rewriting the branch name file...
r28556 # before rewriting names, make sure references are removed
repo.vfs.unlinkpath(_rbcrevs, ignoremissing=True)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 f = repo.vfs.open(_rbcnames, 'wb')
f.write('\0'.join(encoding.fromlocal(b)
for b in self._names[self._rbcnamescount:]))
self._rbcsnameslen = f.tell()
f.close()
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except (IOError, OSError, error.Abort) as inst:
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 repo.ui.debug("couldn't write revision branch cache names: "
"%s\n" % inst)
return
self._rbcnamescount = len(self._names)
start = self._rbcrevslen * _rbcrecsize
if start != len(self._rbcrevs):
Mads Kiilerich
branchcache: make _rbcrevslen handling more safe...
r23863 revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 try:
f = repo.vfs.open(_rbcrevs, 'ab')
if f.tell() != start:
Mads Kiilerich
branchcache: add debug output whenever cache files use truncate...
r23862 repo.ui.debug("truncating %s to %s\n" % (_rbcrevs, start))
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 f.seek(start)
Mads Kiilerich
cache: safer handling of failing seek when writing revision branch cache...
r28557 if f.tell() != start:
start = 0
f.seek(start)
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 f.truncate()
Mads Kiilerich
branchcache: make _rbcrevslen handling more safe...
r23863 end = revs * _rbcrecsize
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 f.write(self._rbcrevs[start:end])
f.close()
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except (IOError, OSError, error.Abort) as inst:
Mads Kiilerich
branchcache: introduce revbranchcache for caching of revision branch names...
r23785 repo.ui.debug("couldn't write revision branch cache: %s\n" %
inst)
return
Mads Kiilerich
branchcache: make _rbcrevslen handling more safe...
r23863 self._rbcrevslen = revs