Rev 445: Merge upstream. in file:///data/jelmer/bzr-svn/nestedtrees/

Jelmer Vernooij jelmer at samba.org
Sat Jul 21 12:43:35 BST 2007


At file:///data/jelmer/bzr-svn/nestedtrees/

------------------------------------------------------------
revno: 445
revision-id: jelmer at samba.org-20070721114332-dv5gy9udnhitsrb3
parent: jelmer at samba.org-20070712085346-e8b64qrt4yth0qcr
parent: jelmer at samba.org-20070712130714-yeyc40asj743ygci
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: nestedtrees
timestamp: Sat 2007-07-21 13:43:32 +0200
message:
  Merge upstream.
modified:
  NEWS                           news-20061231030336-h9fhq245ie0de8bs-1
  commit.py                      commit.py-20060607190346-qvq128wgfubhhgm2-1
  tests/test_fetch.py            test_fetch.py-20070624210302-luvgwjmlfysk5qeq-1
  tests/test_tree.py             test_tree.py-20070103204350-pr8nupes7e5sd2wr-1
  tests/test_upgrade.py          test_upgrade.py-20070106170128-64zt3eqggg4tng1c-1
  tests/test_workingtree.py      test_workingtree.py-20060622191524-0di7bc3q1ckdbybb-1
  transport.py                   transport.py-20060406231150-b3472d06b3a0818d
  workingtree.py                 workingtree.py-20060306120941-b083cb0fdd4a69de
    ------------------------------------------------------------
    revno: 438.1.1.1.86
    revision-id: jelmer at samba.org-20070712130714-yeyc40asj743ygci
    parent: jelmer at samba.org-20070712124222-t6y13tmcn609d0kw
    parent: jelmer at samba.org-20070712083651-u8l1dci17b2t46gy
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: 0.4
    timestamp: Thu 2007-07-12 16:07:14 +0300
    message:
      Merge interface sanitation.
    modified:
      commit.py                      commit.py-20060607190346-qvq128wgfubhhgm2-1
      tests/test_upgrade.py          test_upgrade.py-20070106170128-64zt3eqggg4tng1c-1
      transport.py                   transport.py-20060406231150-b3472d06b3a0818d
        ------------------------------------------------------------
        revno: 438.1.1.1.81.1.1
        revision-id: jelmer at samba.org-20070712083651-u8l1dci17b2t46gy
        parent: jelmer at samba.org-20070710150136-roo08q723iiq6k3g
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: newbranch
        timestamp: Thu 2007-07-12 11:36:51 +0300
        message:
          Saner interface to Subversion delta editors.
        modified:
          commit.py                      commit.py-20060607190346-qvq128wgfubhhgm2-1
          tests/test_upgrade.py          test_upgrade.py-20070106170128-64zt3eqggg4tng1c-1
          transport.py                   transport.py-20060406231150-b3472d06b3a0818d
    ------------------------------------------------------------
    revno: 438.1.1.1.85
    revision-id: jelmer at samba.org-20070712124222-t6y13tmcn609d0kw
    parent: jelmer at samba.org-20070712100827-v4hkpdatn1771hgx
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: 0.4
    timestamp: Thu 2007-07-12 15:42:22 +0300
    message:
      Mention bug #
    modified:
      NEWS                           news-20061231030336-h9fhq245ie0de8bs-1
    ------------------------------------------------------------
    revno: 438.1.1.1.84
    revision-id: jelmer at samba.org-20070712100827-v4hkpdatn1771hgx
    parent: jelmer at samba.org-20070712090326-em3zgjramwc2ff2y
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: 0.4
    timestamp: Thu 2007-07-12 13:08:27 +0300
    message:
      Implement smart_add for working tree.
    modified:
      NEWS                           news-20061231030336-h9fhq245ie0de8bs-1
      tests/test_workingtree.py      test_workingtree.py-20060622191524-0di7bc3q1ckdbybb-1
      workingtree.py                 workingtree.py-20060306120941-b083cb0fdd4a69de
    ------------------------------------------------------------
    revno: 438.1.1.1.83
    revision-id: jelmer at samba.org-20070712090326-em3zgjramwc2ff2y
    parent: jelmer at samba.org-20070712085012-4givggs5uymua380
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: 0.4
    timestamp: Thu 2007-07-12 12:03:26 +0300
    message:
      Fix locking issues with working tree (#125212).
    modified:
      NEWS                           news-20061231030336-h9fhq245ie0de8bs-1
      workingtree.py                 workingtree.py-20060306120941-b083cb0fdd4a69de
=== modified file 'NEWS'
--- a/NEWS	2007-07-10 15:01:36 +0000
+++ b/NEWS	2007-07-12 12:42:22 +0000
@@ -32,6 +32,8 @@
 
    * Be a bit less verbose when trying to connect (#124858)
 
+   * Fix locking issues (#125212)
+
   PERFORMANCE
 
    * do_update() is now used to find the contents of a directory recursively. 
@@ -48,6 +50,8 @@
 
    * Implement SvnRemoteAccess.create_branch().
 
+   * Implement SvnWorkingTree.smart_add() (#79336)
+
 bzr-svn 0.3.4	2007-06-15
 
   BUG FIXES

=== modified file 'commit.py'
--- a/commit.py	2007-06-24 14:08:49 +0000
+++ b/commit.py	2007-07-12 08:36:51 +0000
@@ -141,8 +141,7 @@
         self.modified_dirs.append(file_id)
 
     def _file_process(self, file_id, contents, baton):
-        (txdelta, txbaton) = svn.delta.editor_invoke_apply_textdelta(
-                                self.editor, baton, None, self.pool)
+        (txdelta, txbaton) = self.editor.apply_textdelta(baton, None, self.pool)
 
         svn.delta.svn_txdelta_send_string(contents, txdelta, txbaton, self.pool)
 
@@ -154,8 +153,7 @@
                 mutter('setting %r: %r on branch' % (prop, value))
                 if value is not None:
                     value = value.encode('utf-8')
-                svn.delta.editor_invoke_change_dir_prop(self.editor, baton,
-                            prop, value, self.pool)
+                self.editor.change_dir_prop(baton, prop, value, self.pool)
 
         # Loop over entries of file_id in self.old_inv
         # remove if they no longer exist with the same name
@@ -171,7 +169,7 @@
                     # ... name changed
                     self.new_inventory[child_ie.file_id].name != child_name):
                     mutter('removing %r' % child_ie.file_id)
-                    svn.delta.editor_invoke_delete_entry(self.editor, 
+                    self.editor.delete_entry(
                             os.path.join(self.branch.branch_path, self.old_inv.id2path(child_ie.file_id)), 
                             self.base_revnum, baton, self.pool)
 
@@ -187,7 +185,7 @@
             if not child_ie.file_id in self.old_inv:
                 mutter('adding %s %r' % (child_ie.kind, self.new_inventory.id2path(child_ie.file_id)))
 
-                child_baton = svn.delta.editor_invoke_add_file(self.editor, 
+                child_baton = self.editor.add_file(
                            os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)),
                            baton, None, -1, self.pool)
 
@@ -198,7 +196,7 @@
                                   self.old_inv.id2path(child_ie.file_id), 
                                   self.new_inventory.id2path(child_ie.file_id)))
 
-                child_baton = svn.delta.editor_invoke_add_file(self.editor, 
+                child_baton = self.editor.add_file(
                            os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)), baton, 
                            "%s/%s" % (self.branch.base, self.old_inv.id2path(child_ie.file_id)),
                            self.base_revnum, self.pool)
