130 self._clearable = clearable |
133 self._clearable = clearable |
131 self._allowcommit = allowcommit |
134 self._allowcommit = allowcommit |
132 self._reportonly = reportonly |
135 self._reportonly = reportonly |
133 self._continueflag = continueflag |
136 self._continueflag = continueflag |
134 self._stopflag = stopflag |
137 self._stopflag = stopflag |
|
138 self._childopnames = childopnames |
|
139 self._delegating = False |
135 self._cmdmsg = cmdmsg |
140 self._cmdmsg = cmdmsg |
136 self._cmdhint = cmdhint |
141 self._cmdhint = cmdhint |
137 self._statushint = statushint |
142 self._statushint = statushint |
138 self.abortfunc = abortfunc |
143 self.abortfunc = abortfunc |
139 self.continuefunc = continuefunc |
144 self.continuefunc = continuefunc |
216 need to detect the operation using 'hg status --verbose' |
225 need to detect the operation using 'hg status --verbose' |
217 continueflag is a boolean determines whether or not a command supports |
226 continueflag is a boolean determines whether or not a command supports |
218 `--continue` option or not. |
227 `--continue` option or not. |
219 stopflag is a boolean that determines whether or not a command supports |
228 stopflag is a boolean that determines whether or not a command supports |
220 --stop flag |
229 --stop flag |
|
230 childopnames is a list of other opnames this op uses as sub-steps of its |
|
231 own execution. They must already be added. |
221 cmdmsg is used to pass a different status message in case standard |
232 cmdmsg is used to pass a different status message in case standard |
222 message of the format "abort: cmdname in progress" is not desired. |
233 message of the format "abort: cmdname in progress" is not desired. |
223 cmdhint is used to pass a different hint message in case standard |
234 cmdhint is used to pass a different hint message in case standard |
224 message of the format "To continue: hg cmdname --continue |
235 message of the format "To continue: hg cmdname --continue |
225 To abort: hg cmdname --abort" is not desired. |
236 To abort: hg cmdname --abort" is not desired. |
228 'To abort: hg cmdname --abort') is not desired |
239 'To abort: hg cmdname --abort') is not desired |
229 abortfunc stores the function required to abort an unfinished state. |
240 abortfunc stores the function required to abort an unfinished state. |
230 continuefunc stores the function required to finish an interrupted |
241 continuefunc stores the function required to finish an interrupted |
231 operation. |
242 operation. |
232 """ |
243 """ |
|
244 childopnames = childopnames or [] |
233 statecheckobj = _statecheck( |
245 statecheckobj = _statecheck( |
234 opname, |
246 opname, |
235 fname, |
247 fname, |
236 clearable, |
248 clearable, |
237 allowcommit, |
249 allowcommit, |
238 reportonly, |
250 reportonly, |
239 continueflag, |
251 continueflag, |
240 stopflag, |
252 stopflag, |
|
253 childopnames, |
241 cmdmsg, |
254 cmdmsg, |
242 cmdhint, |
255 cmdhint, |
243 statushint, |
256 statushint, |
244 abortfunc, |
257 abortfunc, |
245 continuefunc, |
258 continuefunc, |
246 ) |
259 ) |
|
260 |
247 if opname == b'merge': |
261 if opname == b'merge': |
248 _unfinishedstates.append(statecheckobj) |
262 _unfinishedstates.append(statecheckobj) |
249 else: |
263 else: |
|
264 # This check enforces that for any op 'foo' which depends on op 'bar', |
|
265 # 'foo' comes before 'bar' in _unfinishedstates. This ensures that |
|
266 # getrepostate() always returns the most specific applicable answer. |
|
267 for childopname in childopnames: |
|
268 if childopname not in _unfinishedstatesbyname: |
|
269 raise error.ProgrammingError( |
|
270 _(b'op %s depends on unknown op %s') % (opname, childopname) |
|
271 ) |
|
272 |
250 _unfinishedstates.insert(0, statecheckobj) |
273 _unfinishedstates.insert(0, statecheckobj) |
|
274 |
|
275 if opname in _unfinishedstatesbyname: |
|
276 raise error.ProgrammingError(_(b'op %s registered twice') % opname) |
|
277 _unfinishedstatesbyname[opname] = statecheckobj |
|
278 |
|
279 |
|
280 def _getparentandchild(opname, childopname): |
|
281 p = _unfinishedstatesbyname.get(opname, None) |
|
282 if not p: |
|
283 raise error.ProgrammingError(_(b'unknown op %s') % opname) |
|
284 if childopname not in p._childopnames: |
|
285 raise error.ProgrammingError( |
|
286 _(b'op %s does not delegate to %s') % (opname, childopname) |
|
287 ) |
|
288 c = _unfinishedstatesbyname[childopname] |
|
289 return p, c |
|
290 |
|
291 |
|
292 @contextlib.contextmanager |
|
293 def delegating(repo, opname, childopname): |
|
294 """context wrapper for delegations from opname to childopname. |
|
295 |
|
296 requires that childopname was specified when opname was registered. |
|
297 |
|
298 Usage: |
|
299 def my_command_foo_that_uses_rebase(...): |
|
300 ... |
|
301 with state.delegating(repo, 'foo', 'rebase'): |
|
302 _run_rebase(...) |
|
303 ... |
|
304 """ |
|
305 |
|
306 p, c = _getparentandchild(opname, childopname) |
|
307 if p._delegating: |
|
308 raise error.ProgrammingError( |
|
309 _(b'cannot delegate from op %s recursively') % opname |
|
310 ) |
|
311 p._delegating = True |
|
312 try: |
|
313 yield |
|
314 except error.ConflictResolutionRequired as e: |
|
315 # Rewrite conflict resolution advice for the parent opname. |
|
316 if e.opname == childopname: |
|
317 raise error.ConflictResolutionRequired(opname) |
|
318 raise e |
|
319 finally: |
|
320 p._delegating = False |
|
321 |
|
322 |
|
323 def ischildunfinished(repo, opname, childopname): |
|
324 """Returns true if both opname and childopname are unfinished.""" |
|
325 |
|
326 p, c = _getparentandchild(opname, childopname) |
|
327 return (p._delegating or p.isunfinished(repo)) and c.isunfinished(repo) |
|
328 |
|
329 |
|
330 def continuechild(ui, repo, opname, childopname): |
|
331 """Checks that childopname is in progress, and continues it.""" |
|
332 |
|
333 p, c = _getparentandchild(opname, childopname) |
|
334 if not ischildunfinished(repo, opname, childopname): |
|
335 raise error.ProgrammingError( |
|
336 _(b'child op %s of parent %s is not unfinished') |
|
337 % (childopname, opname) |
|
338 ) |
|
339 if not c.continuefunc: |
|
340 raise error.ProgrammingError( |
|
341 _(b'op %s has no continue function') % childopname |
|
342 ) |
|
343 return c.continuefunc(ui, repo) |
251 |
344 |
252 |
345 |
253 addunfinished( |
346 addunfinished( |
254 b'update', |
347 b'update', |
255 fname=b'updatestate', |
348 fname=b'updatestate', |