D8714: state: support validated declaration of nested unfinished ops

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

D8714: state: support validated declaration of nested unfinished ops

pulkit (Pulkit Goyal)
dploch created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This enables extensions to define commands that delgate to rebase, evolve, etc. one or more times to also have their own unfinished states for the full sequence of operations without monkey-patching _unfinishedstates.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8714

AFFECTED FILES
  mercurial/state.py

CHANGE DETAILS

diff --git a/mercurial/state.py b/mercurial/state.py
--- a/mercurial/state.py
+++ b/mercurial/state.py
@@ -16,6 +16,8 @@
 
 from __future__ import absolute_import
 
+import contextlib
+
 from .i18n import _
 
 from . import error, pycompat, util
@@ -114,6 +116,7 @@
         reportonly,
         continueflag,
         stopflag,
+        childopnames,
         cmdmsg,
         cmdhint,
         statushint,
@@ -127,6 +130,7 @@
         self._reportonly = reportonly
         self._continueflag = continueflag
         self._stopflag = stopflag
+        self._childopnames = childopnames
         self._cmdmsg = cmdmsg
         self._cmdhint = cmdhint
         self._statushint = statushint
@@ -185,6 +189,7 @@
 
 # A list of statecheck objects for multistep operations like graft.
 _unfinishedstates = []
+_unfinishedstatesbyname = {}
 
 
 def addunfinished(
@@ -195,6 +200,7 @@
     reportonly=False,
     continueflag=False,
     stopflag=False,
+    childopnames=[],
     cmdmsg=b'',
     cmdhint=b'',
     statushint=b'',
@@ -217,6 +223,8 @@
     `--continue` option or not.
     stopflag is a boolean that determines whether or not a command supports
     --stop flag
+    childopnames is a list of other opnames this op uses as sub-steps of its
+    own execution. They must already be added.
     cmdmsg is used to pass a different status message in case standard
     message of the format "abort: cmdname in progress" is not desired.
     cmdhint is used to pass a different hint message in case standard
@@ -237,17 +245,69 @@
         reportonly,
         continueflag,
         stopflag,
+        childopnames,
         cmdmsg,
         cmdhint,
         statushint,
         abortfunc,
         continuefunc,
     )
+
     if opname == b'merge':
         _unfinishedstates.append(statecheckobj)
     else:
+        # This check enforces that for any op 'foo' which depends on op 'bar',
+        # 'foo' comes before 'bar' in _unfinishedstates. This ensures that
+        # getrepostate() always returns the most specific applicable answer.
+        for childopname in childopnames:
+            if childopname not in _unfinishedstatesbyname:
+                raise error.ProgrammingError(
+                    _(b'op %s depends on unknown op %s') % (opname, childopname)
+                )
+
         _unfinishedstates.insert(0, statecheckobj)
 
+    if opname in _unfinishedstatesbyname:
+        raise error.ProgrammingError(_(b'op %s registered twice') % opname)
+    _unfinishedstatesbyname[opname] = statecheckobj
+
+
+@contextlib.contextmanager
+def delegating(repo, opname, childopname):
+    """context wrapper for delegations from opname to childopname.
+
+    requires that childopname was specified when opname was registered.
+
+    Usage:
+      def my_command_foo_that_uses_rebase(...):
+        ...
+        with state.delegating(repo, 'foo', 'rebase'):
+          _run_rebase(...)
+        ...
+    """
+
+    s = _unfinishedstatesbyname[parentopname]
+    if not s:
+        raise error.ProgrammingError(_(b'unknown op %s') % opname)
+    if childopname not in s._childopnames:
+        raise error.ProgrammingError(
+            _(b'op %s does not delegate to %s') % (opname, childopname)
+        )
+    c = _unfinishedstatesbyname[childopname]
+
+    newskipstates = set(repo.ui.configlist(b'commands', b'status.skipstates'))
+    newskipstates.add(childopname)
+    with repo.ui.configoverride(
+        {(b'commands', b'status.skipstates'): newskipstates}
+    ):
+        try:
+            yield
+        except error.ConflictResolutionRequired as e:
+            # Rewrite conflict resolution advice for the parent opname.
+            if e.opname == childopname:
+                raise error.ConflictResolutionRequired(opname)
+            raise e
+
 
 addunfinished(
     b'update',



To: dploch, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel