merge: return a mergeresult obj from manifestmerge(), calculateupdates() (API)
authorPulkit Goyal <7895pulkit@gmail.com>
Thu, 23 Jul 2020 18:03:14 +0530
changeset 45274 0e18861f96ab
parent 45270 c8655782ef19
child 45275 8e8d513941b4
merge: return a mergeresult obj from manifestmerge(), calculateupdates() (API) Earlier, manifestmerge() and calculateupdates() returns a tuple of three things. I wanted to add one more thing to return value. Introducing a special class which represents results of a merge will help understand better and also ease adding new return values. Differential Revision: https://phab.mercurial-scm.org/D8799
hgext/convert/hg.py
hgext/largefiles/overrides.py
mercurial/merge.py
--- a/hgext/convert/hg.py	Thu Jul 30 22:49:51 2020 -0700
+++ b/hgext/convert/hg.py	Thu Jul 23 18:03:14 2020 +0530
@@ -217,7 +217,7 @@
         """
         anc = [p1ctx.ancestor(p2ctx)]
         # Calculate what files are coming from p2
-        actions, diverge, rename = mergemod.calculateupdates(
+        mresult = mergemod.calculateupdates(
             self.repo,
             p1ctx,
             p2ctx,
@@ -228,7 +228,7 @@
             followcopies=False,
         )
 
-        for file, (action, info, msg) in pycompat.iteritems(actions):
+        for file, (action, info, msg) in pycompat.iteritems(mresult.actions):
             if source.targetfilebelongstosource(file):
                 # If the file belongs to the source repo, ignore the p2
                 # since it will be covered by the existing fileset.
--- a/hgext/largefiles/overrides.py	Thu Jul 30 22:49:51 2020 -0700
+++ b/hgext/largefiles/overrides.py	Thu Jul 23 18:03:14 2020 +0530
@@ -543,16 +543,16 @@
     origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
 ):
     overwrite = force and not branchmerge
-    actions, diverge, renamedelete = origfn(
+    mresult = origfn(
         repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
     )
 
     if overwrite:
-        return actions, diverge, renamedelete
+        return mresult
 
     # Convert to dictionary with filename as key and action as value.
     lfiles = set()
-    for f in actions:
+    for f in mresult.actions:
         splitstandin = lfutil.splitstandin(f)
         if splitstandin is not None and splitstandin in p1:
             lfiles.add(splitstandin)
@@ -561,8 +561,8 @@
 
     for lfile in sorted(lfiles):
         standin = lfutil.standin(lfile)
-        (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
-        (sm, sargs, smsg) = actions.get(standin, (None, None, None))
+        (lm, largs, lmsg) = mresult.actions.get(lfile, (None, None, None))
+        (sm, sargs, smsg) = mresult.actions.get(standin, (None, None, None))
         if sm in (b'g', b'dc') and lm != b'r':
             if sm == b'dc':
                 f1, f2, fa, move, anc = sargs
@@ -578,14 +578,22 @@
                 % lfile
             )
             if repo.ui.promptchoice(usermsg, 0) == 0:  # pick remote largefile
-                actions[lfile] = (b'r', None, b'replaced by standin')
-                actions[standin] = (b'g', sargs, b'replaces standin')
+                mresult.actions[lfile] = (b'r', None, b'replaced by standin')
+                mresult.actions[standin] = (b'g', sargs, b'replaces standin')
             else:  # keep local normal file
-                actions[lfile] = (b'k', None, b'replaces standin')
+                mresult.actions[lfile] = (b'k', None, b'replaces standin')
                 if branchmerge:
-                    actions[standin] = (b'k', None, b'replaced by non-standin')
+                    mresult.actions[standin] = (
+                        b'k',
+                        None,
+                        b'replaced by non-standin',
+                    )
                 else:
-                    actions[standin] = (b'r', None, b'replaced by non-standin')
+                    mresult.actions[standin] = (
+                        b'r',
+                        None,
+                        b'replaced by non-standin',
+                    )
         elif lm in (b'g', b'dc') and sm != b'r':
             if lm == b'dc':
                 f1, f2, fa, move, anc = largs
@@ -603,24 +611,32 @@
             if repo.ui.promptchoice(usermsg, 0) == 0:  # keep local largefile
                 if branchmerge:
                     # largefile can be restored from standin safely
-                    actions[lfile] = (b'k', None, b'replaced by standin')
-                    actions[standin] = (b'k', None, b'replaces standin')
+                    mresult.actions[lfile] = (
+                        b'k',
+                        None,
+                        b'replaced by standin',
+                    )
+                    mresult.actions[standin] = (b'k', None, b'replaces standin')
                 else:
                     # "lfile" should be marked as "removed" without
                     # removal of itself
-                    actions[lfile] = (
+                    mresult.actions[lfile] = (
                         b'lfmr',
                         None,
                         b'forget non-standin largefile',
                     )
 
                     # linear-merge should treat this largefile as 're-added'
-                    actions[standin] = (b'a', None, b'keep standin')
+                    mresult.actions[standin] = (b'a', None, b'keep standin')
             else:  # pick remote normal file
-                actions[lfile] = (b'g', largs, b'replaces standin')
-                actions[standin] = (b'r', None, b'replaced by non-standin')
+                mresult.actions[lfile] = (b'g', largs, b'replaces standin')
+                mresult.actions[standin] = (
+                    b'r',
+                    None,
+                    b'replaced by non-standin',
+                )
 
-    return actions, diverge, renamedelete
+    return mresult
 
 
 @eh.wrapfunction(mergestatemod, b'recordupdates')
--- a/mercurial/merge.py	Thu Jul 30 22:49:51 2020 -0700
+++ b/mercurial/merge.py	Thu Jul 23 18:03:14 2020 +0530
@@ -540,6 +540,41 @@
             )
 
 
+class mergeresult(object):
+    ''''An object representing result of merging manifests.
+
+    It has information about what actions need to be performed on dirstate
+    mapping of divergent renames and other such cases. '''
+
+    def __init__(self, actions, diverge, renamedelete):
+        """
+        actions: dict of filename as keys and action related info as values
+        diverge: mapping of source name -> list of dest name for
+                 divergent renames
+        renamedelete: mapping of source name -> list of destinations for files
+                      deleted on one side and renamed on other.
+        """
+
+        self._actions = actions
+        self._diverge = diverge
+        self._renamedelete = renamedelete
+
+    @property
+    def actions(self):
+        return self._actions
+
+    @property
+    def diverge(self):
+        return self._diverge
+
+    @property
+    def renamedelete(self):
+        return self._renamedelete
+
+    def setactions(self, actions):
+        self._actions = actions
+
+
 def manifestmerge(
     repo,
     wctx,
@@ -559,12 +594,7 @@
     matcher = matcher to filter file lists
     acceptremote = accept the incoming changes without prompting
 
-    Returns:
-
-    actions: dict of filename as keys and action related info as values
-    diverge: mapping of source name -> list of dest name for divergent renames
-    renamedelete: mapping of source name -> list of destinations for files
-                  deleted on one side and renamed on other.
+    Returns an object of mergeresult class
     """
     if matcher is not None and matcher.always():
         matcher = None
