changeset 39891:b99903534e06

scmutil: accept multiple predecessors in 'replacements' (API) This changeset makes 'cleanupnodes' accepts multiple predecessors as `replacements` keys. The same as it accepts multiple successors as `replacements` values. To avoid breaking all callers, the old and new ways are currently valid at the same time. We'll deprecate and drop the old way later. This change is the first step toward a better tracking of "fold" event in the evolution history. While working on the "rewind" command (in the evolve extension), we realized that first class tracking of folds are necessary. We already have good tracking of splits. When walking the evolution history from predecessors to successors, that makes for a clear distinction between having multiple successors because of the actual splitting of a changeset or content-divergences. The "rewind" command allows restoring older evolution of a stack of changesets. One of its mode walks the evolution history to automatically find appropriate predecessors. This means walking from successors to predecessors. In this case, we need to be able to make the same distinction between an actual fold and other cases. So we will have to track folds explicitly. This changesets only focus on making it possible to express fold at the `cleanupnodes` API level. The actual tracking will be implemented later.
author Boris Feld <boris.feld@octobus.net>
date Thu, 27 Sep 2018 13:57:50 -0700
parents 1c3f1491965f
children b55747ca518f
files mercurial/scmutil.py
diffstat 1 files changed, 40 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/scmutil.py	Thu Sep 27 13:54:37 2018 -0700
+++ b/mercurial/scmutil.py	Thu Sep 27 13:57:50 2018 -0700
@@ -875,39 +875,52 @@
 
     # translate mapping's other forms
     if not util.safehasattr(replacements, 'items'):
-        replacements = {n: () for n in replacements}
+        replacements = {(n,): () for n in replacements}
+    else:
+        # upgrading non tuple "source" to tuple ones for BC
+        repls = {}
+        for key, value in replacements.items():
+            if not isinstance(key, tuple):
+                key = (key,)
+            repls[key] = value
+        replacements = repls
 
     # Calculate bookmark movements
     if moves is None:
         moves = {}
     # Unfiltered repo is needed since nodes in replacements might be hidden.
     unfi = repo.unfiltered()
-    for oldnode, newnodes in replacements.items():
-        if oldnode in moves:
-            continue
-        if len(newnodes) > 1:
-            # usually a split, take the one with biggest rev number
-            newnode = next(unfi.set('max(%ln)', newnodes)).node()
-        elif len(newnodes) == 0:
-            # move bookmark backwards
-            roots = list(unfi.set('max((::%n) - %ln)', oldnode,
-                                  list(replacements)))
-            if roots:
-                newnode = roots[0].node()
+    for oldnodes, newnodes in replacements.items():
+        for oldnode in oldnodes:
+            if oldnode in moves:
+                continue
+            if len(newnodes) > 1:
+                # usually a split, take the one with biggest rev number
+                newnode = next(unfi.set('max(%ln)', newnodes)).node()
+            elif len(newnodes) == 0:
+                # move bookmark backwards
+                allreplaced = []
+                for rep in replacements:
+                    allreplaced.extend(rep)
+                roots = list(unfi.set('max((::%n) - %ln)', oldnode,
+                                      allreplaced))
+                if roots:
+                    newnode = roots[0].node()
+                else:
+                    newnode = nullid
             else:
-                newnode = nullid
-        else:
-            newnode = newnodes[0]
-        moves[oldnode] = newnode
+                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)
+        for oldnodes, newnodes in replacements.items():
+            for oldnode in oldnodes:
+                for newnode in newnodes:
+                    precursors.setdefault(newnode, []).append(oldnode)
 
         allnewnodes.sort(key=lambda n: unfi[n].rev())
         newphases = {}
@@ -967,18 +980,19 @@
             # NOTE: the filtering and sorting might belong to createmarkers.
             isobs = unfi.obsstore.successors.__contains__
             torev = unfi.changelog.rev
-            sortfunc = lambda ns: torev(ns[0])
+            sortfunc = lambda ns: torev(ns[0][0])
             rels = []
-            for n, s in sorted(replacements.items(), key=sortfunc):
-                if s or not isobs(n):
-                    rel = (unfi[n], tuple(unfi[m] for m in s))
-                    rels.append(rel)
+            for ns, s in sorted(replacements.items(), key=sortfunc):
+                for n in ns:
+                    if s or not isobs(n):
+                        rel = (unfi[n], tuple(unfi[m] for m in s))
+                        rels.append(rel)
             if rels:
                 obsolete.createmarkers(repo, rels, operation=operation,
                                        metadata=metadata)
         else:
             from . import repair # avoid import cycle
-            tostrip = list(replacements)
+            tostrip = list(n for ns in replacements for n in ns)
             if tostrip:
                 repair.delayedstrip(repo.ui, repo, tostrip, operation,
                                     backup=backup)