Patchwork [4,of,6] scmutil: check collision between added file and directory part of tracked ones

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Nov. 14, 2013, 4:08 p.m.
Message ID <74abacdb51c2bafb9207.1384445280@juju>
Download mbox | patch
Permalink /patch/2943/
State Superseded
Headers show

Comments

Katsunori FUJIWARA - Nov. 14, 2013, 4:08 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1384438902 -32400
#      Thu Nov 14 23:21:42 2013 +0900
# Node ID 74abacdb51c2bafb92077c85b9a9e917ddeb82a9
# Parent  284ca308c2eb1db65021f94c1c9c0a93b5f84650
scmutil: check collision between added file and directory part of tracked ones

Before this patch, "hg add" doesn't show any warning messages, when
newly added file causes case-folding collision against directory part
of already tracked ones.

Such adding causes failure of "hg update" on icasefs system.

This patch checks case-folding collision between newly added file and
directory part of already tracked ones.

If "self._exact()" is False, "lower in self._lowereddirs" should be
examined again, because removed files may cause that it is True: in
this case, "self._lowereddirs" is already re-built in "self._exact()".

If no case-folding collision occurs, this delay avoids cost to
eliminate removed files from "self._loweredfiles" and
"self._lowereddirs".

In fact, "lower in self._lowereddirs" is enough for collision check
itself, because it means case-folding collision between file and
directory.

But adding file in NOT "actual not in self._dirstate.dirs()" case
should be aborted in "dirstate._addpath()" (see also issues #660 and
#332).

To prevent regression, this patch tests also that collision between
newly added file and directory part of already removed one is ignored.

Patch

diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -90,6 +90,7 @@ 
         self._abort = abort
         allfiles = '\0'.join(dirstate._map)
         self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
+        self._lowereddirs = dirs(self._loweredfiles)
         self._existingonly = False
         self._dirstate = dirstate
         # The purpose of _newfiles is so that we don't complain about
@@ -101,11 +102,19 @@ 
         if f in self._newfiles:
             return
         fl = encoding.lower(f)
-        if fl in self._loweredfiles and f not in self._dirstate:
+        if (fl in self._loweredfiles and f not in self._dirstate or
+            self._checkdirs(f, fl)):
             self._oncollision(f)
         self._loweredfiles.add(fl)
+        self._lowereddirs.addpath(fl)
         self._newfiles.add(f)
 
+    def _checkdirs(self, actual, lower):
+        if lower in self._lowereddirs:
+            if self._exact() or lower in self._lowereddirs:
+                return actual not in self._dirstate.dirs()
+        return False
+
     def _oncollision(self, f):
         msg = _('possible case-folding collision for %s') % f
         if self._abort:
@@ -123,6 +132,7 @@ 
             existing.append(f)
         allfiles = '\0'.join(existing)
         self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
+        self._lowereddirs = dirs(self._loweredfiles)
         self._existingonly = True
         return False
 
diff --git a/tests/test-casecollision.t b/tests/test-casecollision.t
--- a/tests/test-casecollision.t
+++ b/tests/test-casecollision.t
@@ -63,6 +63,20 @@ 
   $ hg add H/s
   warning: possible case-folding collision for H/s
 
+  $ mkdir -p I/I1
+  $ touch I/I1/x I/i1
+  $ hg add I/I1/x I/i1
+  warning: possible case-folding collision for I/i1
+  $ mkdir -p I/I2
+  $ touch I/I2/x
+  $ hg add I/I2/x
+
+  $ hg commit -m '#0'
+
+  $ hg remove I/I2/x
+  $ touch I/i2
+  $ hg add I/i2
+
 case changing rename must not warn or abort
 
   $ echo c > c