[PATCH 2 of 3] repoview: cache hidden changesets
David Soria Parra
davidsp at fb.com
Thu Aug 7 13:08:46 CDT 2014
# HG changeset patch
# User David Soria Parra <davidsp at fb.com>
# Date 1407356774 25200
# Wed Aug 06 13:26:14 2014 -0700
# Node ID 3da6820d38112cddfc21e2b99ab02957f93d80cf
# Parent 4ac33b2438c3d50d5f1f21423f0a51ed32d12994
repoview: cache hidden changesets
Add a cache for hidden changesets. We cache all hidden changesets computed by
_gethiddenblockers and check if a bookmark, tag or wd parent moved to a hidden
changesets in which case we recompute parts. This significantly speeds up
status, log, etc:
without caching:
$ time hg status
hg status 0.72s user 0.20s system 100% cpu 0.917 total
with caching
$ time hg status
hg status 0.49s user 0.15s system 100% cpu 0.645 total
We invalidate the cache if the append only obsstore size changed from what we
have observed before. In that case we recompute everything again.
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1013,6 +1013,7 @@
def invalidatevolatilesets(self):
self.filteredrevcache.clear()
obsolete.clearobscaches(self)
+ repoview.invalidatecache(self)
def invalidatedirstate(self):
'''Invalidates the dirstate, causing the next call to dirstate
diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -10,6 +10,7 @@
import phases
import util
import obsolete
+import struct
import tags as tagsmod
@@ -56,17 +57,61 @@
blockers.extend([cl.rev(t[0]) for t in tags.values()])
return frozenset(blockers)
+cachefile = 'cache/hidden'
+cachemeta = 'cache/hidden.len'
+def invalidatecache(repo):
+ for filename in [cachefile, cachemeta]:
+ if repo.vfs.exists(filename):
+ repo.vfs.unlink(filename)
+
def computehidden(repo):
"""compute the set of hidden revision to filter
During most operation hidden should be filtered."""
assert not repo.changelog.filteredrevs
+
+ def iscachevalid(repo, hideable):
+ """The cache is invalid if the append-only obsstore has more markers
+ then we observed before."""
+ if repo.vfs.exists(cachefile):
+ oldlen = repo.vfs.tryread(cachemeta)
+ if oldlen and int(oldlen) == len(hideable):
+ return True
+ return False
+
+ hidden = frozenset()
hideable = hideablerevs(repo)
if hideable:
cl = repo.changelog
- blocked = cl.ancestors(_gethiddenblockers(repo), inclusive=True)
- return frozenset(r for r in hideable if r not in blocked)
- return frozenset()
+ if iscachevalid(repo, hideable):
+ data = repo.vfs.read(cachefile)
+ hidden = frozenset(struct.unpack('%sI' % (len(data) / 4), data))
+ else:
+ # recompute cache
+ blocked = cl.ancestors(_gethiddenblockers(repo), inclusive=True)
+ hidden = frozenset(r for r in hideable if r not in blocked)
+
+ # write cache to file
+ try:
+ data = struct.pack('%sI' % len(hidden), *hidden)
+ fh = repo.vfs(cachefile, 'w+b', atomictemp=True)
+ fh.write(data)
+ fh.close()
+
+ # our observed size of the obsstore. used for cache invalidation.
+ fh = repo.vfs(cachemeta, 'w+b', atomictemp=True)
+ fh.write('%d' % len(hideable))
+ fh.close()
+ except IOError:
+ ui.debug(_('error writing hidden changesets cache'))
+
+ # check if we have wd parents, bookmarks or tags pointing to hidden
+ # changesets and remove those.
+ dynamic = hidden & _getdynamicblockers(repo)
+ if dynamic:
+ blocked = cl.ancestors(dynamic, inclusive=True)
+ hidden &= frozenset(r for r in hideable if r not in blocked)
+ return hidden
def computeunserved(repo):
"""compute the set of revision that should be filtered when used a server
More information about the Mercurial-devel
mailing list