[PATCH STABLE] icasefs: abort update, if added/removed files in working causes case folding collision

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Wed Apr 4 04:35:05 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1333530684 -32400
# Branch stable
# Node ID a2faca19606fece6bab91d0fa7f9b421b2d98cc9
# Parent  4d875bb546dc03db33630f5388d7e04939c386a0
icasefs: abort update, if added/removed files in working causes case folding collision

in linear merge by 'hg update' on case insensitive filesystem, case
folding collision problem may occur in steps below.

    1. add or remove file 'a' in working directory
    2. execute 'hg update' which causes linear merge with the revision
       which has not 'a' but 'A'
    3 'hg update' can update 'A' by contents of one in target revision, so
      - it is different from what you store into 'a', or
      - it also works as 'a', which you want to remove, in working context

fixing in d550168f11ce was not enough, because current merge
implementation does not check file statuses in working context in
linear merge.

this patch adds examination of working context for linear merge to
abort 'hg update' in such cases.

diff -r 4d875bb546dc -r a2faca19606f mercurial/merge.py
--- a/mercurial/merge.py	Tue Apr 03 22:01:28 2012 +0200
+++ b/mercurial/merge.py	Wed Apr 04 18:11:24 2012 +0900
@@ -101,7 +101,7 @@
         raise util.Abort(_("untracked files in working directory differ "
                            "from files in requested revision"))
 
-def _checkcollision(mctx, wctx):
+def _checkcollision(mctx, additionals):
     "check for case folding collisions in the destination context"
     folded = {}
     for fn in mctx:
@@ -111,13 +111,12 @@
                              % (fn, folded[fold]))
         folded[fold] = fn
 
-    if wctx:
-        for fn in wctx:
-            fold = util.normcase(fn)
-            mfn = folded.get(fold, None)
-            if mfn and (mfn != fn):
-                raise util.Abort(_("case-folding collision between %s and %s")
-                                 % (mfn, fn))
+    for fn in additionals:
+        fold = util.normcase(fn)
+        mfn = folded.get(fold, None)
+        if mfn and (mfn != fn):
+            raise util.Abort(_("case-folding collision between %s and %s")
+                             % (mfn, fn))
 
 def _forgetremoved(wctx, mctx, branchmerge):
     """
@@ -568,7 +567,15 @@
         if not force:
             _checkunknown(wc, p2, folding)
         if folding:
-            _checkcollision(p2, branchmerge and p1)
+            if branchmerge:
+                _checkcollision(p2, p1)
+            else:
+                dirstate = repo.dirstate
+                def additionals():
+                    for f in dirstate:
+                        if dirstate[f] in 'ra':
+                            yield f
+                _checkcollision(p2, additionals())
         action += _forgetremoved(wc, p2, branchmerge)
         action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
 
diff -r 4d875bb546dc -r a2faca19606f tests/test-casecollision-merge.t
--- a/tests/test-casecollision-merge.t	Tue Apr 03 22:01:28 2012 +0200
+++ b/tests/test-casecollision-merge.t	Wed Apr 04 18:11:24 2012 +0900
@@ -107,3 +107,80 @@
   this is line 2
   this is line 3
   this is added line
+
+  $ cd ..
+
+case-folding collision between newly added file in working directory
+and target context:
+
+  $ hg init repo3
+  $ cd repo3
+
+  $ echo 'this is a' > a
+  $ hg add a
+  $ hg commit -m '#0'
+  $ echo 'this is b' > b
+  $ hg add b
+  $ hg commit -m '#1'
+
+  $ hg update 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo 'this is not b, but B' > B
+  $ hg add B
+  $ hg status -A
+  A B
+  C a
+
+  $ hg update
+  abort: case-folding collision between b and B
+  [255]
+  $ hg parents --template '{rev}\n'
+  0
+  $ hg status -A
+  A B
+  C a
+  $ cat B
+  this is not b, but B
+
+  $ cd ..
+
+case-folding collision between removed file in working directory and
+renamed file in target context:
+
+  $ hg init repo4
+  $ cd repo4
+
+  $ echo 'this is a' > a
+  $ hg add a
+  $ hg commit -m '#0' -u none -d '0 0'
+
+  $ hg import --bypass --exact - <<EOF
+  > # HG changeset patch
+  > # User none
+  > # Date 0 0
+  > # Node ID 17c7d8d89dddd3e683880e0992535c6ee881af9c
+  > # Parent  1007937d73270863665190364d97aa933bd2498e
+  > #1
+  > 
+  > diff --git a/a b/A
+  > rename from a
+  > rename to A
+  > EOF
+  applying patch from stdin
+
+  $ hg update 0
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg remove a
+  $ hg status -A
+  R a
+
+  $ hg update
+  abort: case-folding collision between A and a
+  [255]
+  $ hg parents --template '{rev}\n'
+  0
+  $ hg status -A
+  R a
+
+  $ cd ..


More information about the Mercurial-devel mailing list