Patchwork D7990: merge: start using the per-side copy dicts

login
register
mail settings
Submitter phabricator
Date Jan. 24, 2020, 10:02 p.m.
Message ID <differential-rev-PHID-DREV-mwsiyeyxefkhgpkhgor7-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/44624/
State Superseded
Headers show

Comments

phabricator - Jan. 24, 2020, 10:02 p.m.
martinvonz created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The point of this patch is mostly to clarify `manifestmerge()`. I find
  it much easier to reason about now.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/copies.py
  mercurial/merge.py

CHANGE DETAILS




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

Patch

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -1262,13 +1262,11 @@ 
         for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)
     ]
 
-    copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
+    branch_copies1 = copies.branch_copies()
+    branch_copies2 = copies.branch_copies()
+    diverge = {}
     if followcopies:
-        branch_copies, diverge = copies.mergecopies(repo, wctx, p2, pa)
-        copy = branch_copies.copy
-        renamedelete = branch_copies.renamedelete
-        dirmove = branch_copies.dirmove
-        movewithdir = branch_copies.movewithdir
+        branch_copies1, branch_copies2, diverge = copies.mergecopies(repo, wctx, p2, pa)
 
     boolbm = pycompat.bytestr(bool(branchmerge))
     boolf = pycompat.bytestr(bool(force))
@@ -1280,8 +1278,10 @@ 
     repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
 
     m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
-    copied = set(copy.values())
-    copied.update(movewithdir.values())
+    copied1 = set(branch_copies1.copy.values())
+    copied1.update(branch_copies1.movewithdir.values())
+    copied2 = set(branch_copies2.copy.values())
+    copied2.update(branch_copies2.movewithdir.values())
 
     if b'.hgsubstate' in m1 and wctx.rev() is None:
         # Check whether sub state is modified, and overwrite the manifest
@@ -1301,10 +1301,10 @@ 
         relevantfiles = set(ma.diff(m2).keys())
 
         # For copied and moved files, we need to add the source file too.
-        for copykey, copyvalue in pycompat.iteritems(copy):
+        for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy):
             if copyvalue in relevantfiles:
                 relevantfiles.add(copykey)
-        for movedirkey in movewithdir:
+        for movedirkey in branch_copies1.movewithdir:
             relevantfiles.add(movedirkey)
         filesmatcher = scmutil.matchfiles(repo, relevantfiles)
         matcher = matchmod.intersectmatchers(matcher, filesmatcher)
@@ -1315,7 +1315,9 @@ 
     for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff):
         if n1 and n2:  # file exists on both local and remote side
             if f not in ma:
-                fa = copy.get(f, None)
+                # TODO: what if they're renamed from different sources?
+                fa = (branch_copies1.copy.get(f, None) or
+                      branch_copies2.copy.get(f, None))
                 if fa is not None:
                     actions[f] = (
                         ACTION_MERGE,
@@ -1358,10 +1360,10 @@ 
                         b'versions differ',
                     )
         elif n1:  # file exists only on local side
-            if f in copied:
+            if f in copied2:
                 pass  # we'll deal with it on m2 side
-            elif f in movewithdir:  # directory rename, move local
-                f2 = movewithdir[f]
+            elif f in branch_copies1.movewithdir:  # directory rename, move local
+                f2 = branch_copies1.movewithdir[f]
                 if f2 in m2:
                     actions[f2] = (
                         ACTION_MERGE,
@@ -1374,8 +1376,8 @@ 
                         (f, fl1),
                         b'remote directory rename - move from %s' % f,
                     )
