154 dirty = True |
154 dirty = True |
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 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 try: |
162 self.opener = repo.sopener |
163 for phase, roots in enumerate(phaseroots): |
163 self._phaserevs = None |
164 for h in roots: |
164 |
165 f.write('%i %s\n' % (phase, hex(h))) |
165 def getphaserevs(self, repo, rebuild=False): |
166 finally: |
166 if rebuild or self._phaserevs is None: |
167 f.close() |
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) |
|
197 try: |
|
198 for phase, roots in enumerate(self.phaseroots): |
|
199 for h in roots: |
|
200 f.write('%i %s\n' % (phase, hex(h))) |
|
201 finally: |
|
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. |
171 |
207 |
172 This function move boundary *forward* this means that all nodes are set |
208 This function move boundary *forward* this means that all nodes are set |
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 |
179 # XXX rev phase cache might have been invalidated by a previous loop |
217 # XXX rev phase cache might have been invalidated by a previous loop |
180 # XXX we need to be smarter here |
218 # XXX we need to be smarter here |
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 |
197 if targetphase != 0: |
234 if targetphase != 0: |
198 retractboundary(repo, targetphase, delroots) |
235 retractboundary(repo, targetphase, delroots) |
203 |
240 |
204 This function move boundary *backward* this means that all nodes are set |
241 This function move boundary *backward* this means that all nodes are set |
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): |
227 # Add an extra data to let remote know we are a publishing repo. |
265 # Add an extra data to let remote know we are a publishing repo. |
228 # Publishing repo can't just pretend they are old repo. When pushing to |
266 # Publishing repo can't just pretend they are old repo. When pushing to |