Rev 439: Initial work on support for svn:externals. in file:///home/jelmer/bzr-svn/nestedtrees/

Jelmer Vernooij jelmer at samba.org
Mon Mar 19 15:06:15 GMT 2007


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

------------------------------------------------------------
revno: 439
revision-id: jelmer at samba.org-20070319150611-xqvlyc1japnb1f4z
parent: jelmer at samba.org-20070319112336-stvy6ag2g60xhxva
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: nestedtrees
timestamp: Mon 2007-03-19 16:06:11 +0100
message:
  Initial work on support for svn:externals.
modified:
  __init__.py                    __init__.py-20051008155114-eae558e6cf149e1d
  errors.py                      errors.py-20061226172623-w1sbj8ynpo0eojqp-1
  fetch.py                       fetch.py-20060625004942-x2lfaib8ra707a8p-1
  tests/test_repos.py            test_repos.py-20060508151940-ddc49a59257ca712
  tests/test_tree.py             test_tree.py-20070103204350-pr8nupes7e5sd2wr-1
  tree.py                        tree.py-20060624222557-dudlwqcmkf22lt2s-1
=== modified file '__init__.py'
--- a/__init__.py	2007-03-19 11:23:36 +0000
+++ b/__init__.py	2007-03-19 15:06:11 +0000
@@ -87,7 +87,6 @@
         warning('Needs at least sqlite 3.3.x')
         raise bzrlib.errors.BzrError("incompatible sqlite library")
 
-check_bzrlib_version(required_bzr_version)
 check_bzrlib_version(compatible_bzr_versions)
 check_subversion_version()
 check_pysqlite_version()

=== modified file 'errors.py'
--- a/errors.py	2007-03-17 21:25:43 +0000
+++ b/errors.py	2007-03-19 15:06:11 +0000
@@ -18,6 +18,11 @@
 
 import svn.core
 
+
+class InvalidExternalsDescription(BzrError):
+    _fmt = """Unable to parse externals description."""
+
+
 class NotSvnBranchPath(BzrError):
     _fmt = """{%(branch_path)s}:%(revnum)s is not a valid Svn branch path"""
 

=== modified file 'fetch.py'
--- a/fetch.py	2007-03-19 11:23:36 +0000
+++ b/fetch.py	2007-03-19 15:06:11 +0000
@@ -33,7 +33,8 @@
 from fileids import generate_file_id
 from repository import (SvnRepository, SVN_PROP_BZR_MERGE, SVN_PROP_SVK_MERGE,
                 SVN_PROP_BZR_REVPROP_PREFIX, SvnRepositoryFormat)
-from tree import apply_txdelta_handler
+from tree import (apply_txdelta_handler, parse_externals_description, 
+                  inventory_add_external)
 
 
 def md5_strings(strings):
@@ -59,6 +60,7 @@
         self._revprops = {}
         self._svn_revprops = svn_revprops
         self.pool = Pool()
+        self.externals = {}
 
     def _get_revision(self, revid):
         if self._parent_ids is None:
@@ -129,6 +131,14 @@
         if not file_weave.has_version(self.revid):
             file_weave.add_lines(self.revid, self.dir_baserev[id], [])
 
+        if self.externals.has_key(id):
+            # Add externals. This happens after all children have been added
+            # as they can be grandchildren.
+            for (name, (rev, url)) in self.externals[id].items():
+                inventory_add_external(self.inventory, id, name, rev, url)
+            # FIXME: Externals could've disappeared or only changed. Need 
+            # to keep track of them.
+
     def add_directory(self, path, parent_id, copyfrom_path, copyfrom_revnum, pool):
         path = path.decode("utf-8")
         file_id = self._get_new_id(parent_id, path)
@@ -182,6 +192,8 @@
                       svn.core.SVN_PROP_ENTRY_UUID,
                       svn.core.SVN_PROP_EXECUTABLE):
             pass
