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