Rev 18: Handle deletions. Robust implementation. in http://code.launchpad.net/%7Ev-ladeuil/bzr/upload

Vincent Ladeuil v.ladeuil+lp at free.fr
Sun Mar 23 12:00:34 GMT 2008


At http://code.launchpad.net/%7Ev-ladeuil/bzr/upload

------------------------------------------------------------
revno: 18
revision-id: v.ladeuil+lp at free.fr-20080323120026-lz48rknjd3ii2ywm
parent: v.ladeuil+lp at free.fr-20080322181335-8aphm9mh68tgfvn8
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: upload
timestamp: Sun 2008-03-23 13:00:26 +0100
message:
  Handle deletions. Robust implementation.
  
  * test_upload.py:
  (TestIncrementalUpload.test_delete_dir_and_subdir,
  TestIncrementalUpload.test_delete_one_file_rename_to_deleted,
  TestIncrementalUpload.test_rename_outside_dir_delete_dir): Make
  the trivial deletion fails.
  
  * __init__.py:
  Use lazy_import.
  (cmd_upload.delete_remote_file, cmd_upload.delete_remote_dir,
  cmd_upload.delete_remote_dir_maybe, cmd_upload.finish_deletions):
  Better deletion handling.
  (cmd_upload.upload_tree): Handle dir deletions.
modified:
  __init__.py                    __init__.py-20080307145942-xx1xgifrreovahgz-1
  test_upload.py                 test_upload.py-20080307145942-xx1xgifrreovahgz-2
-------------- next part --------------
=== modified file '__init__.py'
--- a/__init__.py	2008-03-22 18:13:35 +0000
+++ b/__init__.py	2008-03-23 12:00:26 +0000
@@ -37,9 +37,17 @@
 
 from bzrlib import (
     commands,
+    lazy_import,
     option,
     )
-
+lazy_import.lazy_import(globals(), """
+from bzrlib import (
+    branch,
+    errors,
+    revisionspec,
+    transport,
+    )
+""")
 
 class cmd_upload(commands.Command):
     """Upload a working tree, as a whole or incrementally.
@@ -63,15 +71,6 @@
     def run(self, location, full=False, revision=None, remember=None,
             directory=None,
             ):
-        # Import the needed modules but only once we are required to run to
-        # avoid degrading bzr startup time
-        from bzrlib import (
-            branch,
-            errors,
-            revisionspec,
-            transport,
-            )
-
         if directory is None:
             directory = u'.'
         self.branch = branch.Branch.open_containing(directory)[0]
@@ -99,6 +98,7 @@
         self.tree = self.branch.repository.revision_tree(rev_id)
         self.rev_id = rev_id
         self._pending_renames = []
+        self._pending_deletions = []
         if full:
             self.upload_full_tree()
         else:
@@ -130,9 +130,31 @@
         # XXX: handle mode
         self.to_transport.mkdir(relpath)
 
-    def delete_remote_any(self, relpath):
+    def delete_remote_file(self, relpath):
         self.to_transport.delete(relpath)
 
+    def delete_remote_dir(self, relpath):
+        self.to_transport.rmdir(relpath)
+
+    def delete_remote_dir_maybe(self, relpath):
+        """Try to delete relpath, keeping failures to retry later."""
+        try:
+            self.to_transport.rmdir(relpath)
+        # any kind of PathError would be OK, though we normally expect
+        # DirectoryNotEmpty
+        except errors.PathError:
+            self._pending_deletions.append(relpath)
+
+    def finish_deletions(self):
+        if self._pending_deletions:
+            # Process the previously failed deletions in reverse order to
+            # delete children before parents
+            for relpath in reversed(self._pending_deletions):
+                self.to_transport.rmdir(relpath)
+            # The following shouldn't be needed since we use it once per
+            # upload, but better safe than sorry ;-)
+            self._pending_deletions = []
+
     def rename_remote(self, old_relpath, new_relpath):
         """Rename a remote file or directory taking care of collisions.
 
@@ -203,14 +225,17 @@
             # XXX: handle kind_changed
             for (path, id, kind) in changes.removed:
                 if kind is 'file':
-                    self.delete_remote_any(path)
-                # XXX: handle dirs
+                    self.delete_remote_file(path)
+                elif kind is  'directory':
+                    self.delete_remote_dir_maybe(path)
                 else:
                     raise NotImplementedError
+
             for (old_path, new_path, id, kind,
                  content_change, exec_change) in changes.renamed:
                 self.rename_remote(old_path, new_path)
             self.finish_renames()
+            self.finish_deletions()
 
             for (path, id, kind) in changes.added:
                 if kind is 'file':
@@ -219,6 +244,7 @@
                     self.make_remote_dir(path)
                 else:
                     raise NotImplementedError
+
             # XXX: Add a test for exec_change
             for (path, id, kind,
                  content_change, exec_change) in changes.modified:
@@ -226,6 +252,7 @@
                     self.upload_file(path, id)
                 else:
                     raise NotImplementedError
+
             self.set_uploaded_revid(self.rev_id)
         finally:
             self.tree.unlock()

=== modified file 'test_upload.py'
--- a/test_upload.py	2008-03-22 18:13:35 +0000
+++ b/test_upload.py	2008-03-23 12:00:26 +0000
@@ -293,6 +293,50 @@
         self.do_upload()
         self.failIfUpFileExists('hello')
 
+    def test_delete_dir_and_subdir(self):
+        self.make_local_branch()
+        self.add_dir('dir')
+        self.add_dir('dir/subdir')
+        self.add_file('dir/subdir/a', 'foo')
+        self.do_full_upload()
+        self.rename_any('dir/subdir/a', 'a')
+        self.delete_any('dir/subdir')
+        self.delete_any('dir')
+
+        self.assertUpFileEqual('foo', 'dir/subdir/a')
+        self.do_upload()
+        self.failIfUpFileExists('dir/subdir/a')
+        self.failIfUpFileExists('dir/subdir')
+        self.failIfUpFileExists('dir')
+        self.assertUpFileEqual('foo', 'a')
+
+    def test_delete_one_file_rename_to_deleted(self):
+        self.make_local_branch()
+        self.add_file('a', 'foo')
+        self.add_file('b', 'bar')
+        self.do_full_upload()
+        self.delete_any('a')
+        self.rename_any('b', 'a')
+
+        self.assertUpFileEqual('foo', 'a')
+        self.do_upload()
+        self.failIfUpFileExists('b')
+        self.assertUpFileEqual('bar', 'a')
+
+    def test_rename_outside_dir_delete_dir(self):
+        self.make_local_branch()
+        self.add_dir('dir')
+        self.add_file('dir/a', 'foo')
+        self.do_full_upload()
+        self.rename_any('dir/a', 'a')
+        self.delete_any('dir')
+
+        self.assertUpFileEqual('foo', 'dir/a')
+        self.do_upload()
+        self.failIfUpFileExists('dir/a')
+        self.failIfUpFileExists('dir')
+        self.assertUpFileEqual('foo', 'a')
+
 
 class TestBranchUploadLocations(branch_implementations.TestCaseWithBranch):
 



More information about the bazaar-commits mailing list