@@ -208,11 +206,10 @@
                 mutter('open %s %r' % (child_ie.kind, 
                                  self.new_inventory.id2path(child_ie.file_id)))
 
-                child_baton = svn.delta.editor_invoke_open_file(self.editor,
+                child_baton = self.editor.open_file(
                         os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)), 
                         baton, self.base_revnum, self.pool)
 
-
             else:
                 child_baton = None
 
@@ -229,7 +226,8 @@
                         value = svn.core.SVN_PROP_EXECUTABLE_VALUE
                     else:
                         value = None
-                    svn.delta.editor_invoke_change_file_prop(self.editor, child_baton, svn.core.SVN_PROP_EXECUTABLE, value, self.pool)
+                    self.editor.change_file_prop(child_baton, 
+                            svn.core.SVN_PROP_EXECUTABLE, value, self.pool)
 
                 if old_special != (child_ie.kind == 'symlink'):
                     if child_ie.kind == 'symlink':
@@ -237,7 +235,8 @@
                     else:
                         value = None
 
-                    svn.delta.editor_invoke_change_file_prop(self.editor, child_baton, svn.core.SVN_PROP_SPECIAL, value, self.pool)
+                    self.editor.change_file_prop(child_baton, 
+                            svn.core.SVN_PROP_SPECIAL, value, self.pool)
 
             # handle the file
             if child_ie.file_id in self.modified_files:
