Rev 2413: Most of the integration of dirstate and subtree in http://sourcefrog.net/bzr/dirstate-plus-subtree
Martin Pool
mbp at sourcefrog.net
Mon Feb 26 14:24:49 GMT 2007
At http://sourcefrog.net/bzr/dirstate-plus-subtree
------------------------------------------------------------
revno: 2413
revision-id: mbp at sourcefrog.net-20070226142446-sfvznepsaggqnril
parent: mbp at sourcefrog.net-20070226044445-g0o7a0u9qypajqv2
committer: Martin Pool <mbp at sourcefrog.net>
branch nick: dirstate-plus-subtree
timestamp: Tue 2007-02-27 01:24:46 +1100
message:
Most of the integration of dirstate and subtree
modified:
bzrlib/bzrdir.py bzrdir.py-20060131065624-156dfea39c4387cb
bzrlib/dirstate.py dirstate.py-20060728012006-d6mvoihjb3je9peu-1
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/inventory.py inventory.py-20050309040759-6648b84ca2005b37
bzrlib/mutabletree.py mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
bzrlib/tests/test_bzrdir.py test_bzrdir.py-20060131065654-deba40eef51cf220
bzrlib/tests/tree_implementations/test_tree.py test_tree.py-20061215160206-usu7lwcj8aq2n3br-1
bzrlib/tests/workingtree_implementations/test_add_reference.py test_add_reference.p-20061211024451-yo9i1691dgbv1eyn-1
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'bzrlib/bzrdir.py'
--- a/bzrlib/bzrdir.py 2007-02-22 05:51:57 +0000
+++ b/bzrlib/bzrdir.py 2007-02-26 14:24:46 +0000
@@ -2087,40 +2087,46 @@
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
"""
- def register_metadir(self, key, repo, help, native=True, deprecated=False,
- branch_format=None, tree='WorkingTreeFormat3'):
+ def register_metadir(self, key,
+ repository_format, help, native=True, deprecated=False,
+ branch_format=None,
+ tree_format=None):
"""Register a metadir subformat.
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
by the Repository format.
- :param repo: The fully-qualified repository format class name as a
- string.
+ :param repository_format: The fully-qualified repository format class
+ name as a string.
+ :param branch_format: Fully-qualified branch format class name as
+ a string.
+ :param tree_format: Fully-qualified tree format class name as
+ a string.
"""
# This should be expanded to support setting WorkingTree and Branch
# formats, once BzrDirMetaFormat1 supports that.
- def helper():
- import bzrlib.branch
- import bzrlib.repository
- import bzrlib.workingtree
- tree_format = getattr(bzrlib.workingtree, tree)
- mod_name, repo_factory_name = repo.rsplit('.', 1)
+ def _load(full_name):
+ mod_name, factory_name = full_name.rsplit('.', 1)
try:
mod = __import__(mod_name, globals(), locals(),
- [repo_factory_name])
+ [factory_name])
except ImportError, e:
- raise ImportError('failed to load repository %s: %s'
- % (repo, e))
+ raise ImportError('failed to load %s: %s' % (full_name, e))
try:
- repo_format_class = getattr(mod, repo_factory_name)
+ factory = getattr(mod, factory_name)
except AttributeError:
- raise AttributeError('no repository format %r in module %r'
- % (repo, mod))
+ raise AttributeError('no factory %s in module %r'
+ % (full_name, mod))
+ return factory()
+
+ def helper():
bd = BzrDirMetaFormat1()
- bd.workingtree_format = tree_format()
- bd.repository_format = repo_format_class()
if branch_format is not None:
- bd.set_branch_format(getattr(bzrlib.branch, branch_format)())
+ bd.set_branch_format(_load(branch_format))
+ if tree_format is not None:
+ bd.workingtree_format = _load(tree_format)
+ if repository_format is not None:
+ bd.repository_format = _load(repository_format)
return bd
self.register(key, helper, help, native, deprecated)
@@ -2216,7 +2222,7 @@
format_registry.register_metadir('knit',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
'Format using knits. Recommended.',
- branch_format='BzrBranchFormat5')
+ branch_format='bzrlib.branch.BzrBranchFormat5')
format_registry.set_default('knit')
format_registry.register_metadir('metaweave',
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
@@ -2225,16 +2231,24 @@
format_registry.register_metadir('experimental-knit2',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
'Experimental successor to knit. Use at your own risk.',
- branch_format='BzrBranchFormat5',
- tree='WorkingTreeFormat3'
+ branch_format='bzrlib.branch.BzrBranchFormat5',
+ tree_format='bzrlib.workingtree.WorkingTreeFormat3'
)
format_registry.register_metadir('experimental-knit3',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
'Experimental successor to knit2. Use at your own risk.',
- branch_format='BzrBranchFormat5',
- tree='WorkingTreeFormatAB1')
+ branch_format='bzrlib.branch.BzrBranchFormat5',
+ tree_format='bzrlib.workingtree.WorkingTreeFormatAB1')
format_registry.register_metadir('experimental-branch6',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
'Experimental successor to knit. Use at your own risk.',
- branch_format='BzrBranchFormat6',
- tree='WorkingTreeFormat3')
+ branch_format='bzrlib.branch.BzrBranchFormat6',
+ tree_format='bzrlib.workingtree.WorkingTreeFormat3')
+
+format_registry.register_metadir('experimental-reference-dirstate',
+ 'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
+ help='Experimental: dirstate working tree, Branch6, and reference-tree '
+ 'support. Proposed default for bzr 0.15',
+ branch_format='bzrlib.branch.BzrBranchFormat6',
+ tree_format='bzrlib.workingtree_4.WorkingTreeFormat4',
+ )
=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py 2007-02-26 00:45:31 +0000
+++ b/bzrlib/dirstate.py 2007-02-26 14:24:46 +0000
@@ -86,6 +86,8 @@
sha1 value.
'l' is a symlink entry: As for directory, but a symlink. The fingerprint is the
link target.
+'t' is a reference to a nested subtree; the fingerprint is the referenced
+ revision.
--- Format 1 had the following different definition: ---
@@ -226,8 +228,14 @@
specific, and if it is how we detect/parameterise that.
"""
- _kind_to_minikind = {'absent':'a', 'file':'f', 'directory':'d', 'relocated':'r', 'symlink':'l'}
- _minikind_to_kind = {'a':'absent', 'f':'file', 'd':'directory', 'l':'symlink', 'r':'relocated'}
+ _kind_to_minikind = {'absent':'a', 'file':'f', 'directory':'d', 'relocated':'r',
+ 'symlink': 'l',
+ 'tree-reference': 't',
+ }
+ _minikind_to_kind = {'a':'absent', 'f':'file', 'd':'directory', 'l':'symlink',
+ 'r': 'relocated',
+ 't': 'tree-reference',
+ }
_to_yesno = {True:'y', False: 'n'} # TODO profile the performance gain
# of using int conversion rather than a dict here. AND BLAME ANDREW IF
# it is faster.
@@ -285,17 +293,20 @@
self._end_of_header = None
self._bisect_page_size = DirState.BISECT_PAGE_SIZE
- def add(self, path, file_id, kind, stat, link_or_sha1):
+ def add(self, path, file_id, kind, stat, fingerprint):
"""Add a path to be tracked.
:param path: The path within the dirstate - '' is the root, 'foo' is the
path foo within the root, 'foo/bar' is the path bar within foo
within the root.
:param file_id: The file id of the path being added.
- :param kind: The kind of the path.
+ :param kind: The kind of the path, as a string like 'file',
+ 'directory', etc.
:param stat: The output of os.lstate for the path.
- :param link_or_sha1: The sha value of the file, or the target of a
- symlink. '' for directories.
+ :param fingerprint: The sha value of the file,
+ or the target of a symlink,
+ or the referenced revision id for tree-references,
+ or '' for directories.
"""
# adding a file:
# find the block its in.
@@ -340,7 +351,7 @@
minikind = DirState._kind_to_minikind[kind]
if kind == 'file':
entry_data = entry_key, [
- (minikind, link_or_sha1, size, False, packed_stat),
+ (minikind, fingerprint, size, False, packed_stat),
] + parent_info
elif kind == 'directory':
entry_data = entry_key, [
@@ -348,7 +359,11 @@
] + parent_info
elif kind == 'symlink':
entry_data = entry_key, [
- (minikind, link_or_sha1, size, False, packed_stat),
+ (minikind, fingerprint, size, False, packed_stat),
+ ] + parent_info
+ elif kind == 'tree-reference':
+ entry_data = entry_key, [
+ (minikind, fingerprint, 0, False, packed_stat),
] + parent_info
else:
raise errors.BzrError('unknown kind %r' % kind)
@@ -1243,8 +1258,12 @@
fingerprint = inv_entry.text_sha1 or ''
size = inv_entry.text_size or 0
executable = inv_entry.executable
+ elif kind == 'tree-reference':
+ fingerprint = inv_entry.reference_revision or ''
+ size = 0
+ executable = False
else:
- raise Exception
+ raise Exception("can't pack %s" % inv_entry)
return (minikind, fingerprint, size, executable, tree_data)
def _iter_entries(self):
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2007-02-26 02:56:24 +0000
+++ b/bzrlib/errors.py 2007-02-26 14:24:46 +0000
@@ -677,6 +677,9 @@
_fmt = "A write attempt was made in a read only transaction on %(obj)s"
+ # TODO: There should also be an error indicating that you need a write
+ # lock and don't have any lock at all... mbp 20070226
+
def __init__(self, obj):
self.obj = obj
=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py 2007-02-25 15:15:16 +0000
+++ b/bzrlib/inventory.py 2007-02-26 14:24:46 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007 Canonical Ltd
#
# 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
@@ -1361,9 +1361,9 @@
entry_factory = {
- 'directory':InventoryDirectory,
- 'file':InventoryFile,
- 'symlink':InventoryLink,
+ 'directory': InventoryDirectory,
+ 'file': InventoryFile,
+ 'symlink': InventoryLink,
'tree-reference': TreeReference
}
=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py 2007-02-25 15:15:16 +0000
+++ b/bzrlib/mutabletree.py 2007-02-26 14:24:46 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007 Canonical Ltd
#
# 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
=== modified file 'bzrlib/tests/test_bzrdir.py'
--- a/bzrlib/tests/test_bzrdir.py 2007-02-21 06:06:06 +0000
+++ b/bzrlib/tests/test_bzrdir.py 2007-02-26 14:24:46 +0000
@@ -78,7 +78,7 @@
my_format_registry.register_metadir('experimental-knit3',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
'Format using knits',
- tree='WorkingTreeFormatAB1')
+ tree_format='bzrlib.workingtree.WorkingTreeFormatAB1')
my_format_registry.set_default('knit')
my_format_registry.register_metadir(
'experimental-knit2',
@@ -89,7 +89,7 @@
'branch6',
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
'Experimental successor to knit. Use at your own risk.',
- branch_format='BzrBranchFormat6')
+ branch_format='bzrlib.branch.BzrBranchFormat6')
return my_format_registry
def test_format_registry(self):
=== modified file 'bzrlib/tests/tree_implementations/test_tree.py'
--- a/bzrlib/tests/tree_implementations/test_tree.py 2007-02-22 05:51:57 +0000
+++ b/bzrlib/tests/tree_implementations/test_tree.py 2007-02-26 14:24:46 +0000
@@ -40,23 +40,35 @@
def create_nested(self):
work_tree = self.make_branch_and_tree('wt')
- self.skip_if_no_reference(work_tree)
- subtree = self.make_branch_and_tree('wt/subtree')
- subtree.set_root_id('sub-root')
- subtree.commit('foo', rev_id='sub-1')
- work_tree.add_reference(subtree)
+ work_tree.lock_write()
+ try:
+ self.skip_if_no_reference(work_tree)
+ subtree = self.make_branch_and_tree('wt/subtree')
+ subtree.set_root_id('sub-root')
+ subtree.commit('foo', rev_id='sub-1')
+ work_tree.add_reference(subtree)
+ finally:
+ work_tree.unlock()
tree = self._convert_tree(work_tree)
self.skip_if_no_reference(tree)
return tree
def test_get_reference_revision(self):
tree = self.create_nested()
- entry = tree.inventory['sub-root']
+ tree.lock_read()
+ try:
+ entry = tree.inventory['sub-root']
+ finally:
+ tree.unlock()
path = tree.id2path('sub-root')
self.assertEqual('sub-1', tree.get_reference_revision(entry, path))
def test_iter_reference_entries(self):
tree = self.create_nested()
- entry = tree.inventory['sub-root']
+ tree.lock_read()
+ try:
+ entry = tree.inventory['sub-root']
+ finally:
+ tree.unlock()
self.assertEqual([entry], [e for p, e in
tree.iter_reference_entries()])
=== modified file 'bzrlib/tests/workingtree_implementations/test_add_reference.py'
--- a/bzrlib/tests/workingtree_implementations/test_add_reference.py 2007-02-25 15:33:42 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_add_reference.py 2007-02-26 14:24:46 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007 Canonical Ltd
#
# 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
@@ -58,6 +58,7 @@
basis.lock_read()
try:
entry = basis.inventory['sub-tree-root-id']
+ self.assertEqual(entry.kind, 'tree-reference')
sub_tree = tree.get_nested_tree(entry)
self.assertEqual(sub_tree.last_revision(),
entry.reference_revision)
@@ -65,7 +66,6 @@
basis.unlock()
finally:
tree.unlock()
-
def test_add_reference_same_root(self):
tree = self.make_branch_and_tree('tree')
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2007-02-26 02:56:24 +0000
+++ b/bzrlib/tree.py 2007-02-26 14:24:46 +0000
@@ -166,8 +166,9 @@
raise NotImplementedError("subclasses must implement kind")
def get_reference_revision(self, entry, path=None):
- raise NotImplementedError("subclasses must implement "
- "get_reference_revision")
+ raise NotImplementedError("Tree subclass %s must implement "
+ "get_reference_revision"
+ % self.__class__.__name__)
def _comparison_data(self, entry, path):
"""Return a tuple of kind, executable, stat_value for a file.
@@ -634,102 +635,115 @@
path in the specific_files list is not versioned in one of
source, target or extra_trees.
"""
- lookup_trees = [self.source]
- if extra_trees:
- lookup_trees.extend(extra_trees)
- specific_file_ids = self.target.paths2ids(specific_files,
- lookup_trees, require_versioned=require_versioned)
- to_paths = {}
- from_entries_by_dir = list(self.source.inventory.iter_entries_by_dir(
- specific_file_ids=specific_file_ids))
- from_data = dict((e.file_id, (p, e)) for p, e in from_entries_by_dir)
- to_entries_by_dir = list(self.target.inventory.iter_entries_by_dir(
- specific_file_ids=specific_file_ids))
- num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
- entry_count = 0
- for to_path, to_entry in to_entries_by_dir:
- file_id = to_entry.file_id
- to_paths[file_id] = to_path
- entry_count += 1
- changed_content = False
- from_path, from_entry = from_data.get(file_id, (None, None))
- from_versioned = (from_entry is not None)
- if from_entry is not None:
- from_versioned = True
- from_name = from_entry.name
- from_parent = from_entry.parent_id
- from_kind, from_executable, from_stat = \
- self.source._comparison_data(from_entry, from_path)
- entry_count += 1
- else:
- from_versioned = False
- from_kind = None
- from_parent = None
- from_name = None
- from_executable = None
- versioned = (from_versioned, True)
- to_kind, to_executable, to_stat = \
- self.target._comparison_data(to_entry, to_path)
- kind = (from_kind, to_kind)
- if kind[0] != kind[1]:
+ # this must return a sequence rather than a list so that it can hold a
+ # read-lock for the whole time.
+ #
+ # TODO: this really only needs to lock the trees not the branches, so
+ # could do with lock_tree_read() -- mbp 20070227
+ result = []
+ self.source.lock_read()
+ self.target.lock_read()
+ try:
+ lookup_trees = [self.source]
+ if extra_trees:
+ lookup_trees.extend(extra_trees)
+ specific_file_ids = self.target.paths2ids(specific_files,
+ lookup_trees, require_versioned=require_versioned)
+ to_paths = {}
+ from_entries_by_dir = list(self.source.inventory.iter_entries_by_dir(
+ specific_file_ids=specific_file_ids))
+ from_data = dict((e.file_id, (p, e)) for p, e in from_entries_by_dir)
+ to_entries_by_dir = list(self.target.inventory.iter_entries_by_dir(
+ specific_file_ids=specific_file_ids))
+ num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
+ entry_count = 0
+ for to_path, to_entry in to_entries_by_dir:
+ file_id = to_entry.file_id
+ to_paths[file_id] = to_path
+ entry_count += 1
+ changed_content = False
+ from_path, from_entry = from_data.get(file_id, (None, None))
+ from_versioned = (from_entry is not None)
+ if from_entry is not None:
+ from_versioned = True
+ from_name = from_entry.name
+ from_parent = from_entry.parent_id
+ from_kind, from_executable, from_stat = \
+ self.source._comparison_data(from_entry, from_path)
+ entry_count += 1
+ else:
+ from_versioned = False
+ from_kind = None
+ from_parent = None
+ from_name = None
+ from_executable = None
+ versioned = (from_versioned, True)
+ to_kind, to_executable, to_stat = \
+ self.target._comparison_data(to_entry, to_path)
+ kind = (from_kind, to_kind)
+ if kind[0] != kind[1]:
+ changed_content = True
+ elif from_kind == 'file':
+ from_size = self.source._file_size(from_entry, from_stat)
+ to_size = self.target._file_size(to_entry, to_stat)
+ if from_size != to_size:
+ changed_content = True
+ elif (self.source.get_file_sha1(file_id, from_path, from_stat) !=
+ self.target.get_file_sha1(file_id, to_path, to_stat)):
+ changed_content = True
+ elif from_kind == 'symlink':
+ if (self.source.get_symlink_target(file_id) !=
+ self.target.get_symlink_target(file_id)):
+ changed_content = True
+ elif from_kind == 'tree-reference':
+ if (self.source.get_reference_revision(from_entry, from_path)
+ != self.target.get_reference_revision(to_entry, to_path)):
+ changed_content = True
+ parent = (from_parent, to_entry.parent_id)
+ name = (from_name, to_entry.name)
+ executable = (from_executable, to_executable)
+ if pb is not None:
+ pb.update('comparing files', entry_count, num_entries)
+ if (changed_content is not False or versioned[0] != versioned[1]
+ or parent[0] != parent[1] or name[0] != name[1] or
+ executable[0] != executable[1] or include_unchanged):
+ result.append((file_id, to_path, changed_content, versioned, parent,
+ name, kind, executable))
+ def get_to_path(from_entry):
+ if from_entry.parent_id is None:
+ to_path = ''
+ else:
+ if from_entry.parent_id not in to_paths:
+ get_to_path(self.source.inventory[from_entry.parent_id])
+ to_path = osutils.pathjoin(to_paths[from_entry.parent_id],
+ from_entry.name)
+ to_paths[from_entry.file_id] = to_path
+ return to_path
+
+ for path, from_entry in from_entries_by_dir:
+ file_id = from_entry.file_id
+ if file_id in to_paths:
+ continue
+ to_path = get_to_path(from_entry)
+ entry_count += 1
+ if pb is not None:
+ pb.update('comparing files', entry_count, num_entries)
+ versioned = (True, False)
+ parent = (from_entry.parent_id, None)
+ name = (from_entry.name, None)
+ from_kind, from_executable, stat_value = \
+ self.source._comparison_data(from_entry, path)
+ kind = (from_kind, None)
+ executable = (from_executable, None)
changed_content = True
- elif from_kind == 'file':
- from_size = self.source._file_size(from_entry, from_stat)
- to_size = self.target._file_size(to_entry, to_stat)
- if from_size != to_size:
- changed_content = True
- elif (self.source.get_file_sha1(file_id, from_path, from_stat) !=
- self.target.get_file_sha1(file_id, to_path, to_stat)):
- changed_content = True
- elif from_kind == 'symlink':
- if (self.source.get_symlink_target(file_id) !=
- self.target.get_symlink_target(file_id)):
- changed_content = True
- elif from_kind == 'tree-reference':
- if (self.source.get_reference_revision(from_entry, from_path)
- != self.target.get_reference_revision(to_entry, to_path)):
- changed_content = True
- parent = (from_parent, to_entry.parent_id)
- name = (from_name, to_entry.name)
- executable = (from_executable, to_executable)
- if pb is not None:
- pb.update('comparing files', entry_count, num_entries)
- if (changed_content is not False or versioned[0] != versioned[1]
- or parent[0] != parent[1] or name[0] != name[1] or
- executable[0] != executable[1] or include_unchanged):
- yield (file_id, to_path, changed_content, versioned, parent,
- name, kind, executable)
-
- def get_to_path(from_entry):
- if from_entry.parent_id is None:
- to_path = ''
- else:
- if from_entry.parent_id not in to_paths:
- get_to_path(self.source.inventory[from_entry.parent_id])
- to_path = osutils.pathjoin(to_paths[from_entry.parent_id],
- from_entry.name)
- to_paths[from_entry.file_id] = to_path
- return to_path
-
- for path, from_entry in from_entries_by_dir:
- file_id = from_entry.file_id
- if file_id in to_paths:
- continue
- to_path = get_to_path(from_entry)
- entry_count += 1
- if pb is not None:
- pb.update('comparing files', entry_count, num_entries)
- versioned = (True, False)
- parent = (from_entry.parent_id, None)
- name = (from_entry.name, None)
- from_kind, from_executable, stat_value = \
- self.source._comparison_data(from_entry, path)
- kind = (from_kind, None)
- executable = (from_executable, None)
- changed_content = True
- # the parent's path is necessarily known at this point.
- yield(file_id, to_path, changed_content, versioned, parent,
- name, kind, executable)
+ # the parent's path is necessarily known at this point.
+ result.append((file_id, to_path, changed_content, versioned, parent,
+ name, kind, executable))
+ finally:
+ self.source.unlock()
+ self.target.unlock()
+ return result
+
# This was deprecated before 0.12, but did not have an official warning
=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py 2007-02-26 04:44:45 +0000
+++ b/bzrlib/workingtree_4.py 2007-02-26 14:24:46 +0000
@@ -105,10 +105,12 @@
"""This is the Format 4 working tree.
This differs from WorkingTree3 by:
- - having a consolidated internal dirstate.
- - not having a regular inventory attribute.
+ - Having a consolidated internal dirstate, stored in a
+ randomly-accessible sorted file on disk.
+ - Not having a regular inventory attribute. One can be synthesized
+ on demand but this is expensive and should be avoided.
- This is new in bzr TODO FIXME SETMEBEFORE MERGE.
+ This is new in bzr 0.15.
"""
def __init__(self, basedir,
@@ -180,6 +182,14 @@
state.add(f, file_id, kind, None, '')
self._dirty = True
+ @needs_tree_write_lock
+ def add_reference(self, sub_tree):
+ # use standard implementation, which calls back to self._add
+ #
+ # So we don't store the reference_revision in the working dirstate,
+ # it's just recorded at the moment of commit.
+ self._add_reference(sub_tree)
+
def break_lock(self):
"""Break a lock if one is present from another instance.
@@ -217,6 +227,14 @@
self._control_files.break_lock()
self.branch.break_lock()
+ def _comparison_data(self, entry, path):
+ kind, executable, stat_value = \
+ WorkingTree3._comparison_data(self, entry, path)
+ # it looks like a plain directory, but it's really a reference
+ if kind == 'directory' and entry.kind == 'tree-reference':
+ kind = 'tree-reference'
+ return kind, executable, stat_value
+
def current_dirstate(self):
"""Return the current dirstate object.
@@ -225,8 +243,7 @@
:raises errors.NotWriteLocked: when not in a lock.
"""
- if not self._control_files._lock_count:
- raise errors.ObjectNotLocked(self)
+ self._must_be_locked()
return self._current_dirstate()
def _current_dirstate(self):
@@ -357,6 +374,7 @@
"""Get the inventory for the tree. This is only valid within a lock."""
if self._inventory is not None:
return self._inventory
+ self._must_be_locked()
self._generate_inventory()
return self._inventory
@@ -371,6 +389,15 @@
"""
return self.current_dirstate().get_parent_ids()
+ def get_reference_revision(self, entry, path=None):
+ # referenced tree's revision is whatever's currently there
+ return self.get_nested_tree(entry, path).last_revision()
+
+ def get_nested_tree(self, entry, path=None):
+ if path is None:
+ path = self.id2path(entry.file_id)
+ return WorkingTree.open(self.abspath(path))
+
@needs_read_lock
def get_root_id(self):
"""Return the id of this trees root"""
@@ -415,6 +442,21 @@
result.append(key[2])
return iter(result)
+ def kind(self, file_id):
+ # The kind of a file is whatever it actually is on disk, except that
+ # tree-references need to be reported as such rather than as the
+ # directiories
+ #
+ # TODO: Possibly we should check that the directory still really
+ # contains a subtree, at least during commit? mbp 20070227
+ kind = WorkingTree3.kind(self, file_id)
+ if kind == 'directory':
+ # TODO: ask the dirstate not the inventory -- mbp 20060227
+ entry = self.inventory[file_id]
+ if entry.kind == 'tree-reference':
+ kind = 'tree-reference'
+ return kind
+
@needs_read_lock
def _last_revision(self):
"""See Mutable.last_revision."""
@@ -659,6 +701,10 @@
return #rename_tuples
+ def _must_be_locked(self):
+ if not self._control_files._lock_count:
+ raise errors.ObjectNotLocked(self)
+
def _new_tree(self):
"""Initialize the state in this tree to be a new tree."""
self._dirty = True
@@ -1033,6 +1079,8 @@
- uses a LockDir to guard access to it.
"""
+ supports_tree_reference = True
+
def get_format_string(self):
"""See WorkingTreeFormat.get_format_string()."""
return "Bazaar Working Tree format 4\n"
@@ -1093,6 +1141,13 @@
_bzrdir=a_bzrdir,
_control_files=control_files)
+ def __get_matchingbzrdir(self):
+ # please test against something that will let us do tree references
+ return bzrdir.format_registry.make_bzrdir(
+ 'experimental-reference-dirstate')
+
+ _matchingbzrdir = property(__get_matchingbzrdir)
+
class DirStateRevisionTree(Tree):
"""A revision tree pulling the inventory from a dirstate."""
@@ -1112,7 +1167,6 @@
return w.annotate_iter(self.inventory[file_id].revision)
def _comparison_data(self, entry, path):
- """See Tree._comparison_data."""
if entry is None:
return None, False, None
# trust the entry as RevisionTree does, but this may not be
@@ -1156,8 +1210,11 @@
def _generate_inventory(self):
"""Create and set self.inventory from the dirstate object.
+ (So this is only called the first time the inventory is requested for
+ this tree; it then remains in memory.)
+
This is relatively expensive: we have to walk the entire dirstate.
- Ideally we would not, and instead would """
+ """
assert self._locked, 'cannot generate inventory of an unlocked '\
'dirstate revision tree'
# separate call for profiling - makes it clear where the costs are.
@@ -1189,7 +1246,7 @@
# all the paths in this block are not versioned in this tree
continue
for key, entry in block[1]:
- minikind, link_or_sha1, size, executable, revid = entry[parent_index]
+ minikind, fingerprint, size, executable, revid = entry[parent_index]
if minikind in ('a', 'r'): # absent, relocated
# not this tree
continue
@@ -1203,15 +1260,18 @@
if kind == 'file':
inv_entry.executable = executable
inv_entry.text_size = size
- inv_entry.text_sha1 = link_or_sha1
+ inv_entry.text_sha1 = fingerprint
elif kind == 'directory':
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
elif kind == 'symlink':
inv_entry.executable = False
inv_entry.text_size = size
- inv_entry.symlink_target = utf8_decode(link_or_sha1)[0]
+ inv_entry.symlink_target = utf8_decode(fingerprint)[0]
+ elif kind == 'tree-reference':
+ inv_entry.reference_revision = fingerprint
else:
- raise Exception, kind
+ raise AssertionError("cannot convert entry %r into an InventoryEntry"
+ % entry)
# These checks cost us around 40ms on a 55k entry tree
assert file_id not in inv_byid
assert name_unicode not in parent_ie.children
@@ -1272,9 +1332,6 @@
def has_filename(self, filename):
return bool(self.path2id(filename))
- def kind(self, file_id):
- return self.inventory[file_id].kind
-
def is_executable(self, file_id, path=None):
ie = self.inventory[file_id]
if ie.kind != "file":
More information about the bazaar-commits
mailing list