[PATCH 2 of 5 experimental] merge: do not assume that the selected ancestor is the common ancestor

Gilles Moris gilles.moris at free.fr
Tue Sep 21 14:18:18 CDT 2010


# HG changeset patch
# User Gilles Moris <gilles.moris at free.fr>
# Date 1284559372 -7200
# Node ID a3f8841f8a0d1b828d2d62188c487edd6e15bdb1
# Parent  073d7c08c4875a99982bf988145acfedb08068ee
merge: do not assume that the selected ancestor is the common ancestor

In particular, it raises the backward merge detection logic up in the merge
routine instead of doing it independantly in each subroutine.
Note the copies/rename detection will now be called for backward merge,
tough the copies module still doesn't know how to cope with such situation.

I have replaced the ancestor detection of manifestmerge by an algorithm
that tries to find the filecontext related to both side filecontext.
I tried to pay attention to the performances which should be better with
this patch but assumes that if we find a file with the same name in the
ancestor manifest, this is the good one.

I have done the following at some point of time but remove it. Necessary ?
In copies.py, I use the revision returned by _findlimit() for the related() call
instead of the revision of the (assumed common) ancestor.

diff -r 073d7c08c487 -r a3f8841f8a0d mercurial/merge.py
--- a/mercurial/merge.py	Wed Sep 15 15:50:17 2010 +0200
+++ b/mercurial/merge.py	Wed Sep 15 16:02:52 2010 +0200
@@ -8,7 +8,7 @@
 from node import nullid, nullrev, hex, bin
 from i18n import _
 import util, filemerge, copies, subrepo
-import errno, os, shutil
+import errno, heapq, os, shutil
 
 class mergestate(object):
     '''track 3-way merge state of individual files'''
@@ -152,8 +152,6 @@
 
     if overwrite:
         pa = p1
-    elif pa == p2: # backwards
-        pa = p1.p1()
     elif pa and repo.ui.configbool("merge", "followcopies", True):
         dirs = repo.ui.configbool("merge", "followdirs", True)
         copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
@@ -259,6 +257,44 @@
     action.sort(key=actionkey)
     substate = wctx.substate # prime
 
+    # derived from copies.findlimit()
+    def fancestor(fc1, f1, fc2, f2):
+        # shortcuts
+        if f1 in actx:
+            return actx[f1]
+        elif f2 in actx:
+            return actx[f2]
+
+        res = None
+        arev = -actx.rev()
+        side = {fc1: -1, fc2: 1}
+        visit = []
+        if fc1.rev() is None: # working directory
+            heapq.heappush(visit, (-len(repo.changelog), fc1))
+        else:
+            heapq.heappush(visit, (-fc1.rev(), fc1))
+        if fc2.rev() is None: # working directory
+            heapq.heappush(visit, (-len(repo.changelog), fc2))
+        else:
+            heapq.heappush(visit, (-fc2.rev(), fc2))
+
+        while visit:
+            r, fc = heapq.heappop(visit)
+            if r == arev and side[fc] == 0:
+                return fc # exact match
+
+            for p in fc.parents():
+                if p not in side:
+                    side[p] = side[fc]
+                    heapq.heappush(visit, (-p.rev(), p))
+                elif side[p] and side[p] != side[fc]:
+                    side[p] = 0
+
+        if not res:
+            res = repo.filectx(f1, fileid=nullrev)
+
+        return res
+
     # prescan for merges
     u = repo.ui
     for a in action:
@@ -270,12 +306,7 @@
             repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
             fcl = wctx[f]
             fco = mctx[f2]
-            if mctx == actx: # backwards, use working dir parent as ancestor
-                fca = fcl.parents()[0]
-            else:
-                fca = fcl.ancestor(fco, actx)
-            if not fca:
-                fca = repo.filectx(f, fileid=nullrev)
+            fca = fancestor(fcl, f, fco, f2)
             ms.add(fcl, fco, fca, fd, flags)
             if f != fd and move:
                 moves.append(f)
@@ -308,7 +339,7 @@
             removed += 1
         elif m == "m": # merge
             if f == '.hgsubstate': # subrepo states need updating
-                subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
+                subrepo.submerge(repo, wctx, mctx, actx)
                 continue
             f2, fd, flags, move = a[2:]
             r = ms.resolve(fd, wctx, mctx)
@@ -490,8 +521,10 @@
                 raise util.Abort(_("outstanding uncommitted changes "
                                    "(use 'hg status' to list changes)"))
         elif not overwrite:
-            if pa == p1 or pa == p2: # linear
+            if pa == p1: # linear forward
                 pass # all good
+            elif pa == p2: # linear backward
+                pa = p1
             elif wc.files() or wc.deleted():
                 raise util.Abort(_("crosses branches (use 'hg merge' to merge "
                                  "or use 'hg update -C' to discard changes)"))
diff -r 073d7c08c487 -r a3f8841f8a0d tests/test-up-local-change.t
--- a/tests/test-up-local-change.t	Wed Sep 15 15:50:17 2010 +0200
+++ b/tests/test-up-local-change.t	Wed Sep 15 16:02:52 2010 +0200
@@ -65,6 +65,7 @@
   summary:     2
   
   $ hg --debug up 0
+    searching for copies back to rev 1
   resolving manifests
    overwrite False partial False
    local 1e71731e6fbb+ ancestor 1e71731e6fbb remote c19d34741b0a


More information about the Mercurial-devel mailing list