@@ -245,7 +244,7 @@
                                    child_baton)
 
             if child_baton is not None:
-                svn.delta.editor_invoke_close_file(self.editor, child_baton, None, self.pool)
+                self.editor.close_file(child_baton, None, self.pool)
 
         # Loop over subdirectories of file_id in self.new_inventory
         for child_name in self.new_inventory[file_id].children:
@@ -256,8 +255,7 @@
             # add them if they didn't exist in old_inv 
             if not child_ie.file_id in self.old_inv:
                 mutter('adding dir %r' % child_ie.name)
-                child_baton = svn.delta.editor_invoke_add_directory(
-                           self.editor, 
+                child_baton = self.editor.add_directory(
                            os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)),
                            baton, None, -1, self.pool)
 
@@ -265,8 +263,7 @@
             elif self.old_inv.id2path(child_ie.file_id) != self.new_inventory.id2path(child_ie.file_id):
                 mutter('copy dir %r -> %r' % (self.old_inv.id2path(child_ie.file_id), 
                                          self.new_inventory.id2path(child_ie.file_id)))
-                child_baton = svn.delta.editor_invoke_add_directory(
-                           self.editor, 
+                child_baton = self.editor.add_directory(
                            os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)),
                            baton, 
                            "%s/%s" % (self.branch.base, self.old_inv.id2path(child_ie.file_id)),
@@ -277,7 +274,7 @@
             elif self.new_inventory[child_ie.file_id].revision is None:
                 mutter('open dir %r' % self.new_inventory.id2path(child_ie.file_id))
 
