[PATCH 3 of 3] rfc: merge: detect directory case collisions (issue4892)

Mads Kiilerich mads at kiilerich.com
Wed Oct 14 03:58:49 CDT 2015


# HG changeset patch
# User Mads Kiilerich <madski at unity3d.com>
# Date 1444688185 -7200
#      Tue Oct 13 00:16:25 2015 +0200
# Node ID 8998f6855afb4af90f143ff4b8b7938c8fa09f81
# Parent  3a03a1042b79d1c84b7cfa32defb5508afb35502
rfc: merge: detect directory case collisions (issue4892)

With directory case collisions (without file collisions), all manifest files
_can_ be placed in the file system. It will however mix the content of two
directories up and new files will most likely be added with the wrong casing.

Such situations do thus not have to fail but it might be convenient to avoid
the situation. It could thus just deserve a warning ... and/or it could just as
much make sense to abort on case sensitive file systems too.

An alternative implementation would be to find longest common folded prefix,
backtrack to last /, then see if the unfolded strings are the same. That will
however be hard unless it can be assumed the folded and unfolded strings always
have the same length.

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -443,6 +443,19 @@ def _checkcollision(repo, wmf, actions):
             # the folded prefix matches but actual casing is different
             raise error.Abort(_("case-folding collision between "
                                 "%s and directory of %s") % (lastfull, f))
+        try:
+            # chop last path element from prefix until collision or exception
+            while not fold.startswith(foldprefix):
+                foldprefix = foldprefix[:foldprefix.rindex('/', None, -1) + 1]
+                unfoldprefix = \
+                    unfoldprefix[:unfoldprefix.rindex('/', None, -1) + 1]
+        except ValueError:
+            pass # 'substring not found' because no '/' left and no collision
+        else:
+            if not f.startswith(unfoldprefix):
+                # folded collision and original casing was different
+                raise error.Abort(_("case-folding collision between "
+                                    "directories of %s and %s") % (lastfull, f))
         foldprefix = fold + '/'
         unfoldprefix = f + '/'
         lastfull = f
diff --git a/tests/test-casecollision-merge.t b/tests/test-casecollision-merge.t
--- a/tests/test-casecollision-merge.t
+++ b/tests/test-casecollision-merge.t
@@ -227,8 +227,8 @@ Directory case-folding collision:
   $ hg ci -Aqm2
 
   $ hg merge 0
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  abort: case-folding collision between directories of aA/a and Aa/b
+  [255]
 
   $ cd ..
 


More information about the Mercurial-devel mailing list