-            elif f in copy:
-                f2 = copy[f]
+            elif f in branch_copies1.copy:
+                f2 = branch_copies1.copy[f]
                 actions[f] = (
                     ACTION_MERGE,
                     (f, f2, f2, False, pa.node()),
@@ -1399,10 +1401,10 @@ 
                 else:
                     actions[f] = (ACTION_REMOVE, None, b'other deleted')
         elif n2:  # file exists only on remote side
-            if f in copied:
+            if f in copied1:
                 pass  # we'll deal with it on m1 side
-            elif f in movewithdir:
-                f2 = movewithdir[f]
+            elif f in branch_copies2.movewithdir:
+                f2 = branch_copies2.movewithdir[f]
                 if f2 in m1:
                     actions[f2] = (
                         ACTION_MERGE,
@@ -1415,8 +1417,8 @@ 
                         (f, fl2),
                         b'local directory rename - get from %s' % f,
                     )
-            elif f in copy:
-                f2 = copy[f]
+            elif f in branch_copies2.copy:
+                f2 = branch_copies2.copy[f]
                 if f2 in m2:
                     actions[f] = (
                         ACTION_MERGE,
@@ -1453,10 +1455,10 @@ 
                     )
             elif n2 != ma[f]:
                 df = None
-                for d in dirmove:
+                for d in branch_copies1.dirmove:
                     if f.startswith(d):
                         # new file added in a directory that was moved
-                        df = dirmove[d] + f[len(d) :]
+                        df = branch_copies1.dirmove[d] + f[len(d):]
                         break
                 if df is not None and df in m1:
                     actions[df] = (
@@ -1483,6 +1485,9 @@ 
         # Updates "actions" in place
         _filternarrowactions(narrowmatch, branchmerge, actions)
 
+    renamedelete = branch_copies1.renamedelete
+    renamedelete.update(branch_copies2.renamedelete)
+
     return actions, diverge, renamedelete
 
 
diff --git a/mercurial/copies.py b/mercurial/copies.py
--- a/mercurial/copies.py
+++ b/mercurial/copies.py
@@ -463,19 +463,19 @@ 
     """
     # avoid silly behavior for update from empty dir
     if not c1 or not c2 or c1 == c2:
-        return branch_copies(), {}
+        return branch_copies(), branch_copies(), {}
 
     narrowmatch = c1.repo().narrowmatch()
 
     # avoid silly behavior for parent -> working dir
     if c2.node() is None and c1.node() == repo.dirstate.p1():
-        return branch_copies(_dirstatecopies(repo, narrowmatch)), {}
+        return branch_copies(_dirstatecopies(repo, narrowmatch)), branch_copies(), {}
 
     copytracing = repo.ui.config(b'experimental', b'copytrace')
     if stringutil.parsebool(copytracing) is False:
         # stringutil.parsebool() returns None when it is unable to parse the
         # value, so we should rely on making sure copytracing is on such cases
-        return branch_copies(), {}
+        return branch_copies(), branch_copies(), {}
 
     if usechangesetcentricalgo(repo):
         # The heuristics don't make sense when we need changeset-centric algos
@@ -576,7 +576,7 @@ 
     copies2 = pathcopies(base, c2)
 
     if not (copies1 or copies2):
-        return branch_copies(), {}
+        return branch_copies(), branch_copies(), {}
 
     inversecopies1 = {}
     inversecopies2 = {}
@@ -679,12 +679,10 @@ 
     dirmove1, movewithdir2 = _dir_renames(repo, c1, copy1, copies1, u2)
     dirmove2, movewithdir1 = _dir_renames(repo, c2, copy2, copies2, u1)
 
-    copy1.update(copy2)
-    renamedelete1.update(renamedelete2)
-    movewithdir1.update(movewithdir2)
-    dirmove1.update(dirmove2)
+    branch_copies1 = branch_copies(copy1, renamedelete1, dirmove1, movewithdir1)
+    branch_copies2 = branch_copies(copy2, renamedelete2, dirmove2, movewithdir2)
 
-    return branch_copies(copy1, renamedelete1, dirmove1, movewithdir1),  diverge
+    return branch_copies1, branch_copies2, diverge
 
 
 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
@@ -782,8 +780,6 @@ 
     if c2.rev() is None:
         c2 = c2.p1()
 
-    copies = {}
-
     changedfiles = set()
     m1 = c1.manifest()
     if not repo.revs(b'%d::%d', base.rev(), c2.rev()):
@@ -803,10 +799,11 @@ 
         changedfiles.update(ctx.files())
         ctx = ctx.p1()
 
+    copies2 = {}
     cp = _forwardcopies(base, c2)
     for dst, src in pycompat.iteritems(cp):
         if src in m1:
-            copies[dst] = src
+            copies2[dst] = src
 
     # file is missing if it isn't present in the destination, but is present in
     # the base and present in the source.
@@ -815,6 +812,7 @@ 
     filt = lambda f: f not in m1 and f in base and f in c2
     missingfiles = [f for f in changedfiles if filt(f)]
 
+    copies1 = {}
     if missingfiles:
         basenametofilename = collections.defaultdict(list)
         dirnametofilename = collections.defaultdict(list)
@@ -856,10 +854,10 @@ 
                     # if there are a few related copies then we'll merge
                     # changes into all of them. This matches the behaviour
                     # of upstream copytracing
-                    copies[candidate] = f
+                    copies1[candidate] = f
 
 
-    return branch_copies(copies), {}
+    return branch_copies(copies1), branch_copies(copies2), {}
 
 
 def _related(f1, f2):