Patchwork D5991: copies: do copy tracing based on ctx.p[12]copies() if configured

login
register
mail settings
Submitter phabricator
Date Feb. 21, 2019, 2:35 a.m.
Message ID <9273200574c334d3bbe83f57e05f5f42@localhost.localdomain>
Download mbox | patch
Permalink /patch/38850/
State Not Applicable
Headers show

Comments

phabricator - Feb. 21, 2019, 2:35 a.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG2d562af6014b: copies: do copy tracing based on ctx.p[12]copies() if configured (authored by martinvonz, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D5991?vs=14161&id=14171

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

AFFECTED FILES
  mercurial/configitems.py
  mercurial/copies.py
  tests/test-copies.t

CHANGE DETAILS




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

Patch

diff --git a/tests/test-copies.t b/tests/test-copies.t
--- a/tests/test-copies.t
+++ b/tests/test-copies.t
@@ -1,9 +1,17 @@ 
+#testcases filelog compatibility
 
   $ cat >> $HGRCPATH << EOF
   > [alias]
   > l = log -G -T '{rev} {desc}\n{files}\n'
   > EOF
 
+#if compatibility
+  $ cat >> $HGRCPATH << EOF
+  > [experimental]
+  > copies.read-from = compatibility
+  > EOF
+#endif
+
   $ REPONUM=0
   $ newrepo() {
   >     cd $TESTTMP
@@ -338,7 +346,7 @@ 
   $ hg debugpathcopies 1 2
   x -> z
   $ hg debugpathcopies 0 2
-  x -> z
+  x -> z (filelog !)
 
 Copy file that exists on both sides of the merge, different content
   $ newrepo
@@ -476,7 +484,8 @@ 
   $ hg debugpathcopies 1 4
   $ hg debugpathcopies 2 4
   $ hg debugpathcopies 0 4
-  x -> z
+  x -> z (filelog !)
+  y -> z (compatibility !)
   $ hg debugpathcopies 1 5
   $ hg debugpathcopies 2 5
   $ hg debugpathcopies 0 5
diff --git a/mercurial/copies.py b/mercurial/copies.py
--- a/mercurial/copies.py
+++ b/mercurial/copies.py
@@ -166,6 +166,10 @@ 
     # files might have to be traced back to the fctx parent of the last
     # one-side-only changeset, but not further back than that
     repo = a._repo
+
+    if repo.ui.config('experimental', 'copies.read-from') == 'compatibility':
+        return _changesetforwardcopies(a, b, match)
+
     debug = repo.ui.debugflag and repo.ui.configbool('devel', 'debug.copies')
     dbg = repo.ui.debug
     if debug:
@@ -216,6 +220,76 @@ 
                 % (util.timer() - start))
     return cm
 
+def _changesetforwardcopies(a, b, match):
+    if a.rev() == node.nullrev:
+        return {}
+
+    repo = a.repo()
+    children = {}
+    cl = repo.changelog
+    missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
+    for r in missingrevs:
+        for p in cl.parentrevs(r):
+            if p == node.nullrev:
+                continue
+            if p not in children:
+                children[p] = [r]
+            else:
+                children[p].append(r)
+
+    roots = set(children) - set(missingrevs)
+    # 'work' contains 3-tuples of a (revision number, parent number, copies).
+    # The parent number is only used for knowing which parent the copies dict
+    # came from.
+    work = [(r, 1, {}) for r in roots]
+    heapq.heapify(work)
+    while work:
+        r, i1, copies1 = heapq.heappop(work)
+        if work and work[0][0] == r:
+            # We are tracing copies from both parents
+            r, i2, copies2 = heapq.heappop(work)
+            copies = {}
+            ctx = repo[r]
+            p1man, p2man = ctx.p1().manifest(), ctx.p2().manifest()
+            allcopies = set(copies1) | set(copies2)
+            # TODO: perhaps this filtering should be done as long as ctx
+            # is merge, whether or not we're tracing from both parent.
+            for dst in allcopies:
+                if not match(dst):
+                    continue
+                if dst not in copies2:
+                    # Copied on p1 side: mark as copy from p1 side if it didn't
+                    # already exist on p2 side
+                    if dst not in p2man:
+                        copies[dst] = copies1[dst]
+                elif dst not in copies1:
+                    # Copied on p2 side: mark as copy from p2 side if it didn't
+                    # already exist on p1 side
+                    if dst not in p1man:
+                        copies[dst] = copies2[dst]
+                else:
+                    # Copied on both sides: mark as copy from p1 side
+                    copies[dst] = copies1[dst]
+        else:
+            copies = copies1
+        if r == b.rev():
+            return copies
+        for c in children[r]:
+            childctx = repo[c]
+            if r == childctx.p1().rev():
+                parent = 1
+                childcopies = childctx.p1copies()
+            else:
+                assert r == childctx.p2().rev()
+                parent = 2
+                childcopies = childctx.p2copies()
+            if not match.always():
+                childcopies = {dst: src for dst, src in childcopies.items()
+                               if match(dst)}
+            childcopies = _chain(a, childctx, copies, childcopies)
+            heapq.heappush(work, (c, parent, childcopies))
+    assert False
+
 def _forwardcopies(a, b, match=None):
     """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
 
diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -482,6 +482,9 @@ 
 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
     default=100,
 )
+coreconfigitem('experimental', 'copies.read-from',
+    default="filelog-only",
+)
 coreconfigitem('experimental', 'crecordtest',
     default=None,
 )