Rev 374: Improve working tree handling. in http://people.samba.org/bzr/jelmer/bzr-svn/bzr.dev

Jelmer Vernooij jelmer at samba.org
Wed Jan 3 22:25:34 GMT 2007


------------------------------------------------------------
revno: 374
revision-id: jelmer at samba.org-20070103222400-7bjzm0ys0s39japf
parent: jelmer at samba.org-20070103121508-uccwc69drzhsk496
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: main
timestamp: Wed 2007-01-03 23:24:00 +0100
message:
  Improve working tree handling.
added:
  tests/test_tree.py             test_tree.py-20070103204350-pr8nupes7e5sd2wr-1
modified:
  checkout.py                    workingtree.py-20060306120941-b083cb0fdd4a69de
  repository.py                  repository.py-20060306123302-1f8c5069b3fe0265
  tests/__init__.py              __init__.py-20060508151940-e9f4d914801a2535
  tests/test_commit.py           test_commit.py-20060624213521-l5kcufywkh9mnilk-1
  tests/test_fileids.py          test_fileids.py-20060622131341-19gyrlgqy8yl2od5-1
  tests/test_workingtree.py      test_workingtree.py-20060622191524-0di7bc3q1ckdbybb-1
  tree.py                        tree.py-20060624222557-dudlwqcmkf22lt2s-1
=== added file 'tests/test_tree.py'
--- a/tests/test_tree.py	1970-01-01 00:00:00 +0000
+++ b/tests/test_tree.py	2007-01-03 22:24:00 +0000
@@ -0,0 +1,75 @@
+# Copyright (C) 2007 Jelmer Vernooij <jelmer at samba.org>
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from bzrlib.bzrdir import BzrDir
+from bzrlib.errors import NoRepositoryPresent
+from bzrlib.tests import TestCase
+from bzrlib.workingtree import WorkingTree
+
+from tree import SvnBasisTree
+from tests import TestCaseWithSubversionRepository
+
+class TestBasisTree(TestCaseWithSubversionRepository):
+    def test_executable(self):
+        repos_url = self.make_client("d", "dc")
+        self.build_tree({"dc/file": "x"})
+        self.client_add("dc/file")
+        self.client_set_prop("dc/file", "svn:executable", "*")
+        self.client_commit("dc", "executable")
+        self.client_update("dc")
+        tree = SvnBasisTree(WorkingTree.open("dc"))
+        self.assertTrue(tree.is_executable(tree.inventory.path2id("file")))
+
+    def test_executable_changed(self):
+        repos_url = self.make_client("d", "dc")
+        self.build_tree({"dc/file": "x"})
+        self.client_add("dc/file")
+        self.client_commit("dc", "executable")
+        self.client_update("dc")
+        self.client_set_prop("dc/file", "svn:executable", "*")
+        tree = SvnBasisTree(WorkingTree.open("dc"))
+        self.assertFalse(tree.is_executable(tree.inventory.path2id("file")))
+
+    def test_symlink(self):
+        repos_url = self.make_client("d", "dc")
+        import os
+        os.symlink("target", "dc/file")
+        self.build_tree({"dc/file": "x"})
+        self.client_add("dc/file")
+        self.client_commit("dc", "symlink")
+        self.client_update("dc")
+        tree = SvnBasisTree(WorkingTree.open("dc"))
+        self.assertEqual('symlink', 
+                         tree.inventory[tree.inventory.path2id("file")].kind)
+        self.assertEqual("target",
+                         tree.inventory[tree.inventory.path2id("file")].symlink_target)
+
+    def test_symlink_next(self):
+        repos_url = self.make_client("d", "dc")
+        import os
+        os.symlink("target", "dc/file")
+        self.build_tree({"dc/file": "x", "dc/bla": "p"})
+        self.client_add("dc/file")
+        self.client_add("dc/bla")
+        self.client_commit("dc", "symlink")
+        self.build_tree({"dc/bla": "p"})
+        self.client_commit("dc", "change")
+        self.client_update("dc")
+        tree = SvnBasisTree(WorkingTree.open("dc"))
+        self.assertEqual('symlink', 
+                         tree.inventory[tree.inventory.path2id("file")].kind)
+        self.assertEqual("target",
+                         tree.inventory[tree.inventory.path2id("file")].symlink_target)

