Merge error code improvements from 0.4.
[jelmer/subvertpy.git] / transport.py
index 3ba94851acb6dfc45633b15bb06afd0ea4f11d63..cdff8ba3120805b5032decd05ace9c794d7dab28 100644 (file)
@@ -21,31 +21,20 @@ from bzrlib.errors import (NoSuchFile, NotBranchError, TransportNotPossible,
 from bzrlib.trace import mutter
 from bzrlib.transport import Transport
 
-from svn.core import SubversionException, Pool
-import svn.ra
-import svn.core
-import svn.client
+from bzrlib.plugins.svn.core import SubversionException
+from bzrlib.plugins.svn.auth import create_auth_baton
 
-from bzrlib.plugins.svn import properties
+from bzrlib.plugins.svn import core, properties, ra
 from bzrlib.plugins.svn.errors import convert_svn_error, NoSvnRepositoryPresent, ERR_BAD_URL, ERR_RA_SVN_REPOS_NOT_FOUND, ERR_FS_ALREADY_EXISTS, ERR_FS_NOT_FOUND
 import urlparse
 import urllib
 
-svn_config = svn.core.svn_config_get_config(None)
+svn_config = core.get_config()
 
 def get_client_string():
     """Return a string that can be send as part of the User Agent string."""
     return "bzr%s+bzr-svn%s" % (bzrlib.__version__, bzrlib.plugins.svn.__version__)
 
-def create_svn_client(url):
-    from auth import create_auth_baton
-    client = svn.client.create_context()
-    client.auth_baton = create_auth_baton(url)
-    client.config = svn_config
-    return client
-
-
 # Don't run any tests on SvnTransport as it is not intended to be 
 # a full implementation of Transport
 def get_test_permutations():
@@ -101,373 +90,23 @@ def needs_busy(unbound):
     return convert
 
 
-class Editor(object):
-    """Simple object wrapper around the Subversion delta editor interface."""
-    def __init__(self, connection, (editor, editor_baton)):
-        self.editor = editor
-        self.editor_baton = editor_baton
-        self.recent_baton = []
-        self._connection = connection
-
-    @convert_svn_error
-    def open_root(self, base_revnum):
-        assert self.recent_baton == [], "root already opened"
-        baton = svn.delta.editor_invoke_open_root(self.editor, 
-                self.editor_baton, base_revnum)
-        self.recent_baton.append(baton)
-        return baton
-
-    @convert_svn_error
-    def close_directory(self, baton, *args, **kwargs):
-        assert self.recent_baton.pop() == baton, \
-                "only most recently opened baton can be closed"
-        svn.delta.editor_invoke_close_directory(self.editor, baton, *args, **kwargs)
-
-    @convert_svn_error
-    def close(self):
-        assert self.recent_baton == []
-        svn.delta.editor_invoke_close_edit(self.editor, self.editor_baton)
-        self._connection._unmark_busy()
-
-    @convert_svn_error
-    def apply_textdelta(self, baton, *args, **kwargs):
-        assert self.recent_baton[-1] == baton
-        return svn.delta.editor_invoke_apply_textdelta(self.editor, baton,
-                *args, **kwargs)
-
-    @convert_svn_error
-    def change_dir_prop(self, baton, name, value, pool=None):
-        assert self.recent_baton[-1] == baton
-        return svn.delta.editor_invoke_change_dir_prop(self.editor, baton, 
-                                                       name, value, pool)
-
-    @convert_svn_error
-    def delete_entry(self, *args, **kwargs):
-        return svn.delta.editor_invoke_delete_entry(self.editor, *args, **kwargs)
-
-    @convert_svn_error
-    def add_file(self, path, parent_baton, *args, **kwargs):
-        assert self.recent_baton[-1] == parent_baton
-        baton = svn.delta.editor_invoke_add_file(self.editor, path, 
-            parent_baton, *args, **kwargs)
-        self.recent_baton.append(baton)
-        return baton
-
-    @convert_svn_error
-    def open_file(self, path, parent_baton, *args, **kwargs):
-        assert self.recent_baton[-1] == parent_baton
-        baton = svn.delta.editor_invoke_open_file(self.editor, path, 
-                                                 parent_baton, *args, **kwargs)
-        self.recent_baton.append(baton)
-        return baton
-
-    @convert_svn_error
-    def change_file_prop(self, baton, name, value, pool=None):
-        assert self.recent_baton[-1] == baton
-        svn.delta.editor_invoke_change_file_prop(self.editor, baton, name, 
-                                                 value, pool)
-
-    @convert_svn_error
-    def close_file(self, baton, *args, **kwargs):
-        assert self.recent_baton.pop() == baton
-        svn.delta.editor_invoke_close_file(self.editor, baton, *args, **kwargs)
-
-    @convert_svn_error
-    def add_directory(self, path, parent_baton, *args, **kwargs):
-        assert self.recent_baton[-1] == parent_baton
-        baton = svn.delta.editor_invoke_add_directory(self.editor, path, 
-            parent_baton, *args, **kwargs)
-        self.recent_baton.append(baton)
-        return baton
-
-    @convert_svn_error
-    def open_directory(self, path, parent_baton, *args, **kwargs):
-        assert self.recent_baton[-1] == parent_baton
-        baton = svn.delta.editor_invoke_open_directory(self.editor, path, 
-            parent_baton, *args, **kwargs)
-        self.recent_baton.append(baton)
-        return baton
-
-
-class Connection(object):
-    """An single connection to a Subversion repository. This usually can 
-    only do one operation at a time."""
-    def __init__(self, url):
-        self._busy = False
-        self._root = None
-        self._client = create_svn_client(url)
-        self._unbusy_handler = None
-        try:
-            self.mutter('opening SVN RA connection to %r' % url)
-            self._ra = svn.client.open_ra_session(url.encode('utf8'), 
-                    self._client)
-        except SubversionException, (_, num):
-            if num == ERR_RA_SVN_REPOS_NOT_FOUND:
-                raise NoSvnRepositoryPresent(url=url)
-            if num == ERR_BAD_URL:
-                raise InvalidURL(url)
-            raise
-        self.url = url
-
-    class Reporter(object):
-        def __init__(self, connection, (reporter, report_baton)):
-            self._reporter = reporter
-            self._baton = report_baton
-            self._connection = connection
-
-        @convert_svn_error
-        def set_path(self, path, revnum, start_empty, lock_token, pool=None):
-            svn.ra.reporter2_invoke_set_path(self._reporter, self._baton, 
-                        path, revnum, start_empty, lock_token, pool)
-
-        @convert_svn_error
-        def delete_path(self, path, pool=None):
-            svn.ra.reporter2_invoke_delete_path(self._reporter, self._baton,
-                    path, pool)
-
-        @convert_svn_error
-        def link_path(self, path, url, revision, start_empty, lock_token, 
-                      pool=None):
-            svn.ra.reporter2_invoke_link_path(self._reporter, self._baton,
-                    path, url, revision, start_empty, lock_token,
-                    pool)
-
-        @convert_svn_error
-        def finish_report(self, pool=None):
-            try:
-                svn.ra.reporter2_invoke_finish_report(self._reporter, 
-                        self._baton, pool)
-            finally:
-                self._connection._unmark_busy()
-
-        @convert_svn_error
-        def abort_report(self, pool=None):
-            try:
-                svn.ra.reporter2_invoke_abort_report(self._reporter, 
-                        self._baton, pool)
-            finally:
-                self._connection._unmark_busy()
-
-    def is_busy(self):
-        return self._busy
-
-    def _mark_busy(self):
-        assert not self._busy, "already busy"
-        self._busy = True
-
-    def set_unbusy_handler(self, handler):
-        self._unbusy_handler = handler
-
-    def _unmark_busy(self):
-        assert self._busy, "not busy"
-        self._busy = False
-        if self._unbusy_handler is not None:
-            self._unbusy_handler()
-            self._unbusy_handler = None
-
-    def mutter(self, text):
-        if 'transport' in debug.debug_flags:
-            mutter(text)
-
-    @convert_svn_error
-    @needs_busy
-    def get_uuid(self):
-        self.mutter('svn get-uuid')
-        return svn.ra.get_uuid(self._ra)
-
-    @convert_svn_error
-    @needs_busy
-    def get_repos_root(self):
-        if self._root is None:
-            self.mutter("svn get-repos-root")
-            self._root = svn.ra.get_repos_root(self._ra)
-        return self._root
-
-    @convert_svn_error
-    @needs_busy
-    def get_latest_revnum(self):
-        self.mutter("svn get-latest-revnum")
-        return svn.ra.get_latest_revnum(self._ra)
-
-    def _make_editor(self, editor, pool=None):
-        edit, edit_baton = svn.delta.make_editor(editor, pool)
-        self._edit = edit
-        self._edit_baton = edit_baton
-        return self._edit, self._edit_baton
-
-    @convert_svn_error
-    def do_switch(self, switch_rev, recurse, switch_url, editor, pool=None):
-        self.mutter('svn switch -r %d -> %r' % (switch_rev, switch_url))
-        self._mark_busy()
-        edit, edit_baton = self._make_editor(editor, pool)
-        return self.Reporter(self, svn.ra.do_switch(self._ra, switch_rev, "", 
-                             recurse, switch_url, edit, edit_baton, pool))
-
-    @convert_svn_error
-    def change_rev_prop(self, revnum, name, value, pool=None):
-        self.mutter('svn revprop -r%d --set %s=%s' % (revnum, name, value))
-        svn.ra.change_rev_prop(self._ra, revnum, name, value)
-
-    @convert_svn_error
-    @needs_busy
-    def get_lock(self, path):
-        return svn.ra.get_lock(self._ra, path)
-
-    @convert_svn_error
-    @needs_busy
-    def unlock(self, locks, break_lock=False):
-        def lock_cb(baton, path, do_lock, lock, ra_err, pool):
-            pass
-        return svn.ra.unlock(self._ra, locks, break_lock, lock_cb)
-    @convert_svn_error
-    @needs_busy
-    def get_dir(self, path, revnum, pool=None, kind=False):
-        self.mutter("svn ls -r %d '%r'" % (revnum, path))
-        assert len(path) == 0 or path[0] != "/"
-        # ra_dav backends fail with strange errors if the path starts with a 
-        # slash while other backends don't.
-        if hasattr(svn.ra, 'get_dir2'):
-            fields = 0
-            if kind:
-                fields += svn.core.SVN_DIRENT_KIND
-            return svn.ra.get_dir2(self._ra, path, revnum, fields)
-        else:
-            return svn.ra.get_dir(self._ra, path, revnum)
-
-    @convert_svn_error
-    @needs_busy
-    def check_path(self, path, revnum):
-        assert len(path) == 0 or path[0] != "/"
-        self.mutter("svn check_path -r%d %s" % (revnum, path))
-        return svn.ra.check_path(self._ra, path.encode('utf-8'), revnum)
-
-    @convert_svn_error
-    @needs_busy
-    def mkdir(self, relpath, mode=None):
-        assert len(relpath) == 0 or relpath[0] != "/"
-        path = urlutils.join(self.url, relpath)
-        try:
-            svn.client.mkdir([path.encode("utf-8")], self._client)
-        except SubversionException, (msg, num):
-            if num == ERR_FS_NOT_FOUND:
-                raise NoSuchFile(path)
-            if num == ERR_FS_ALREADY_EXISTS:
-                raise FileExists(path)
-            raise
-
-    @convert_svn_error
-    def replay(self, revision, low_water_mark, send_deltas, editor, pool=None):
-        self.mutter('svn replay -r%r:%r' % (low_water_mark, revision))
-        self._mark_busy()
-        edit, edit_baton = self._make_editor(editor, pool)
-        svn.ra.replay(self._ra, revision, low_water_mark, send_deltas,
-                      edit, edit_baton, pool)
-
-    @convert_svn_error
-    def do_update(self, revnum, recurse, editor, pool=None):
-        self.mutter('svn update -r %r' % revnum)
-        self._mark_busy()
-        edit, edit_baton = self._make_editor(editor, pool)
-        return self.Reporter(self, svn.ra.do_update(self._ra, revnum, "", 
-                             recurse, edit, edit_baton, pool))
-
-    @convert_svn_error
-    def has_capability(self, cap):
-        return svn.ra.has_capability(self._ra, cap)
-
-    @convert_svn_error
-    def revprop_list(self, revnum, pool=None):
-        self.mutter('svn revprop-list -r %r' % revnum)
-        return svn.ra.rev_proplist(self._ra, revnum, pool)
-
-    @convert_svn_error
-    def get_commit_editor(self, revprops, done_cb, lock_token, keep_locks):
-        self._mark_busy()
-        try:
-            if hasattr(svn.ra, 'get_commit_editor3'):
-                editor = svn.ra.get_commit_editor3(self._ra, revprops, done_cb, 
-                                                  lock_token, keep_locks)
-            elif revprops.keys() != [properties.PROP_REVISION_LOG]:
-                raise NotImplementedError()
-            else:
-                editor = svn.ra.get_commit_editor2(self._ra, 
-                            revprops[properties.PROP_REVISION_LOG],
-                            done_cb, lock_token, keep_locks)
-
-            return Editor(self, editor)
-        except:
-            self._unmark_busy()
-            raise
+def Connection(url):
+    try:
+        mutter('opening SVN RA connection to %r' % url)
+        ret = ra.RemoteAccess(url.encode('utf8'), 
+                auth=create_auth_baton(url))
+        # FIXME: Callbacks
+    except SubversionException, (_, num):
+        if num in (ERR_RA_SVN_REPOS_NOT_FOUND,):
+            raise NoSvnRepositoryPresent(url=url)
+        if num == ERR_BAD_URL:
+            raise InvalidURL(url)
+        raise
 
-    class SvnLock(object):
-        def __init__(self, connection, tokens):
-            self._tokens = tokens
-            self._connection = connection
+    from bzrlib.plugins.svn import lazy_check_versions
+    lazy_check_versions()
 
-        def unlock(self):
-            self._connection.unlock(self.locks)
-
-    @convert_svn_error
-    @needs_busy
-    def lock_write(self, path_revs, comment=None, steal_lock=False):
-        tokens = {}
-        def lock_cb(baton, path, do_lock, lock, ra_err, pool):
-            tokens[path] = lock
-        svn.ra.lock(self._ra, path_revs, comment, steal_lock, lock_cb)
-        return SvnLock(self, tokens)
-
-    @convert_svn_error
-    @needs_busy
-    def get_log(self, paths, from_revnum, to_revnum, limit, 
-                discover_changed_paths, strict_node_history, revprops, rcvr, 
-                pool=None):
-        # No paths starting with slash, please
-        assert paths is None or all([not p.startswith("/") for p in paths])
-        if (paths is None and 
-            (svn.core.SVN_VER_MINOR < 6 or (
-             svn.core.SVN_VER_REVISION < 31470 and svn.core.SVN_VER_REVISION != 0))):
-            paths = ["/"]
-        self.mutter('svn log %r:%r %r (limit: %r)' % (from_revnum, to_revnum, paths, limit))
-        if hasattr(svn.ra, 'get_log2'):
-            return svn.ra.get_log2(self._ra, paths, 
-                           from_revnum, to_revnum, limit, 
-                           discover_changed_paths, strict_node_history, False, 
-                           revprops, rcvr, pool)
-
-        class LogEntry(object):
-            def __init__(self, changed_paths, rev, author, date, message):
-                self.changed_paths = changed_paths
-                self.revprops = {}
-                if properties.PROP_REVISION_AUTHOR in revprops:
-                    self.revprops[properties.PROP_REVISION_AUTHOR] = author
-                if properties.PROP_REVISION_LOG in revprops:
-                    self.revprops[properties.PROP_REVISION_LOG] = message
-                if properties.PROP_REVISION_DATE in revprops:
-                    self.revprops[properties.PROP_REVISION_DATE] = date
-                # FIXME: Check other revprops
-                # FIXME: Handle revprops is None
-                self.revision = rev
-                self.has_children = None
-
-        def rcvr_convert(orig_paths, rev, author, date, message, pool):
-            rcvr(LogEntry(orig_paths, rev, author, date, message), pool)
-
-        return svn.ra.get_log(self._ra, paths, 
-                              from_revnum, to_revnum, limit, discover_changed_paths, 
-                              strict_node_history, rcvr_convert, pool)
-
-    @convert_svn_error
-    @needs_busy
-    def reparent(self, url):
-        if self.url == url:
-            return
-        if hasattr(svn.ra, 'reparent'):
-            self.mutter('svn reparent %r' % url)
-            svn.ra.reparent(self._ra, url)
-            self.url = url
-        else:
-            raise NotImplementedError(self.reparent)
+    return ret
 
 
 class ConnectionPool(object):
@@ -478,7 +117,7 @@ class ConnectionPool(object):
     def get(self, url):
         # Check if there is an existing connection we can use
         for c in self.connections:
-            assert not c.is_busy(), "busy connection in pool"
+            assert not c.busy, "busy connection in pool"
             if c.url == url:
                 self.connections.remove(c)
                 return c
@@ -497,7 +136,7 @@ class ConnectionPool(object):
             raise
 
     def add(self, connection):
-        assert not connection.is_busy(), "adding busy connection in pool"
+        assert not connection.busy, "adding busy connection in pool"
         self.connections.add(connection)
     
 
@@ -508,7 +147,6 @@ class SvnRaTransport(Transport):
     to fool Bazaar. """
     @convert_svn_error
     def __init__(self, url="", _backing_url=None, pool=None):
-        self.pool = Pool()
         bzr_url = url
         self.svn_url = bzr_to_svn_url(url)
         # _backing_url is an evil hack so the root directory of a repository 
@@ -553,6 +191,7 @@ class SvnRaTransport(Transport):
 
     def get_uuid(self):
         conn = self.get_connection()
+        self.mutter('svn get-uuid')
         try:
             return conn.get_uuid()
         finally:
@@ -567,6 +206,7 @@ class SvnRaTransport(Transport):
 
     def get_svn_repos_root(self):
         conn = self.get_connection()
+        self.mutter('svn get-repos-root')
         try:
             return conn.get_repos_root()
         finally:
@@ -574,19 +214,19 @@ class SvnRaTransport(Transport):
 
     def get_latest_revnum(self):
         conn = self.get_connection()
+        self.mutter('svn get-latest-revnum')
         try:
             return conn.get_latest_revnum()
         finally:
             self.add_connection(conn)
 
-    def do_switch(self, switch_rev, recurse, switch_url, editor, pool=None):
+    def do_switch(self, switch_rev, recurse, switch_url, editor):
         conn = self._open_real_transport()
-        conn.set_unbusy_handler(lambda: self.add_connection(conn))
-        return conn.do_switch(switch_rev, recurse, switch_url, editor, pool)
+        self.mutter('svn do-switch -r%d %s' % (switch_rev, switch_url))
+        return conn.do_switch(switch_rev, "", recurse, switch_url, editor)
 
     def iter_log(self, paths, from_revnum, to_revnum, limit, discover_changed_paths, 
                  strict_node_history, revprops):
-
         assert paths is None or isinstance(paths, list)
         assert paths is None or all([isinstance(x, str) for x in paths])
         assert isinstance(from_revnum, int) and isinstance(to_revnum, int)
@@ -594,10 +234,11 @@ class SvnRaTransport(Transport):
         from threading import Thread, Semaphore
 
         class logfetcher(Thread):
-            def __init__(self, transport, **kwargs):
+            def __init__(self, transport, *args, **kwargs):
                 Thread.__init__(self)
                 self.setDaemon(True)
                 self.transport = transport
+                self.args = args
                 self.kwargs = kwargs
                 self.pending = []
                 self.conn = None
@@ -615,12 +256,12 @@ class SvnRaTransport(Transport):
 
             def run(self):
                 assert self.conn is None, "already running"
-                def rcvr(log_entry, pool):
-                    self.pending.append((log_entry.changed_paths, log_entry.revision, log_entry.revprops))
+                def rcvr(*args):
+                    self.pending.append(args)
                     self.semaphore.release()
                 self.conn = self.transport.get_connection()
                 try:
-                    self.conn.get_log(rcvr=rcvr, **self.kwargs)
+                    self.conn.get_log(callback=rcvr, *self.args, **self.kwargs)
                     self.pending.append(None)
                 except Exception, e:
                     self.pending.append(e)
@@ -631,15 +272,17 @@ class SvnRaTransport(Transport):
         else:
             newpaths = [self._request_path(path) for path in paths]
         
-        fetcher = logfetcher(self, paths=newpaths, from_revnum=from_revnum, to_revnum=to_revnum, limit=limit, discover_changed_paths=discover_changed_paths, strict_node_history=strict_node_history, revprops=revprops)
+        fetcher = logfetcher(self, paths=newpaths, start=from_revnum, end=to_revnum, limit=limit, discover_changed_paths=discover_changed_paths, strict_node_history=strict_node_history, revprops=revprops)
         fetcher.start()
         return iter(fetcher.next, None)
 
-    def get_log(self, paths, from_revnum, to_revnum, limit, discover_changed_paths, 
-                strict_node_history, revprops, rcvr, pool=None):
+    def get_log(self, rcvr, paths, from_revnum, to_revnum, limit, discover_changed_paths, 
+                strict_node_history, revprops):
         assert paths is None or isinstance(paths, list), "Invalid paths"
         assert paths is None or all([isinstance(x, str) for x in paths])
 
+        self.mutter('svn log -r%d:%d %r' % (from_revnum, to_revnum, paths))
+
         if paths is None:
             newpaths = None
         else:
@@ -647,10 +290,10 @@ class SvnRaTransport(Transport):
 
         conn = self.get_connection()
         try:
-            return conn.get_log(newpaths, 
+            return conn.get_log(rcvr, newpaths, 
                     from_revnum, to_revnum,
                     limit, discover_changed_paths, strict_node_history, 
-                    revprops, rcvr, pool)
+                    revprops)
         finally:
             self.add_connection(conn)
 
@@ -659,18 +302,20 @@ class SvnRaTransport(Transport):
             return self.connections.get(self.svn_url)
         return self.get_connection()
 
-    def change_rev_prop(self, revnum, name, value, pool=None):
+    def change_rev_prop(self, revnum, name, value):
         conn = self.get_connection()
+        self.mutter('svn change-revprop -r%d %s=%s' % (revnum, name, value))
         try:
-            return conn.change_rev_prop(revnum, name, value, pool)
+            return conn.change_rev_prop(revnum, name, value)
         finally:
             self.add_connection(conn)
 
-    def get_dir(self, path, revnum, pool=None, kind=False):
+    def get_dir(self, path, revnum, kind=False):
         path = self._request_path(path)
         conn = self.get_connection()
+        self.mutter('svn get-dir -r%d %s' % (revnum, path))
         try:
-            return conn.get_dir(path, revnum, pool, kind)
+            return conn.get_dir(path, revnum, kind)
         finally:
             self.add_connection(conn)
 
@@ -703,6 +348,7 @@ class SvnRaTransport(Transport):
     def check_path(self, path, revnum):
         path = self._request_path(path)
         conn = self.get_connection()
+        self.mutter('svn check-path -r%d %s' % (revnum, path))
         try:
             return conn.check_path(path, revnum)
         finally:
@@ -710,41 +356,45 @@ class SvnRaTransport(Transport):
 
     def mkdir(self, relpath, mode=None):
         conn = self.get_connection()
+        self.mutter('svn mkdir %s' % (relpath,))
         try:
             return conn.mkdir(relpath, mode)
         finally:
             self.add_connection(conn)
 
-    def replay(self, revision, low_water_mark, send_deltas, editor, pool=None):
+    def replay(self, revision, low_water_mark, send_deltas, editor):
         conn = self._open_real_transport()
+        self.mutter('svn replay -r%d:%d' % (low_water_mark,revision))
         try:
             return conn.replay(revision, low_water_mark, 
-                                             send_deltas, editor, pool)
+                                             send_deltas, editor)
         finally:
             self.add_connection(conn)
 
-    def do_update(self, revnum, recurse, editor, pool=None):
+    def do_update(self, revnum, recurse, editor):
         conn = self._open_real_transport()
-        conn.set_unbusy_handler(lambda: self.add_connection(conn))
-        return conn.do_update(revnum, recurse, editor, pool)
+        self.mutter('svn do-update -r%d' % (revnum,))
+        return conn.do_update(revnum, "", recurse, editor)
 
     def has_capability(self, cap):
         conn = self.get_connection()
+        self.mutter('svn has-capability %s' % (cap,))
         try:
             return conn.has_capability(cap)
         finally:
             self.add_connection(conn)
 
-    def revprop_list(self, revnum, pool=None):
+    def revprop_list(self, revnum):
         conn = self.get_connection()
+        self.mutter('svn revprop-list -r%d' % (revnum,))
         try:
-            return conn.revprop_list(revnum, pool)
+            return conn.rev_proplist(revnum)
         finally:
             self.add_connection(conn)
 
     def get_commit_editor(self, revprops, done_cb, lock_token, keep_locks):
         conn = self._open_real_transport()
-        conn.set_unbusy_handler(lambda: self.add_connection(conn))
+        self.mutter('svn get-commit-editor %r' % (revprops,))
         return conn.get_commit_editor(revprops, done_cb,
                                      lock_token, keep_locks)