##// END OF EJS Templates
hidden: remove unnecessary guard condition...
Martin von Zweigbergk -
r32587:e4d0b2ef default
parent child Browse files
Show More
@@ -1,253 +1,252 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 from __future__ import absolute_import
10 10
11 11 import copy
12 12
13 13 from .node import nullrev
14 14 from . import (
15 15 obsolete,
16 16 phases,
17 17 tags as tagsmod,
18 18 )
19 19
20 20 def hideablerevs(repo):
21 21 """Revision candidates to be hidden
22 22
23 23 This is a standalone function to allow extensions to wrap it.
24 24
25 25 Because we use the set of immutable changesets as a fallback subset in
26 26 branchmap (see mercurial.branchmap.subsettable), you cannot set "public"
27 27 changesets as "hideable". Doing so would break multiple code assertions and
28 28 lead to crashes."""
29 29 return obsolete.getrevs(repo, 'obsolete')
30 30
31 31 def pinnedrevs(repo):
32 32 """revisions blocking hidden changesets from being filtered
33 33 """
34 34
35 35 cl = repo.changelog
36 36 pinned = set()
37 37 pinned.update([par.rev() for par in repo[None].parents()])
38 38 pinned.update([cl.rev(bm) for bm in repo._bookmarks.values()])
39 39
40 40 tags = {}
41 41 tagsmod.readlocaltags(repo.ui, repo, tags, {})
42 42 if tags:
43 43 rev, nodemap = cl.rev, cl.nodemap
44 44 pinned.update(rev(t[0]) for t in tags.values() if t[0] in nodemap)
45 45 return pinned
46 46
47 47
48 48 def _revealancestors(pfunc, hidden, revs):
49 49 """reveals contiguous chains of hidden ancestors of 'revs' by removing them
50 50 from 'hidden'
51 51
52 52 - pfunc(r): a funtion returning parent of 'r',
53 53 - hidden: the (preliminary) hidden revisions, to be updated
54 54 - revs: iterable of revnum,
55 55
56 56 (Ancestors are revealed exclusively, i.e. the elements in 'revs' are
57 57 *not* revealed)
58 58 """
59 59 stack = list(revs)
60 60 while stack:
61 61 for p in pfunc(stack.pop()):
62 62 if p != nullrev and p in hidden:
63 63 hidden.remove(p)
64 64 stack.append(p)
65 65
66 66 def computehidden(repo):
67 67 """compute the set of hidden revision to filter
68 68
69 69 During most operation hidden should be filtered."""
70 70 assert not repo.changelog.filteredrevs
71 71
72 72 hidden = hideablerevs(repo)
73 73 if hidden:
74 74 hidden = set(hidden - pinnedrevs(repo))
75 75 pfunc = repo.changelog.parentrevs
76 76 mutablephases = (phases.draft, phases.secret)
77 77 mutable = repo._phasecache.getrevset(repo, mutablephases)
78 78
79 visible = set(mutable - hidden)
80 if visible:
81 _revealancestors(pfunc, hidden, visible)
79 visible = mutable - hidden
80 _revealancestors(pfunc, hidden, visible)
82 81 return frozenset(hidden)
83 82
84 83 def computeunserved(repo):
85 84 """compute the set of revision that should be filtered when used a server
86 85
87 86 Secret and hidden changeset should not pretend to be here."""
88 87 assert not repo.changelog.filteredrevs
89 88 # fast path in simple case to avoid impact of non optimised code
90 89 hiddens = filterrevs(repo, 'visible')
91 90 if phases.hassecret(repo):
92 91 cl = repo.changelog
93 92 secret = phases.secret
94 93 getphase = repo._phasecache.phase
95 94 first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret])
96 95 revs = cl.revs(start=first)
97 96 secrets = set(r for r in revs if getphase(repo, r) >= secret)
98 97 return frozenset(hiddens | secrets)
99 98 else:
100 99 return hiddens
101 100
102 101 def computemutable(repo):
103 102 """compute the set of revision that should be filtered when used a server
104 103
105 104 Secret and hidden changeset should not pretend to be here."""
106 105 assert not repo.changelog.filteredrevs
107 106 # fast check to avoid revset call on huge repo
108 107 if any(repo._phasecache.phaseroots[1:]):
109 108 getphase = repo._phasecache.phase
110 109 maymutable = filterrevs(repo, 'base')
111 110 return frozenset(r for r in maymutable if getphase(repo, r))
112 111 return frozenset()
113 112
114 113 def computeimpactable(repo):
115 114 """Everything impactable by mutable revision
116 115
117 116 The immutable filter still have some chance to get invalidated. This will
118 117 happen when:
119 118
120 119 - you garbage collect hidden changeset,
121 120 - public phase is moved backward,
122 121 - something is changed in the filtering (this could be fixed)
123 122
124 123 This filter out any mutable changeset and any public changeset that may be
125 124 impacted by something happening to a mutable revision.
126 125
127 126 This is achieved by filtered everything with a revision number egal or
128 127 higher than the first mutable changeset is filtered."""
129 128 assert not repo.changelog.filteredrevs
130 129 cl = repo.changelog
131 130 firstmutable = len(cl)
132 131 for roots in repo._phasecache.phaseroots[1:]:
133 132 if roots:
134 133 firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
135 134 # protect from nullrev root
136 135 firstmutable = max(0, firstmutable)
137 136 return frozenset(xrange(firstmutable, len(cl)))
138 137
139 138 # function to compute filtered set
140 139 #
141 140 # When adding a new filter you MUST update the table at:
142 141 # mercurial.branchmap.subsettable
143 142 # Otherwise your filter will have to recompute all its branches cache
144 143 # from scratch (very slow).
145 144 filtertable = {'visible': computehidden,
146 145 'served': computeunserved,
147 146 'immutable': computemutable,
148 147 'base': computeimpactable}
149 148
150 149 def filterrevs(repo, filtername):
151 150 """returns set of filtered revision for this filter name"""
152 151 if filtername not in repo.filteredrevcache:
153 152 func = filtertable[filtername]
154 153 repo.filteredrevcache[filtername] = func(repo.unfiltered())
155 154 return repo.filteredrevcache[filtername]
156 155
157 156 class repoview(object):
158 157 """Provide a read/write view of a repo through a filtered changelog
159 158
160 159 This object is used to access a filtered version of a repository without
161 160 altering the original repository object itself. We can not alter the
162 161 original object for two main reasons:
163 162 - It prevents the use of a repo with multiple filters at the same time. In
164 163 particular when multiple threads are involved.
165 164 - It makes scope of the filtering harder to control.
166 165
167 166 This object behaves very closely to the original repository. All attribute
168 167 operations are done on the original repository:
169 168 - An access to `repoview.someattr` actually returns `repo.someattr`,
170 169 - A write to `repoview.someattr` actually sets value of `repo.someattr`,
171 170 - A deletion of `repoview.someattr` actually drops `someattr`
172 171 from `repo.__dict__`.
173 172
174 173 The only exception is the `changelog` property. It is overridden to return
175 174 a (surface) copy of `repo.changelog` with some revisions filtered. The
176 175 `filtername` attribute of the view control the revisions that need to be
177 176 filtered. (the fact the changelog is copied is an implementation detail).
178 177
179 178 Unlike attributes, this object intercepts all method calls. This means that
180 179 all methods are run on the `repoview` object with the filtered `changelog`
181 180 property. For this purpose the simple `repoview` class must be mixed with
182 181 the actual class of the repository. This ensures that the resulting
183 182 `repoview` object have the very same methods than the repo object. This
184 183 leads to the property below.
185 184
186 185 repoview.method() --> repo.__class__.method(repoview)
187 186
188 187 The inheritance has to be done dynamically because `repo` can be of any
189 188 subclasses of `localrepo`. Eg: `bundlerepo` or `statichttprepo`.
190 189 """
191 190
192 191 def __init__(self, repo, filtername):
193 192 object.__setattr__(self, r'_unfilteredrepo', repo)
194 193 object.__setattr__(self, r'filtername', filtername)
195 194 object.__setattr__(self, r'_clcachekey', None)
196 195 object.__setattr__(self, r'_clcache', None)
197 196
198 197 # not a propertycache on purpose we shall implement a proper cache later
199 198 @property
200 199 def changelog(self):
201 200 """return a filtered version of the changeset
202 201
203 202 this changelog must not be used for writing"""
204 203 # some cache may be implemented later
205 204 unfi = self._unfilteredrepo
206 205 unfichangelog = unfi.changelog
207 206 # bypass call to changelog.method
208 207 unfiindex = unfichangelog.index
209 208 unfilen = len(unfiindex) - 1
210 209 unfinode = unfiindex[unfilen - 1][7]
211 210
212 211 revs = filterrevs(unfi, self.filtername)
213 212 cl = self._clcache
214 213 newkey = (unfilen, unfinode, hash(revs), unfichangelog._delayed)
215 214 # if cl.index is not unfiindex, unfi.changelog would be
216 215 # recreated, and our clcache refers to garbage object
217 216 if (cl is not None and
218 217 (cl.index is not unfiindex or newkey != self._clcachekey)):
219 218 cl = None
220 219 # could have been made None by the previous if
221 220 if cl is None:
222 221 cl = copy.copy(unfichangelog)
223 222 cl.filteredrevs = revs
224 223 object.__setattr__(self, r'_clcache', cl)
225 224 object.__setattr__(self, r'_clcachekey', newkey)
226 225 return cl
227 226
228 227 def unfiltered(self):
229 228 """Return an unfiltered version of a repo"""
230 229 return self._unfilteredrepo
231 230
232 231 def filtered(self, name):
233 232 """Return a filtered version of a repository"""
234 233 if name == self.filtername:
235 234 return self
236 235 return self.unfiltered().filtered(name)
237 236
238 237 # everything access are forwarded to the proxied repo
239 238 def __getattr__(self, attr):
240 239 return getattr(self._unfilteredrepo, attr)
241 240
242 241 def __setattr__(self, attr, value):
243 242 return setattr(self._unfilteredrepo, attr, value)
244 243
245 244 def __delattr__(self, attr):
246 245 return delattr(self._unfilteredrepo, attr)
247 246
248 247 # The `requirements` attribute is initialized during __init__. But
249 248 # __getattr__ won't be called as it also exists on the class. We need
250 249 # explicit forwarding to main repo here
251 250 @property
252 251 def requirements(self):
253 252 return self._unfilteredrepo.requirements
General Comments 0
You need to be logged in to leave comments. Login now