=== modified file 'checkout.py'
--- a/checkout.py	2007-01-03 12:15:08 +0000
+++ b/checkout.py	2007-01-03 22:24:00 +0000
@@ -19,7 +19,7 @@
 from bzrlib.errors import (InvalidRevisionId, NotBranchError, NoSuchFile,
                            NoRepositoryPresent)
 from bzrlib.inventory import (Inventory, InventoryDirectory, InventoryFile,
-                              ROOT_ID)
+                              InventoryLink, ROOT_ID)
 from bzrlib.lockable_files import TransportLock, LockableFiles
 from bzrlib.lockdir import LockDir
 from bzrlib.osutils import rand_bytes, fingerprint_file
@@ -54,10 +54,23 @@
         self.client_ctx.log_msg_func2 = svn.client.svn_swig_py_get_commit_log_func
         self.client_ctx.log_msg_baton2 = self.log_message_func
 
-        self._set_inventory(self.read_working_inventory(), dirty=False)
+        wc = self._get_wc()
+        try:
+            self.base_revnum = svn.wc.get_ancestry(self.basedir, wc)[1]
+        finally:
+            svn.wc.adm_close(wc)
 
         self.base_revid = branch.repository.generate_revision_id(
                     self.base_revnum, branch.branch_path)
+
+        if self.base_revid != NULL_REVISION:
+            (bp, rev) = self.branch.repository.parse_revision_id(self.base_revid)
+            self.base_fileids = self.branch.repository.get_fileid_map(rev, bp)
+        else:
+            self.base_fileids = {"": (ROOT_ID, NULL_REVISION)}
+
+        self._set_inventory(self.read_working_inventory(), dirty=False)
+
         self.controldir = os.path.join(self.basedir, svn.wc.get_adm_dir(), 'bzr')
         try:
             os.makedirs(self.controldir)
@@ -77,7 +90,7 @@
         pass
 
     def get_ignore_list(self):
-        ignores = svn.wc.get_default_ignores(svn_config)
+        ignores = [svn.wc.get_adm_dir()] + svn.wc.get_default_ignores(svn_config)
 
         def dir_add(wc, prefix):
             ignorestr = svn.wc.prop_get(svn.core.SVN_PROP_IGNORE, self.abspath(prefix).rstrip("/"), wc)
@@ -112,22 +125,6 @@
     def _write_inventory(self, inv):
         pass
 
-    def is_ignored(self, filename):
-        if svn.wc.is_adm_dir(os.path.basename(filename)):
-            return True
-
-        (wc, name) = self._get_rel_wc(filename)
-        assert wc
-        try:
-            ignores = svn.wc.get_ignores(svn_config, wc)
-            from fnmatch import fnmatch
-            for pattern in ignores:
-                if fnmatch(name, pattern):
-                    return True
-            return False
-        finally:
-            svn.wc.adm_close(wc)
-
     def is_control_filename(self, path):
         return svn.wc.is_adm_dir(path)
 
@@ -175,22 +172,44 @@
         finally:
             svn.wc.adm_close(to_wc)
 
+    def path_to_file_id(self, revnum, current_revnum, path):
+        """Generate a bzr file id from a Subversion file name. 
+        
+        :param revnum: Revision number.
+        :param path: Absolute path.
+        :return: Tuple with file id and revision id.
+        """
+        assert isinstance(revnum, int) and revnum >= 0
+        assert isinstance(path, basestring)
+
+        (bp, rp) = self.branch.repository.scheme.unprefix(path)
+        return self.base_fileids[rp]
+
     def read_working_inventory(self):
         inv = Inventory()
 
         def add_file_to_inv(relpath, id, revid, parent_id):
             """Add a file to the inventory."""