+        elif name == svn.core.SVN_PROP_EXTERNALS:
+            self.externals[id] = parse_externals_description(value)
         elif name.startswith(svn.core.SVN_PROP_WC_PREFIX):
             pass
         else:
@@ -198,6 +210,8 @@
             self.is_symlink = (value != None)
         elif name == svn.core.SVN_PROP_ENTRY_COMMITTED_REV:
             self.last_file_rev = int(value)
+        elif name == svn.core.SVN_PROP_EXTERNALS:
+            mutter('svn:externals property on file!')
         elif name in (svn.core.SVN_PROP_ENTRY_COMMITTED_DATE,
                       svn.core.SVN_PROP_ENTRY_LAST_AUTHOR,
                       svn.core.SVN_PROP_ENTRY_LOCK_TOKEN,

=== modified file 'tests/test_repos.py'
--- a/tests/test_repos.py	2007-03-16 12:09:02 +0000
+++ b/tests/test_repos.py	2007-03-19 15:06:11 +0000
@@ -670,6 +670,29 @@
         newrepos = dir.create_repository()
         oldrepos.copy_content_into(newrepos)
 
+    def test_fetch_externals(self):
+        repos_url1 = self.make_client('d1', 'dc1')
+        self.build_tree({'dc1/proj1/trunk/file': "data"})
+        self.client_add("dc1/proj1")
+        self.client_commit("dc1", "My Message")
+
+        repos_url2 = self.make_client('d2', 'dc2')
+        self.build_tree({'dc2/somedir': None})
+        self.client_add("dc2/somedir")
+        self.client_set_prop("dc2/somedir", "svn:externals", 
+                str("bla\t%s/proj1/trunk\n" % repos_url1))
+        self.client_commit("dc2", "My Message")
+
+        oldrepos = Repository.open(repos_url2)
+        dir = BzrDir.create("f")
+        newrepos = dir.create_repository()
+        oldrepos.copy_content_into(newrepos)
+        inv = oldrepos.get_inventory(oldrepos.generate_revision_id(0, ""))
+        self.assertTrue(inv.has_filename("somedir/bla"))
+        self.assertTrue(inv.has_filename("somedir/bla/file"))
+        self.assertEqual(inv.path2id("somedir/bla"), "blaid")
+        self.assertEqual(inv.path2id("somedir/bla/file"), "blaid")
+
     def test_fetch_special_char(self):
         repos_url = self.make_client('d', 'dc')
         self.build_tree({u'dc/trunk/f\x2cle': "data"})

=== modified file 'tests/test_tree.py'
--- a/tests/test_tree.py	2007-01-14 05:57:00 +0000
+++ b/tests/test_tree.py	2007-03-19 15:06:11 +0000
@@ -19,7 +19,7 @@
 from bzrlib.tests import TestCase
 from bzrlib.workingtree import WorkingTree
 
-from tree import SvnBasisTree
+from tree import SvnBasisTree, parse_externals_description
 from tests import TestCaseWithSubversionRepository
 
 class TestBasisTree(TestCaseWithSubversionRepository):
@@ -86,3 +86,25 @@
         self.assertFalse(tree.inventory[tree.inventory.path2id("file")].executable)
         self.assertFalse(wt.inventory[wt.inventory.path2id("file")].executable)
 
+
+class TestExternalsParser(TestCase):
+    def test_parse_externals(self):
+        self.assertEqual({
+                'third-party/sounds': (None, "http://sounds.red-bean.com/repos"),
+                'third-party/skins': (None, "http://skins.red-bean.com/repositories/skinproj"),
+                'third-party/skins/toolkit': (21, "http://svn.red-bean.com/repos/skin-maker")},
+            parse_externals_description(
+"""third-party/sounds             http://sounds.red-bean.com/repos
+third-party/skins              http://skins.red-bean.com/repositories/skinproj
+third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker"""))
+
+    def test_parse_comment(self):
+        self.assertEqual({
+            'third-party/sounds': (None, "http://sounds.red-bean.com/repos")
+                },
+            parse_externals_description(
+"""
+
+third-party/sounds             http://sounds.red-bean.com/repos
+#third-party/skins              http://skins.red-bean.com/repositories/skinproj
+#third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker"""))

=== modified file 'tree.py'
--- a/tree.py	2007-03-16 12:09:02 +0000
+++ b/tree.py	2007-03-19 15:06:11 +0000
@@ -34,6 +34,46 @@
 import svn.core, svn.wc, svn.delta
 from svn.core import SubversionException, Pool
 
+from errors import InvalidExternalsDescription
+
+def parse_externals_description(val):
+    """Parse an svn:externals property value.
+
+    :returns: dictionary with local names as keys, (revnum, url)
+              as value. revnum is the revision number and is 
+              set to None if not applicable.
+    """
+    ret = {}
+    for l in val.splitlines():
+        if l == "" or l[0] == "#":
+            continue
+        pts = l.rsplit(None, 2) 
+        if len(pts) == 3:
+            assert pts[1].startswith("-r")
+            ret[pts[0]] = (int(pts[1][2:]), pts[2])
+        elif len(pts) == 2:
+            ret[pts[0]] = (None, pts[1])
+        else:
+            raise InvalidExternalsDescription
+    return ret
+
+
+def inventory_add_external(inv, parent_id, name, rev, url):
+    """Add an svn:externals entry to an inventory as a tree-reference.
+    
+    :param inv: Inventory to add to.
+    :param parent_id: File id of directory the entry was set on.
+    :param name: Name of the entry, relative to entry with parent_id.
+    :param rev: Revision of tree that's being referenced, or None if no 
+                specific revision is being referenced.
+    :param url: URL of referenced tree.
+    """
+    # FIXME: Find id of parent of name
+    (dir, base) = os.path.split(name)
+    os.path.join(inv.id2path(dir), dir)
+    pass
+
+
 def apply_txdelta_handler(src_stream, target_stream, pool):
     assert hasattr(src_stream, 'read')
     assert hasattr(target_stream, 'write')
@@ -79,6 +119,7 @@
         self.last_revnum = {}
         self.dir_revnum = {}
         self.dir_ignores = {}
+        self.externals = {}
         self.pool = pool
 
     def set_target_revision(self, revnum):
@@ -106,6 +147,8 @@
             self.dir_revnum[id] = int(value)
         elif name == svn.core.SVN_PROP_IGNORE:
             self.dir_ignores[id] = value
+        elif name == svn.core.SVN_PROP_EXTERNALS:
+            self.externals[id] = parse_externals_description(value)
         elif name == SVN_PROP_BZR_MERGE or name == SVN_PROP_SVK_MERGE:
             if id != self.tree._inventory.root.file_id:
                 mutter('%r set on non-root dir!' % SVN_PROP_BZR_MERGE)
@@ -132,6 +175,8 @@
             self.is_executable = (value != None)
         elif name == svn.core.SVN_PROP_SPECIAL:
             self.is_symlink = (value != None)
+        elif name == svn.core.SVN_PROP_EXTERNALS:
+            mutter('svn:externals property on file!')
         elif name == svn.core.SVN_PROP_ENTRY_COMMITTED_REV:
             self.last_file_rev = int(value)
         elif name in (svn.core.SVN_PROP_ENTRY_COMMITTED_DATE,
@@ -151,10 +196,16 @@
         self.is_executable = False
         return path
 
-    def close_dir(self, id):
+    def close_directory(self, id):
         if id in self.tree._inventory and self.dir_ignores.has_key(id):
             self.tree._inventory[id].ignores = self.dir_ignores[id]
 
+        if self.externals.has_key(id):
+            # Add externals. This happens after all children have been added
+            # as they can be grandchildren.
+            for name in self.externals[id]:
+                inventory_add_external(self.inventory, id, name, rev, url)
+
     def close_file(self, path, checksum):
         file_id, revision_id = self.tree.id_map[path]
         if self.is_symlink:




More information about the bazaar-commits mailing list