hgext/convert/subversion.py
branchstable
changeset 15599 c6be93a4c378
parent 15381 c519cd8f0169
child 15605 2ad5b8937d0d
child 15750 03d04296cfab
--- a/hgext/convert/subversion.py	Wed Nov 30 15:11:00 2011 +0100
+++ b/hgext/convert/subversion.py	Thu Dec 01 20:42:24 2011 +0100
@@ -50,10 +50,21 @@
         mod = '/' + parts[1]
     return parts[0][4:], mod, int(revnum)
 
+def quote(s):
+    # As of svn 1.7, many svn calls expect "canonical" paths. In
+    # theory, we should call svn.core.*canonicalize() on all paths
+    # before passing them to the API.  Instead, we assume the base url
+    # is canonical and copy the behaviour of svn URL encoding function
+    # so we can extend it safely with new components. The "safe"
+    # characters were taken from the "svn_uri__char_validity" table in
+    # libsvn_subr/path.c.
+    return urllib.quote(s, "!$&'()*+,-./:=@_~")
+
 def geturl(path):
     try:
         return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
     except SubversionException:
+        # svn.client.url_from_path() fails with local repositories
         pass
     if os.path.isdir(path):
         path = os.path.normpath(os.path.abspath(path))
@@ -62,8 +73,8 @@
         # Module URL is later compared with the repository URL returned
         # by svn API, which is UTF-8.
         path = encoding.tolocal(path)
-        return 'file://%s' % urllib.quote(path)
-    return path
+        path = 'file://%s' % quote(path)
+    return svn.core.svn_path_canonicalize(path)
 
 def optrev(number):
     optrev = svn.core.svn_opt_revision_t()
@@ -306,7 +317,7 @@
 
     def exists(self, path, optrev):
         try:
-            svn.client.ls(self.url.rstrip('/') + '/' + urllib.quote(path),
+            svn.client.ls(self.url.rstrip('/') + '/' + quote(path),
                                  optrev, False, self.ctx)
             return True
         except SubversionException:
@@ -358,7 +369,7 @@
         # Check if branches bring a few more heads to the list
         if branches:
             rpath = self.url.strip('/')
-            branchnames = svn.client.ls(rpath + '/' + urllib.quote(branches),
+            branchnames = svn.client.ls(rpath + '/' + quote(branches),
                                         rev, False, self.ctx)
             for branch in branchnames.keys():
                 module = '%s/%s/%s' % (oldmodule, branches, branch)
@@ -394,7 +405,7 @@
         else:
             # Perform a full checkout on roots
             uuid, module, revnum = revsplit(rev)
-            entries = svn.client.ls(self.baseurl + urllib.quote(module),
+            entries = svn.client.ls(self.baseurl + quote(module),
                                     optrev(revnum), True, self.ctx)
             files = [n for n, e in entries.iteritems()
                      if e.kind == svn.core.svn_node_file]
@@ -595,7 +606,7 @@
         """Reparent the svn transport and return the previous parent."""
         if self.prevmodule == module:
             return module
-        svnurl = self.baseurl + urllib.quote(module)
+        svnurl = self.baseurl + quote(module)
         prevmodule = self.prevmodule
         if prevmodule is None:
             prevmodule = ''
@@ -866,7 +877,7 @@
         """Enumerate all files in path at revnum, recursively."""
         path = path.strip('/')
         pool = Pool()
-        rpath = '/'.join([self.baseurl, urllib.quote(path)]).strip('/')
+        rpath = '/'.join([self.baseurl, quote(path)]).strip('/')
         entries = svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool)
         if path:
             path += '/'