-            file = InventoryFile(id, os.path.basename(relpath), parent_id)
-            file.revision = revid
-            try:
-                data = fingerprint_file(open(self.abspath(relpath)))
-                file.text_sha1 = data['sha1']
-                file.text_size = data['size']
-                file.executable = self.is_executable(id, relpath)
+            if os.path.islink(self.abspath(relpath)):
+                file = InventoryLink(id, os.path.basename(relpath), parent_id)
+                file.revision = revid
+                file.symlink_target = os.readlink(self.abspath(relpath))
+                file.text_sha1 = None
+                file.text_size = None
+                file.executable = False
                 inv.add(file)
-            except IOError:
-                # Ignore non-existing files
-                pass
+            else:
+                file = InventoryFile(id, os.path.basename(relpath), parent_id)
+                file.revision = revid
+                try:
+                    data = fingerprint_file(open(self.abspath(relpath)))
+                    file.text_sha1 = data['sha1']
+                    file.text_size = data['size']
+                    file.executable = self.is_executable(id, relpath)
+                    inv.add(file)
+                except IOError:
+                    # Ignore non-existing files
+                    pass
 
         def find_copies(url, relpath=""):
             wc = self._get_wc(relpath)
@@ -219,7 +238,7 @@
                 assert entry.revision >= 0
                 # Keep old id
                 mutter('stay: %r' % relpath)
-                return self.branch.repository.path_to_file_id(entry.revision, 
+                return self.path_to_file_id(entry.cmt_rev, entry.revision, 
                         relpath)
             elif entry.schedule == svn.wc.schedule_delete:
                 return (None, None)
@@ -229,22 +248,18 @@
                 # and has no other copies -> in that case, take id of other file
                 mutter('copies(%r): %r' % (relpath, list(find_copies(entry.copyfrom_url))))
                 if entry.copyfrom_url and list(find_copies(entry.copyfrom_url)) == [relpath]:
-                    return self.branch.repository.path_to_file_id(entry.copyfrom_rev,
-                        entry.copyfrom_url[len(entry.repos):])
+                    return self.path_to_file_id(entry.copyfrom_rev, entry.revision,
+                            entry.copyfrom_url[len(entry.repos):])
                 return ("NEW-" + escape_svn_path(entry.url[len(entry.repos):].strip("/")), None)
 
         def add_dir_to_inv(relpath, wc, parent_id):
             entries = svn.wc.entries_read(wc, False)
-
             entry = entries[""]
-            
             (id, revid) = find_ids(entry)
-
             if id is None:
+                mutter('no id for %r' % entry.url)
                 return
 
-            self.base_revnum = max(self.base_revnum, entry.revision)
-
             # First handle directory itself
             if relpath == "":
                 inv.add_path("", 'directory', ROOT_ID)
@@ -273,7 +288,7 @@
                 else:
                     (subid, subrevid) = find_ids(entry)
                     if subid:
-                        self.base_revnum = max(self.base_revnum, entry.revision)
+                        mutter('no id for %r' % entry.url)
                         add_file_to_inv(subrelpath, subid, subrevid, id)
 
         wc = self._get_wc() 
@@ -288,8 +303,11 @@
         mutter('setting last revision to %r' % revid)
         if revid is None or revid == NULL_REVISION:
             self.base_revid = revid
+            self.base_fileids = self.branch.repository.get_fileid_map(0, "")
             return
 
+        (bp, rev) = self.branch.repository.parse_revision_id(revid)
+        self.base_fileids = self.branch.repository.get_fileid_map(rev, bp)
         # TODO: Implement more efficient version
         newrev = self.branch.repository.get_revision(revid)
         newrevtree = self.branch.repository.revision_tree(revid)
@@ -327,7 +345,6 @@
             svn.wc.adm_close(wc)
         self.base_revid = revid
 
-
     def log_message_func(self, items, pool):
         """ Simple log message provider for unit tests. """
         return self._message
@@ -352,6 +369,8 @@
                 commit_info.revision, self.branch.branch_path)
 
         self.base_revid = revid
