202 has_tr = repo.currenttransaction() is not None |
202 has_tr = repo.currenttransaction() is not None |
203 if not has_tr and self._changing_level == 0 and self._dirty: |
203 if not has_tr and self._changing_level == 0 and self._dirty: |
204 msg = "entering a changing context, but dirstate is already dirty" |
204 msg = "entering a changing context, but dirstate is already dirty" |
205 raise error.ProgrammingError(msg) |
205 raise error.ProgrammingError(msg) |
206 |
206 |
|
207 assert self._changing_level >= 0 |
207 # different type of change are mutually exclusive |
208 # different type of change are mutually exclusive |
208 if self._change_type is None: |
209 if self._change_type is None: |
209 assert self._changing_level == 0 |
210 assert self._changing_level == 0 |
210 self._change_type = change_type |
211 self._change_type = change_type |
211 elif self._change_type != change_type: |
212 elif self._change_type != change_type: |
213 'trying to open "%s" dirstate-changing context while a "%s" is' |
214 'trying to open "%s" dirstate-changing context while a "%s" is' |
214 ' already open' |
215 ' already open' |
215 ) |
216 ) |
216 msg %= (change_type, self._change_type) |
217 msg %= (change_type, self._change_type) |
217 raise error.ProgrammingError(msg) |
218 raise error.ProgrammingError(msg) |
|
219 should_write = False |
218 self._changing_level += 1 |
220 self._changing_level += 1 |
219 try: |
221 try: |
220 yield |
222 yield |
221 except: # re-raises |
223 except: # re-raises |
222 self.invalidate() |
224 self.invalidate() # this will set `_invalidated_context` |
223 raise |
225 raise |
224 finally: |
226 finally: |
225 tr = repo.currenttransaction() |
227 assert self._changing_level > 0 |
226 if self._changing_level > 0: |
228 self._changing_level -= 1 |
227 if self._invalidated_context: |
229 # If the dirstate is being invalidated, call invalidate again. |
228 # make sure we invalidate anything an upper context might |
230 # This will throw away anything added by a upper context and |
229 # have changed. |
231 # reset the `_invalidated_context` flag when relevant |
230 self.invalidate() |
232 if self._changing_level <= 0: |
231 self._changing_level -= 1 |
233 self._change_type = None |
232 # The invalidation is complete once we exit the final context |
234 assert self._changing_level == 0 |
233 # manager |
235 if self._invalidated_context: |
234 if self._changing_level <= 0: |
236 # make sure we invalidate anything an upper context might |
235 self._change_type = None |
237 # have changed. |
236 assert self._changing_level == 0 |
238 self.invalidate() |
237 if self._invalidated_context: |
239 else: |
238 self._invalidated_context = False |
240 should_write = self._changing_level <= 0 |
239 else: |
241 tr = repo.currenttransaction() |
240 # When an exception occured, `_invalidated_context` |
242 if has_tr != (tr is not None): |
241 # would have been set to True by the `invalidate` |
243 if has_tr: |
242 # call earlier. |
244 m = "transaction vanished while changing dirstate" |
243 # |
245 else: |
244 # We don't have more straightforward code, because the |
246 m = "transaction appeared while changing dirstate" |
245 # Exception catching (and the associated `invalidate` |
247 raise error.ProgrammingError(m) |
246 # calling) might have been called by a nested context |
248 if should_write: |
247 # instead of the top level one. |
249 self.write(tr) |
248 self.write(tr) |
|
249 if has_tr != (tr is not None): |
|
250 if has_tr: |
|
251 m = "transaction vanished while changing dirstate" |
|
252 else: |
|
253 m = "transaction appeared while changing dirstate" |
|
254 raise error.ProgrammingError(m) |
|
255 |
250 |
256 @contextlib.contextmanager |
251 @contextlib.contextmanager |
257 def changing_parents(self, repo): |
252 def changing_parents(self, repo): |
258 with self._changing(repo, CHANGE_TYPE_PARENTS) as c: |
253 with self._changing(repo, CHANGE_TYPE_PARENTS) as c: |
259 yield c |
254 yield c |