[PATCH] store: encode first period or space char in filenames (issue1713)

Augie Fackler durin42 at gmail.com
Sat Aug 28 17:18:41 UTC 2010


This looks reasonable to me, but I didn't apply and test - happy to do so if that'd help.

On Aug 22, 2010, at 1:08 PM, Adrian Buehlmann wrote:

> # HG changeset patch
> # User Adrian Buehlmann <adrian at cadifra.com>
> # Date 1282498604 -7200
> # Node ID 49011289480fd59ea2dcf29ea021c4e1e672714a
> # Parent  1fe4702fe2dfc3f3afcb43eac34692241fabf65e
> store: encode first period or space char in filenames (issue1713)
> 
> - Mac OS X has problems with filenames starting with '._'
>  (e.g. '.FOO' -> '._f_o_o' is now encoded as '~2e_f_o_o')
> 
> - Explorer of Windows Vista and Windows 7 strip leading spaces of
>  path elements of filenames when copying trees
> 
> Above problems are avoided by encoding the first space (as '~20') or
> period (as '~2e') of all path elements.
> 
> This introduces a new entry 'dotencode' in .hg/requirements, that is,
> a new repository filename layout (inside .hg/store).
> 
> Newly created repositories require 'dotencode' by default. Specifying
> 
>  [format]
>  dotencode = False
> 
> in a config file will use the old format instead.
> 
> Prior Mercurial versions will abort with the message
> 
>   abort: requirement 'dotencode' not supported!
> 
> when trying to access a local repository that requires 'dotencode'.
> 
> New 'dotencode' repositories can be converted to the previous repository
> format with
> 
>  hg --config format.dotencode=0 clone --pull repoA repoB
> 
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -21,7 +21,8 @@ propertycache = util.propertycache
> 
> class localrepository(repo.repository):
>     capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
> -    supported = set('revlogv1 store fncache shared parentdelta'.split())
> +    supported = set('revlogv1 store fncache shared'
> +                    ' parentdelta dotencode'.split())
> 
>     def __init__(self, baseui, path=None, create=0):
>         repo.repository.__init__(self)
> @@ -50,6 +51,8 @@ class localrepository(repo.repository):
>                     requirements.append("store")
>                     if self.ui.configbool('format', 'usefncache', True):
>                         requirements.append("fncache")
> +                        if self.ui.configbool('format', 'dotencode', True):
> +                            requirements.append('dotencode')
>                     # create an invalid changelog
>                     self.opener("00changelog.i", "a").write(
>                         '\0\0\0\2' # represents revlogv2
> diff --git a/mercurial/store.py b/mercurial/store.py
> --- a/mercurial/store.py
> +++ b/mercurial/store.py
> @@ -71,7 +71,7 @@ lowerencode = _build_lower_encodefun()
> _windows_reserved_filenames = '''con prn aux nul
>     com1 com2 com3 com4 com5 com6 com7 com8 com9
>     lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
> -def auxencode(path):
> +def _auxencode(path, dotencode):
>     res = []
>     for n in path.split('/'):
>         if n:
> @@ -83,13 +83,15 @@ def auxencode(path):
>             if n[-1] in '. ':
>                 # encode last period or space ('foo...' -> 'foo..~2e')
>                 n = n[:-1] + "~%02x" % ord(n[-1])
> +            if dotencode and n[0] in '. ':
> +                n = "~%02x" % ord(n[0]) + n[1:]
>         res.append(n)
>     return '/'.join(res)
> 
> MAX_PATH_LEN_IN_HGSTORE = 120
> DIR_PREFIX_LEN = 8
> _MAX_SHORTENED_DIRS_LEN = 8 * (DIR_PREFIX_LEN + 1) - 4
> -def hybridencode(path):
> +def _hybridencode(path, auxencode):
>     '''encodes path with a length limit
> 
>     Encodes all paths that begin with 'data/', according to the following.
> @@ -282,7 +284,8 @@ class fncache(object):
>         return iter(self.entries)
> 
> class fncachestore(basicstore):
> -    def __init__(self, path, opener, pathjoiner):
> +    def __init__(self, path, opener, pathjoiner, encode):
> +        self.encode = encode
>         self.pathjoiner = pathjoiner
>         self.path = self.pathjoiner(path, 'store')
>         self.createmode = _calcmode(self.path)
> @@ -294,11 +297,11 @@ class fncachestore(basicstore):
>         def fncacheopener(path, mode='r', *args, **kw):
>             if mode not in ('r', 'rb') and path.startswith('data/'):
>                 fnc.add(path)
> -            return op(hybridencode(path), mode, *args, **kw)
> +            return op(self.encode(path), mode, *args, **kw)
>         self.opener = fncacheopener
> 
>     def join(self, f):
> -        return self.pathjoiner(self.path, hybridencode(f))
> +        return self.pathjoiner(self.path, self.encode(f))
> 
>     def datafiles(self):
>         rewrite = False
> @@ -306,7 +309,7 @@ class fncachestore(basicstore):
>         pjoin = self.pathjoiner
>         spath = self.path
>         for f in self.fncache:
> -            ef = hybridencode(f)
> +            ef = self.encode(f)
>             try:
>                 st = os.stat(pjoin(spath, ef))
>                 yield f, ef, st.st_size
> @@ -328,6 +331,8 @@ def store(requirements, path, opener, pa
>     pathjoiner = pathjoiner or os.path.join
>     if 'store' in requirements:
>         if 'fncache' in requirements:
> -            return fncachestore(path, opener, pathjoiner)
> +            auxencode = lambda f: _auxencode(f, 'dotencode' in requirements)
> +            encode = lambda f: _hybridencode(f, auxencode)
> +            return fncachestore(path, opener, pathjoiner, encode)
>         return encodedstore(path, opener, pathjoiner)
>     return basicstore(path, opener, pathjoiner)
> diff --git a/tests/test-hybridencode.py b/tests/test-hybridencode.py
> --- a/tests/test-hybridencode.py
> +++ b/tests/test-hybridencode.py
> @@ -2,7 +2,10 @@
> 
> from mercurial import store
> 
> -enc = store.hybridencode # used for fncache repo format
> +auxencode = lambda f: store._auxencode(f, True)
> +hybridencode = lambda f: store._hybridencode(f, auxencode)
> +
> +enc = hybridencode # used for 'dotencode' repo format
> 
> def show(s):
>     print "A = '%s'" % s
> @@ -22,4 +25,5 @@ show('data/Project Planning/Resources/An
>      'Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt')
> show('data/Project.Planning/Resources/AnotherLongDirectoryName/'
>      'Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt')
> -show('data/foo.../foo   / /a./_. /__/.x../    bla/something.i')
> +show('data/foo.../foo   / /a./_. /__/.x../    bla/.FOO/something.i')
> +
> diff --git a/tests/test-hybridencode.py.out b/tests/test-hybridencode.py.out
> --- a/tests/test-hybridencode.py.out
> +++ b/tests/test-hybridencode.py.out
> @@ -16,6 +16,6 @@ B = 'dh/project_/resource/anotherl/follo
> A = 'data/Project.Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt'
> B = 'dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilena0fd7c506f5c9d58204444fc67e9499006bd2d445.txt'
> 
> -A = 'data/foo.../foo   / /a./_. /__/.x../    bla/something.i'
> -B = 'data/foo..~2e/foo  ~20/~20/a~2e/__.~20/____/.x.~2e/    bla/something.i'
> +A = 'data/foo.../foo   / /a./_. /__/.x../    bla/.FOO/something.i'
> +B = 'data/foo..~2e/foo  ~20/~20/a~2e/__.~20/____/~2ex.~2e/~20   bla/~2e_f_o_o/something.i'
> 
> diff --git a/tests/test-init.t b/tests/test-init.t
> --- a/tests/test-init.t
> +++ b/tests/test-init.t
> @@ -42,6 +42,7 @@ creating 'local'
>   revlogv1
>   store
>   fncache
> +  dotencode
>   $ echo this > local/foo
>   $ hg ci --cwd local -A -m "init" -d "1000000 0"
>   adding foo
> @@ -154,3 +155,4 @@ creating 'local/sub/repo'
>   revlogv1
>   store
>   fncache
> +  dotencode




More information about the Mercurial-devel mailing list