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

login
register
mail settings
Submitter Mads Kiilerich
Date Oct. 14, 2015, 8:58 a.m.
Message ID <8998f6855afb4af90f14.1444813129@localhost.localdomain>
Download mbox | patch
Permalink /patch/11043/
State Deferred
Headers show

Comments

Mads Kiilerich - Oct. 14, 2015, 8:58 a.m.
# HG changeset patch
# User Mads Kiilerich <madski@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.
Matt Mackall - Oct. 17, 2015, 6:57 p.m.
On Wed, 2015-10-14 at 10:58 +0200, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@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)

Let's discuss this at the sprint. Since it's a bug-fix we can consider
it post-freeze.

Patch

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 ..