Comments
Patch
@@ -9,6 +9,7 @@ from node import bin, hex, nullid, nullr
import encoding
import util
import time
+import struct, array
def _filename(repo):
"""name of a branch head cache file for a given repo or repoview"""
@@ -285,3 +286,95 @@ class branchheadcache(dict):
duration = time.time() - starttime
repo.ui.log('branchheadcache', 'updated %s branch head cache '
'in %.4f seconds\n', repo.filtername, duration)
+
+filename = 'cache/branchnames'
+formatversion = 2345164374
+headerfmt = '>LLL' # file header: version, start of records, length of records
+recfmt = '>20sH' # a record: node hash, branch name reference
+headersize = struct.calcsize(headerfmt)
+recsize = struct.calcsize(recfmt)
+
+class revbranchcache(object):
+ """Persistent cache mapping from revision number to branch.
+ Consistency is guaranteed by verifying the node hash."""
+
+ def __init__(self, repo):
+ self._repo = repo
+ self._loaded = False
+ self._dirty = False
+ self._names = [] # branch names referenced from recfmt records
+ self._records = array.array('c') # bytes with structs of type recfmt
+
+ def _load(self):
+ """Load cached branch names."""
+ try:
+ data = self._repo.vfs.open(filename).read()
+ except IOError:
+ data = ''
+
+ self._dirty = True
+ reporecslen = len(self._repo) * recsize
+ if len(data) >= headersize:
+ # header
+ v, recsstart, recslen = struct.unpack_from(headerfmt, data)
+ if v == formatversion and len(data) == recsstart + recslen:
+ # between header and records: \0 separated branch names
+ if recsstart != headersize:
+ self._names = \
+ data[headersize:recsstart].split('\0')
+ # read records, cap at repo size
+ self._records.fromstring(
+ buffer(data, recsstart, min(recslen, reporecslen)))
+ # only dirty if too many records (after strip)
+ self._dirty = recslen > reporecslen
+ else:
+ self._repo.ui.debug('branch cache file was invalid\n')
+
+ # pad to repo size
+ if len(self._records) < reporecslen:
+ self._records.extend(
+ '\xff' * (reporecslen - len(self._records)))
+
+ self._branchnamesindex = dict((b, r)
+ for r, b in enumerate(self._names))
+ self._node = self._repo.changelog.node
+ self._branchinfo = self._repo.changelog.branchinfo
+ self._loaded = True
+
+ def branch(self, rev):
+ """Return branch name of rev, using and updating persistent cache."""
+ if not self._loaded:
+ self._load()
+
+ node = self._node(rev)
+ cachenode, branchidx = struct.unpack_from(recfmt, self._records,
+ rev * recsize)
+ if cachenode == node and branchidx < len(self._names):
+ return self._names[branchidx]
+ b, _close = self._branchinfo(rev)
+ if b in self._branchnamesindex:
+ branchidx = self._branchnamesindex[b]
+ else:
+ branchidx = len(self._names)
+ self._names.append(b)
+ self._branchnamesindex[b] = branchidx
+ struct.pack_into(recfmt, self._records, rev * recsize,
+ node, branchidx)
+ self._dirty = True
+ return b
+
+ def save(self):
+ """Save branch cache if it is dirty."""
+ if self._dirty:
+ self._repo.ui.debug('writing branch cache file\n')
+ try:
+ f = self._repo.vfs.open(filename, 'w', atomictemp=True)
+ s = '\0'.join(self._names)
+ f.write(struct.pack(headerfmt, formatversion,
+ headersize + len(s), len(self._records)))
+ f.write(s)
+ f.write(self._records)
+ f.close()
+ except IOError:
+ pass
+ self._dirty = False
@@ -297,8 +297,10 @@ class localrepository(object):
# - bookmark changes
self.filteredrevcache = {}
+ self.revbranchcache = branchmap.revbranchcache(self)
+
def close(self):
- pass
+ self.revbranchcache.save()
def _restrictcapabilities(self, caps):
# bundle2 is not ready for prime time, drop it unless explicitly