+        self.base_fileids = self.branch.repository.get_fileid_map(
+                commit_info.revision, self.branch.branch_path)
         #FIXME: Use public API:
         self.branch.revision_history()
         self.branch._revision_history.append(revid)
@@ -383,7 +402,7 @@
         if self.base_revid is None or self.base_revid == NULL_REVISION:
             return self.branch.repository.revision_tree(self.base_revid)
 
-        return SvnBasisTree(self, self.base_revid)
+        return SvnBasisTree(self)
 
     def pull(self, source, overwrite=False, stop_revision=None):
         if stop_revision is None:
@@ -398,7 +417,6 @@
     def get_file_sha1(self, file_id, path=None, stat_value=None):
         if not path:
             path = self._inventory.id2path(file_id)
-
         return fingerprint_file(open(self.abspath(path)))['sha1']
 
     def _get_bzr_merges(self):

=== modified file 'repository.py'
--- a/repository.py	2007-01-03 11:19:26 +0000
+++ b/repository.py	2007-01-03 22:24:00 +0000
@@ -245,28 +245,6 @@
         return self.fileid_map.apply_changes(uuid, revnum, branch, changes, 
                                              renames)
 
-    def path_to_file_id(self, revnum, path):
-        """Generate a bzr file id from a Subversion file name. 
-        
-        :param revnum: Revision number.
-        :param path: Absolute path.
-        :return: Tuple with file id and revision id.
-        """
-        assert isinstance(revnum, int) and revnum >= 0
-        assert isinstance(path, basestring)
-
-        if revnum == 0:
-            from fileids import generate_svn_file_id
-            return (generate_svn_file_id(self.uuid, 0, "", ""), NULL_REVISION)
-
-        (bp, rp) = self.scheme.unprefix(path)
-
-        map = self.get_fileid_map(revnum, bp)
-        try:
-            return map[rp]
-        except KeyError:
-            raise NoSuchFile(path=rp)
-
     def all_revision_ids(self):
         for (bp, rev) in self.follow_history(self.transport.get_latest_revnum()):
             yield self.generate_revision_id(rev, bp)

=== modified file 'tests/__init__.py'
--- a/tests/__init__.py	2006-12-31 20:57:48 +0000
+++ b/tests/__init__.py	2007-01-03 22:24:00 +0000
@@ -240,6 +240,7 @@
             'test_repos', 
             'test_scheme', 
             'test_transport',
+            'test_tree',
             'test_workingtree']
     suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names]))
 

=== modified file 'tests/test_commit.py'
--- a/tests/test_commit.py	2007-01-01 22:18:53 +0000
+++ b/tests/test_commit.py	2007-01-03 22:24:00 +0000
@@ -36,6 +36,7 @@
         self.assertEqual(
             "svn-v%d:1@%s-" % (MAPPING_VERSION, wt.branch.repository.uuid), 
                          wt.commit(message="data"))
+        self.client_update("dc")
         self.assertEqual(
             "svn-v%d:1@%s-" % (MAPPING_VERSION, wt.branch.repository.uuid), 
                          wt.branch.last_revision())

=== modified file 'tests/test_fileids.py'
--- a/tests/test_fileids.py	2007-01-03 11:19:26 +0000
+++ b/tests/test_fileids.py	2007-01-03 22:24:00 +0000
@@ -143,8 +143,8 @@
         self.assertEqual(inv1.path2id("dir"), inv2.path2id("dir"))
         self.assertEqual(inv1.path2id("dir/file"), inv2.path2id("dir/file"))
 