-                child_baton = svn.delta.editor_invoke_open_directory(self.editor, 
+                child_baton = self.editor.open_directory(
                         os.path.join(self.branch.branch_path, self.new_inventory.id2path(child_ie.file_id)), 
                         baton, self.base_revnum, self.pool)
             else:
@@ -288,10 +285,14 @@
                 self._dir_process(self.new_inventory.id2path(child_ie.file_id), 
                         child_ie.file_id, child_baton)
 
-            svn.delta.editor_invoke_close_directory(self.editor, child_baton, 
-                                             self.pool)
+            self.editor.close_directory(child_baton, self.pool)
 
     def open_branch_batons(self, root, elements):
+        """Open a specified directory given a baton for the repository root.
+
+        :param root: Baton for the repository root
+        :param elements: List of directory names to open
+        """
         ret = [root]
 
         mutter('opening branch %r' % elements)
@@ -301,7 +302,7 @@
                 revnum = self.base_revnum
             else:
                 revnum = -1
-            ret.append(svn.delta.editor_invoke_open_directory(self.editor, 
+            ret.append(self.editor.open_directory(
                 "/".join(elements[0:i+1]), ret[-1], revnum, self.pool))
 
         return ret
@@ -315,7 +316,7 @@
         
         mutter('obtaining commit editor')
         self.revnum = None
-        self.editor, editor_baton = self.repository.transport.get_commit_editor(
+        self.editor = self.repository.transport.get_commit_editor(
             message.encode("utf-8"), done, None, False)
 
         if self.branch.last_revision() is None:
@@ -324,8 +325,7 @@
             self.base_revnum = self.branch.lookup_revision_id(
                           self.branch.last_revision())
 
-        root = svn.delta.editor_invoke_open_root(self.editor, editor_baton, 
-                                                 self.base_revnum)
+        root = self.editor.open_root(self.base_revnum)
         
         branch_batons = self.open_branch_batons(root,
                                 self.branch.branch_path.split("/"))
@@ -334,10 +334,9 @@
 
         branch_batons.reverse()
         for baton in branch_batons:
-            svn.delta.editor_invoke_close_directory(self.editor, baton, 
-                                             self.pool)
+            self.editor.close_directory(baton, self.pool)
 
-        svn.delta.editor_invoke_close_edit(self.editor, editor_baton)
+        self.editor.close()
 
         assert self.revnum is not None
         revid = self.branch.generate_revision_id(self.revnum)
@@ -397,6 +396,12 @@
 
 
 def replay_delta(builder, delta, old_tree):
+    """Replays a delta to a commit builder.
+
+    :param builder: The commit builder.
+    :param delta: Treedelta to apply
+    :param old_tree: Original tree on top of which the delta should be applied
+    """
     for (_, ie) in builder.new_inventory.entries():
         if not delta.touches_file_id(ie.file_id):
             continue
@@ -473,8 +478,8 @@
 
     This will do a new commit in the target branch.
 
-    :param target: Repository to push to
-    :param source: Repository to pull the revision from
+    :param target: Branch to push to
+    :param source: Branch to pull the revision from
     :param revision_id: Revision id of the revision to push
     """
     assert isinstance(source, Branch)

=== modified file 'tests/test_fetch.py'
--- a/tests/test_fetch.py	2007-07-06 16:04:29 +0000
+++ b/tests/test_fetch.py	2007-07-21 11:43:32 +0000
@@ -72,7 +72,7 @@
         from bzrlib.repofmt.knitrepo import RepositoryFormatKnit3
         newrepos = RepositoryFormatKnit3().initialize(BzrDir.create("f"))
         oldrepos.copy_content_into(newrepos)
-        inv = oldrepos.get_inventory(oldrepos.generate_revision_id(1, ""))
+        inv = oldrepos.get_inventory(oldrepos.generate_revision_id(1, "", "none"))
         self.assertTrue(inv.has_filename("somedir/bla"))
         self.assertEqual(inv.path2id("somedir/bla"), Branch.open("%s/proj1/trunk" % repos_url1).get_root_id())
 

=== modified file 'tests/test_tree.py'
--- a/tests/test_tree.py	2007-07-06 16:04:29 +0000
+++ b/tests/test_tree.py	2007-07-21 11:43:32 +0000
@@ -21,6 +21,7 @@
 from bzrlib.workingtree import WorkingTree
 
 from fileids import generate_svn_file_id
+from revids import generate_svn_revision_id
 from tree import (SvnBasisTree, parse_externals_description, 
                   inventory_add_external)
 from tests import TestCaseWithSubversionRepository
@@ -131,27 +132,29 @@
         repos_url = self.make_client('d', 'dc')
         repos = Repository.open(repos_url)
         inv = Inventory(root_id='blabloe')
-        inventory_add_external(inv, 'blabloe', 'blie/bla', repos.generate_revision_id(1, ""), None, repos_url)
+        inventory_add_external(inv, 'blabloe', 'blie/bla', generate_svn_revision_id(repos.uuid, 1, "", "none"), 
+                               None, repos_url)
         self.assertEqual(TreeReference(generate_svn_file_id(repos.uuid, 0, "", ""),
-             'bla', inv.path2id('blie'), revision=repos.generate_revision_id(1, "")), inv[inv.path2id('blie/bla')])
+             'bla', inv.path2id('blie'), revision=generate_svn_revision_id(repos.uuid, 1, "", "none")), 
+             inv[inv.path2id('blie/bla')])
 
     def test_add_simple_norev(self):
         repos_url = self.make_client('d', 'dc')
         repos = Repository.open(repos_url)
         inv = Inventory(root_id='blabloe')
-        inventory_add_external(inv, 'blabloe', 'bla', repos.generate_revision_id(1, ""), None, repos_url)
+        inventory_add_external(inv, 'blabloe', 'bla', generate_svn_revision_id(repos.uuid, 1, "", "none"), None, repos_url)
         self.assertEqual(TreeReference(generate_svn_file_id(repos.uuid, 0, "", ""),
-             'bla', 'blabloe', revision=repos.generate_revision_id(1, "")), inv[inv.path2id('bla')])
+             'bla', 'blabloe', revision=generate_svn_revision_id(repos.uuid, 1, "", "none")), inv[inv.path2id('bla')])
 
     def test_add_simple_rev(self):
         repos_url = self.make_client('d', 'dc')
         repos = Repository.open(repos_url)
         inv = Inventory(root_id='blabloe')
-        inventory_add_external(inv, 'blabloe', 'bla', repos.generate_revision_id(1, ""), 20, repos_url)
+        inventory_add_external(inv, 'blabloe', 'bla', generate_svn_revision_id(repos.uuid, 1, "", "none"), 0, repos_url)
         self.assertEqual(TreeReference(generate_svn_file_id(repos.uuid, 0, "", ""),
-             'bla', 'blabloe', revision=repos.generate_revision_id(1, ""),
-             reference_revision=repos.generate_revision_id(20, "")
+             'bla', 'blabloe', revision=generate_svn_revision_id(repos.uuid, 1, "", "none"),
+             reference_revision=generate_svn_revision_id(repos.uuid, 0, "", "none")
              ), inv[inv.path2id('bla')])
         ie = inv[inv.path2id('bla')]
-        self.assertEqual(repos.generate_revision_id(20, ""), ie.reference_revision)
-        self.assertEqual(repos.generate_revision_id(1, ""), ie.revision)
+        self.assertEqual(generate_svn_revision_id(repos.uuid, 0, "", "none"), ie.reference_revision)
+        self.assertEqual(generate_svn_revision_id(repos.uuid, 1, "", "none"), ie.revision)

=== modified file 'tests/test_upgrade.py'
--- a/tests/test_upgrade.py	2007-07-05 16:28:11 +0000
+++ b/tests/test_upgrade.py	2007-07-12 08:36:51 +0000
@@ -101,7 +101,7 @@
 
         self.assertTrue(newrepos.has_revision("svn-v1:1@%s-" % oldrepos.uuid))
 
-        upgrade_repository(newrepos, oldrepos, allow_change=True)
+        upgrade_repository(newrepos, oldrepos, allow_changes=True)
 
         self.assertTrue(newrepos.has_revision(oldrepos.generate_revision_id(1, "", "none")))
 
@@ -124,7 +124,7 @@
         file("f/a", 'w').write("moredata")
         wt.commit(message='fix moredata', rev_id="customrev")
 
-        upgrade_repository(newrepos, oldrepos, allow_change=True)
+        upgrade_repository(newrepos, oldrepos, allow_changes=True)
 
         self.assertTrue(newrepos.has_revision(oldrepos.generate_revision_id(1, "", "none")))
         self.assertTrue(newrepos.has_revision("customrev-svn%d-upgrade" % MAPPING_VERSION))
@@ -152,7 +152,7 @@
         wt.add(["a"], ["specificid"])
         wt.commit(message='fix moredata', rev_id="customrev")
 
-        upgrade_repository(newrepos, oldrepos, allow_change=True)
+        upgrade_repository(newrepos, oldrepos, allow_changes=True)
 
         tree = newrepos.revision_tree("customrev-svn%d-upgrade" % MAPPING_VERSION)
         self.assertEqual("specificid", tree.inventory.path2id("a"))
@@ -188,7 +188,7 @@
         vf.clone_text("customrev-svn%d-upgrade" % MAPPING_VERSION,
                 "svn-v1:1@%s-" % oldrepos.uuid, ["svn-v1:1@%s-" % oldrepos.uuid])
 
-        upgrade_repository(newrepos, oldrepos, allow_change=True)
+        upgrade_repository(newrepos, oldrepos, allow_changes=True)
 
         self.assertTrue(newrepos.has_revision(oldrepos.generate_revision_id(1, "", "none")))
         self.assertTrue(newrepos.has_revision("customrev-svn%d-upgrade" % MAPPING_VERSION))
@@ -215,7 +215,7 @@
         file("f/a", 'w').write("blackfield")
         wt.commit(message='fix it again', rev_id="anotherrev")
 
-        renames = upgrade_repository(newrepos, oldrepos, allow_change=True)
+        renames = upgrade_repository(newrepos, oldrepos, allow_changes=True)
         self.assertEqual({
             "svn-v1:1@%s-" % oldrepos.uuid: oldrepos.generate_revision_id(1, "", "none"),
             "customrev": "customrev-svn%d-upgrade" % MAPPING_VERSION,

=== modified file 'tests/test_workingtree.py'
--- a/tests/test_workingtree.py	2007-07-12 08:50:12 +0000
+++ b/tests/test_workingtree.py	2007-07-12 10:08:27 +0000
@@ -56,15 +56,83 @@
         self.assertTrue(inv.has_filename("bl"))
         self.assertFalse(inv.has_filename("aa"))
 
-    def test_smart_add(self):
+    def test_smart_add_file(self):
         self.make_client('a', 'dc')
         self.build_tree({"dc/bl": "data"})
         tree = self.open_checkout("dc")
-        tree.smart_add(["bl"])
-
-        inv = tree.read_working_inventory()
-        self.assertIsInstance(inv, Inventory)
-        self.assertTrue(inv.has_filename("bl"))
+        tree.smart_add(["dc/bl"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertFalse(inv.has_filename("aa"))
+
+    def test_smart_add_recurse(self):
+        self.make_client('a', 'dc')
+        self.build_tree({"dc/bl/foo": "data"})
+        tree = self.open_checkout("dc")
+        tree.smart_add(["dc/bl"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertTrue(inv.has_filename("bl/foo"))
+        self.assertFalse(inv.has_filename("aa"))
+
+    def test_smart_add_recurse_more(self):
+        self.make_client('a', 'dc')
+        self.build_tree({"dc/bl/foo/da": "data"})
+        tree = self.open_checkout("dc")
+        tree.smart_add(["dc/bl"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertTrue(inv.has_filename("bl/foo"))
+        self.assertTrue(inv.has_filename("bl/foo/da"))
+        self.assertFalse(inv.has_filename("aa"))
+
+    def test_smart_add_more(self):
+        self.make_client('a', 'dc')
+        self.build_tree({"dc/bl/foo/da": "data", "dc/ha": "contents"})
+        tree = self.open_checkout("dc")
+        tree.smart_add(["dc/bl", "dc/ha"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertTrue(inv.has_filename("bl/foo"))
+        self.assertTrue(inv.has_filename("bl/foo/da"))
+        self.assertTrue(inv.has_filename("ha"))
+        self.assertFalse(inv.has_filename("aa"))
+
+    def test_smart_add_ignored(self):
+        self.make_client('a', 'dc')
+        self.build_tree({"dc/.bzrignore": "bl/ha"})
+        self.build_tree({"dc/bl/foo/da": "data", "dc/bl/ha": "contents"})
+        tree = self.open_checkout("dc")
+        tree.smart_add(["dc/bl"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertTrue(inv.has_filename("bl/foo"))
+        self.assertTrue(inv.has_filename("bl/foo/da"))
+        self.assertFalse(inv.has_filename("ha"))
+        self.assertFalse(inv.has_filename("aa"))
+
+    def test_smart_add_toplevel(self):
+        self.make_client('a', 'dc')
+        self.build_tree({"dc/bl/foo/da": "data", "dc/ha": "contents"})
+        tree = self.open_checkout("dc")
+        tree.smart_add(["dc"])
+
+        inv = tree.read_working_inventory()
+        self.assertIsInstance(inv, Inventory)
+        self.assertTrue(inv.has_filename("bl"))
+        self.assertTrue(inv.has_filename("bl/foo"))
+        self.assertTrue(inv.has_filename("bl/foo/da"))
+        self.assertTrue(inv.has_filename("ha"))
         self.assertFalse(inv.has_filename("aa"))
 
     def test_add_nolist(self):
@@ -123,6 +191,7 @@
     def test_unlock(self):
         self.make_client('a', 'dc')
         tree = self.open_checkout("dc")
+        tree.lock_read()
         tree.unlock()
 
     def test_get_ignore_list_empty(self):

=== modified file 'transport.py'
--- a/transport.py	2007-07-09 16:48:26 +0000
+++ b/transport.py	2007-07-12 08:36:51 +0000
@@ -86,6 +86,54 @@
     return url.rstrip('/')
 
 
+class Editor:
+    """Simple object wrapper around the Subversion delta editor interface."""
+    def __init__(self, (editor, editor_baton)):
+        self.editor = editor
+        self.editor_baton = editor_baton
+
+    def open_root(self, base_revnum):
+        return svn.delta.editor_invoke_open_root(self.editor, 
+                self.editor_baton, base_revnum)
+
+    def close_directory(self, *args, **kwargs):
+        svn.delta.editor_invoke_close_directory(self.editor, *args, **kwargs)
+
+    def close(self):
+        svn.delta.editor_invoke_close_edit(self.editor, self.editor_baton)
+
+    def apply_textdelta(self, *args, **kwargs):
+        return svn.delta.editor_invoke_apply_textdelta(self.editor, 
+                *args, **kwargs)
+
+    def change_dir_prop(self, *args, **kwargs):
+        return svn.delta.editor_invoke_change_dir_prop(self.editor, *args, 
+                                                       **kwargs)
+
+    def delete_entry(self, *args, **kwargs):
+        return svn.delta.editor_invoke_delete_entry(self.editor, *args, **kwargs)
+
+    def add_file(self, *args, **kwargs):
+        return svn.delta.editor_invoke_add_file(self.editor, *args, **kwargs)
+
+    def open_file(self, *args, **kwargs):
+        return svn.delta.editor_invoke_open_file(self.editor, *args, **kwargs)
+
+    def change_file_prop(self, *args, **kwargs):
+        svn.delta.editor_invoke_change_file_prop(self.editor, *args, **kwargs)
+
+    def close_file(self, *args, **kwargs):
+        svn.delta.editor_invoke_close_file(self.editor, *args, **kwargs)
+
+    def add_directory(self, *args, **kwargs):
+        return svn.delta.editor_invoke_add_directory(self.editor, *args, 
+                                                     **kwargs)
+
+    def open_directory(self, *args, **kwargs):
+        return svn.delta.editor_invoke_open_directory(self.editor, *args, 
+                                                      **kwargs)
+
+
 class SvnRaTransport(Transport):
     """Fake transport for Subversion-related namespaces.
     
@@ -269,7 +317,7 @@
     @need_lock
     @convert_svn_error
     def get_commit_editor(self, *args, **kwargs):
-        return svn.ra.get_commit_editor(self._ra, *args, **kwargs)
+        return Editor(svn.ra.get_commit_editor(self._ra, *args, **kwargs))
 
     def listable(self):
         """See Transport.listable().

=== modified file 'workingtree.py'
--- a/workingtree.py	2007-07-12 08:50:12 +0000
+++ b/workingtree.py	2007-07-12 10:08:27 +0000
@@ -15,6 +15,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """Checkouts and working trees (working copies)."""
 
+import bzrlib
 from bzrlib.branch import PullResult
 from bzrlib.bzrdir import BzrDirFormat, BzrDir
 from bzrlib.errors import (InvalidRevisionId, NotBranchError, NoSuchFile,
@@ -22,7 +23,7 @@
 from bzrlib.inventory import Inventory, InventoryFile, InventoryLink
 from bzrlib.lockable_files import TransportLock, LockableFiles
 from bzrlib.lockdir import LockDir
-from bzrlib.osutils import fingerprint_file
+from bzrlib.osutils import file_kind, fingerprint_file
 from bzrlib.revision import NULL_REVISION
 from bzrlib.trace import mutter
 from bzrlib.tree import RevisionTree
@@ -469,8 +470,48 @@
 
         return revid
 
+    def smart_add(self, file_list, recurse=True, action=None, save=True):
+        assert isinstance(recurse, bool)
+        if action is None:
+            action = bzrlib.add.AddAction()
+        # TODO: use action
+        if not file_list:
+            # no paths supplied: add the entire tree.
+            file_list = [u'.']
+        ignored = {}
+        added = []
+
+        for file_path in file_list:
+            todo = []
+            file_path = os.path.abspath(file_path)
+            f = self.relpath(file_path)
+            wc = self._get_wc(os.path.dirname(f), write_lock=True)
+            try:
+                if not self.inventory.has_filename(f):
+                    if save:
+                        mutter('adding %r' % file_path)
+                        svn.wc.add2(file_path, wc, None, 0, None, None, None)
+                    added.append(file_path)
+                if recurse and file_kind(file_path) == 'directory':
+                    # Filter out ignored files and update ignored
+                    for c in os.listdir(file_path):
+                        if self.is_control_filename(c):
+                            continue
+                        c_path = os.path.join(file_path, c)
+                        ignore_glob = self.is_ignored(c)
+                        if ignore_glob is not None:
+                            ignored.setdefault(ignore_glob, []).append(c_path)
+                        todo.append(c_path)
+            finally:
+                svn.wc.adm_close(wc)
+            if todo != []:
+                cadded, cignored = self.smart_add(todo, recurse, action, save)
+                added.extend(cadded)
+                ignored.update(cignored)
+        return added, ignored
+
     def add(self, files, ids=None, kinds=None):
-        # FIXME: Use kinds
+        # TODO: Use kinds
         if isinstance(files, str):
             files = [files]
             if isinstance(ids, str):
@@ -480,8 +521,8 @@
             ids.reverse()
         assert isinstance(files, list)
         for f in files:
+            wc = self._get_wc(os.path.dirname(f), write_lock=True)
             try:
-                wc = self._get_wc(os.path.dirname(f), write_lock=True)
                 try:
                     svn.wc.add2(os.path.join(self.basedir, f), wc, None, 0, 
                             None, None, None)
@@ -628,6 +669,17 @@
 
         return []
 
+    def _reset_data(self):
+        pass
+
+    def unlock(self):
+        # reverse order of locking.
+        try:
+            return self._control_files.unlock()
+        finally:
+            self.branch.unlock()
+
+
 
 class SvnWorkingTreeFormat(WorkingTreeFormat):
     """Subversion working copy format."""




More information about the bazaar-commits mailing list