Initial work supporting write locks
authorJelmer Vernooij <jelmer@samba.org>
Mon, 16 Jul 2007 13:52:37 +0000 (15:52 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Mon, 16 Jul 2007 13:52:37 +0000 (15:52 +0200)
__init__.py
commit.py
fetch.py
tests/test_transport.py
transport.py

index 40eb3d787405ca99f73798f5a15b6d483ede9459..6bdf94e3a6eb2eef9662df30e9dd95ad21531a58 100644 (file)
@@ -90,8 +90,10 @@ register_transport_proto('svn+http://',
     help="Access of Subversion smart servers over HTTP.")
 register_transport_proto('svn+https://',
     help="Access of Subversion smart servers over secure HTTP.")
-register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport', 'SvnRaTransport')
-register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport', 'SvnRaTransport')
+register_lazy_transport('svn://', 'bzrlib.plugins.svn.transport', 
+                        'SvnRaTransport')
+register_lazy_transport('svn+', 'bzrlib.plugins.svn.transport', 
+                        'SvnRaTransport')
 
 BzrDirFormat.register_control_format(format.SvnFormat)
 BzrDirFormat.register_control_format(workingtree.SvnWorkingTreeDirFormat)
index cb934693cc812c4e328b9fe5f6365acfec97c56f..25541bb87894aa8794c2827af74a4c8544739bff 100644 (file)
--- a/commit.py
+++ b/commit.py
@@ -314,29 +314,33 @@ class SvnCommitBuilder(RootCommitBuilder):
             self.date = date
             self.author = author
         
-        mutter('obtaining commit editor')
-        self.revnum = None
-        self.editor = self.repository.transport.get_commit_editor(
-            message.encode("utf-8"), done, None, False)
+        lock = self.repository.transport.lock(".")
+        try:
+            mutter('obtaining commit editor')
+            self.revnum = None
+            self.editor = self.repository.transport.get_commit_editor(
+                message.encode("utf-8"), done, None, False)
 
-        if self.branch.last_revision() is None:
-            self.base_revnum = 0
-        else:
-            self.base_revnum = self.branch.lookup_revision_id(
-                          self.branch.last_revision())
+            if self.branch.last_revision() is None:
+                self.base_revnum = 0
+            else:
+                self.base_revnum = self.branch.lookup_revision_id(
+                              self.branch.last_revision())
 
-        root = self.editor.open_root(self.base_revnum)
-        
-        branch_batons = self.open_branch_batons(root,
-                                self.branch.branch_path.split("/"))
+            root = self.editor.open_root(self.base_revnum)
+            
+            branch_batons = self.open_branch_batons(root,
+                                    self.branch.branch_path.split("/"))
 
-        self._dir_process("", self.new_inventory.root.file_id, branch_batons[-1])
+            self._dir_process("", self.new_inventory.root.file_id, branch_batons[-1])
 
-        branch_batons.reverse()
-        for baton in branch_batons:
-            self.editor.close_directory(baton, self.pool)
+            branch_batons.reverse()
+            for baton in branch_batons:
+                self.editor.close_directory(baton, self.pool)
 
-        self.editor.close()
+            self.editor.close()
+        finally:
+            lock.unlock()
 
         assert self.revnum is not None
         revid = self.branch.generate_revision_id(self.revnum)
index 2c5cede094463b9926594f1218425dc334339cec..26174dd61d271fd6e8f43cc5fd214439b191dd99 100644 (file)
--- a/fetch.py
+++ b/fetch.py
@@ -444,9 +444,9 @@ class InterFromSvnRepository(InterRepository):
                     # Report status of existing paths
                     reporter.set_path("", parent_revnum, False, None, pool)
 
-                transport.lock()
+                lock = transport.lock_read(".")
                 reporter.finish_report(pool)
-                transport.unlock()
+                lock.unlock()
 
                 prev_inv = editor.inventory
                 prev_revid = revid
index c60fb3460c9b300a1d4235cf8aa9663fc48875d1..d229cf32aed1da3e291ddbc5c35cf20997622314 100644 (file)
@@ -46,6 +46,22 @@ class SvnRaTest(TestCaseWithSubversionRepository):
         t.reparent("%s/foo" % repos_url)
         self.assertEqual("%s/foo" % repos_url, t.base)
 
+    def test_lock_read(self):
+        repos_url = self.make_client('d', 'dc')
+        t = SvnRaTransport(repos_url)
+        self.assertFalse(t.is_locked())
+        lock = t.lock_read(".")
+        self.assertTrue(t.is_locked())
+        lock.unlock()
+
+    def test_lock_write(self):
+        repos_url = self.make_client('d', 'dc')
+        t = SvnRaTransport(repos_url)
+        self.assertFalse(t.is_locked())
+        lock = t.lock_write(".")
+        self.assertTrue(t.is_locked())
+        lock.unlock()
+
     def test_listable(self):
         repos_url = self.make_client('d', 'dc')
         t = SvnRaTransport(repos_url)
index 6dbc1005553feb77c9de5ff3e7ab4850090e6be8..4cc10e4dd2f286ebf4340901f951acc14c96ed6c 100644 (file)
@@ -32,18 +32,6 @@ from errors import convert_svn_error
 svn_config = svn.core.svn_config_get_config(None)
 
 
-def need_lock(unbound):
-    def locked(self, *args, **kwargs):
-        self.lock()
-        try:
-            return unbound(self, *args, **kwargs)
-        finally:
-            self.unlock()
-    locked.__doc__ = unbound.__doc__
-    locked.__name__ = unbound.__name__
-    return locked
-
-
 def _create_auth_baton(pool):
     """Create a Subversion authentication baton. """
     # Give the client context baton a suite of authentication
@@ -142,7 +130,6 @@ class SvnRaTransport(Transport):
     @convert_svn_error
     def __init__(self, url=""):
         self.pool = Pool()
-        self.is_locked = False
         bzr_url = url
         self.svn_url = bzr_to_svn_url(url)
         Transport.__init__(self, bzr_url)
@@ -190,14 +177,6 @@ class SvnRaTransport(Transport):
             svn.ra.reporter2_invoke_abort_report(self._reporter, 
                     self._baton, pool)
 
-    def lock(self):
-        assert (not self.is_locked)
-        self.is_locked = True
-
-    def unlock(self):
-        assert self.is_locked
-        self.is_locked = False
-
     def has(self, relpath):
         """See Transport.has()."""
         # TODO: Raise TransportNotPossible here instead and 
@@ -214,37 +193,31 @@ class SvnRaTransport(Transport):
         """See Transport.stat()."""
         raise TransportNotPossible('stat not supported on Subversion')
 
-    @need_lock
     @convert_svn_error
     def get_uuid(self):
         mutter('svn get-uuid')
         return svn.ra.get_uuid(self._ra)
 
-    @need_lock
     @convert_svn_error
     def get_repos_root(self):
         mutter("svn get-repos-root")
         return svn.ra.get_repos_root(self._ra)
 
-    @need_lock
     @convert_svn_error
     def get_latest_revnum(self):
         mutter("svn get-latest-revnum")
         return svn.ra.get_latest_revnum(self._ra)
 
-    @need_lock
     @convert_svn_error
     def do_switch(self, switch_rev, switch_target, recurse, switch_url, *args, **kwargs):
         mutter('svn switch -r %d %r -> %r' % (switch_rev, switch_target, switch_url))
         return self.Reporter(svn.ra.do_switch(self._ra, switch_rev, switch_target, recurse, switch_url, *args, **kwargs))
 
-    @need_lock
     @convert_svn_error
     def get_log(self, path, from_revnum, to_revnum, *args, **kwargs):
         mutter('svn log %r:%r %r' % (from_revnum, to_revnum, path))
         return svn.ra.get_log(self._ra, [path], from_revnum, to_revnum, *args, **kwargs)
 
-    @need_lock
     @convert_svn_error
     def reparent(self, url):
         url = url.rstrip("/")
@@ -258,7 +231,6 @@ class SvnRaTransport(Transport):
         else:
             self._ra = svn.client.open_ra_session(self.svn_url.encode('utf8'), 
                     self._client, self.pool)
-    @need_lock
     @convert_svn_error
     def get_dir(self, path, revnum, pool=None, kind=False):
         mutter("svn ls -r %d '%r'" % (revnum, path))
@@ -288,14 +260,39 @@ class SvnRaTransport(Transport):
             raise
         return dirents.keys()
 
-    @need_lock
+    @convert_svn_error
+    def get_lock(self, path):
+        return svn.ra.get_lock(self._ra, path)
+
+    class SvnLock:
+        def __init__(self, transport, tokens):
+            self._tokens = tokens
+            self._transport = transport
+
+        def unlock(self):
+            self.transport.unlock(self.locks)
+
+
+    @convert_svn_error
+    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
+    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
     def check_path(self, path, revnum, *args, **kwargs):
         assert len(path) == 0 or path[0] != "/"
         mutter("svn check_path -r%d %s" % (revnum, path))
         return svn.ra.check_path(self._ra, path.encode('utf-8'), revnum, *args, **kwargs)
 
-    @need_lock
     @convert_svn_error
     def mkdir(self, relpath, mode=None):
         path = "%s/%s" % (self.svn_url, relpath)
@@ -308,13 +305,11 @@ class SvnRaTransport(Transport):
                 raise FileExists(path)
             raise
 
-    @need_lock
     @convert_svn_error
     def do_update(self, revnum, path, *args, **kwargs):
         mutter('svn update -r %r %r' % (revnum, path))
         return self.Reporter(svn.ra.do_update(self._ra, revnum, path, *args, **kwargs))
 
-    @need_lock
     @convert_svn_error
     def get_commit_editor(self, *args, **kwargs):
         return Editor(svn.ra.get_commit_editor(self._ra, *args, **kwargs))
@@ -331,10 +326,6 @@ class SvnRaTransport(Transport):
         def unlock(self):
             pass
 
-    def lock_write(self, relpath):
-        """See Transport.lock_write()."""
-        return self.PhonyLock()
-
     def lock_read(self, relpath):
         """See Transport.lock_read()."""
         return self.PhonyLock()