##// END OF EJS Templates
phases: introduce phasecache...
Patrick Mezard -
r16657:b6081c2c default
parent child Browse files
Show More
@@ -883,7 +883,7 b' class queue(object):'
883 def finish(self, repo, revs):
883 def finish(self, repo, revs):
884 # Manually trigger phase computation to ensure phasedefaults is
884 # Manually trigger phase computation to ensure phasedefaults is
885 # executed before we remove the patches.
885 # executed before we remove the patches.
886 repo._phaserev
886 repo._phasecache
887 patches = self._revpatches(repo, sorted(revs))
887 patches = self._revpatches(repo, sorted(revs))
888 qfinished = self._cleanup(patches, len(patches))
888 qfinished = self._cleanup(patches, len(patches))
889 if qfinished and repo.ui.configbool('mq', 'secret', False):
889 if qfinished and repo.ui.configbool('mq', 'secret', False):
@@ -4378,7 +4378,7 b' def phase(ui, repo, *revs, **opts):'
4378 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4378 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4379 if not nodes:
4379 if not nodes:
4380 raise util.Abort(_('empty revision set'))
4380 raise util.Abort(_('empty revision set'))
4381 olddata = repo._phaserev[:]
4381 olddata = repo._phasecache.getphaserevs(repo)[:]
4382 phases.advanceboundary(repo, targetphase, nodes)
4382 phases.advanceboundary(repo, targetphase, nodes)
4383 if opts['force']:
4383 if opts['force']:
4384 phases.retractboundary(repo, targetphase, nodes)
4384 phases.retractboundary(repo, targetphase, nodes)
@@ -4386,7 +4386,7 b' def phase(ui, repo, *revs, **opts):'
4386 lock.release()
4386 lock.release()
4387 if olddata is not None:
4387 if olddata is not None:
4388 changes = 0
4388 changes = 0
4389 newdata = repo._phaserev
4389 newdata = repo._phasecache.getphaserevs(repo)
4390 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4390 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4391 rejected = [n for n in nodes
4391 rejected = [n for n in nodes
4392 if newdata[repo[n].rev()] < targetphase]
4392 if newdata[repo[n].rev()] < targetphase]
@@ -191,12 +191,7 b' class changectx(object):'
191 def bookmarks(self):
191 def bookmarks(self):
192 return self._repo.nodebookmarks(self._node)
192 return self._repo.nodebookmarks(self._node)
193 def phase(self):
193 def phase(self):
194 if self._rev == -1:
194 return self._repo._phasecache.phase(self._repo, self._rev)
195 return phases.public
196 if self._rev >= len(self._repo._phaserev):
197 # outdated cache
198 del self._repo._phaserev
199 return self._repo._phaserev[self._rev]
200 def phasestr(self):
195 def phasestr(self):
201 return phases.phasenames[self.phase()]
196 return phases.phasenames[self.phase()]
202 def mutable(self):
197 def mutable(self):
@@ -105,7 +105,7 b' def findcommonoutgoing(repo, other, only'
105 og.commonheads, _any, _hds = commoninc
105 og.commonheads, _any, _hds = commoninc
106
106
107 # compute outgoing
107 # compute outgoing
108 if not repo._phaseroots[phases.secret]:
108 if not repo._phasecache.phaseroots[phases.secret]:
109 og.missingheads = onlyheads or repo.heads()
109 og.missingheads = onlyheads or repo.heads()
110 elif onlyheads is None:
110 elif onlyheads is None:
111 # use visible heads as it should be cached
111 # use visible heads as it should be cached
@@ -41,7 +41,6 b' class localrepository(repo.repository):'
41 self.wopener = scmutil.opener(self.root)
41 self.wopener = scmutil.opener(self.root)
42 self.baseui = baseui
42 self.baseui = baseui
43 self.ui = baseui.copy()
43 self.ui = baseui.copy()
44 self._dirtyphases = False
45 # A list of callback to shape the phase if no data were found.
44 # A list of callback to shape the phase if no data were found.
46 # Callback are in the form: func(repo, roots) --> processed root.
45 # Callback are in the form: func(repo, roots) --> processed root.
47 # This list it to be filled by extension during repo setup
46 # This list it to be filled by extension during repo setup
@@ -182,22 +181,8 b' class localrepository(repo.repository):'
182 bookmarks.write(self)
181 bookmarks.write(self)
183
182
184 @storecache('phaseroots')
183 @storecache('phaseroots')
185 def _phaseroots(self):
184 def _phasecache(self):
186 phaseroots, self._dirtyphases = phases.readroots(
185 return phases.phasecache(self, self._phasedefaults)
187 self, self._phasedefaults)
188 return phaseroots
189
190 @propertycache
191 def _phaserev(self):
192 cache = [phases.public] * len(self)
193 for phase in phases.trackedphases:
194 roots = map(self.changelog.rev, self._phaseroots[phase])
195 if roots:
196 for rev in roots:
197 cache[rev] = phase
198 for rev in self.changelog.descendants(*roots):
199 cache[rev] = phase
200 return cache
201
186
202 @storecache('00changelog.i')
187 @storecache('00changelog.i')
203 def changelog(self):
188 def changelog(self):
@@ -604,10 +589,11 b' class localrepository(repo.repository):'
604
589
605 def known(self, nodes):
590 def known(self, nodes):
606 nm = self.changelog.nodemap
591 nm = self.changelog.nodemap
592 pc = self._phasecache
607 result = []
593 result = []
608 for n in nodes:
594 for n in nodes:
609 r = nm.get(n)
595 r = nm.get(n)
610 resp = not (r is None or self._phaserev[r] >= phases.secret)
596 resp = not (r is None or pc.phase(self, r) >= phases.secret)
611 result.append(resp)
597 result.append(resp)
612 return result
598 return result
613
599
@@ -863,7 +849,6 b' class localrepository(repo.repository):'
863 pass
849 pass
864
850
865 delcache('_tagscache')
851 delcache('_tagscache')
866 delcache('_phaserev')
867
852
868 self._branchcache = None # in UTF-8
853 self._branchcache = None # in UTF-8
869 self._branchcachetip = None
854 self._branchcachetip = None
@@ -931,9 +916,8 b' class localrepository(repo.repository):'
931
916
932 def unlock():
917 def unlock():
933 self.store.write()
918 self.store.write()
934 if self._dirtyphases:
919 if '_phasecache' in vars(self):
935 phases.writeroots(self, self._phaseroots)
920 self._phasecache.write()
936 self._dirtyphases = False
937 for k, ce in self._filecache.items():
921 for k, ce in self._filecache.items():
938 if k == 'dirstate':
922 if k == 'dirstate':
939 continue
923 continue
@@ -99,7 +99,7 b' Note: old client behave as publish serve'
99 """
99 """
100
100
101 import errno
101 import errno
102 from node import nullid, bin, hex, short
102 from node import nullid, nullrev, bin, hex, short
103 from i18n import _
103 from i18n import _
104
104
105 allphases = public, draft, secret = range(3)
105 allphases = public, draft, secret = range(3)
@@ -124,7 +124,7 b' def _filterunknown(ui, changelog, phaser'
124 updated = True
124 updated = True
125 return updated
125 return updated
126
126
127 def readroots(repo, phasedefaults=None):
127 def _readroots(repo, phasedefaults=None):
128 """Read phase roots from disk
128 """Read phase roots from disk
129
129
130 phasedefaults is a list of fn(repo, roots) callable, which are
130 phasedefaults is a list of fn(repo, roots) callable, which are
@@ -156,15 +156,51 b' def readroots(repo, phasedefaults=None):'
156 dirty = True
156 dirty = True
157 return roots, dirty
157 return roots, dirty
158
158
159 def writeroots(repo, phaseroots):
159 class phasecache(object):
160 """Write phase roots from disk"""
160 def __init__(self, repo, phasedefaults):
161 f = repo.sopener('phaseroots', 'w', atomictemp=True)
161 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
162 self.opener = repo.sopener
163 self._phaserevs = None
164
165 def getphaserevs(self, repo, rebuild=False):
166 if rebuild or self._phaserevs is None:
167 revs = [public] * len(repo.changelog)
168 for phase in trackedphases:
169 roots = map(repo.changelog.rev, self.phaseroots[phase])
170 if roots:
171 for rev in roots:
172 revs[rev] = phase
173 for rev in repo.changelog.descendants(*roots):
174 revs[rev] = phase
175 self._phaserevs = revs
176 return self._phaserevs
177
178 def invalidatephaserevs(self):
179 self._phaserevs = None
180
181 def phase(self, repo, rev):
182 # We need a repo argument here to be able to build _phaserev
183 # if necessary. The repository instance is not stored in
184 # phasecache to avoid reference cycles. The changelog instance
185 # is not stored because it is a filecache() property and can
186 # be replaced without us being notified.
187 if rev == nullrev:
188 return public
189 if self._phaserevs is None or rev >= len(self._phaserevs):
190 self._phaserevs = self.getphaserevs(repo, rebuild=True)
191 return self._phaserevs[rev]
192
193 def write(self):
194 if not self.dirty:
195 return
196 f = self.opener('phaseroots', 'w', atomictemp=True)
162 try:
197 try:
163 for phase, roots in enumerate(phaseroots):
198 for phase, roots in enumerate(self.phaseroots):
164 for h in roots:
199 for h in roots:
165 f.write('%i %s\n' % (phase, hex(h)))
200 f.write('%i %s\n' % (phase, hex(h)))
166 finally:
201 finally:
167 f.close()
202 f.close()
203 self.dirty = False
168
204
169 def advanceboundary(repo, targetphase, nodes):
205 def advanceboundary(repo, targetphase, nodes):
170 """Add nodes to a phase changing other nodes phases if necessary.
206 """Add nodes to a phase changing other nodes phases if necessary.
@@ -173,6 +209,8 b' def advanceboundary(repo, targetphase, n'
173 in the target phase or kept in a *lower* phase.
209 in the target phase or kept in a *lower* phase.
174
210
175 Simplify boundary to contains phase roots only."""
211 Simplify boundary to contains phase roots only."""
212 phcache = repo._phasecache
213
176 delroots = [] # set of root deleted by this path
214 delroots = [] # set of root deleted by this path
177 for phase in xrange(targetphase + 1, len(allphases)):
215 for phase in xrange(targetphase + 1, len(allphases)):
178 # filter nodes that are not in a compatible phase already
216 # filter nodes that are not in a compatible phase already
@@ -181,16 +219,15 b' def advanceboundary(repo, targetphase, n'
181 nodes = [n for n in nodes if repo[n].phase() >= phase]
219 nodes = [n for n in nodes if repo[n].phase() >= phase]
182 if not nodes:
220 if not nodes:
183 break # no roots to move anymore
221 break # no roots to move anymore
184 roots = repo._phaseroots[phase]
222 roots = phcache.phaseroots[phase]
185 olds = roots.copy()
223 olds = roots.copy()
186 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
224 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
187 roots.clear()
225 roots.clear()
188 roots.update(ctx.node() for ctx in ctxs)
226 roots.update(ctx.node() for ctx in ctxs)
189 if olds != roots:
227 if olds != roots:
190 # invalidate cache (we probably could be smarter here
228 # invalidate cache (we probably could be smarter here
191 if '_phaserev' in vars(repo):
229 phcache.invalidatephaserevs()
192 del repo._phaserev
230 phcache.dirty = True
193 repo._dirtyphases = True
194 # some roots may need to be declared for lower phases
231 # some roots may need to be declared for lower phases
195 delroots.extend(olds - roots)
232 delroots.extend(olds - roots)
196 # declare deleted root in the target phase
233 # declare deleted root in the target phase
@@ -205,22 +242,23 b' def retractboundary(repo, targetphase, n'
205 in the target phase or kept in a *higher* phase.
242 in the target phase or kept in a *higher* phase.
206
243
207 Simplify boundary to contains phase roots only."""
244 Simplify boundary to contains phase roots only."""
208 currentroots = repo._phaseroots[targetphase]
245 phcache = repo._phasecache
246
247 currentroots = phcache.phaseroots[targetphase]
209 newroots = [n for n in nodes if repo[n].phase() < targetphase]
248 newroots = [n for n in nodes if repo[n].phase() < targetphase]
210 if newroots:
249 if newroots:
211 currentroots.update(newroots)
250 currentroots.update(newroots)
212 ctxs = repo.set('roots(%ln::)', currentroots)
251 ctxs = repo.set('roots(%ln::)', currentroots)
213 currentroots.intersection_update(ctx.node() for ctx in ctxs)
252 currentroots.intersection_update(ctx.node() for ctx in ctxs)
214 if '_phaserev' in vars(repo):
253 phcache.invalidatephaserevs()
215 del repo._phaserev
254 phcache.dirty = True
216 repo._dirtyphases = True
217
255
218
256
219 def listphases(repo):
257 def listphases(repo):
220 """List phases root for serialisation over pushkey"""
258 """List phases root for serialisation over pushkey"""
221 keys = {}
259 keys = {}
222 value = '%i' % draft
260 value = '%i' % draft
223 for root in repo._phaseroots[draft]:
261 for root in repo._phasecache.phaseroots[draft]:
224 keys[hex(root)] = value
262 keys[hex(root)] = value
225
263
226 if repo.ui.configbool('phases', 'publish', True):
264 if repo.ui.configbool('phases', 'publish', True):
@@ -263,7 +301,7 b' def pushphase(repo, nhex, oldphasestr, n'
263 def visibleheads(repo):
301 def visibleheads(repo):
264 """return the set of visible head of this repo"""
302 """return the set of visible head of this repo"""
265 # XXX we want a cache on this
303 # XXX we want a cache on this
266 sroots = repo._phaseroots[secret]
304 sroots = repo._phasecache.phaseroots[secret]
267 if sroots:
305 if sroots:
268 # XXX very slow revset. storing heads or secret "boundary" would help.
306 # XXX very slow revset. storing heads or secret "boundary" would help.
269 revset = repo.set('heads(not (%ln::))', sroots)
307 revset = repo.set('heads(not (%ln::))', sroots)
@@ -279,7 +317,7 b' def visiblebranchmap(repo):'
279 """return a branchmap for the visible set"""
317 """return a branchmap for the visible set"""
280 # XXX Recomputing this data on the fly is very slow. We should build a
318 # XXX Recomputing this data on the fly is very slow. We should build a
281 # XXX cached version while computin the standard branchmap version.
319 # XXX cached version while computin the standard branchmap version.
282 sroots = repo._phaseroots[secret]
320 sroots = repo._phasecache.phaseroots[secret]
283 if sroots:
321 if sroots:
284 vbranchmap = {}
322 vbranchmap = {}
285 for branch, nodes in repo.branchmap().iteritems():
323 for branch, nodes in repo.branchmap().iteritems():
@@ -463,7 +463,8 b' def draft(repo, subset, x):'
463 """``draft()``
463 """``draft()``
464 Changeset in draft phase."""
464 Changeset in draft phase."""
465 getargs(x, 0, 0, _("draft takes no arguments"))
465 getargs(x, 0, 0, _("draft takes no arguments"))
466 return [r for r in subset if repo._phaserev[r] == phases.draft]
466 pc = repo._phasecache
467 return [r for r in subset if pc.phase(repo, r) == phases.draft]
467
468
468 def filelog(repo, subset, x):
469 def filelog(repo, subset, x):
469 """``filelog(pattern)``
470 """``filelog(pattern)``
@@ -852,7 +853,8 b' def public(repo, subset, x):'
852 """``public()``
853 """``public()``
853 Changeset in public phase."""
854 Changeset in public phase."""
854 getargs(x, 0, 0, _("public takes no arguments"))
855 getargs(x, 0, 0, _("public takes no arguments"))
855 return [r for r in subset if repo._phaserev[r] == phases.public]
856 pc = repo._phasecache
857 return [r for r in subset if pc.phase(repo, r) == phases.public]
856
858
857 def remote(repo, subset, x):
859 def remote(repo, subset, x):
858 """``remote([id [,path]])``
860 """``remote([id [,path]])``
@@ -1031,7 +1033,8 b' def secret(repo, subset, x):'
1031 """``secret()``
1033 """``secret()``
1032 Changeset in secret phase."""
1034 Changeset in secret phase."""
1033 getargs(x, 0, 0, _("secret takes no arguments"))
1035 getargs(x, 0, 0, _("secret takes no arguments"))
1034 return [r for r in subset if repo._phaserev[r] == phases.secret]
1036 pc = repo._phasecache
1037 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1035
1038
1036 def sort(repo, subset, x):
1039 def sort(repo, subset, x):
1037 """``sort(set[, [-]key...])``
1040 """``sort(set[, [-]key...])``
General Comments 0
You need to be logged in to leave comments. Login now