changeset 329:235022089da6

merge with stable
author Dan Villiom Podlaski Christiansen <danchr@gmail.com>
date Sat, 09 May 2009 12:36:17 +0200
parents 48ec2d62dc29 (diff) 067914ecb4eb (current diff)
children 5f8f2fd4fd54
files __init__.py hg_delta_editor.py wrappers.py
diffstat 7 files changed, 101 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/__init__.py
+++ b/__init__.py
@@ -18,22 +18,17 @@ import traceback
 
 from mercurial import commands
 from mercurial import extensions
+from mercurial import hg
 from mercurial import util as hgutil
 
 from svn import core
 
 import svncommands
-import tag_repo
+import svnrepo
 import util
 import wrappers
 import svnexternals
 
-def reposetup(ui, repo):
-    if not util.is_svn_repo(repo):
-        return
-
-    repo.__class__ = tag_repo.generate_repo_class(ui, repo)
-
 def uisetup(ui):
     """Do our UI setup.
 
@@ -55,21 +50,6 @@ def uisetup(ui):
                                    wrappers.push)
     entry[1].append(('', 'svn', None, "push to subversion"))
     entry[1].append(('', 'svn-stupid', None, "use stupid replay during push to svn"))
-    entry = extensions.wrapcommand(commands.table, 'pull',
-                                   wrappers.pull)
-    entry[1].append(('', 'svn', None, "pull from subversion"))
-    entry[1].append(('', 'svn-stupid', None, "use stupid replay during pull from svn"))
-
-    entry = extensions.wrapcommand(commands.table, 'clone',
-                                   wrappers.clone)
-    entry[1].extend([#('', 'skipto-rev', '0', 'skip commits before this revision.'),
-                     ('', 'svn-stupid', False, 'be stupid and use diffy replay.'),
-                     ('', 'svn-tag-locations', 'tags', 'Relative path to Subversion tags.'),
-                     ('', 'svn-authors', '', 'username mapping filename'),
-                     ('', 'svn-filemap', '',
-                      'remap file to exclude paths or include only certain paths'),
-                     ('', 'svn-no-branchnames', False, "don't record branch names in hg"),
-                     ])
 
     try:
         rebase = extensions.find('rebase')
@@ -116,7 +96,12 @@ def svn(ui, repo, subcommand, *args, **o
         else:
             raise
 
+def reposetup(ui, repo):
+    if repo.local():
+       svnrepo.generate_repo_class(ui, repo)
 
+for scheme in ('svn', 'svn+ssh', 'svn+http', 'svn+file'):
+    hg.schemes[scheme] = svnrepo
 
 cmdtable = {
     "svn":
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -89,12 +89,11 @@ class HgChangeReceiver(delta.Editor):
         self.ui = ui_
         if repo:
             self.repo = repo
+            self.__setup_repo(repo)
             self.path = os.path.normpath(os.path.join(self.repo.path, '..'))
         elif path:
             self.path = path
             self.__setup_repo(path)
-        else: #pragma: no cover
-            raise TypeError("Expected either path or repo argument")
 
         self.subdir = subdir
         if self.subdir and self.subdir[0] == '/':
@@ -144,19 +143,27 @@ class HgChangeReceiver(delta.Editor):
             date = self.lastdate
         return date
 
-    def __setup_repo(self, repo_path):
+    def __setup_repo(self, arg):
         """Verify the repo is going to work out for us.
 
         This method will fail an assertion if the repo exists but doesn't have
         the Subversion metadata.
         """
-        if os.path.isdir(repo_path) and len(os.listdir(repo_path)):
-            self.repo = hg.repository(self.ui, repo_path)
+        if isinstance(arg, basestring):
+            self.path = arg
+            self.repo = hg.repository(self.ui, self.path, create=True)
+        elif arg:
+            self.repo = arg
+            self.path = os.path.normpath(os.path.join(self.repo.path, '..'))
+        else: #pragma: no cover
+            raise TypeError("editor requires either a path or a repository "
+                            "specified")
+
+        if os.path.isdir(self.meta_data_dir) and os.listdir(self.meta_data_dir):
             assert os.path.isfile(self.revmap_file)
             assert os.path.isfile(self.svn_url_file)
             assert os.path.isfile(self.uuid_file)
         else:
-            self.repo = hg.repository(self.ui, repo_path, create=True)
             os.makedirs(os.path.dirname(self.uuid_file))
             f = open(self.revmap_file, 'w')
             f.write('%s\n' % util.REVMAP_FILE_VERSION)
rename from tag_repo.py
rename to svnrepo.py
--- a/tag_repo.py
+++ b/svnrepo.py
@@ -1,23 +1,87 @@
 from mercurial import node
+from mercurial import util as hgutil
+import mercurial.repo
 
 import hg_delta_editor
+import util
+import wrappers
 
+def generate_repo_class(ui, repo):
+    def localsvn(fn):
+        '''
+        Filter for instance methods which only apply to local Subversion
+        repositories.
+        '''
+        if util.is_svn_repo(repo):
+            return fn
+        else:
+            original = repo.__getattribute__(fn.__name__)
+            return original
 
-def tags_from_tag_info(repo):
-    hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
-    for tag, source in hg_editor.tags.iteritems():
-        source_ha = hg_editor.get_parent_revision(source[1]+1, source[0])
-        yield 'tag/%s'%tag, node.hex(source_ha)
-
+    def remotesvn(fn):
+        '''
+        Filter for instance methods which require the first argument
+        to be a remote Subversion repository instance.
+        '''
+        original = repo.__getattribute__(fn.__name__)
+        def wrapper(self, *args, **opts):
+            if not isinstance(args[0], svnremoterepo):
+                return original(*args, **opts)
+            else:
+                return fn(self, *args, **opts)
+        wrapper.__name__ = fn.__name__ + '_wrapper'
+        wrapper.__doc__ = fn.__doc__
+        return wrapper
 
-def generate_repo_class(ui, repo):
+    class svnlocalrepo(repo.__class__):
+        @remotesvn
+        def pull(self, remote, heads=None, force=False):
+            try:
+                lock = self.wlock()
+                wrappers.pull(None, self.ui, self, source=remote.path,
+                              svn=True, rev=heads, force=force)
+            except KeyboardInterrupt:
+                pass
+            finally:
+                lock.release()
 
-    class svntagrepo(repo.__class__):
+        @localsvn
         def tags(self):
-            tags = dict((k, node.bin(v))
-                        for k,v in tags_from_tag_info(self))
-            hg_tags = super(svntagrepo, self).tags()
-            tags.update(hg_tags)
+            tags = super(svnlocalrepo, self).tags()
+            hg_editor = hg_delta_editor.HgChangeReceiver(repo=self)
+            for tag, source in hg_editor.tags.iteritems():
+                target = hg_editor.get_parent_revision(source[1]+1, source[0])
+                tags['tag/%s' % tag] = node.hex(target)
             return tags
 
-    return svntagrepo
+    repo.__class__ = svnlocalrepo
+
+class svnremoterepo(mercurial.repo.repository):
+    def __init__(self, ui, path):
+        self.ui = ui
+        self.path = path
+        self.capabilities = set(['lookup'])
+
+    def url(self):
+        return self.path
+
+    def lookup(self, key):
+        return key
+
+    def cancopy(self):
+        return False
+
+    def heads(self, *args, **opts):
+        """
+        Whenever this function is hit, we abort. The traceback is useful for
+        figuring out where to intercept the functionality.
+        """
+        raise hgutil.Abort('command unavailable for Subversion repositories')
+
+def instance(ui, url, create):
+    if create:
+        raise hgutil.Abort('cannot create new remote Subversion repository')
+
+    if url.startswith('svn+') and not url.startswith('svn+ssh:'):
+        url = url[4:]
+    return svnremoterepo(ui, util.normalize_url(url))
--- a/tests/test_fetch_branches.py
+++ b/tests/test_fetch_branches.py
@@ -5,7 +5,6 @@ from mercurial import node
 from mercurial import ui
 
 import test_util
-import wrappers
 
 
 class TestFetchBranches(test_util.TestBase):
@@ -18,7 +17,7 @@ class TestFetchBranches(test_util.TestBa
     def _load_fixture_and_fetch_with_anchor(self, fixture_name, anchor):
         test_util.load_svndump_fixture(self.repo_path, fixture_name)
         source = '%s#%s' % (test_util.fileurl(self.repo_path), anchor)
-        wrappers.clone(None, ui.ui(), source=source, dest=self.wc_path)
+        repo = hg.clone(ui.ui(), source=source, dest=self.wc_path)
         return hg.repository(ui.ui(), self.wc_path)
 
     def test_unrelatedbranch(self, stupid=False):
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -30,7 +30,7 @@ def fileurl(path):
     path = urllib.pathname2url(path)
     if drive:
         drive = '/' + drive
-    url = 'file://%s%s' % (drive, path)
+    url = 'svn+file://%s%s' % (drive, path)
     return url
 
 def load_svndump_fixture(path, fixture_name):
--- a/util.py
+++ b/util.py
@@ -24,7 +24,7 @@ def version(ui):
 
 
 def normalize_url(svnurl):
-    if svnurl.startswith('svn+http'):
+    if svnurl.startswith('svn+') and not svnurl.startswith('svn+ssh'):
         svnurl = svnurl[4:]
     url, revs, checkout = hg.parseurl(svnurl)
     url = url.rstrip('/')
--- a/wrappers.py
+++ b/wrappers.py
@@ -243,7 +243,7 @@ def pull(orig, ui, repo, source="default
     svn_stupid = opts.pop('svn_stupid', False)
     create_new_dest = opts.pop('create_new_dest', False)
     url = ((repo and repo.ui) or ui).expandpath(source)
-    if not (cmdutil.issvnurl(url) or svn or create_new_dest):
+    if orig and not (cmdutil.issvnurl(url) or svn or create_new_dest):
         return orig(ui, repo, source=source, *args, **opts)
     svn_url = url
     svn_url = util.normalize_url(svn_url)