[PATCH 2 of 2 V2] scmutil: make filename collision auditing extensible

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Tue Jul 10 05:13:37 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1341915110 -32400
# Node ID 73511ff988bbe84d6db9d9814b03a468c16ed173
# Parent  28fb268f6b4d629cb6755a87bc70e10cc3387d42
scmutil: make filename collision auditing extensible

Before this patch, logic of filename collision auditing is fixed and
can't be extended without directly patching up or replacing
"casecollisionauditor".

This patch allows users to add any custom check logic for filename
collision auditing by adding an entry to "namecollisioncheckers"
table.

Entries of "namecollisioncheckers" should be a tuple of two elements:

  - the class to check filename collision: this class should have
    "add()" to build initial state up and "check()" to check filename
    collision.

  - the function to check whether execution should be aborted forcibly
    or not, if collision is detected: this takes "ui" argument.

This patch also add "checkcollisionalert()", because forcible aborting
configuration may be different between this and
"checkportabilityalert()", if portability checking and collision
auditing are customized differently.

diff -r 28fb268f6b4d -r 73511ff988bb contrib/perf.py
--- a/contrib/perf.py	Tue Jul 10 19:11:50 2012 +0900
+++ b/contrib/perf.py	Tue Jul 10 19:11:50 2012 +0900
@@ -177,7 +177,7 @@
     ui.popbuffer()
 
 def perfcca(ui, repo):
-    timer(lambda: scmutil.casecollisionauditor(ui, False, repo[None]))
+    timer(lambda: scmutil.namecollisionauditor(ui, False, repo[None]))
 
 def perffncacheload(ui, repo):
     from mercurial import scmutil, store
diff -r 28fb268f6b4d -r 73511ff988bb mercurial/cmdutil.py
--- a/mercurial/cmdutil.py	Tue Jul 10 19:11:50 2012 +0900
+++ b/mercurial/cmdutil.py	Tue Jul 10 19:11:50 2012 +0900
@@ -1200,9 +1200,9 @@
     names = []
     wctx = repo[None]
     cca = None
-    abort, warn = scmutil.checkportabilityalert(ui)
-    if abort or warn:
-        cca = scmutil.casecollisionauditor(ui, abort, wctx)
+    confabort, forceabort, warn = scmutil.checkcollisionalert(ui)
+    if confabort or forceabort or warn:
+        cca = scmutil.namecollisionauditor(ui, confabort, wctx)
     for f in repo.walk(match):
         exact = match.exact(f)
         if exact or not explicitonly and f not in repo.dirstate:
diff -r 28fb268f6b4d -r 73511ff988bb mercurial/scmutil.py
--- a/mercurial/scmutil.py	Tue Jul 10 19:11:50 2012 +0900
+++ b/mercurial/scmutil.py	Tue Jul 10 19:11:50 2012 +0900
@@ -61,23 +61,54 @@
             return confabort, True, warn
     return confabort, False, warn
 
-class casecollisionauditor(object):
+class namecollisionauditor(object):
     def __init__(self, ui, abort, existingiter):
         self._ui = ui
         self._abort = abort
-        self._map = {}
+        self._checkers = [(c(ui), fa) for c, fa in namecollisioncheckers]
         for f in existingiter:
-            self._map[encoding.lower(f)] = f
+            for checker, forceabort in self._checkers:
+                checker.add(f)
 
     def __call__(self, f):
+        for checker, forceabort in self._checkers:
+            msg = checker.check(f)
+            if msg:
+                if self._abort or forceabort(self._ui):
+                    raise util.Abort(msg)
+                self._ui.warn(_("warning: %s\n") % msg)
+
+class casecollisionchecker(object):
+    def __init__(self, ui):
+        self._map = {}
+    def add(self, f):
+        self._map[encoding.lower(f)] = f
+    def check(self, f):
         fl = encoding.lower(f)
         map = self._map
         if fl in map and map[fl] != f:
             msg = _('possible case-folding collision for %s') % f
-            if self._abort:
-                raise util.Abort(msg)
-            self._ui.warn(_("warning: %s\n") % msg)
+        else:
+            msg = None
         map[fl] = f
+        return msg
+
+namecollisioncheckers = [
+    (casecollisionchecker, lambda ui: os.name == 'nt'),
+]
+
+def checkcollisionalert(ui):
+    '''check if the user's config requests nothing, a warning, or abort for
+    filenames colliding with each other.
+
+    This returns a tuple of three values below:
+
+    - whether "ui.portablefilenames" is configured as "abort" or not
+    - whether there are any reasons to abort when collision is detecetd
+      other than "ui.portablefilenames" configuration or not
+    - whehter "ui.portablefilenames" is configured as "warning" or not
+    '''
+    return _checkfilenamealert(ui, namecollisioncheckers)
 
 class pathauditor(object):
     '''ensure that a filesystem path contains no banned components.
diff -r 28fb268f6b4d -r 73511ff988bb tests/test-casecollision.t
--- a/tests/test-casecollision.t	Tue Jul 10 19:11:50 2012 +0900
+++ b/tests/test-casecollision.t	Tue Jul 10 19:11:50 2012 +0900
@@ -69,3 +69,57 @@
   $ hg ci -qAmx
   $ hg mv c C
   $ cd ..
+
+Test for extensibility of filename collision check:
+
+  $ hg init extensible-collisioncheck
+  $ cd extensible-collisioncheck
+
+  $ cat > fold-a2z.py <<EOF
+  > from mercurial import scmutil
+  > def foldname(name):
+  >     return name.replace('a', 'z')
+  > class collisionchecker(object):
+  >    def __init__(self, ui):
+  >        self._map = {}
+  >    def add(self, f):
+  >        self._map[foldname(f)] = f
+  >    def check(self, f):
+  >        ff = foldname(f)
+  >        map = self._map
+  >        if ff in map and map[ff] != f:
+  >            msg = "possible filename collision for %s" % f
+  >        else:
+  >            msg = None
+  >        map[ff] = f
+  >        return msg
+  > def forceabort(ui):
+  >     # abort regardless of ui.portablefilenames, if forceabort=True
+  >     return ui.configbool('fold-a2z', 'forceabort')
+  > def extsetup(ui):
+  >     scmutil.namecollisioncheckers.append((collisionchecker, forceabort))
+  > EOF
+  $ cat > .hg/hgrc <<EOF
+  > [extensions]
+  > fold-a2z = ./fold-a2z.py
+  > EOF
+  $ echo a1 > a1
+  $ echo a2 > a2
+  $ echo a3 > a3
+  $ echo a4 > a4
+  $ hg add a1 a2 a3 a4
+  $ echo z1 > z1
+  $ hg add z1
+  warning: possible filename collision for z1
+  $ echo z2 > z2
+  $ hg --config ui.portablefilenames=ignore add z2
+  $ echo z3 > z3
+  $ hg --config ui.portablefilenames=abort add z3
+  abort: possible filename collision for z3
+  [255]
+  $ echo z4 > z4
+  $ hg --config fold-a2z.forceabort=true add z4
+  abort: possible filename collision for z4
+  [255]
+
+  $ cd ..


More information about the Mercurial-devel mailing list