comparison mercurial/repoview.py @ 24565:2f7cb6e6acdd

repoview: improve compute staticblockers perf Previously we would compute the repoview's static blockers by finding all the children of hidden commits that were not hidden. This was O(number of commits since first hidden change) since 'children' requires walking every commit from tip until the first hidden change. The new algorithm walks all heads down until it sees a public commit. This makes the computation O(number of draft) commits, which is much faster in large repositories with a large number of commits and a low number of drafts. On a large repo with 1000+ obsolete markers and the earliest draft commit around tip~200000, this improves computehidden perf by 200x (2s to 0.01s).
author Durham Goode <durham@fb.com>
date Wed, 01 Apr 2015 12:50:10 -0700
parents a41902aac76d
children 9e558b788daa
comparison
equal deleted inserted replaced
24564:5ec4bda3097a 24565:2f7cb6e6acdd
4 # Logilab SA <contact@logilab.fr> 4 # Logilab SA <contact@logilab.fr>
5 # 5 #
6 # This software may be used and distributed according to the terms of the 6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version. 7 # GNU General Public License version 2 or any later version.
8 8
9 import collections
9 import copy 10 import copy
10 import error 11 import error
11 import phases 12 import phases
12 import util 13 import util
13 import obsolete 14 import obsolete
14 import struct 15 import struct
15 import tags as tagsmod 16 import tags as tagsmod
17 from node import nullrev
16 18
17 def hideablerevs(repo): 19 def hideablerevs(repo):
18 """Revisions candidates to be hidden 20 """Revisions candidates to be hidden
19 21
20 This is a standalone function to help extensions to wrap it.""" 22 This is a standalone function to help extensions to wrap it."""
21 return obsolete.getrevs(repo, 'obsolete') 23 return obsolete.getrevs(repo, 'obsolete')
22 24
23 def _getstaticblockers(repo): 25 def _getstatichidden(repo):
24 """Cacheable revisions blocking hidden changesets from being filtered. 26 """Cacheable revisions blocking hidden changesets from being filtered.
25 27
26 Additional non-cached hidden blockers are computed in _getdynamicblockers. 28 Additional non-cached hidden blockers are computed in _getdynamicblockers.
27 This is a standalone function to help extensions to wrap it.""" 29 This is a standalone function to help extensions to wrap it."""
28 assert not repo.changelog.filteredrevs 30 assert not repo.changelog.filteredrevs
29 hideable = hideablerevs(repo) 31 hideable = hideablerevs(repo)
30 blockers = set()
31 if hideable: 32 if hideable:
32 # We use cl to avoid recursive lookup from repo[xxx] 33 actuallyhidden = {}
33 cl = repo.changelog 34 getphase = repo._phasecache.phase
34 firsthideable = min(hideable) 35 getparentrevs = repo.changelog.parentrevs
35 revs = cl.revs(start=firsthideable) 36 queue = collections.deque((r, False) for r in repo.changelog.headrevs())
36 tofilter = repo.revs( 37 while queue:
37 '(%ld) and children(%ld)', list(revs), list(hideable)) 38 rev, blocked = queue.popleft()
38 blockers.update([r for r in tofilter if r not in hideable]) 39 phase = getphase(repo, rev)
39 return blockers 40 # Skip nodes which are public (guaranteed to not be hidden) and
41 # nodes which have already been processed and won't be blocked by
42 # the previous node.
43 if phase == 0 or (not blocked and rev in actuallyhidden):
44 continue
45 if rev in hideable:
46 if blocked:
47 actuallyhidden[rev] = False
48 else:
49 actuallyhidden.setdefault(rev, True)
50 else:
51 blocked = True
52
53 for parent in (p for p in getparentrevs(rev) if p != nullrev):
54 queue.append((parent, blocked))
55 return set(rev for rev, hidden in actuallyhidden.iteritems() if hidden)
40 56
41 def _getdynamicblockers(repo): 57 def _getdynamicblockers(repo):
42 """Non-cacheable revisions blocking hidden changesets from being filtered. 58 """Non-cacheable revisions blocking hidden changesets from being filtered.
43 59
44 Get revisions that will block hidden changesets and are likely to change, 60 Get revisions that will block hidden changesets and are likely to change,
135 hideable = hideablerevs(repo) 151 hideable = hideablerevs(repo)
136 if hideable: 152 if hideable:
137 cl = repo.changelog 153 cl = repo.changelog
138 hidden = tryreadcache(repo, hideable) 154 hidden = tryreadcache(repo, hideable)
139 if hidden is None: 155 if hidden is None:
140 blocked = cl.ancestors(_getstaticblockers(repo), inclusive=True) 156 hidden = frozenset(_getstatichidden(repo))
141 hidden = frozenset(r for r in hideable if r not in blocked)
142 trywritehiddencache(repo, hideable, hidden) 157 trywritehiddencache(repo, hideable, hidden)
143 158
144 # check if we have wd parents, bookmarks or tags pointing to hidden 159 # check if we have wd parents, bookmarks or tags pointing to hidden
145 # changesets and remove those. 160 # changesets and remove those.
146 dynamic = hidden & _getdynamicblockers(repo) 161 dynamic = hidden & _getdynamicblockers(repo)