Patchwork D3815: scmutil: make cleanupnodes optionally also fix the phase

login
register
mail settings
Submitter phabricator
Date June 19, 2018, 9:16 p.m.
Message ID <differential-rev-PHID-DREV-qdoy2y46sq76rfv7l7g3-req@phab.mercurial-scm.org>
Download mbox | patch
Permalink /patch/32336/
State Superseded
Headers show

Comments

phabricator - June 19, 2018, 9:16 p.m.
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This also updates all applicable callers so we get some testing of it.
  
  Note that rebase and histedit can't be fixed yet because they call
  cleanupnodes() only at the end and the phase may have been changed by
  the user when the rebase/histedit was interrupted (due to merge
  conflicts).
  
  Also not that amend can't rely on cleanupnodes, because it allows the
  phase to be overridden on the command line (using `hg amend
  --secret`).

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  contrib/phabricator.py
  hgext/fix.py
  hgext/uncommit.py
  mercurial/cmdutil.py
  mercurial/scmutil.py

CHANGE DETAILS




To: martinvonz, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -779,7 +779,8 @@ 
     def __contains__(self, node):
         return self._revcontains(self._torev(node))
 
-def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
+def cleanupnodes(repo, replacements, operation, moves=None, metadata=None,
+                 fixphase=False):
     """do common cleanups when old nodes are replaced by new nodes
 
     That includes writing obsmarkers or stripping nodes, and moving bookmarks.
@@ -825,11 +826,34 @@ 
             newnode = newnodes[0]
         moves[oldnode] = newnode
 
+    allnewnodes = [n for ns in replacements.values() for n in ns]
+    toretract = {}
+    toadvance = {}
+    if fixphase:
+        precursors = {}
+        for oldnode, newnodes in replacements.items():
+            for newnode in newnodes:
+                precursors.setdefault(newnode, []).append(oldnode)
+
+        allnewnodes.sort(key=lambda n: repo[n].rev())
+        newphase = {}
+        def phase(ctx):
+            return newphase.get(ctx.node(), ctx.phase())
+        for newnode in allnewnodes:
+            ctx = repo[newnode]
+            oldphase = max(repo[oldnode].phase()
+                           for oldnode in precursors[newnode])
+            parentphase = max(phase(p) for p in ctx.parents())
+            newphase[newnode] = max(oldphase, parentphase)
+            if newphase[newnode] > ctx.phase():
+                toretract.setdefault(newphase[newnode], []).append(newnode)
+            elif newphase[newnode] < ctx.phase():
+                toadvance.setdefault(newphase[newnode], []).append(newnode)
+
     with repo.transaction('cleanup') as tr:
         # Move bookmarks
         bmarks = repo._bookmarks
         bmarkchanges = []
-        allnewnodes = [n for ns in replacements.values() for n in ns]
         for oldnode, newnode in moves.items():
             oldbmarks = repo.nodebookmarks(oldnode)
             if not oldbmarks:
@@ -850,6 +874,11 @@ 
         if bmarkchanges:
             bmarks.applychanges(repo, tr, bmarkchanges)
 
+        for phase, nodes in toretract.items():
+            phases.retractboundary(repo, tr, phase, nodes)
+        for phase, nodes in toadvance.items():
+            phases.advanceboundary(repo, tr, phase, nodes)
+
         # Obsolete or strip nodes
         if obsolete.isenabled(repo, obsolete.createmarkersopt):
             # If a node is already obsoleted, and we want to obsolete it
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -784,16 +784,12 @@ 
                                 extra=extra,
                                 branch=label)
 
-            commitphase = ctx.phase()
-            overrides = {('phases', 'new-commit'): commitphase}
-            with repo.ui.configoverride(overrides, 'branch-change'):
-                newnode = repo.commitctx(mc)
-
+            newnode = repo.commitctx(mc)
             replacements[ctx.node()] = (newnode,)
             ui.debug('new node id is %s\n' % hex(newnode))
 
         # create obsmarkers and move bookmarks
-        scmutil.cleanupnodes(repo, replacements, 'branch-change')
+        scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True)
 
         # move the working copy too
         wctx = repo[None]
diff --git a/hgext/uncommit.py b/hgext/uncommit.py
--- a/hgext/uncommit.py
+++ b/hgext/uncommit.py
@@ -91,12 +91,7 @@ 
                          user=ctx.user(),
                          date=ctx.date(),
                          extra=ctx.extra())
-    # phase handling
-    commitphase = ctx.phase()
-    overrides = {('phases', 'new-commit'): commitphase}
-    with repo.ui.configoverride(overrides, 'uncommit'):
-        newid = repo.commitctx(new)
-    return newid
+    return repo.commitctx(new)
 
 def _fixdirstate(repo, oldctx, newctx, status):
     """ fix the dirstate after switching the working directory from oldctx to
