##// END OF EJS Templates
repoview: add _gethiddenblockers method...
Sean Farley -
r20940:b0822c23 default
parent child Browse files
Show More
@@ -1,218 +1,243 b''
1 1 # repoview.py - Filtered view of a localrepo object
2 2 #
3 3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import copy
10 10 import phases
11 11 import util
12 12 import obsolete
13 13 import tags as tagsmod
14 14
15 15
16 16 def hideablerevs(repo):
17 17 """Revisions candidates to be hidden
18 18
19 19 This is a standalone function to help extensions to wrap it."""
20 20 return obsolete.getrevs(repo, 'obsolete')
21 21
22 def _gethiddenblockers(repo):
23 """Get revisions that will block hidden changesets from being filtered
24
25 This is a standalone function to help extensions to wrap it."""
26 assert not repo.changelog.filteredrevs
27 hideable = hideablerevs(repo)
28 blockers = []
29 if hideable:
30 cl = repo.changelog
31 firsthideable = min(hideable)
32 revs = cl.revs(start=firsthideable)
33 tofilter = repo.revs(
34 '(%ld) and children(%ld)', list(revs), list(hideable))
35 blockers = [r for r in tofilter if r not in hideable]
36 for par in repo[None].parents():
37 blockers.append(par.rev())
38 for bm in repo._bookmarks.values():
39 blockers.append(repo[bm].rev())
40 tags = {}
41 tagsmod.readlocaltags(repo.ui, repo, tags, {})
42 if tags:
43 blockers.extend(repo[t[0]].rev() for t in tags.values())
44 blockers.extend(repo[t].rev() for t in tags)
45 return blockers
46
22 47 def computehidden(repo):
23 48 """compute the set of hidden revision to filter
24 49
25 50 During most operation hidden should be filtered."""
26 51 assert not repo.changelog.filteredrevs
27 52 hideable = hideablerevs(repo)
28 53 if hideable:
29 54 cl = repo.changelog
30 55 firsthideable = min(hideable)
31 56 revs = cl.revs(start=firsthideable)
32 57 tofilter = repo.revs(
33 58 '(%ld) and children(%ld)', list(revs), list(hideable))
34 59 blockers = [r for r in tofilter if r not in hideable]
35 60 for par in repo[None].parents():
36 61 blockers.append(par.rev())
37 62 for bm in repo._bookmarks.values():
38 63 blockers.append(repo[bm].rev())
39 64 tags = {}
40 65 tagsmod.readlocaltags(repo.ui, repo, tags, {})
41 66 if tags:
42 67 blockers.extend(repo[t[0]].rev() for t in tags.values())
43 68 blocked = cl.ancestors(blockers, inclusive=True)
44 69 return frozenset(r for r in hideable if r not in blocked)
45 70 return frozenset()
46 71
47 72 def computeunserved(repo):
48 73 """compute the set of revision that should be filtered when used a server
49 74
50 75 Secret and hidden changeset should not pretend to be here."""
51 76 assert not repo.changelog.filteredrevs
52 77 # fast path in simple case to avoid impact of non optimised code
53 78 hiddens = filterrevs(repo, 'visible')
54 79 if phases.hassecret(repo):
55 80 cl = repo.changelog
56 81 secret = phases.secret
57 82 getphase = repo._phasecache.phase
58 83 first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret])
59 84 revs = cl.revs(start=first)
60 85 secrets = set(r for r in revs if getphase(repo, r) >= secret)
61 86 return frozenset(hiddens | secrets)
62 87 else:
63 88 return hiddens
64 89
65 90 def computemutable(repo):
66 91 """compute the set of revision that should be filtered when used a server
67 92
68 93 Secret and hidden changeset should not pretend to be here."""
69 94 assert not repo.changelog.filteredrevs
70 95 # fast check to avoid revset call on huge repo
71 96 if util.any(repo._phasecache.phaseroots[1:]):
72 97 getphase = repo._phasecache.phase
73 98 maymutable = filterrevs(repo, 'base')
74 99 return frozenset(r for r in maymutable if getphase(repo, r))
75 100 return frozenset()
76 101
77 102 def computeimpactable(repo):
78 103 """Everything impactable by mutable revision
79 104
80 105 The immutable filter still have some chance to get invalidated. This will
81 106 happen when:
82 107
83 108 - you garbage collect hidden changeset,
84 109 - public phase is moved backward,
85 110 - something is changed in the filtering (this could be fixed)
86 111
87 112 This filter out any mutable changeset and any public changeset that may be
88 113 impacted by something happening to a mutable revision.
89 114
90 115 This is achieved by filtered everything with a revision number egal or
91 116 higher than the first mutable changeset is filtered."""
92 117 assert not repo.changelog.filteredrevs
93 118 cl = repo.changelog
94 119 firstmutable = len(cl)
95 120 for roots in repo._phasecache.phaseroots[1:]:
96 121 if roots:
97 122 firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
98 123 # protect from nullrev root
99 124 firstmutable = max(0, firstmutable)
100 125 return frozenset(xrange(firstmutable, len(cl)))
101 126
102 127 # function to compute filtered set
103 128 #
104 129 # When adding a new filter you MUST update the table at:
105 130 # mercurial.branchmap.subsettable
106 131 # Otherwise your filter will have to recompute all its branches cache
107 132 # from scratch (very slow).
108 133 filtertable = {'visible': computehidden,
109 134 'served': computeunserved,
110 135 'immutable': computemutable,
111 136 'base': computeimpactable}
112 137
113 138 def filterrevs(repo, filtername):
114 139 """returns set of filtered revision for this filter name"""
115 140 if filtername not in repo.filteredrevcache:
116 141 func = filtertable[filtername]
117 142 repo.filteredrevcache[filtername] = func(repo.unfiltered())
118 143 return repo.filteredrevcache[filtername]
119 144
120 145 class repoview(object):
121 146 """Provide a read/write view of a repo through a filtered changelog
122 147
123 148 This object is used to access a filtered version of a repository without
124 149 altering the original repository object itself. We can not alter the
125 150 original object for two main reasons:
126 151 - It prevents the use of a repo with multiple filters at the same time. In
127 152 particular when multiple threads are involved.
128 153 - It makes scope of the filtering harder to control.
129 154
130 155 This object behaves very closely to the original repository. All attribute
131 156 operations are done on the original repository:
132 157 - An access to `repoview.someattr` actually returns `repo.someattr`,
133 158 - A write to `repoview.someattr` actually sets value of `repo.someattr`,
134 159 - A deletion of `repoview.someattr` actually drops `someattr`
135 160 from `repo.__dict__`.
136 161
137 162 The only exception is the `changelog` property. It is overridden to return
138 163 a (surface) copy of `repo.changelog` with some revisions filtered. The
139 164 `filtername` attribute of the view control the revisions that need to be
140 165 filtered. (the fact the changelog is copied is an implementation detail).
141 166
142 167 Unlike attributes, this object intercepts all method calls. This means that
143 168 all methods are run on the `repoview` object with the filtered `changelog`
144 169 property. For this purpose the simple `repoview` class must be mixed with
145 170 the actual class of the repository. This ensures that the resulting
146 171 `repoview` object have the very same methods than the repo object. This
147 172 leads to the property below.
148 173
149 174 repoview.method() --> repo.__class__.method(repoview)
150 175
151 176 The inheritance has to be done dynamically because `repo` can be of any
152 177 subclasses of `localrepo`. Eg: `bundlerepo` or `statichttprepo`.
153 178 """
154 179
155 180 def __init__(self, repo, filtername):
156 181 object.__setattr__(self, '_unfilteredrepo', repo)
157 182 object.__setattr__(self, 'filtername', filtername)
158 183 object.__setattr__(self, '_clcachekey', None)
159 184 object.__setattr__(self, '_clcache', None)
160 185
161 186 # not a propertycache on purpose we shall implement a proper cache later
162 187 @property
163 188 def changelog(self):
164 189 """return a filtered version of the changeset
165 190
166 191 this changelog must not be used for writing"""
167 192 # some cache may be implemented later
168 193 unfi = self._unfilteredrepo
169 194 unfichangelog = unfi.changelog
170 195 revs = filterrevs(unfi, self.filtername)
171 196 cl = self._clcache
172 197 newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs))
173 198 if cl is not None:
174 199 # we need to check curkey too for some obscure reason.
175 200 # MQ test show a corruption of the underlying repo (in _clcache)
176 201 # without change in the cachekey.
177 202 oldfilter = cl.filteredrevs
178 203 try:
179 204 cl.filterrevs = () # disable filtering for tip
180 205 curkey = (len(cl), cl.tip(), hash(oldfilter))
181 206 finally:
182 207 cl.filteredrevs = oldfilter
183 208 if newkey != self._clcachekey or newkey != curkey:
184 209 cl = None
185 210 # could have been made None by the previous if
186 211 if cl is None:
187 212 cl = copy.copy(unfichangelog)
188 213 cl.filteredrevs = revs
189 214 object.__setattr__(self, '_clcache', cl)
190 215 object.__setattr__(self, '_clcachekey', newkey)
191 216 return cl
192 217
193 218 def unfiltered(self):
194 219 """Return an unfiltered version of a repo"""
195 220 return self._unfilteredrepo
196 221
197 222 def filtered(self, name):
198 223 """Return a filtered version of a repository"""
199 224 if name == self.filtername:
200 225 return self
201 226 return self.unfiltered().filtered(name)
202 227
203 228 # everything access are forwarded to the proxied repo
204 229 def __getattr__(self, attr):
205 230 return getattr(self._unfilteredrepo, attr)
206 231
207 232 def __setattr__(self, attr, value):
208 233 return setattr(self._unfilteredrepo, attr, value)
209 234
210 235 def __delattr__(self, attr):
211 236 return delattr(self._unfilteredrepo, attr)
212 237
213 238 # The `requirements` attribute is initialized during __init__. But
214 239 # __getattr__ won't be called as it also exists on the class. We need
215 240 # explicit forwarding to main repo here
216 241 @property
217 242 def requirements(self):
218 243 return self._unfilteredrepo.requirements
General Comments 0
You need to be logged in to leave comments. Login now