155 if _filterunknown(repo.ui, repo.changelog, roots): |
155 if _filterunknown(repo.ui, repo.changelog, roots): |
156 dirty = True |
156 dirty = True |
157 return roots, dirty |
157 return roots, dirty |
158 |
158 |
159 class phasecache(object): |
159 class phasecache(object): |
160 def __init__(self, repo, phasedefaults): |
160 def __init__(self, repo, phasedefaults, _load=True): |
161 self.phaseroots, self.dirty = _readroots(repo, phasedefaults) |
161 if _load: |
162 self.opener = repo.sopener |
162 # Cheap trick to allow shallow-copy without copy module |
163 self._phaserevs = None |
163 self.phaseroots, self.dirty = _readroots(repo, phasedefaults) |
|
164 self.opener = repo.sopener |
|
165 self._phaserevs = None |
|
166 |
|
167 def copy(self): |
|
168 # Shallow copy meant to ensure isolation in |
|
169 # advance/retractboundary(), nothing more. |
|
170 ph = phasecache(None, None, _load=False) |
|
171 ph.phaseroots = self.phaseroots[:] |
|
172 ph.dirty = self.dirty |
|
173 ph.opener = self.opener |
|
174 ph._phaserevs = self._phaserevs |
|
175 return ph |
|
176 |
|
177 def replace(self, phcache): |
|
178 for a in 'phaseroots dirty opener _phaserevs'.split(): |
|
179 setattr(self, a, getattr(phcache, a)) |
164 |
180 |
165 def getphaserevs(self, repo, rebuild=False): |
181 def getphaserevs(self, repo, rebuild=False): |
166 if rebuild or self._phaserevs is None: |
182 if rebuild or self._phaserevs is None: |
167 revs = [public] * len(repo.changelog) |
183 revs = [public] * len(repo.changelog) |
168 for phase in trackedphases: |
184 for phase in trackedphases: |
200 f.write('%i %s\n' % (phase, hex(h))) |
213 f.write('%i %s\n' % (phase, hex(h))) |
201 finally: |
214 finally: |
202 f.close() |
215 f.close() |
203 self.dirty = False |
216 self.dirty = False |
204 |
217 |
|
218 def _updateroots(self, phase, newroots): |
|
219 self.phaseroots[phase] = newroots |
|
220 self._phaserevs = None |
|
221 self.dirty = True |
|
222 |
|
223 def advanceboundary(self, repo, targetphase, nodes): |
|
224 # Be careful to preserve shallow-copied values: do not update |
|
225 # phaseroots values, replace them. |
|
226 |
|
227 delroots = [] # set of root deleted by this path |
|
228 for phase in xrange(targetphase + 1, len(allphases)): |
|
229 # filter nodes that are not in a compatible phase already |
|
230 nodes = [n for n in nodes |
|
231 if self.phase(repo, repo[n].rev()) >= phase] |
|
232 if not nodes: |
|
233 break # no roots to move anymore |
|
234 olds = self.phaseroots[phase] |
|
235 roots = set(ctx.node() for ctx in repo.set( |
|
236 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes)) |
|
237 if olds != roots: |
|
238 self._updateroots(phase, roots) |
|
239 # some roots may need to be declared for lower phases |
|
240 delroots.extend(olds - roots) |
|
241 # declare deleted root in the target phase |
|
242 if targetphase != 0: |
|
243 self.retractboundary(repo, targetphase, delroots) |
|
244 |
|
245 def retractboundary(self, repo, targetphase, nodes): |
|
246 # Be careful to preserve shallow-copied values: do not update |
|
247 # phaseroots values, replace them. |
|
248 |
|
249 currentroots = self.phaseroots[targetphase] |
|
250 newroots = [n for n in nodes |
|
251 if self.phase(repo, repo[n].rev()) < targetphase] |
|
252 if newroots: |
|
253 currentroots = currentroots.copy() |
|
254 currentroots.update(newroots) |
|
255 ctxs = repo.set('roots(%ln::)', currentroots) |
|
256 currentroots.intersection_update(ctx.node() for ctx in ctxs) |
|
257 self._updateroots(targetphase, currentroots) |
|
258 |
205 def advanceboundary(repo, targetphase, nodes): |
259 def advanceboundary(repo, targetphase, nodes): |
206 """Add nodes to a phase changing other nodes phases if necessary. |
260 """Add nodes to a phase changing other nodes phases if necessary. |
207 |
261 |
208 This function move boundary *forward* this means that all nodes are set |
262 This function move boundary *forward* this means that all nodes are set |
209 in the target phase or kept in a *lower* phase. |
263 in the target phase or kept in a *lower* phase. |
210 |
264 |
211 Simplify boundary to contains phase roots only.""" |
265 Simplify boundary to contains phase roots only.""" |
212 phcache = repo._phasecache |
266 phcache = repo._phasecache.copy() |
213 |
267 phcache.advanceboundary(repo, targetphase, nodes) |
214 delroots = [] # set of root deleted by this path |
268 repo._phasecache.replace(phcache) |
215 for phase in xrange(targetphase + 1, len(allphases)): |
|
216 # filter nodes that are not in a compatible phase already |
|
217 # XXX rev phase cache might have been invalidated by a previous loop |
|
218 # XXX we need to be smarter here |
|
219 nodes = [n for n in nodes if repo[n].phase() >= phase] |
|
220 if not nodes: |
|
221 break # no roots to move anymore |
|
222 roots = phcache.phaseroots[phase] |
|
223 olds = roots.copy() |
|
224 ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes)) |
|
225 roots.clear() |
|
226 roots.update(ctx.node() for ctx in ctxs) |
|
227 if olds != roots: |
|
228 # invalidate cache (we probably could be smarter here |
|
229 phcache.invalidatephaserevs() |
|
230 phcache.dirty = True |
|
231 # some roots may need to be declared for lower phases |
|
232 delroots.extend(olds - roots) |
|
233 # declare deleted root in the target phase |
|
234 if targetphase != 0: |
|
235 retractboundary(repo, targetphase, delroots) |
|
236 |
|
237 |
269 |
238 def retractboundary(repo, targetphase, nodes): |
270 def retractboundary(repo, targetphase, nodes): |
239 """Set nodes back to a phase changing other nodes phases if necessary. |
271 """Set nodes back to a phase changing other nodes phases if necessary. |
240 |
272 |
241 This function move boundary *backward* this means that all nodes are set |
273 This function move boundary *backward* this means that all nodes are set |
242 in the target phase or kept in a *higher* phase. |
274 in the target phase or kept in a *higher* phase. |
243 |
275 |
244 Simplify boundary to contains phase roots only.""" |
276 Simplify boundary to contains phase roots only.""" |
245 phcache = repo._phasecache |
277 phcache = repo._phasecache.copy() |
246 |
278 phcache.retractboundary(repo, targetphase, nodes) |
247 currentroots = phcache.phaseroots[targetphase] |
279 repo._phasecache.replace(phcache) |
248 newroots = [n for n in nodes if repo[n].phase() < targetphase] |
|
249 if newroots: |
|
250 currentroots.update(newroots) |
|
251 ctxs = repo.set('roots(%ln::)', currentroots) |
|
252 currentroots.intersection_update(ctx.node() for ctx in ctxs) |
|
253 phcache.invalidatephaserevs() |
|
254 phcache.dirty = True |
|
255 |
|
256 |
280 |
257 def listphases(repo): |
281 def listphases(repo): |
258 """List phases root for serialisation over pushkey""" |
282 """List phases root for serialisation over pushkey""" |
259 keys = {} |
283 keys = {} |
260 value = '%i' % draft |
284 value = '%i' % draft |