@@ -183,7 +178,7 @@ 
                 # Fully removed the old commit
                 mapping[old.node()] = ()
 
-            scmutil.cleanupnodes(repo, mapping, 'uncommit')
+            scmutil.cleanupnodes(repo, mapping, 'uncommit', fixphase=True)
 
             with repo.dirstate.parentchange():
                 repo.dirstate.setparents(newid, node.nullid)
@@ -242,12 +237,7 @@ 
                                 user=predctx.user(),
                                 date=predctx.date(),
                                 extra=extras)
-        # phase handling
-        commitphase = curctx.phase()
-        overrides = {('phases', 'new-commit'): commitphase}
-        with repo.ui.configoverride(overrides, 'uncommit'):
-            newprednode = repo.commitctx(newctx)
-
+        newprednode = repo.commitctx(newctx)
         newpredctx = repo[newprednode]
         dirstate = repo.dirstate
 
@@ -257,4 +247,4 @@ 
             _fixdirstate(repo, curctx, newpredctx, s)
 
         mapping = {curctx.node(): (newprednode,)}
-        scmutil.cleanupnodes(repo, mapping, 'unamend')
+        scmutil.cleanupnodes(repo, mapping, 'unamend', fixphase=True)
diff --git a/hgext/fix.py b/hgext/fix.py
--- a/hgext/fix.py
+++ b/hgext/fix.py
@@ -158,7 +158,7 @@ 
                 del filedata[rev]
 
         replacements = {prec: [succ] for prec, succ in replacements.iteritems()}
-        scmutil.cleanupnodes(repo, replacements, 'fix')
+        scmutil.cleanupnodes(repo, replacements, 'fix', fixphase=True)
 
 def getworkqueue(ui, repo, pats, opts, revstofix, basectxs):
     """"Constructs the list of files to be fixed at specific revisions
@@ -484,25 +484,23 @@ 
             isexec=fctx.isexec(),
             copied=copied)
 
-    overrides = {('phases', 'new-commit'): ctx.phase()}
-    with ui.configoverride(overrides, source='fix'):
-        memctx = context.memctx(
-            repo,
-            parents=(newp1node, newp2node),
-            text=ctx.description(),
-            files=set(ctx.files()) | set(filedata.keys()),
-            filectxfn=filectxfn,
-            user=ctx.user(),
-            date=ctx.date(),
-            extra=ctx.extra(),
-            branch=ctx.branch(),
-            editor=None)
-        sucnode = memctx.commit()
-        prenode = ctx.node()
-        if prenode == sucnode:
-            ui.debug('node %s already existed\n' % (ctx.hex()))
-        else:
-            replacements[ctx.node()] = sucnode
+    memctx = context.memctx(
+        repo,
+        parents=(newp1node, newp2node),
+        text=ctx.description(),
+        files=set(ctx.files()) | set(filedata.keys()),
+        filectxfn=filectxfn,
+        user=ctx.user(),
+        date=ctx.date(),
+        extra=ctx.extra(),
+        branch=ctx.branch(),
+        editor=None)
+    sucnode = memctx.commit()
+    prenode = ctx.node()
+    if prenode == sucnode:
+        ui.debug('node %s already existed\n' % (ctx.hex()))
+    else:
+        replacements[ctx.node()] = sucnode
 
 def getfixers(ui):
     """Returns a map of configured fixer tools indexed by their names
diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -580,9 +580,7 @@ 
                         repo, old, parents=parents, text=newdesc,
                         user=old.user(), date=old.date(), extra=old.extra())
 
-                    overrides = {(b'phases', b'new-commit'): old.phase()}
-                    with ui.configoverride(overrides, b'phabsend'):
-                        newnode = new.commit()
+                    newnode = new.commit()
 
                     mapping[old.node()] = [newnode]
                     # Update diff property
@@ -592,7 +590,7 @@ 
                 if tagname in repo.tags():
                     tags.tag(repo, tagname, nullid, message=None, user=None,
                              date=None, local=True)
-            scmutil.cleanupnodes(repo, mapping, b'phabsend')
+            scmutil.cleanupnodes(repo, mapping, b'phabsend', fixphase=True)
             if wnode in mapping:
                 unfi.setparents(mapping[wnode][0])