[PATCH STABLE] match: fix a caseonly rename + explicit path commit on icasefs (issue4768)

Matt Harbison mharbison72 at gmail.com
Fri Aug 7 01:56:06 UTC 2015


# HG changeset patch
# User Matt Harbison <matt_harbison at yahoo.com>
# Date 1438909216 14400
#      Thu Aug 06 21:00:16 2015 -0400
# Branch stable
# Node ID c54ca3f987fd1eaaba3a26d62806270fff535346
# Parent  79f0cb97d7537a7c2948f8f9b0a89148825a3a1d
match: fix a caseonly rename + explicit path commit on icasefs (issue4768)

The problem was that the former name and the new name are both normalized to the
case in dirstate, so matcher._files would be ['ABC.txt', 'ABC.txt'].
localrepo.commit() calls localrepo.status(), passing along the matcher.  Inside
dirstate.status(), _walkexplicit() simply grabs matcher.files() and processes
those items.  Since the old name isn't present, it is silently dropped.  There's
a fundamental tension here, because the status command should also accept files
that don't match the filesystem, so we can't drop the normalization in status.
The problem originated in baa11dde8c0e.

Unfortunately with this change, the case of the old file must still be specified
exactly, or the old file is again silently excluded.  I went back to
baa11dde8c0e^, and that had the same behavior, so we are no worse off.  I'm open
to ideas from a matcher or dirstate expert on how to fix that half.

diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -386,7 +386,8 @@
     def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
                  ctx, listsubrepos=False, badfn=None):
         init = super(icasefsmatcher, self).__init__
-        self._dsnormalize = ctx.repo().dirstate.normalize
+        self._dirstate = ctx.repo().dirstate
+        self._dsnormalize = self._dirstate.normalize
 
         init(root, cwd, patterns, include, exclude, default, auditor=auditor,
              ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
@@ -402,7 +403,13 @@
         kindpats = []
         for kind, pats, source in self._kp:
             if kind not in ('re', 'relre'):  # regex can't be normalized
+                p = pats
                 pats = self._dsnormalize(pats)
+
+                # Preserve the original to handle a case only rename.
+                if p != pats and p in self._dirstate:
+                    kindpats.append((kind, p, source))
+
             kindpats.append((kind, pats, source))
         return kindpats
 
diff --git a/tests/test-add.t b/tests/test-add.t
--- a/tests/test-add.t
+++ b/tests/test-add.t
@@ -232,9 +232,17 @@
   -xyz
   +def
 
+  $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
+  moving CapsDir1/CapsDir/AbC.txt to CapsDir1/CapsDir/ABC.txt (glob)
+  $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
+
+  $ hg status -A capsdir1/capsdir
+  M CapsDir1/CapsDir/SubDir/Def.txt
+  C CapsDir1/CapsDir/ABC.txt
+
   $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
   $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
-  removing CapsDir1/CapsDir/AbC.txt (glob)
+  removing CapsDir1/CapsDir/ABC.txt (glob)
   removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
 #endif
 


More information about the Mercurial-devel mailing list