@@ -845,7 +875,7 @@
     renamedelete = branch_copies1.renamedelete
     renamedelete.update(branch_copies2.renamedelete)
 
-    return actions, diverge, renamedelete
+    return mergeresult(actions, diverge, renamedelete)
 
 
 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
@@ -891,13 +921,13 @@
 
     Also filters out actions which are unrequired if repository is sparse.
 
-    Returns same 3 element tuple as manifestmerge().
+    Returns mergeresult object same as manifestmerge().
     """
     # Avoid cycle.
     from . import sparse
 
     if len(ancestors) == 1:  # default
-        actions, diverge, renamedelete = manifestmerge(
+        mresult = manifestmerge(
             repo,
             wctx,
             mctx,
@@ -908,7 +938,7 @@
             acceptremote,
             followcopies,
         )
-        _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
+        _checkunknownfiles(repo, wctx, mctx, force, mresult.actions, mergeforce)
 
     else:  # only when merge.preferancestor=* - the default
         repo.ui.note(
@@ -927,7 +957,7 @@
         diverge, renamedelete = None, None
         for ancestor in ancestors:
             repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
-            actions, diverge1, renamedelete1 = manifestmerge(
+            mresult1 = manifestmerge(
                 repo,
                 wctx,
                 mctx,
@@ -939,16 +969,20 @@
                 followcopies,
                 forcefulldiff=True,
             )
-            _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
+            _checkunknownfiles(
+                repo, wctx, mctx, force, mresult1.actions, mergeforce
+            )
 
             # Track the shortest set of warning on the theory that bid
             # merge will correctly incorporate more information
-            if diverge is None or len(diverge1) < len(diverge):
-                diverge = diverge1
-            if renamedelete is None or len(renamedelete) < len(renamedelete1):
-                renamedelete = renamedelete1
+            if diverge is None or len(mresult1.diverge) < len(diverge):
+                diverge = mresult1.diverge
+            if renamedelete is None or len(renamedelete) < len(
+                mresult1.renamedelete
+            ):
+                renamedelete = mresult1.renamedelete
 
-            for f, a in sorted(pycompat.iteritems(actions)):
+            for f, a in sorted(pycompat.iteritems(mresult1.actions)):
                 m, args, msg = a
                 if m == mergestatemod.ACTION_GET_OTHER_AND_STORE:
                     m = mergestatemod.ACTION_GET
@@ -1000,17 +1034,19 @@
             actions[f] = l[0]
             continue
         repo.ui.note(_(b'end of auction\n\n'))
+        mresult = mergeresult(actions, diverge, renamedelete)
 
     if wctx.rev() is None:
         fractions = _forgetremoved(wctx, mctx, branchmerge)
-        actions.update(fractions)
+        mresult.actions.update(fractions)
 
     prunedactions = sparse.filterupdatesactions(
-        repo, wctx, mctx, branchmerge, actions
+        repo, wctx, mctx, branchmerge, mresult.actions
     )
-    _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
+    _resolvetrivial(repo, wctx, mctx, ancestors[0], mresult.actions)
 
-    return prunedactions, diverge, renamedelete
+    mresult.setactions(prunedactions)
+    return mresult
 
 
 def _getcwd():
@@ -1734,7 +1770,7 @@
             followcopies = False
 
         ### calculate phase
-        actionbyfile, diverge, renamedelete = calculateupdates(
+        mresult = calculateupdates(
             repo,
             wc,
             p2,
@@ -1747,6 +1783,8 @@
             mergeforce=mergeforce,
         )
 
+        actionbyfile = mresult.actions
+
         if updatecheck == UPDATECHECK_NO_CONFLICT:
             for f, (m, args, msg) in pycompat.iteritems(actionbyfile):
                 if m not in (
@@ -1840,7 +1878,7 @@
                 _checkcollision(repo, wc.manifest(), actions)
 
         # divergent renames
-        for f, fl in sorted(pycompat.iteritems(diverge)):
+        for f, fl in sorted(pycompat.iteritems(mresult.diverge)):
             repo.ui.warn(
                 _(
                     b"note: possible conflict - %s was renamed "
@@ -1852,7 +1890,7 @@
                 repo.ui.warn(b" %s\n" % nf)
 
         # rename and delete
-        for f, fl in sorted(pycompat.iteritems(renamedelete)):
+        for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)):
             repo.ui.warn(
                 _(
                     b"note: possible conflict - %s was deleted "