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