[PATCH STABLE] icasefs: make case-folding collision detection as deletion aware (issue3648)

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Sun Oct 28 11:34:47 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1351439226 -32400
# Branch stable
# Node ID aa0ac74e7962d04be0150ffc69a31e150052b576
# Parent  7b0b1da49f15c2aa20a8e7abe8fa1be26191e4fb
icasefs: make case-folding collision detection as deletion aware (issue3648)

Before this patch, case-folding collision is checked simply between
manifests of each merged revisions.

So, files may be considered as colliding each other, even though one
of them is already deleted on one of merged branches: in such case,
merge causes deleting it, so case-folding collision doesn't occur.

This patch checks whether both of files colliding each other still
remain after merge or not, and ignores collision if at least one of
them is deleted by merge.

In the case that one of colliding files is deleted on one of merged
branches and changed on another, file is considered to still remain
after merge, even though it may be deleted by merge, if "deleting" of
it is chosen in "manifestmerge()".

This avoids fail to merge by case-folding collisions after choices
from "changing" and "deleting" of files.

"added locally (linear merge specific)" and "added locally" code paths
in "_remains()" should be tested in existing case-folding collision
tests.

diff -r 7b0b1da49f15 -r aa0ac74e7962 mercurial/merge.py
--- a/mercurial/merge.py	Tue Oct 23 21:25:22 2012 -0700
+++ b/mercurial/merge.py	Mon Oct 29 00:47:06 2012 +0900
@@ -99,7 +99,26 @@
         raise util.Abort(_("untracked files in working directory differ "
                            "from files in requested revision"))
 
-def _checkcollision(mctx, wctx):
+def _remains(f, m, ma, workingctx=False):
+    """check whether specified file remains after merge.
+
+    It is assumed that specified file is not contained in the manifest
+    of the other context.
+    """
+    if f in ma:
+        n = m[f]
+        if n != ma[f]:
+            return True # because it is changed locally
+            # even though it doesn't remain, if "remote deleted" is
+            # chosen in manifestmerge()
+        elif workingctx and n[20:] == "a":
+            return True # because it is added locally (linear merge specific)
+        else:
+            return False # because it is removed remotely
+    else:
+        return True # because it is added locally
+
+def _checkcollision(mctx, extractxs):
     "check for case folding collisions in the destination context"
     folded = {}
     for fn in mctx:
@@ -109,7 +128,8 @@
                              % (fn, folded[fold]))
         folded[fold] = fn
 
-    if wctx:
+    if extractxs:
+        wctx, actx = extractxs
         # class to delay looking up copy mapping
         class pathcopies(object):
             @util.propertycache
@@ -121,7 +141,9 @@
         for fn in wctx:
             fold = util.normcase(fn)
             mfn = folded.get(fold, None)
-            if mfn and mfn != fn and pc.map.get(mfn) != fn:
+            if (mfn and mfn != fn and pc.map.get(mfn) != fn and
+                _remains(fn, wctx.manifest(), actx.manifest(), True) and
+                _remains(mfn, mctx.manifest(), actx.manifest())):
                 raise util.Abort(_("case-folding collision between %s and %s")
                                  % (mfn, fn))
 
@@ -595,7 +617,7 @@
                 (force or not wc.dirty(missing=True, branch=False))):
                 _checkcollision(p2, None)
             else:
-                _checkcollision(p2, wc)
+                _checkcollision(p2, (wc, pa))
         if not force:
             _checkunknown(repo, wc, p2)
         action += _forgetremoved(wc, p2, branchmerge)
diff -r 7b0b1da49f15 -r aa0ac74e7962 tests/test-casecollision-merge.t
--- a/tests/test-casecollision-merge.t	Tue Oct 23 21:25:22 2012 -0700
+++ b/tests/test-casecollision-merge.t	Mon Oct 29 00:47:06 2012 +0900
@@ -94,6 +94,77 @@
 
   $ cd ..
 
+test for deletion awareness of case-folding collision check: issue3648
+
+  $ hg init issue3648
+  $ cd issue3648
+
+  $ mkdir a
+  $ echo a > a/a
+  $ hg commit -Am '#0'
+  adding a/a
+  $ hg remove a
+  removing a/a
+  $ hg commit -m '#1'
+  $ mkdir A
+  $ echo A > A/a
+  $ hg commit -Am '#2'
+  adding A/a
+  $ hg update 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo x > x
+  $ hg commit -Am '#3'
+  adding x
+  created new head
+
+(1) manifest-base case-folding collision should be ignored, because
+one of colliding files is removed on remote branch
+
+  $ hg merge
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg status -A
+  M A/a
+  R a/a
+  C x
+
+(2) manifest-base case-folding collision should be ignored, because
+one of colliding files is removed on local branch
+
+  $ hg update -C 2
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg merge
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg status -A
+  M x
+  C A/a
+
+(3) changing the file removed on another branch should cause
+case-folding collision, because changing may prevent the file from
+being removed by merge.
+
+  $ hg update -C 3
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo a >> a/a
+  $ hg commit -m '#4'
+
+  $ hg merge 2
+  abort: case-folding collision between A/a and a/a
+  [255]
+  $ hg status -A
+  C a/a
+  C x
+
+  $ hg update -C 2
+  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg merge 4
+  abort: case-folding collision between a/a and A/a
+  [255]
+  $ hg status -A
+  C A/a
+
+  $ cd ..
 
 ################################
 test for linear updates


More information about the Mercurial-devel mailing list