-        fileid, revid = repository.path_to_file_id(2, 
-                            "branches/mybranch/dir/file")
+        fileid, revid = repository.get_fileid_map(2, 
+                            "branches/mybranch")["dir/file"]
         self.assertEqual(fileid, inv1.path2id("dir/file"))
         self.assertEqual(
                 "svn-v%d:1@%s-trunk" % (MAPPING_VERSION, repository.uuid), 

=== modified file 'tests/test_workingtree.py'
--- a/tests/test_workingtree.py	2007-01-03 12:15:08 +0000
+++ b/tests/test_workingtree.py	2007-01-03 22:24:00 +0000
@@ -75,13 +75,13 @@
     def test_get_ignore_list_empty(self):
         self.make_client('a', 'dc')
         tree = WorkingTree.open("dc")
-        self.assertEqual(svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" "), tree.get_ignore_list())
+        self.assertEqual([".svn"] + svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" "), tree.get_ignore_list())
 
     def test_get_ignore_list_onelevel(self):
         self.make_client('a', 'dc')
         self.client_set_prop("dc", "svn:ignore", "*.d\n*.c\n")
         tree = WorkingTree.open("dc")
-        self.assertEqual(svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" ") + ["./*.d", "./*.c"], tree.get_ignore_list())
+        self.assertEqual([".svn"] + svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" ") + ["./*.d", "./*.c"], tree.get_ignore_list())
 
     def test_get_ignore_list_morelevel(self):
         self.make_client('a', 'dc')
@@ -90,9 +90,7 @@
         self.client_add("dc/x")
         self.client_set_prop("dc/x", "svn:ignore", "*.e\n")
         tree = WorkingTree.open("dc")
-        self.assertEqual(svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" ") + ["./*.d", "./*.c", "./x/*.e"], tree.get_ignore_list())
-
-
+        self.assertEqual([".svn"] + svn.core.SVN_CONFIG_DEFAULT_GLOBAL_IGNORES.split(" ") + ["./*.d", "./*.c", "./x/*.e"], tree.get_ignore_list())
 
     def test_add_reopen(self):
         self.make_client('a', 'dc')
@@ -165,6 +163,7 @@
         self.build_tree({"dc/bl": "data"})
         self.client_add("dc/bl")
         self.client_commit("dc", "Bla")
