1757
2607
return ShelfManager(self, self._transport)
1760
class InventoryWorkingTree(WorkingTree,
1761
bzrlib.mutabletree.MutableInventoryTree):
1762
"""Base class for working trees that are inventory-oriented.
1764
The inventory is held in the `Branch` working-inventory, and the
1765
files are in a directory on disk.
1767
It is possible for a `WorkingTree` to have a filename which is
1768
not listed in the Inventory and vice versa.
1771
def __init__(self, basedir='.',
1772
branch=DEPRECATED_PARAMETER,
1774
_control_files=None,
1778
"""Construct a InventoryWorkingTree instance. This is not a public API.
1780
:param branch: A branch to override probing for the branch.
1782
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1783
branch=branch, _control_files=_control_files, _internal=_internal,
1784
_format=_format, _bzrdir=_bzrdir)
1786
self._detect_case_handling()
1788
if _inventory is None:
1789
# This will be acquired on lock_read() or lock_write()
1790
self._inventory_is_modified = False
1791
self._inventory = None
1793
# the caller of __init__ has provided an inventory,
1794
# we assume they know what they are doing - as its only
1795
# the Format factory and creation methods that are
1796
# permitted to do this.
1797
self._set_inventory(_inventory, dirty=False)
1799
def _set_inventory(self, inv, dirty):
1800
"""Set the internal cached inventory.
1802
:param inv: The inventory to set.
1803
:param dirty: A boolean indicating whether the inventory is the same
1804
logical inventory as whats on disk. If True the inventory is not
1805
the same and should be written to disk or data will be lost, if
1806
False then the inventory is the same as that on disk and any
1807
serialisation would be unneeded overhead.
1809
self._inventory = inv
1810
self._inventory_is_modified = dirty
1812
def _detect_case_handling(self):
1813
wt_trans = self.bzrdir.get_workingtree_transport(None)
1815
wt_trans.stat(self._format.case_sensitive_filename)
2610
class WorkingTree2(WorkingTree):
2611
"""This is the Format 2 working tree.
2613
This was the first weave based working tree.
2614
- uses os locks for locking.
2615
- uses the branch last-revision.
2618
def __init__(self, *args, **kwargs):
2619
super(WorkingTree2, self).__init__(*args, **kwargs)
2620
# WorkingTree2 has more of a constraint that self._inventory must
2621
# exist. Because this is an older format, we don't mind the overhead
2622
# caused by the extra computation here.
2624
# Newer WorkingTree's should only have self._inventory set when they
2626
if self._inventory is None:
2627
self.read_working_inventory()
2629
def _get_check_refs(self):
2630
"""Return the references needed to perform a check of this tree."""
2631
return [('trees', self.last_revision())]
2633
def lock_tree_write(self):
2634
"""See WorkingTree.lock_tree_write().
2636
In Format2 WorkingTrees we have a single lock for the branch and tree
2637
so lock_tree_write() degrades to lock_write().
2639
self.branch.lock_write()
2641
return self._control_files.lock_write()
2643
self.branch.unlock()
2647
# do non-implementation specific cleanup
2650
# we share control files:
2651
if self._control_files._lock_count == 3:
2652
# _inventory_is_modified is always False during a read lock.
2653
if self._inventory_is_modified:
2655
self._write_hashcache_if_dirty()
2657
# reverse order of locking.
2659
return self._control_files.unlock()
2661
self.branch.unlock()
2664
class WorkingTree3(WorkingTree):
2665
"""This is the Format 3 working tree.
2667
This differs from the base WorkingTree by:
2668
- having its own file lock
2669
- having its own last-revision property.
2671
This is new in bzr 0.8
2675
def _last_revision(self):
2676
"""See Mutable.last_revision."""
2678
return self._transport.get_bytes('last-revision')
1816
2679
except errors.NoSuchFile:
1817
self.case_sensitive = True
1819
self.case_sensitive = False
1821
self._setup_directory_is_tree_reference()
1823
def _serialize(self, inventory, out_file):
1824
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1827
def _deserialize(selt, in_file):
1828
return xml5.serializer_v5.read_inventory(in_file)
1830
@needs_tree_write_lock
1831
def _write_inventory(self, inv):
1832
"""Write inventory as the current inventory."""
1833
self._set_inventory(inv, dirty=True)
1836
# XXX: This method should be deprecated in favour of taking in a proper
1837
# new Inventory object.
1838
@needs_tree_write_lock
1839
def set_inventory(self, new_inventory_list):
1840
from bzrlib.inventory import (Inventory,
1844
inv = Inventory(self.get_root_id())
1845
for path, file_id, parent, kind in new_inventory_list:
1846
name = os.path.basename(path)
1849
# fixme, there should be a factory function inv,add_??
1850
if kind == 'directory':
1851
inv.add(InventoryDirectory(file_id, name, parent))
1852
elif kind == 'file':
1853
inv.add(InventoryFile(file_id, name, parent))
1854
elif kind == 'symlink':
1855
inv.add(InventoryLink(file_id, name, parent))
1857
raise errors.BzrError("unknown kind %r" % kind)
1858
self._write_inventory(inv)
1860
def _write_basis_inventory(self, xml):
1861
"""Write the basis inventory XML to the basis-inventory file"""
1862
path = self._basis_inventory_name()
1864
self._transport.put_file(path, sio,
1865
mode=self.bzrdir._get_file_mode())
1867
def _reset_data(self):
1868
"""Reset transient data that cannot be revalidated."""
1869
self._inventory_is_modified = False
1870
f = self._transport.get('inventory')
1872
result = self._deserialize(f)
1875
self._set_inventory(result, dirty=False)
1877
def _set_root_id(self, file_id):
1878
"""Set the root id for this tree, in a format specific manner.
1880
:param file_id: The file id to assign to the root. It must not be
1881
present in the current inventory or an error will occur. It must
1882
not be None, but rather a valid file id.
1884
inv = self._inventory
1885
orig_root_id = inv.root.file_id
1886
# TODO: it might be nice to exit early if there was nothing
1887
# to do, saving us from trigger a sync on unlock.
1888
self._inventory_is_modified = True
1889
# we preserve the root inventory entry object, but
1890
# unlinkit from the byid index
1891
del inv._byid[inv.root.file_id]
1892
inv.root.file_id = file_id
1893
# and link it into the index with the new changed id.
1894
inv._byid[inv.root.file_id] = inv.root
1895
# and finally update all children to reference the new id.
1896
# XXX: this should be safe to just look at the root.children
1897
# list, not the WHOLE INVENTORY.
1900
if entry.parent_id == orig_root_id:
1901
entry.parent_id = inv.root.file_id
1903
def all_file_ids(self):
1904
"""See Tree.iter_all_file_ids"""
1905
return set(self.inventory)
1907
@needs_tree_write_lock
1908
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1909
"""See MutableTree.set_parent_trees."""
1910
parent_ids = [rev for (rev, tree) in parents_list]
1911
for revision_id in parent_ids:
1912
_mod_revision.check_not_reserved_id(revision_id)
1914
self._check_parents_for_ghosts(parent_ids,
1915
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1917
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
1919
if len(parent_ids) == 0:
1920
leftmost_parent_id = _mod_revision.NULL_REVISION
1921
leftmost_parent_tree = None
1923
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1925
if self._change_last_revision(leftmost_parent_id):
1926
if leftmost_parent_tree is None:
1927
# If we don't have a tree, fall back to reading the
1928
# parent tree from the repository.
1929
self._cache_basis_inventory(leftmost_parent_id)
1931
inv = leftmost_parent_tree.inventory
1932
xml = self._create_basis_xml_from_inventory(
1933
leftmost_parent_id, inv)
1934
self._write_basis_inventory(xml)
1935
self._set_merges_from_parent_ids(parent_ids)
1937
def _cache_basis_inventory(self, new_revision):
1938
"""Cache new_revision as the basis inventory."""
1939
# TODO: this should allow the ready-to-use inventory to be passed in,
1940
# as commit already has that ready-to-use [while the format is the
1943
# this double handles the inventory - unpack and repack -
1944
# but is easier to understand. We can/should put a conditional
1945
# in here based on whether the inventory is in the latest format
1946
# - perhaps we should repack all inventories on a repository
1948
# the fast path is to copy the raw xml from the repository. If the
1949
# xml contains 'revision_id="', then we assume the right
1950
# revision_id is set. We must check for this full string, because a
1951
# root node id can legitimately look like 'revision_id' but cannot
1953
xml = self.branch.repository._get_inventory_xml(new_revision)
1954
firstline = xml.split('\n', 1)[0]
1955
if (not 'revision_id="' in firstline or
1956
'format="7"' not in firstline):
1957
inv = self.branch.repository._serializer.read_inventory_from_string(
1959
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1960
self._write_basis_inventory(xml)
1961
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1964
def _basis_inventory_name(self):
1965
return 'basis-inventory-cache'
1967
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1968
"""Create the text that will be saved in basis-inventory"""
1969
inventory.revision_id = revision_id
1970
return xml7.serializer_v7.write_inventory_to_string(inventory)
2680
return _mod_revision.NULL_REVISION
2682
def _change_last_revision(self, revision_id):
2683
"""See WorkingTree._change_last_revision."""
2684
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2686
self._transport.delete('last-revision')
2687
except errors.NoSuchFile:
2691
self._transport.put_bytes('last-revision', revision_id,
2692
mode=self.bzrdir._get_file_mode())
2695
def _get_check_refs(self):
2696
"""Return the references needed to perform a check of this tree."""
2697
return [('trees', self.last_revision())]
1972
2699
@needs_tree_write_lock
1973
2700
def set_conflicts(self, conflicts):
1993
2720
raise errors.ConflictFormatError()
1994
2721
except StopIteration:
1995
2722
raise errors.ConflictFormatError()
1996
reader = _mod_rio.RioReader(confile)
1997
return _mod_conflicts.ConflictList.from_stanzas(reader)
2723
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
1999
2725
confile.close()
2001
def read_basis_inventory(self):
2002
"""Read the cached basis inventory."""
2003
path = self._basis_inventory_name()
2004
return self._transport.get_bytes(path)
2007
def read_working_inventory(self):
2008
"""Read the working inventory.
2010
:raises errors.InventoryModified: read_working_inventory will fail
2011
when the current in memory inventory has been modified.
2013
# conceptually this should be an implementation detail of the tree.
2014
# XXX: Deprecate this.
2015
# ElementTree does its own conversion from UTF-8, so open in
2017
if self._inventory_is_modified:
2018
raise errors.InventoryModified(self)
2019
f = self._transport.get('inventory')
2021
result = self._deserialize(f)
2024
self._set_inventory(result, dirty=False)
2028
def get_root_id(self):
2029
"""Return the id of this trees root"""
2030
return self._inventory.root.file_id
2032
def has_id(self, file_id):
2033
# files that have been deleted are excluded
2034
inv = self.inventory
2035
if not inv.has_id(file_id):
2037
path = inv.id2path(file_id)
2038
return osutils.lexists(self.abspath(path))
2040
def has_or_had_id(self, file_id):
2041
if file_id == self.inventory.root.file_id:
2043
return self.inventory.has_id(file_id)
2045
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2047
"""Iterate through file_ids for this tree.
2049
file_ids are in a WorkingTree if they are in the working inventory
2050
and the working file exists.
2052
inv = self._inventory
2053
for path, ie in inv.iter_entries():
2054
if osutils.lexists(self.abspath(path)):
2057
@needs_tree_write_lock
2058
def set_last_revision(self, new_revision):
2059
"""Change the last revision in the working tree."""
2060
if self._change_last_revision(new_revision):
2061
self._cache_basis_inventory(new_revision)
2063
def _get_check_refs(self):
2064
"""Return the references needed to perform a check of this tree.
2066
The default implementation returns no refs, and is only suitable for
2067
trees that have no local caching and can commit on ghosts at any time.
2069
:seealso: bzrlib.check for details about check_refs.
2074
def _check(self, references):
2075
"""Check the tree for consistency.
2077
:param references: A dict with keys matching the items returned by
2078
self._get_check_refs(), and values from looking those keys up in
2081
tree_basis = self.basis_tree()
2082
tree_basis.lock_read()
2084
repo_basis = references[('trees', self.last_revision())]
2085
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2086
raise errors.BzrCheckError(
2087
"Mismatched basis inventory content.")
2093
def check_state(self):
2094
"""Check that the working state is/isn't valid."""
2095
check_refs = self._get_check_refs()
2097
for ref in check_refs:
2100
refs[ref] = self.branch.repository.revision_tree(value)
2103
@needs_tree_write_lock
2104
def reset_state(self, revision_ids=None):
2105
"""Reset the state of the working tree.
2107
This does a hard-reset to a last-known-good state. This is a way to
2108
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2110
if revision_ids is None:
2111
revision_ids = self.get_parent_ids()
2112
if not revision_ids:
2113
rt = self.branch.repository.revision_tree(
2114
_mod_revision.NULL_REVISION)
2116
rt = self.branch.repository.revision_tree(revision_ids[0])
2117
self._write_inventory(rt.inventory)
2118
self.set_parent_ids(revision_ids)
2121
"""Write the in memory inventory to disk."""
2122
# TODO: Maybe this should only write on dirty ?
2123
if self._control_files._lock_mode != 'w':
2124
raise errors.NotWriteLocked(self)
2126
self._serialize(self._inventory, sio)
2128
self._transport.put_file('inventory', sio,
2129
mode=self.bzrdir._get_file_mode())
2130
self._inventory_is_modified = False
2132
def get_file_mtime(self, file_id, path=None):
2133
"""See Tree.get_file_mtime."""
2135
path = self.inventory.id2path(file_id)
2136
return os.lstat(self.abspath(path)).st_mtime
2138
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2139
file_id = self.path2id(path)
2141
# For unversioned files on win32, we just assume they are not
2144
return self._inventory[file_id].executable
2146
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2147
mode = stat_result.st_mode
2148
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2150
if not supports_executable():
2151
def is_executable(self, file_id, path=None):
2152
return self._inventory[file_id].executable
2154
_is_executable_from_path_and_stat = \
2155
_is_executable_from_path_and_stat_from_basis
2157
def is_executable(self, file_id, path=None):
2159
path = self.id2path(file_id)
2160
mode = os.lstat(self.abspath(path)).st_mode
2161
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2163
_is_executable_from_path_and_stat = \
2164
_is_executable_from_path_and_stat_from_stat
2166
@needs_tree_write_lock
2167
def _add(self, files, ids, kinds):
2168
"""See MutableTree._add."""
2169
# TODO: Re-adding a file that is removed in the working copy
2170
# should probably put it back with the previous ID.
2171
# the read and write working inventory should not occur in this
2172
# function - they should be part of lock_write and unlock.
2173
inv = self.inventory
2174
for f, file_id, kind in zip(files, ids, kinds):
2176
inv.add_path(f, kind=kind)
2178
inv.add_path(f, kind=kind, file_id=file_id)
2179
self._inventory_is_modified = True
2181
def revision_tree(self, revision_id):
2182
"""See WorkingTree.revision_id."""
2183
if revision_id == self.last_revision():
2185
xml = self.read_basis_inventory()
2186
except errors.NoSuchFile:
2190
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2191
# dont use the repository revision_tree api because we want
2192
# to supply the inventory.
2193
if inv.revision_id == revision_id:
2194
return revisiontree.InventoryRevisionTree(
2195
self.branch.repository, inv, revision_id)
2196
except errors.BadInventoryFormat:
2198
# raise if there was no inventory, or if we read the wrong inventory.
2199
raise errors.NoSuchRevisionInTree(self, revision_id)
2202
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2203
"""See Tree.annotate_iter
2205
This implementation will use the basis tree implementation if possible.
2206
Lines not in the basis are attributed to CURRENT_REVISION
2208
If there are pending merges, lines added by those merges will be
2209
incorrectly attributed to CURRENT_REVISION (but after committing, the
2210
attribution will be correct).
2212
maybe_file_parent_keys = []
2213
for parent_id in self.get_parent_ids():
2215
parent_tree = self.revision_tree(parent_id)
2216
except errors.NoSuchRevisionInTree:
2217
parent_tree = self.branch.repository.revision_tree(parent_id)
2218
parent_tree.lock_read()
2220
if not parent_tree.has_id(file_id):
2222
ie = parent_tree.inventory[file_id]
2223
if ie.kind != 'file':
2224
# Note: this is slightly unnecessary, because symlinks and
2225
# directories have a "text" which is the empty text, and we
2226
# know that won't mess up annotations. But it seems cleaner
2228
parent_text_key = (file_id, ie.revision)
2229
if parent_text_key not in maybe_file_parent_keys:
2230
maybe_file_parent_keys.append(parent_text_key)
2232
parent_tree.unlock()
2233
graph = _mod_graph.Graph(self.branch.repository.texts)
2234
heads = graph.heads(maybe_file_parent_keys)
2235
file_parent_keys = []
2236
for key in maybe_file_parent_keys:
2238
file_parent_keys.append(key)
2240
# Now we have the parents of this content
2241
annotator = self.branch.repository.texts.get_annotator()
2242
text = self.get_file_text(file_id)
2243
this_key =(file_id, default_revision)
2244
annotator.add_special_text(this_key, file_parent_keys, text)
2245
annotations = [(key[-1], line)
2246
for key, line in annotator.annotate_flat(this_key)]
2250
def merge_modified(self):
2251
"""Return a dictionary of files modified by a merge.
2253
The list is initialized by WorkingTree.set_merge_modified, which is
2254
typically called after we make some automatic updates to the tree
2257
This returns a map of file_id->sha1, containing only files which are
2258
still in the working inventory and have that text hash.
2261
hashfile = self._transport.get('merge-hashes')
2262
except errors.NoSuchFile:
2267
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2268
raise errors.MergeModifiedFormatError()
2269
except StopIteration:
2270
raise errors.MergeModifiedFormatError()
2271
for s in _mod_rio.RioReader(hashfile):
2272
# RioReader reads in Unicode, so convert file_ids back to utf8
2273
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2274
if not self.inventory.has_id(file_id):
2276
text_hash = s.get("hash")
2277
if text_hash == self.get_file_sha1(file_id):
2278
merge_hashes[file_id] = text_hash
2284
def subsume(self, other_tree):
2285
def add_children(inventory, entry):
2286
for child_entry in entry.children.values():
2287
inventory._byid[child_entry.file_id] = child_entry
2288
if child_entry.kind == 'directory':
2289
add_children(inventory, child_entry)
2290
if other_tree.get_root_id() == self.get_root_id():
2291
raise errors.BadSubsumeSource(self, other_tree,
2292
'Trees have the same root')
2294
other_tree_path = self.relpath(other_tree.basedir)
2295
except errors.PathNotChild:
2296
raise errors.BadSubsumeSource(self, other_tree,
2297
'Tree is not contained by the other')
2298
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2299
if new_root_parent is None:
2300
raise errors.BadSubsumeSource(self, other_tree,
2301
'Parent directory is not versioned.')
2302
# We need to ensure that the result of a fetch will have a
2303
# versionedfile for the other_tree root, and only fetching into
2304
# RepositoryKnit2 guarantees that.
2305
if not self.branch.repository.supports_rich_root():
2306
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2307
other_tree.lock_tree_write()
2309
new_parents = other_tree.get_parent_ids()
2310
other_root = other_tree.inventory.root
2311
other_root.parent_id = new_root_parent
2312
other_root.name = osutils.basename(other_tree_path)
2313
self.inventory.add(other_root)
2314
add_children(self.inventory, other_root)
2315
self._write_inventory(self.inventory)
2316
# normally we don't want to fetch whole repositories, but i think
2317
# here we really do want to consolidate the whole thing.
2318
for parent_id in other_tree.get_parent_ids():
2319
self.branch.fetch(other_tree.branch, parent_id)
2320
self.add_parent_tree_id(parent_id)
2323
other_tree.bzrdir.retire_bzrdir()
2325
@needs_tree_write_lock
2326
def extract(self, file_id, format=None):
2327
"""Extract a subtree from this tree.
2329
A new branch will be created, relative to the path for this tree.
2333
segments = osutils.splitpath(path)
2334
transport = self.branch.bzrdir.root_transport
2335
for name in segments:
2336
transport = transport.clone(name)
2337
transport.ensure_base()
2340
sub_path = self.id2path(file_id)
2341
branch_transport = mkdirs(sub_path)
2343
format = self.bzrdir.cloning_metadir()
2344
branch_transport.ensure_base()
2345
branch_bzrdir = format.initialize_on_transport(branch_transport)
2347
repo = branch_bzrdir.find_repository()
2348
except errors.NoRepositoryPresent:
2349
repo = branch_bzrdir.create_repository()
2350
if not repo.supports_rich_root():
2351
raise errors.RootNotRich()
2352
new_branch = branch_bzrdir.create_branch()
2353
new_branch.pull(self.branch)
2354
for parent_id in self.get_parent_ids():
2355
new_branch.fetch(self.branch, parent_id)
2356
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2357
if tree_transport.base != branch_transport.base:
2358
tree_bzrdir = format.initialize_on_transport(tree_transport)
2359
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2360
target_branch=new_branch)
2362
tree_bzrdir = branch_bzrdir
2363
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2364
wt.set_parent_ids(self.get_parent_ids())
2365
my_inv = self.inventory
2366
child_inv = inventory.Inventory(root_id=None)
2367
new_root = my_inv[file_id]
2368
my_inv.remove_recursive_id(file_id)
2369
new_root.parent_id = None
2370
child_inv.add(new_root)
2371
self._write_inventory(my_inv)
2372
wt._write_inventory(child_inv)
2375
def list_files(self, include_root=False, from_dir=None, recursive=True):
2376
"""List all files as (path, class, kind, id, entry).
2378
Lists, but does not descend into unversioned directories.
2379
This does not include files that have been deleted in this
2380
tree. Skips the control directory.
2382
:param include_root: if True, return an entry for the root
2383
:param from_dir: start from this directory or None for the root
2384
:param recursive: whether to recurse into subdirectories or not
2386
# list_files is an iterator, so @needs_read_lock doesn't work properly
2387
# with it. So callers should be careful to always read_lock the tree.
2388
if not self.is_locked():
2389
raise errors.ObjectNotLocked(self)
2391
inv = self.inventory
2392
if from_dir is None and include_root is True:
2393
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2394
# Convert these into local objects to save lookup times
2395
pathjoin = osutils.pathjoin
2396
file_kind = self._kind
2398
# transport.base ends in a slash, we want the piece
2399
# between the last two slashes
2400
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2402
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2404
# directory file_id, relative path, absolute path, reverse sorted children
2405
if from_dir is not None:
2406
from_dir_id = inv.path2id(from_dir)
2407
if from_dir_id is None:
2408
# Directory not versioned
2410
from_dir_abspath = pathjoin(self.basedir, from_dir)
2412
from_dir_id = inv.root.file_id
2413
from_dir_abspath = self.basedir
2414
children = os.listdir(from_dir_abspath)
2416
# jam 20060527 The kernel sized tree seems equivalent whether we
2417
# use a deque and popleft to keep them sorted, or if we use a plain
2418
# list and just reverse() them.
2419
children = collections.deque(children)
2420
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2422
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2425
f = children.popleft()
2426
## TODO: If we find a subdirectory with its own .bzr
2427
## directory, then that is a separate tree and we
2428
## should exclude it.
2430
# the bzrdir for this tree
2431
if transport_base_dir == f:
2434
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2435
# and 'f' doesn't begin with one, we can do a string op, rather
2436
# than the checks of pathjoin(), all relative paths will have an extra slash
2438
fp = from_dir_relpath + '/' + f
2441
fap = from_dir_abspath + '/' + f
2443
dir_ie = inv[from_dir_id]
2444
if dir_ie.kind == 'directory':
2445
f_ie = dir_ie.children.get(f)
2450
elif self.is_ignored(fp[1:]):
2453
# we may not have found this file, because of a unicode
2454
# issue, or because the directory was actually a symlink.
2455
f_norm, can_access = osutils.normalized_filename(f)
2456
if f == f_norm or not can_access:
2457
# No change, so treat this file normally
2460
# this file can be accessed by a normalized path
2461
# check again if it is versioned
2462
# these lines are repeated here for performance
2464
fp = from_dir_relpath + '/' + f
2465
fap = from_dir_abspath + '/' + f
2466
f_ie = inv.get_child(from_dir_id, f)
2469
elif self.is_ignored(fp[1:]):
2476
# make a last minute entry
2478
yield fp[1:], c, fk, f_ie.file_id, f_ie
2481
yield fp[1:], c, fk, None, fk_entries[fk]()
2483
yield fp[1:], c, fk, None, TreeEntry()
2486
if fk != 'directory':
2489
# But do this child first if recursing down
2491
new_children = os.listdir(fap)
2493
new_children = collections.deque(new_children)
2494
stack.append((f_ie.file_id, fp, fap, new_children))
2495
# Break out of inner loop,
2496
# so that we start outer loop with child
2499
# if we finished all children, pop it off the stack
2502
@needs_tree_write_lock
2503
def move(self, from_paths, to_dir=None, after=False):
2506
to_dir must exist in the inventory.
2508
If to_dir exists and is a directory, the files are moved into
2509
it, keeping their old names.
2511
Note that to_dir is only the last component of the new name;
2512
this doesn't change the directory.
2514
For each entry in from_paths the move mode will be determined
2517
The first mode moves the file in the filesystem and updates the
2518
inventory. The second mode only updates the inventory without
2519
touching the file on the filesystem.
2521
move uses the second mode if 'after == True' and the target is
2522
either not versioned or newly added, and present in the working tree.
2524
move uses the second mode if 'after == False' and the source is
2525
versioned but no longer in the working tree, and the target is not
2526
versioned but present in the working tree.
2528
move uses the first mode if 'after == False' and the source is
2529
versioned and present in the working tree, and the target is not
2530
versioned and not present in the working tree.
2532
Everything else results in an error.
2534
This returns a list of (from_path, to_path) pairs for each
2535
entry that is moved.
2540
# check for deprecated use of signature
2542
raise TypeError('You must supply a target directory')
2543
# check destination directory
2544
if isinstance(from_paths, basestring):
2546
inv = self.inventory
2547
to_abs = self.abspath(to_dir)
2548
if not isdir(to_abs):
2549
raise errors.BzrMoveFailedError('',to_dir,
2550
errors.NotADirectory(to_abs))
2551
if not self.has_filename(to_dir):
2552
raise errors.BzrMoveFailedError('',to_dir,
2553
errors.NotInWorkingDirectory(to_dir))
2554
to_dir_id = inv.path2id(to_dir)
2555
if to_dir_id is None:
2556
raise errors.BzrMoveFailedError('',to_dir,
2557
errors.NotVersionedError(path=to_dir))
2559
to_dir_ie = inv[to_dir_id]
2560
if to_dir_ie.kind != 'directory':
2561
raise errors.BzrMoveFailedError('',to_dir,
2562
errors.NotADirectory(to_abs))
2564
# create rename entries and tuples
2565
for from_rel in from_paths:
2566
from_tail = splitpath(from_rel)[-1]
2567
from_id = inv.path2id(from_rel)
2569
raise errors.BzrMoveFailedError(from_rel,to_dir,
2570
errors.NotVersionedError(path=from_rel))
2572
from_entry = inv[from_id]
2573
from_parent_id = from_entry.parent_id
2574
to_rel = pathjoin(to_dir, from_tail)
2575
rename_entry = InventoryWorkingTree._RenameEntry(
2578
from_tail=from_tail,
2579
from_parent_id=from_parent_id,
2580
to_rel=to_rel, to_tail=from_tail,
2581
to_parent_id=to_dir_id)
2582
rename_entries.append(rename_entry)
2583
rename_tuples.append((from_rel, to_rel))
2585
# determine which move mode to use. checks also for movability
2586
rename_entries = self._determine_mv_mode(rename_entries, after)
2588
original_modified = self._inventory_is_modified
2591
self._inventory_is_modified = True
2592
self._move(rename_entries)
2594
# restore the inventory on error
2595
self._inventory_is_modified = original_modified
2597
self._write_inventory(inv)
2598
return rename_tuples
2600
@needs_tree_write_lock
2601
def rename_one(self, from_rel, to_rel, after=False):
2604
This can change the directory or the filename or both.
2606
rename_one has several 'modes' to work. First, it can rename a physical
2607
file and change the file_id. That is the normal mode. Second, it can
2608
only change the file_id without touching any physical file.
2610
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2611
versioned but present in the working tree.
2613
rename_one uses the second mode if 'after == False' and 'from_rel' is
2614
versioned but no longer in the working tree, and 'to_rel' is not
2615
versioned but present in the working tree.
2617
rename_one uses the first mode if 'after == False' and 'from_rel' is
2618
versioned and present in the working tree, and 'to_rel' is not
2619
versioned and not present in the working tree.
2621
Everything else results in an error.
2623
inv = self.inventory
2626
# create rename entries and tuples
2627
from_tail = splitpath(from_rel)[-1]
2628
from_id = inv.path2id(from_rel)
2630
# if file is missing in the inventory maybe it's in the basis_tree
2631
basis_tree = self.branch.basis_tree()
2632
from_id = basis_tree.path2id(from_rel)
2634
raise errors.BzrRenameFailedError(from_rel,to_rel,
2635
errors.NotVersionedError(path=from_rel))
2636
# put entry back in the inventory so we can rename it
2637
from_entry = basis_tree.inventory[from_id].copy()
2640
from_entry = inv[from_id]
2641
from_parent_id = from_entry.parent_id
2642
to_dir, to_tail = os.path.split(to_rel)
2643
to_dir_id = inv.path2id(to_dir)
2644
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2646
from_tail=from_tail,
2647
from_parent_id=from_parent_id,
2648
to_rel=to_rel, to_tail=to_tail,
2649
to_parent_id=to_dir_id)
2650
rename_entries.append(rename_entry)
2652
# determine which move mode to use. checks also for movability
2653
rename_entries = self._determine_mv_mode(rename_entries, after)
2655
# check if the target changed directory and if the target directory is
2657
if to_dir_id is None:
2658
raise errors.BzrMoveFailedError(from_rel,to_rel,
2659
errors.NotVersionedError(path=to_dir))
2661
# all checks done. now we can continue with our actual work
2662
mutter('rename_one:\n'
2667
' to_dir_id {%s}\n',
2668
from_id, from_rel, to_rel, to_dir, to_dir_id)
2670
self._move(rename_entries)
2671
self._write_inventory(inv)
2673
class _RenameEntry(object):
2674
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2675
to_rel, to_tail, to_parent_id, only_change_inv=False,
2677
self.from_rel = from_rel
2678
self.from_id = from_id
2679
self.from_tail = from_tail
2680
self.from_parent_id = from_parent_id
2681
self.to_rel = to_rel
2682
self.to_tail = to_tail
2683
self.to_parent_id = to_parent_id
2684
self.change_id = change_id
2685
self.only_change_inv = only_change_inv
2687
def _determine_mv_mode(self, rename_entries, after=False):
2688
"""Determines for each from-to pair if both inventory and working tree
2689
or only the inventory has to be changed.
2691
Also does basic plausability tests.
2693
inv = self.inventory
2695
for rename_entry in rename_entries:
2696
# store to local variables for easier reference
2697
from_rel = rename_entry.from_rel
2698
from_id = rename_entry.from_id
2699
to_rel = rename_entry.to_rel
2700
to_id = inv.path2id(to_rel)
2701
only_change_inv = False
2704
# check the inventory for source and destination
2706
raise errors.BzrMoveFailedError(from_rel,to_rel,
2707
errors.NotVersionedError(path=from_rel))
2708
if to_id is not None:
2710
# allow it with --after but only if dest is newly added
2712
basis = self.basis_tree()
2715
if not basis.has_id(to_id):
2716
rename_entry.change_id = True
2721
raise errors.BzrMoveFailedError(from_rel,to_rel,
2722
errors.AlreadyVersionedError(path=to_rel))
2724
# try to determine the mode for rename (only change inv or change
2725
# inv and file system)
2727
if not self.has_filename(to_rel):
2728
raise errors.BzrMoveFailedError(from_id,to_rel,
2729
errors.NoSuchFile(path=to_rel,
2730
extra="New file has not been created yet"))
2731
only_change_inv = True
2732
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2733
only_change_inv = True
2734
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2735
only_change_inv = False
2736
elif (not self.case_sensitive
2737
and from_rel.lower() == to_rel.lower()
2738
and self.has_filename(from_rel)):
2739
only_change_inv = False
2741
# something is wrong, so lets determine what exactly
2742
if not self.has_filename(from_rel) and \
2743
not self.has_filename(to_rel):
2744
raise errors.BzrRenameFailedError(from_rel,to_rel,
2745
errors.PathsDoNotExist(paths=(str(from_rel),
2748
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2749
rename_entry.only_change_inv = only_change_inv
2750
return rename_entries
2752
def _move(self, rename_entries):
2753
"""Moves a list of files.
2755
Depending on the value of the flag 'only_change_inv', the
2756
file will be moved on the file system or not.
2758
inv = self.inventory
2761
for entry in rename_entries:
2763
self._move_entry(entry)
2765
self._rollback_move(moved)
2769
def _rollback_move(self, moved):
2770
"""Try to rollback a previous move in case of an filesystem error."""
2771
inv = self.inventory
2774
self._move_entry(WorkingTree._RenameEntry(
2775
entry.to_rel, entry.from_id,
2776
entry.to_tail, entry.to_parent_id, entry.from_rel,
2777
entry.from_tail, entry.from_parent_id,
2778
entry.only_change_inv))
2779
except errors.BzrMoveFailedError, e:
2780
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2781
" The working tree is in an inconsistent state."
2782
" Please consider doing a 'bzr revert'."
2783
" Error message is: %s" % e)
2785
def _move_entry(self, entry):
2786
inv = self.inventory
2787
from_rel_abs = self.abspath(entry.from_rel)
2788
to_rel_abs = self.abspath(entry.to_rel)
2789
if from_rel_abs == to_rel_abs:
2790
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2791
"Source and target are identical.")
2793
if not entry.only_change_inv:
2795
osutils.rename(from_rel_abs, to_rel_abs)
2797
raise errors.BzrMoveFailedError(entry.from_rel,
2800
to_id = inv.path2id(entry.to_rel)
2801
inv.remove_recursive_id(to_id)
2802
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2804
@needs_tree_write_lock
2805
def unversion(self, file_ids):
2806
"""Remove the file ids in file_ids from the current versioned set.
2808
When a file_id is unversioned, all of its children are automatically
2811
:param file_ids: The file ids to stop versioning.
2812
:raises: NoSuchId if any fileid is not currently versioned.
2814
for file_id in file_ids:
2815
if not self._inventory.has_id(file_id):
2816
raise errors.NoSuchId(self, file_id)
2817
for file_id in file_ids:
2818
if self._inventory.has_id(file_id):
2819
self._inventory.remove_recursive_id(file_id)
2821
# in the future this should just set a dirty bit to wait for the
2822
# final unlock. However, until all methods of workingtree start
2823
# with the current in -memory inventory rather than triggering
2824
# a read, it is more complex - we need to teach read_inventory
2825
# to know when to read, and when to not read first... and possibly
2826
# to save first when the in memory one may be corrupted.
2827
# so for now, we just only write it if it is indeed dirty.
2829
self._write_inventory(self._inventory)
2831
def stored_kind(self, file_id):
2832
"""See Tree.stored_kind"""
2833
return self.inventory[file_id].kind
2836
"""Yield all unversioned files in this WorkingTree.
2838
If there are any unversioned directories then only the directory is
2839
returned, not all its children. But if there are unversioned files
2840
under a versioned subdirectory, they are returned.
2842
Currently returned depth-first, sorted by name within directories.
2843
This is the same order used by 'osutils.walkdirs'.
2845
## TODO: Work from given directory downwards
2846
for path, dir_entry in self.inventory.directories():
2847
# mutter("search for unknowns in %r", path)
2848
dirabs = self.abspath(path)
2849
if not isdir(dirabs):
2850
# e.g. directory deleted
2854
for subf in os.listdir(dirabs):
2855
if self.bzrdir.is_control_filename(subf):
2857
if subf not in dir_entry.children:
2860
can_access) = osutils.normalized_filename(subf)
2861
except UnicodeDecodeError:
2862
path_os_enc = path.encode(osutils._fs_enc)
2863
relpath = path_os_enc + '/' + subf
2864
raise errors.BadFilenameEncoding(relpath,
2866
if subf_norm != subf and can_access:
2867
if subf_norm not in dir_entry.children:
2868
fl.append(subf_norm)
2874
subp = pathjoin(path, subf)
2877
def _walkdirs(self, prefix=""):
2878
"""Walk the directories of this tree.
2880
:param prefix: is used as the directrory to start with.
2881
:returns: a generator which yields items in the form::
2883
((curren_directory_path, fileid),
2884
[(file1_path, file1_name, file1_kind, None, file1_id,
2887
_directory = 'directory'
2888
# get the root in the inventory
2889
inv = self.inventory
2890
top_id = inv.path2id(prefix)
2894
pending = [(prefix, '', _directory, None, top_id, None)]
2897
currentdir = pending.pop()
2898
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2899
top_id = currentdir[4]
2901
relroot = currentdir[0] + '/'
2904
# FIXME: stash the node in pending
2906
if entry.kind == 'directory':
2907
for name, child in entry.sorted_children():
2908
dirblock.append((relroot + name, name, child.kind, None,
2909
child.file_id, child.kind
2911
yield (currentdir[0], entry.file_id), dirblock
2912
# push the user specified dirs from dirblock
2913
for dir in reversed(dirblock):
2914
if dir[2] == _directory:
2918
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2919
"""Registry for working tree formats."""
2921
def __init__(self, other_registry=None):
2922
super(WorkingTreeFormatRegistry, self).__init__(other_registry)
2923
self._default_format = None
2924
self._default_format_key = None
2926
def get_default(self):
2927
"""Return the current default format."""
2928
if (self._default_format_key is not None and
2929
self._default_format is None):
2930
self._default_format = self.get(self._default_format_key)
2931
return self._default_format
2933
def set_default(self, format):
2934
"""Set the default format."""
2935
self._default_format = format
2936
self._default_format_key = None
2938
def set_default_key(self, format_string):
2939
"""Set the default format by its format string."""
2940
self._default_format_key = format_string
2941
self._default_format = None
2944
format_registry = WorkingTreeFormatRegistry()
2947
class WorkingTreeFormat(controldir.ControlComponentFormat):
2728
# do non-implementation specific cleanup
2730
if self._control_files._lock_count == 1:
2731
# _inventory_is_modified is always False during a read lock.
2732
if self._inventory_is_modified:
2734
self._write_hashcache_if_dirty()
2735
# reverse order of locking.
2737
return self._control_files.unlock()
2739
self.branch.unlock()
2742
def get_conflicted_stem(path):
2743
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2744
if path.endswith(suffix):
2745
return path[:-len(suffix)]
2748
class WorkingTreeFormat(object):
2948
2749
"""An encapsulation of the initialization and open routines for a format.
2950
2751
Formats provide three things:
3053
@symbol_versioning.deprecated_method(
3054
symbol_versioning.deprecated_in((2, 4, 0)))
3055
2826
def register_format(klass, format):
3056
format_registry.register(format)
3059
@symbol_versioning.deprecated_method(
3060
symbol_versioning.deprecated_in((2, 4, 0)))
3061
def register_extra_format(klass, format):
3062
format_registry.register_extra(format)
3065
@symbol_versioning.deprecated_method(
3066
symbol_versioning.deprecated_in((2, 4, 0)))
3067
def unregister_extra_format(klass, format):
3068
format_registry.unregister_extra(format)
3071
@symbol_versioning.deprecated_method(
3072
symbol_versioning.deprecated_in((2, 4, 0)))
3073
def get_formats(klass):
3074
return format_registry._get_all()
3077
@symbol_versioning.deprecated_method(
3078
symbol_versioning.deprecated_in((2, 4, 0)))
2827
klass._formats[format.get_format_string()] = format
3079
2830
def set_default_format(klass, format):
3080
format_registry.set_default(format)
2831
klass._default_format = format
3083
@symbol_versioning.deprecated_method(
3084
symbol_versioning.deprecated_in((2, 4, 0)))
3085
2834
def unregister_format(klass, format):
3086
format_registry.remove(format)
3089
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3090
"bzrlib.workingtree_4", "WorkingTreeFormat4")
3091
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3092
"bzrlib.workingtree_4", "WorkingTreeFormat5")
3093
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3094
"bzrlib.workingtree_4", "WorkingTreeFormat6")
3095
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
3096
"bzrlib.workingtree_3", "WorkingTreeFormat3")
3097
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")
2835
del klass._formats[format.get_format_string()]
2838
class WorkingTreeFormat2(WorkingTreeFormat):
2839
"""The second working tree format.
2841
This format modified the hash cache from the format 1 hash cache.
2844
upgrade_recommended = True
2846
def get_format_description(self):
2847
"""See WorkingTreeFormat.get_format_description()."""
2848
return "Working tree format 2"
2850
def _stub_initialize_on_transport(self, transport, file_mode):
2851
"""Workaround: create control files for a remote working tree.
2853
This ensures that it can later be updated and dealt with locally,
2854
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2855
no working tree. (See bug #43064).
2858
inv = inventory.Inventory()
2859
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2861
transport.put_file('inventory', sio, file_mode)
2862
transport.put_bytes('pending-merges', '', file_mode)
2864
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2865
accelerator_tree=None, hardlink=False):
2866
"""See WorkingTreeFormat.initialize()."""
2867
if not isinstance(a_bzrdir.transport, LocalTransport):
2868
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2869
if from_branch is not None:
2870
branch = from_branch
2872
branch = a_bzrdir.open_branch()
2873
if revision_id is None:
2874
revision_id = _mod_revision.ensure_null(branch.last_revision())
2877
branch.generate_revision_history(revision_id)
2880
inv = inventory.Inventory()
2881
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2887
basis_tree = branch.repository.revision_tree(revision_id)
2888
if basis_tree.inventory.root is not None:
2889
wt.set_root_id(basis_tree.get_root_id())
2890
# set the parent list and cache the basis tree.
2891
if _mod_revision.is_null(revision_id):
2894
parent_trees = [(revision_id, basis_tree)]
2895
wt.set_parent_trees(parent_trees)
2896
transform.build_tree(basis_tree, wt)
2900
super(WorkingTreeFormat2, self).__init__()
2901
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2903
def open(self, a_bzrdir, _found=False):
2904
"""Return the WorkingTree object for a_bzrdir
2906
_found is a private parameter, do not use it. It is used to indicate
2907
if format probing has already been done.
2910
# we are being called directly and must probe.
2911
raise NotImplementedError
2912
if not isinstance(a_bzrdir.transport, LocalTransport):
2913
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2914
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2920
class WorkingTreeFormat3(WorkingTreeFormat):
2921
"""The second working tree format updated to record a format marker.
2924
- exists within a metadir controlling .bzr
2925
- includes an explicit version marker for the workingtree control
2926
files, separate from the BzrDir format
2927
- modifies the hash cache format
2929
- uses a LockDir to guard access for writes.
2932
upgrade_recommended = True
2934
def get_format_string(self):
2935
"""See WorkingTreeFormat.get_format_string()."""
2936
return "Bazaar-NG Working Tree format 3"
2938
def get_format_description(self):
2939
"""See WorkingTreeFormat.get_format_description()."""
2940
return "Working tree format 3"
2942
_lock_file_name = 'lock'
2943
_lock_class = LockDir
2945
_tree_class = WorkingTree3
2947
def __get_matchingbzrdir(self):
2948
return bzrdir.BzrDirMetaFormat1()
2950
_matchingbzrdir = property(__get_matchingbzrdir)
2952
def _open_control_files(self, a_bzrdir):
2953
transport = a_bzrdir.get_workingtree_transport(None)
2954
return LockableFiles(transport, self._lock_file_name,
2957
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2958
accelerator_tree=None, hardlink=False):
2959
"""See WorkingTreeFormat.initialize().
2961
:param revision_id: if supplied, create a working tree at a different
2962
revision than the branch is at.
2963
:param accelerator_tree: A tree which can be used for retrieving file
2964
contents more quickly than the revision tree, i.e. a workingtree.
2965
The revision tree will be used for cases where accelerator_tree's
2966
content is different.
2967
:param hardlink: If true, hard-link files from accelerator_tree,
2970
if not isinstance(a_bzrdir.transport, LocalTransport):
2971
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2972
transport = a_bzrdir.get_workingtree_transport(self)
2973
control_files = self._open_control_files(a_bzrdir)
2974
control_files.create_lock()
2975
control_files.lock_write()
2976
transport.put_bytes('format', self.get_format_string(),
2977
mode=a_bzrdir._get_file_mode())
2978
if from_branch is not None:
2979
branch = from_branch
2981
branch = a_bzrdir.open_branch()
2982
if revision_id is None:
2983
revision_id = _mod_revision.ensure_null(branch.last_revision())
2984
# WorkingTree3 can handle an inventory which has a unique root id.
2985
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2986
# those trees. And because there isn't a format bump inbetween, we
2987
# are maintaining compatibility with older clients.
2988
# inv = Inventory(root_id=gen_root_id())
2989
inv = self._initial_inventory()
2990
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2996
_control_files=control_files)
2997
wt.lock_tree_write()
2999
basis_tree = branch.repository.revision_tree(revision_id)
3000
# only set an explicit root id if there is one to set.
3001
if basis_tree.inventory.root is not None:
3002
wt.set_root_id(basis_tree.get_root_id())
3003
if revision_id == _mod_revision.NULL_REVISION:
3004
wt.set_parent_trees([])
3006
wt.set_parent_trees([(revision_id, basis_tree)])
3007
transform.build_tree(basis_tree, wt)
3009
# Unlock in this order so that the unlock-triggers-flush in
3010
# WorkingTree is given a chance to fire.
3011
control_files.unlock()
3015
def _initial_inventory(self):
3016
return inventory.Inventory()
3019
super(WorkingTreeFormat3, self).__init__()
3021
def open(self, a_bzrdir, _found=False):
3022
"""Return the WorkingTree object for a_bzrdir
3024
_found is a private parameter, do not use it. It is used to indicate
3025
if format probing has already been done.
3028
# we are being called directly and must probe.
3029
raise NotImplementedError
3030
if not isinstance(a_bzrdir.transport, LocalTransport):
3031
raise errors.NotLocalUrl(a_bzrdir.transport.base)
3032
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
3035
def _open(self, a_bzrdir, control_files):
3036
"""Open the tree itself.
3038
:param a_bzrdir: the dir for the tree.
3039
:param control_files: the control files for the tree.
3041
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
3045
_control_files=control_files)
3048
return self.get_format_string()
3051
__default_format = WorkingTreeFormat6()
3052
WorkingTreeFormat.register_format(__default_format)
3053
WorkingTreeFormat.register_format(WorkingTreeFormat5())
3054
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3055
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3056
WorkingTreeFormat.set_default_format(__default_format)
3057
# formats which have no format string are not discoverable
3058
# and not independently creatable, so are not registered.
3059
_legacy_formats = [WorkingTreeFormat2(),