##// END OF EJS Templates
branchmap: move the cache file name into a dedicated function...
branchmap: move the cache file name into a dedicated function Filtered view of the repo will want to write they file name in a different file.

File last commit:

r18185:5a047276 default
r18185:5a047276 default
Show More
branchmap.py
212 lines | 8.4 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
Pierre-Yves David
branchmap: extract read logic from repo
r18118 from node import bin, hex, nullid, nullrev
Pierre-Yves David
branchmap: extract write logic from localrepo
r18117 import encoding
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 import util
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):
"""name of a branchcache file for a given repo"""
return "cache/branchheads"
Pierre-Yves David
branchmap: extract read logic from repo
r18118 def read(repo):
try:
Pierre-Yves David
branchmap: move the cache file name into a dedicated function...
r18185 f = repo.opener(_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: add the tiprev (cache key) on the branchmap object...
r18126 return branchcache()
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')
Pierre-Yves David
branchmap: extract read logic from repo
r18118 for l in lines:
if not l:
continue
node, label = l.split(" ", 1)
label = encoding.tolocal(label.strip())
if not node in repo:
Pierre-Yves David
branchmap: improve invalid cache message when reading...
r18166 raise ValueError('node %s does not exist' % node)
Pierre-Yves David
branchmap: extract read logic from repo
r18118 partial.setdefault(label, []).append(bin(node))
except KeyboardInterrupt:
raise
except Exception, inst:
if repo.ui.debugflag:
Pierre-Yves David
branchmap: improve invalid cache message when reading...
r18166 repo.ui.warn(('invalid branchheads cache: %s\n') % inst)
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 partial = branchcache()
return partial
Pierre-Yves David
branchmap: extract read logic from repo
r18118
Pierre-Yves David
branchmap: make update responsible to update the cache key...
r18130
Pierre-Yves David
branchmap: extract _updatebranchcache from repo
r18120
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121 def updatecache(repo):
repo = repo.unfiltered() # Until we get a smarter cache management
cl = repo.changelog
Pierre-Yves David
branchmap: add the tipnode (cache key) on the branchcache object...
r18125 partial = repo._branchcache
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121
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: extract updatebranchcache from repo
r18121
catip = repo._cacheabletip()
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 # if partial.tiprev == catip: cache is already up to date
# if partial.tiprev > catip: we have uncachable element in `partial` can't
# write on disk
if partial.tiprev < catip:
ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, catip))
Pierre-Yves David
branchmap: make update a method
r18131 partial.update(repo, ctxgen)
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 partial.write(repo)
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121 # If cacheable tip were lower than actual tip, we need to update the
# cache up to tip. This update (from cacheable to actual tip) is not
# written to disk since it's not cacheable.
Pierre-Yves David
branchmap: improve computation of target tip...
r18167 tiprev = cl.rev(cl.tip())
Pierre-Yves David
branchmap: add the tiprev (cache key) on the branchmap object...
r18126 if partial.tiprev < tiprev:
ctxgen = (repo[r] for r in cl.revs(partial.tiprev + 1, tiprev))
Pierre-Yves David
branchmap: make update a method
r18131 partial.update(repo, ctxgen)
Pierre-Yves David
branchmap: extract updatebranchcache from repo
r18121 repo._branchcache = partial
Pierre-Yves David
branchmap: store branchcache in a dedicated object...
r18124
class branchcache(dict):
"""A dict like object that hold branches heads cache"""
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
filteredhash=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
def _hashfiltered(self, repo):
"""build hash of revision filtered in the current cache
Tracking tipnode and tiprev is not enough to ensure validaty of the
cache as they do not help to distinct cache that ignored various
revision bellow tiprev.
To detect such difference, we build a cache of all ignored revisions.
"""
cl = repo.changelog
if not cl.filteredrevs:
return None
key = None
revs = sorted(r for r in cl.filteredrevs if r <= self.tiprev)
if revs:
s = util.sha1()
for rev in revs:
s.update('%s;' % rev)
key = s.digest()
return key
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 def validfor(self, repo):
"""Is the cache content valide regarding a repo
- False when cached tipnode are unknown or if we detect a strip.
- 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))
and (self.filteredhash == self._hashfiltered(repo)))
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 except IndexError:
return False
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 def write(self, repo):
try:
Pierre-Yves David
branchmap: move the cache file name into a dedicated function...
r18185 f = repo.opener(_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')
Pierre-Yves David
branchmap: make write a method on the branchmap object
r18128 for label, nodes in self.iteritems():
for node in nodes:
f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
f.close()
except (IOError, OSError):
pass
Pierre-Yves David
branchmap: make update a method
r18131
def update(self, repo, ctxgen):
"""Given a branchhead cache, self, that may have extra nodes or be
missing heads, and a generator of nodes that are at least a superset of
heads missing, this function updates self to be correct.
"""
cl = repo.changelog
# collect new branch entries
newbranches = {}
for c in ctxgen:
newbranches.setdefault(c.branch(), []).append(c.node())
# 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)
for branch, newnodes in newbranches.iteritems():
bheads = self.setdefault(branch, [])
# Remove candidate heads that no longer are in the repo (e.g., as
# the result of a strip that just happened). Avoid using 'node in
# self' here because that dives down into branchcache code somewhat
# recursively.
bheadrevs = [cl.rev(node) for node in bheads
if cl.hasnode(node)]
newheadrevs = [cl.rev(node) for node in newnodes
if cl.hasnode(node)]
ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
# Remove duplicates - nodes that are in newheadrevs and are already
# in bheadrevs. This can happen if you strip a node whose parent
# was already a head (because they're on different branches).
bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
# Starting from tip means fewer passes over reachable. If we know
# the new candidates are not ancestors of existing heads, we don't
# have to examine ancestors of existing heads
if ctxisnew:
iterrevs = sorted(newheadrevs)
else:
iterrevs = list(bheadrevs)
# This loop 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.
while iterrevs:
latest = iterrevs.pop()
if latest not in bheadrevs:
continue
ancestors = set(cl.ancestors([latest],
bheadrevs[0]))
if ancestors:
bheadrevs = [b for b in bheadrevs if b not in ancestors]
self[branch] = [cl.node(rev) for rev in bheadrevs]
tiprev = max(bheadrevs)
if tiprev > self.tiprev:
self.tipnode = cl.node(tiprev)
self.tiprev = tiprev
# There may be branches that cease to exist when the last commit in the
# branch was stripped. This code filters them out. Note that the
# branch that ceased to exist may not be in newbranches because
# newbranches is the set of candidate heads, which when you strip the
# last commit in a branch will be the parent branch.
droppednodes = []
for branch in self.keys():
nodes = [head for head in self[branch]
if cl.hasnode(head)]
if not nodes:
droppednodes.extend(nodes)
del self[branch]
Pierre-Yves David
branchmap: move validity logic in the object itself...
r18132 if ((not self.validfor(repo)) or (self.tipnode in droppednodes)):
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
Pierre-Yves David
branchmap: takes filtered revision in account for cache calculation...
r18168 self.filteredhash = self._hashfiltered(repo)