Patchwork [5,of,6] scmutil: check collision between tracked files and directory part of added one

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Nov. 14, 2013, 4:08 p.m.
Message ID <d6fdf886584165c6515f.1384445281@juju>
Download mbox | patch
Permalink /patch/2942/
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 d6fdf886584165c6515f0d8b0a8d69721581488e
# Parent  74abacdb51c2bafb92077c85b9a9e917ddeb82a9
scmutil: check collision between tracked files and directory part of added one

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

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

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

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

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

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

But adding file in NOT "dirstate[actual] in 'r?'" case should be
aborted in "dirstate._addpath()" (see also issues #660 and #332).

For collision detection between files, only "dirstate[actual] in '?'"
(= "actual not in dirstate") case is treated as collision, because
reusing name of already removed file is treated as reverting of it,
even though it also reverts collision again.

But for collision detection between directories, "dirstate[actual] in
'r'" case should be also treated as collision: it means that there is
at least one colliding file already tracked (or newly added), because
"self._loweredfiles" doesn't contain removed files (at this point).

This patch tests collision detection by multiple path patterns
corresponded to detections in the first, the middle and the last of
"while dparts" loop.

Patch

diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -97,6 +97,7 @@ 
         # case collisions if someone were to call this object with the
         # same filename twice.
         self._newfiles = set()
+        self._newdirs = dirs([])
 
     def __call__(self, f):
         if f in self._newfiles:
@@ -105,9 +106,30 @@ 
         if (fl in self._loweredfiles and f not in self._dirstate or
             self._checkdirs(f, fl)):
             self._oncollision(f)
+        else:
+            dparts = f.split('/')
+            dparts.pop()
+            dlparts = fl.split('/')
+            while dparts:
+                dlparts.pop()
+                d = '/'.join(dparts)
+                if d in self._newdirs:
+                    break
+                dl = '/'.join(dlparts)
+                if self._checkfiles(d, dl):
+                    self._oncollision(f)
+                    break
+                dparts.pop()
         self._loweredfiles.add(fl)
         self._lowereddirs.addpath(fl)
         self._newfiles.add(f)
+        self._newdirs.addpath(f)
+
+    def _checkfiles(self, actual, lower):
+        if lower in self._loweredfiles:
+            if self._exact() or lower in self._loweredfiles:
+                return self._dirstate[actual] in 'r?'
+        return False
 
     def _checkdirs(self, actual, lower):
         if lower in self._lowereddirs:
diff --git a/tests/test-casecollision.t b/tests/test-casecollision.t
--- a/tests/test-casecollision.t
+++ b/tests/test-casecollision.t
@@ -71,12 +71,28 @@ 
   $ touch I/I2/x
   $ hg add I/I2/x
 
+  $ mkdir -p J/J/j1 J/j2/J j3/J/J
+  $ touch J/J/J1 J/J2 J3 J/J/j1/y J/j2/J/y j3/J/J/y
+  $ hg add J/J/J1 J/J2 J3 J/J/j1/y J/j2/J/y j3/J/J/y
+  warning: possible case-folding collision for J/J/j1/y
+  warning: possible case-folding collision for J/j2/J/y
+  warning: possible case-folding collision for j3/J/J/y
+  $ touch J/J4 J/j4
+  $ hg add J/J4 J/j4
+  warning: possible case-folding collision for J/j4
+
   $ hg commit -m '#0'
 
   $ hg remove I/I2/x
   $ touch I/i2
   $ hg add I/i2
 
+  $ hg remove J/j4
+  $ mkdir J/j4
+  $ touch J/j4/y
+  $ hg add J/j4/y
+  warning: possible case-folding collision for J/j4/y
+
 case changing rename must not warn or abort
 
   $ echo c > c