+        self.client_update("dc")
         tree = WorkingTree.open("dc")
         self.assertEqual(
             "svn-v%d:1@%s-" % (MAPPING_VERSION, tree.branch.repository.uuid),
@@ -288,6 +287,16 @@
         inv = tree.read_working_inventory()
         self.assertTrue(inv[inv.path2id("bla")].executable)
 
+    def test_symlink(self):
+        self.make_client('a', 'dc')
+        import os
+        os.symlink("target", "dc/bla")
+        self.client_add("dc/bla")
+        tree = WorkingTree.open("dc")
+        inv = tree.read_working_inventory()
+        self.assertEqual('symlink', inv[inv.path2id("bla")].kind)
+        self.assertEqual("target", inv[inv.path2id("bla")].symlink_target)
+
     def test_pending_merges(self):
         self.make_client('a', 'dc')
         self.build_tree({"dc/bl": None})

=== modified file 'tree.py'
--- a/tree.py	2007-01-03 12:15:08 +0000
+++ b/tree.py	2007-01-03 22:24:00 +0000
@@ -55,8 +55,7 @@
         self._revision_id = revision_id
         pool = Pool()
         (self.branch_path, self.revnum) = repository.parse_revision_id(revision_id)
-        self._inventory = Inventory(ROOT_ID)
-        self._inventory.revision_id = revision_id
+        self._inventory = Inventory()
         self.id_map = repository.get_fileid_map(self.revnum, self.branch_path)
         self.editor = TreeBuildEditor(self, pool)
         self.file_data = {}
@@ -193,14 +192,89 @@
         return apply_txdelta_handler(StringIO(""), self.file_stream, self.pool)
 
 
-class SvnBasisTree(SvnRevisionTree):
+class SvnBasisTree(RevisionTree):
     """Optimized version of SvnRevisionTree."""
-    def __init__(self, workingtree, revid):
-        super(SvnBasisTree, self).__init__(workingtree.branch.repository, revid)
+    def __init__(self, workingtree):
         self.workingtree = workingtree
+        self._revision_id = workingtree.branch.repository.generate_revision_id(
+                workingtree.base_revnum, workingtree.branch.branch_path)
+        self.id_map = workingtree.branch.repository.get_fileid_map(
+                workingtree.base_revnum, workingtree.branch.branch_path)
+        self._inventory = Inventory()
+        self._repository = workingtree.branch.repository
+
+        def _get_props(relpath):
+            path = self.workingtree.abspath(relpath)
+            wc = workingtree._get_wc()
+            try:
+                return svn.wc.get_prop_diffs(path, wc)
+            finally:
+                svn.wc.adm_close(wc)
+
+        def add_file_to_inv(relpath, id, revid):
+            props = _get_props(relpath)
+            if props.has_key(svn.core.SVN_PROP_SPECIAL):
+                ie = self._inventory.add_path(relpath, 'symlink', id)
+                ie.symlink_target = open(self._abspath(relpath)).read()[len("link "):]
+                ie.text_sha1 = None
+                ie.text_size = None
+                ie.text_id = None
+            else:
+                ie = self._inventory.add_path(relpath, 'file', id)
+                data = osutils.fingerprint_file(open(self._abspath(relpath)))
+                ie.text_sha1 = data['sha1']
+                ie.text_size = data['size']
+            ie.executable = props.has_key(svn.core.SVN_PROP_EXECUTABLE)
+            ie.revision = revid
+            return ie
+
+        def find_ids(entry):
+            relpath = entry.url[len(entry.repos):].strip("/")
+            if entry.schedule in (svn.wc.schedule_normal, 
+                                  svn.wc.schedule_delete, 
+                                  svn.wc.schedule_replace):
+                return self.id_map[workingtree.branch.repository.scheme.unprefix(relpath)[1]]
+
+        def add_dir_to_inv(relpath, wc, parent_id):
+            entries = svn.wc.entries_read(wc, False)
+            entry = entries[""]
+            (id, revid) = find_ids(entry)
+
+            # First handle directory itself
+            ie = self._inventory.add_path(relpath, 'directory', id)
+            ie.revision = revid
+
+            for name in entries:
+                if name == "":
+                    continue
+
+                subrelpath = os.path.join(relpath, name)
+
+                entry = entries[name]
+                assert entry
+                
+                if entry.kind == svn.core.svn_node_dir:
+                    subwc = svn.wc.adm_open3(wc, 
+                            self.workingtree.abspath(subrelpath), 
+                                             False, 0, None)
+                    try:
+                        add_dir_to_inv(subrelpath, subwc, id)
+                    finally:
+                        svn.wc.adm_close(subwc)
+                else:
+                    (subid, subrevid) = find_ids(entry)
+                    add_file_to_inv(subrelpath, subid, subrevid)
+
+        wc = workingtree._get_wc() 
+        try:
+            add_dir_to_inv("", wc, None)
+        finally:
+            svn.wc.adm_close(wc)
+
+    def _abspath(self, relpath):
+        return svn.wc.get_pristine_copy_path(self.workingtree.abspath(relpath))
 
     def get_file_lines(self, file_id):
-        path = self.id2path(file_id)
-        base_copy = svn.wc.get_pristine_copy_path(self.workingtree.abspath(path))
+        base_copy = self._abspath(self.id2path(file_id))
         return osutils.split_lines(open(base_copy).read())
 




More information about the bazaar-commits mailing list