2608
1848
return ShelfManager(self, self._transport)
2611
class WorkingTree2(WorkingTree):
2612
"""This is the Format 2 working tree.
2614
This was the first weave based working tree.
2615
- uses os locks for locking.
2616
- uses the branch last-revision.
1851
class InventoryWorkingTree(WorkingTree,
1852
bzrlib.mutabletree.MutableInventoryTree):
1853
"""Base class for working trees that are inventory-oriented.
1855
The inventory is held in the `Branch` working-inventory, and the
1856
files are in a directory on disk.
1858
It is possible for a `WorkingTree` to have a filename which is
1859
not listed in the Inventory and vice versa.
2619
def __init__(self, *args, **kwargs):
2620
super(WorkingTree2, self).__init__(*args, **kwargs)
2621
# WorkingTree2 has more of a constraint that self._inventory must
2622
# exist. Because this is an older format, we don't mind the overhead
2623
# caused by the extra computation here.
2625
# Newer WorkingTree's should only have self._inventory set when they
2627
if self._inventory is None:
2628
self.read_working_inventory()
2630
def _get_check_refs(self):
2631
"""Return the references needed to perform a check of this tree."""
2632
return [('trees', self.last_revision())]
2634
def lock_tree_write(self):
2635
"""See WorkingTree.lock_tree_write().
2637
In Format2 WorkingTrees we have a single lock for the branch and tree
2638
so lock_tree_write() degrades to lock_write().
2640
self.branch.lock_write()
2642
return self._control_files.lock_write()
2644
self.branch.unlock()
2648
# do non-implementation specific cleanup
2651
# we share control files:
2652
if self._control_files._lock_count == 3:
2653
# _inventory_is_modified is always False during a read lock.
2654
if self._inventory_is_modified:
2656
self._write_hashcache_if_dirty()
2658
# reverse order of locking.
2660
return self._control_files.unlock()
1862
def __init__(self, basedir='.',
1863
branch=DEPRECATED_PARAMETER,
1865
_control_files=None,
1869
"""Construct a InventoryWorkingTree instance. This is not a public API.
1871
:param branch: A branch to override probing for the branch.
1873
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1874
branch=branch, _control_files=_control_files, _internal=_internal,
1875
_format=_format, _bzrdir=_bzrdir)
1877
if _inventory is None:
1878
# This will be acquired on lock_read() or lock_write()
1879
self._inventory_is_modified = False
1880
self._inventory = None
1882
# the caller of __init__ has provided an inventory,
1883
# we assume they know what they are doing - as its only
1884
# the Format factory and creation methods that are
1885
# permitted to do this.
1886
self._set_inventory(_inventory, dirty=False)
1888
def _set_inventory(self, inv, dirty):
1889
"""Set the internal cached inventory.
1891
:param inv: The inventory to set.
1892
:param dirty: A boolean indicating whether the inventory is the same
1893
logical inventory as whats on disk. If True the inventory is not
1894
the same and should be written to disk or data will be lost, if
1895
False then the inventory is the same as that on disk and any
1896
serialisation would be unneeded overhead.
1898
self._inventory = inv
1899
self._inventory_is_modified = dirty
1901
def _serialize(self, inventory, out_file):
1902
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1905
def _deserialize(selt, in_file):
1906
return xml5.serializer_v5.read_inventory(in_file)
1908
@needs_tree_write_lock
1909
def _write_inventory(self, inv):
1910
"""Write inventory as the current inventory."""
1911
self._set_inventory(inv, dirty=True)
1914
# XXX: This method should be deprecated in favour of taking in a proper
1915
# new Inventory object.
1916
@needs_tree_write_lock
1917
def set_inventory(self, new_inventory_list):
1918
from bzrlib.inventory import (Inventory,
1922
inv = Inventory(self.get_root_id())
1923
for path, file_id, parent, kind in new_inventory_list:
1924
name = os.path.basename(path)
1927
# fixme, there should be a factory function inv,add_??
1928
if kind == 'directory':
1929
inv.add(InventoryDirectory(file_id, name, parent))
1930
elif kind == 'file':
1931
inv.add(InventoryFile(file_id, name, parent))
1932
elif kind == 'symlink':
1933
inv.add(InventoryLink(file_id, name, parent))
1935
raise errors.BzrError("unknown kind %r" % kind)
1936
self._write_inventory(inv)
1938
def _write_basis_inventory(self, xml):
1939
"""Write the basis inventory XML to the basis-inventory file"""
1940
path = self._basis_inventory_name()
1942
self._transport.put_file(path, sio,
1943
mode=self.bzrdir._get_file_mode())
1945
def _reset_data(self):
1946
"""Reset transient data that cannot be revalidated."""
1947
self._inventory_is_modified = False
1948
f = self._transport.get('inventory')
1950
result = self._deserialize(f)
2662
self.branch.unlock()
2665
class WorkingTree3(WorkingTree):
2666
"""This is the Format 3 working tree.
2668
This differs from the base WorkingTree by:
2669
- having its own file lock
2670
- having its own last-revision property.
2672
This is new in bzr 0.8
2676
def _last_revision(self):
2677
"""See Mutable.last_revision."""
1953
self._set_inventory(result, dirty=False)
1955
def _set_root_id(self, file_id):
1956
"""Set the root id for this tree, in a format specific manner.
1958
:param file_id: The file id to assign to the root. It must not be
1959
present in the current inventory or an error will occur. It must
1960
not be None, but rather a valid file id.
1962
inv = self._inventory
1963
orig_root_id = inv.root.file_id
1964
# TODO: it might be nice to exit early if there was nothing
1965
# to do, saving us from trigger a sync on unlock.
1966
self._inventory_is_modified = True
1967
# we preserve the root inventory entry object, but
1968
# unlinkit from the byid index
1969
del inv._byid[inv.root.file_id]
1970
inv.root.file_id = file_id
1971
# and link it into the index with the new changed id.
1972
inv._byid[inv.root.file_id] = inv.root
1973
# and finally update all children to reference the new id.
1974
# XXX: this should be safe to just look at the root.children
1975
# list, not the WHOLE INVENTORY.
1978
if entry.parent_id == orig_root_id:
1979
entry.parent_id = inv.root.file_id
1981
def all_file_ids(self):
1982
"""See Tree.iter_all_file_ids"""
1983
return set(self.inventory)
1985
def _cache_basis_inventory(self, new_revision):
1986
"""Cache new_revision as the basis inventory."""
1987
# TODO: this should allow the ready-to-use inventory to be passed in,
1988
# as commit already has that ready-to-use [while the format is the
2679
return self._transport.get_bytes('last-revision')
2680
except errors.NoSuchFile:
2681
return _mod_revision.NULL_REVISION
2683
def _change_last_revision(self, revision_id):
2684
"""See WorkingTree._change_last_revision."""
2685
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2687
self._transport.delete('last-revision')
2688
except errors.NoSuchFile:
2692
self._transport.put_bytes('last-revision', revision_id,
2693
mode=self.bzrdir._get_file_mode())
2696
def _get_check_refs(self):
2697
"""Return the references needed to perform a check of this tree."""
2698
return [('trees', self.last_revision())]
1991
# this double handles the inventory - unpack and repack -
1992
# but is easier to understand. We can/should put a conditional
1993
# in here based on whether the inventory is in the latest format
1994
# - perhaps we should repack all inventories on a repository
1996
# the fast path is to copy the raw xml from the repository. If the
1997
# xml contains 'revision_id="', then we assume the right
1998
# revision_id is set. We must check for this full string, because a
1999
# root node id can legitimately look like 'revision_id' but cannot
2001
xml = self.branch.repository._get_inventory_xml(new_revision)
2002
firstline = xml.split('\n', 1)[0]
2003
if (not 'revision_id="' in firstline or
2004
'format="7"' not in firstline):
2005
inv = self.branch.repository._serializer.read_inventory_from_string(
2007
xml = self._create_basis_xml_from_inventory(new_revision, inv)
2008
self._write_basis_inventory(xml)
2009
except (errors.NoSuchRevision, errors.RevisionNotPresent):
2012
def _basis_inventory_name(self):
2013
return 'basis-inventory-cache'
2015
def _create_basis_xml_from_inventory(self, revision_id, inventory):
2016
"""Create the text that will be saved in basis-inventory"""
2017
inventory.revision_id = revision_id
2018
return xml7.serializer_v7.write_inventory_to_string(inventory)
2700
2020
@needs_tree_write_lock
2701
2021
def set_conflicts(self, conflicts):
2721
2041
raise errors.ConflictFormatError()
2722
2042
except StopIteration:
2723
2043
raise errors.ConflictFormatError()
2724
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2044
reader = _mod_rio.RioReader(confile)
2045
return _mod_conflicts.ConflictList.from_stanzas(reader)
2726
2047
confile.close()
2729
# do non-implementation specific cleanup
2731
if self._control_files._lock_count == 1:
2732
# _inventory_is_modified is always False during a read lock.
2733
if self._inventory_is_modified:
2735
self._write_hashcache_if_dirty()
2736
# reverse order of locking.
2738
return self._control_files.unlock()
2740
self.branch.unlock()
2743
def get_conflicted_stem(path):
2744
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2745
if path.endswith(suffix):
2746
return path[:-len(suffix)]
2749
class WorkingTreeFormat(object):
2049
def read_basis_inventory(self):
2050
"""Read the cached basis inventory."""
2051
path = self._basis_inventory_name()
2052
return self._transport.get_bytes(path)
2055
def read_working_inventory(self):
2056
"""Read the working inventory.
2058
:raises errors.InventoryModified: read_working_inventory will fail
2059
when the current in memory inventory has been modified.
2061
# conceptually this should be an implementation detail of the tree.
2062
# XXX: Deprecate this.
2063
# ElementTree does its own conversion from UTF-8, so open in
2065
if self._inventory_is_modified:
2066
raise errors.InventoryModified(self)
2067
f = self._transport.get('inventory')
2069
result = self._deserialize(f)
2072
self._set_inventory(result, dirty=False)
2076
def get_root_id(self):
2077
"""Return the id of this trees root"""
2078
return self._inventory.root.file_id
2080
def has_id(self, file_id):
2081
# files that have been deleted are excluded
2082
inv = self.inventory
2083
if not inv.has_id(file_id):
2085
path = inv.id2path(file_id)
2086
return osutils.lexists(self.abspath(path))
2088
def has_or_had_id(self, file_id):
2089
if file_id == self.inventory.root.file_id:
2091
return self.inventory.has_id(file_id)
2093
__contains__ = has_id
2095
# should be deprecated - this is slow and in any case treating them as a
2096
# container is (we now know) bad style -- mbp 20070302
2097
## @deprecated_method(zero_fifteen)
2099
"""Iterate through file_ids for this tree.
2101
file_ids are in a WorkingTree if they are in the working inventory
2102
and the working file exists.
2104
inv = self._inventory
2105
for path, ie in inv.iter_entries():
2106
if osutils.lexists(self.abspath(path)):
2109
@needs_tree_write_lock
2110
def set_last_revision(self, new_revision):
2111
"""Change the last revision in the working tree."""
2112
if self._change_last_revision(new_revision):
2113
self._cache_basis_inventory(new_revision)
2115
@needs_tree_write_lock
2116
def reset_state(self, revision_ids=None):
2117
"""Reset the state of the working tree.
2119
This does a hard-reset to a last-known-good state. This is a way to
2120
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2122
if revision_ids is None:
2123
revision_ids = self.get_parent_ids()
2124
if not revision_ids:
2125
rt = self.branch.repository.revision_tree(
2126
_mod_revision.NULL_REVISION)
2128
rt = self.branch.repository.revision_tree(revision_ids[0])
2129
self._write_inventory(rt.inventory)
2130
self.set_parent_ids(revision_ids)
2133
"""Write the in memory inventory to disk."""
2134
# TODO: Maybe this should only write on dirty ?
2135
if self._control_files._lock_mode != 'w':
2136
raise errors.NotWriteLocked(self)
2138
self._serialize(self._inventory, sio)
2140
self._transport.put_file('inventory', sio,
2141
mode=self.bzrdir._get_file_mode())
2142
self._inventory_is_modified = False
2145
def get_file_sha1(self, file_id, path=None, stat_value=None):
2147
path = self._inventory.id2path(file_id)
2148
return self._hashcache.get_sha1(path, stat_value)
2150
def get_file_mtime(self, file_id, path=None):
2151
"""See Tree.get_file_mtime."""
2153
path = self.inventory.id2path(file_id)
2154
return os.lstat(self.abspath(path)).st_mtime
2156
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2157
file_id = self.path2id(path)
2159
# For unversioned files on win32, we just assume they are not
2162
return self._inventory[file_id].executable
2164
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2165
mode = stat_result.st_mode
2166
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2168
if not supports_executable():
2169
def is_executable(self, file_id, path=None):
2170
return self._inventory[file_id].executable
2172
_is_executable_from_path_and_stat = \
2173
_is_executable_from_path_and_stat_from_basis
2175
def is_executable(self, file_id, path=None):
2177
path = self.id2path(file_id)
2178
mode = os.lstat(self.abspath(path)).st_mode
2179
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2181
_is_executable_from_path_and_stat = \
2182
_is_executable_from_path_and_stat_from_stat
2184
@needs_tree_write_lock
2185
def _add(self, files, ids, kinds):
2186
"""See MutableTree._add."""
2187
# TODO: Re-adding a file that is removed in the working copy
2188
# should probably put it back with the previous ID.
2189
# the read and write working inventory should not occur in this
2190
# function - they should be part of lock_write and unlock.
2191
inv = self.inventory
2192
for f, file_id, kind in zip(files, ids, kinds):
2194
inv.add_path(f, kind=kind)
2196
inv.add_path(f, kind=kind, file_id=file_id)
2197
self._inventory_is_modified = True
2199
def revision_tree(self, revision_id):
2200
"""See WorkingTree.revision_id."""
2201
if revision_id == self.last_revision():
2203
xml = self.read_basis_inventory()
2204
except errors.NoSuchFile:
2208
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2209
# dont use the repository revision_tree api because we want
2210
# to supply the inventory.
2211
if inv.revision_id == revision_id:
2212
return revisiontree.InventoryRevisionTree(
2213
self.branch.repository, inv, revision_id)
2214
except errors.BadInventoryFormat:
2216
# raise if there was no inventory, or if we read the wrong inventory.
2217
raise errors.NoSuchRevisionInTree(self, revision_id)
2220
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2221
"""See Tree.annotate_iter
2223
This implementation will use the basis tree implementation if possible.
2224
Lines not in the basis are attributed to CURRENT_REVISION
2226
If there are pending merges, lines added by those merges will be
2227
incorrectly attributed to CURRENT_REVISION (but after committing, the
2228
attribution will be correct).
2230
maybe_file_parent_keys = []
2231
for parent_id in self.get_parent_ids():
2233
parent_tree = self.revision_tree(parent_id)
2234
except errors.NoSuchRevisionInTree:
2235
parent_tree = self.branch.repository.revision_tree(parent_id)
2236
parent_tree.lock_read()
2238
if file_id not in parent_tree:
2240
ie = parent_tree.inventory[file_id]
2241
if ie.kind != 'file':
2242
# Note: this is slightly unnecessary, because symlinks and
2243
# directories have a "text" which is the empty text, and we
2244
# know that won't mess up annotations. But it seems cleaner
2246
parent_text_key = (file_id, ie.revision)
2247
if parent_text_key not in maybe_file_parent_keys:
2248
maybe_file_parent_keys.append(parent_text_key)
2250
parent_tree.unlock()
2251
graph = _mod_graph.Graph(self.branch.repository.texts)
2252
heads = graph.heads(maybe_file_parent_keys)
2253
file_parent_keys = []
2254
for key in maybe_file_parent_keys:
2256
file_parent_keys.append(key)
2258
# Now we have the parents of this content
2259
annotator = self.branch.repository.texts.get_annotator()
2260
text = self.get_file_text(file_id)
2261
this_key =(file_id, default_revision)
2262
annotator.add_special_text(this_key, file_parent_keys, text)
2263
annotations = [(key[-1], line)
2264
for key, line in annotator.annotate_flat(this_key)]
2268
def merge_modified(self):
2269
"""Return a dictionary of files modified by a merge.
2271
The list is initialized by WorkingTree.set_merge_modified, which is
2272
typically called after we make some automatic updates to the tree
2275
This returns a map of file_id->sha1, containing only files which are
2276
still in the working inventory and have that text hash.
2279
hashfile = self._transport.get('merge-hashes')
2280
except errors.NoSuchFile:
2285
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2286
raise errors.MergeModifiedFormatError()
2287
except StopIteration:
2288
raise errors.MergeModifiedFormatError()
2289
for s in _mod_rio.RioReader(hashfile):
2290
# RioReader reads in Unicode, so convert file_ids back to utf8
2291
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2292
if file_id not in self.inventory:
2294
text_hash = s.get("hash")
2295
if text_hash == self.get_file_sha1(file_id):
2296
merge_hashes[file_id] = text_hash
2302
def subsume(self, other_tree):
2303
def add_children(inventory, entry):
2304
for child_entry in entry.children.values():
2305
inventory._byid[child_entry.file_id] = child_entry
2306
if child_entry.kind == 'directory':
2307
add_children(inventory, child_entry)
2308
if other_tree.get_root_id() == self.get_root_id():
2309
raise errors.BadSubsumeSource(self, other_tree,
2310
'Trees have the same root')
2312
other_tree_path = self.relpath(other_tree.basedir)
2313
except errors.PathNotChild:
2314
raise errors.BadSubsumeSource(self, other_tree,
2315
'Tree is not contained by the other')
2316
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2317
if new_root_parent is None:
2318
raise errors.BadSubsumeSource(self, other_tree,
2319
'Parent directory is not versioned.')
2320
# We need to ensure that the result of a fetch will have a
2321
# versionedfile for the other_tree root, and only fetching into
2322
# RepositoryKnit2 guarantees that.
2323
if not self.branch.repository.supports_rich_root():
2324
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2325
other_tree.lock_tree_write()
2327
new_parents = other_tree.get_parent_ids()
2328
other_root = other_tree.inventory.root
2329
other_root.parent_id = new_root_parent
2330
other_root.name = osutils.basename(other_tree_path)
2331
self.inventory.add(other_root)
2332
add_children(self.inventory, other_root)
2333
self._write_inventory(self.inventory)
2334
# normally we don't want to fetch whole repositories, but i think
2335
# here we really do want to consolidate the whole thing.
2336
for parent_id in other_tree.get_parent_ids():
2337
self.branch.fetch(other_tree.branch, parent_id)
2338
self.add_parent_tree_id(parent_id)
2341
other_tree.bzrdir.retire_bzrdir()
2343
@needs_tree_write_lock
2344
def extract(self, file_id, format=None):
2345
"""Extract a subtree from this tree.
2347
A new branch will be created, relative to the path for this tree.
2351
segments = osutils.splitpath(path)
2352
transport = self.branch.bzrdir.root_transport
2353
for name in segments:
2354
transport = transport.clone(name)
2355
transport.ensure_base()
2358
sub_path = self.id2path(file_id)
2359
branch_transport = mkdirs(sub_path)
2361
format = self.bzrdir.cloning_metadir()
2362
branch_transport.ensure_base()
2363
branch_bzrdir = format.initialize_on_transport(branch_transport)
2365
repo = branch_bzrdir.find_repository()
2366
except errors.NoRepositoryPresent:
2367
repo = branch_bzrdir.create_repository()
2368
if not repo.supports_rich_root():
2369
raise errors.RootNotRich()
2370
new_branch = branch_bzrdir.create_branch()
2371
new_branch.pull(self.branch)
2372
for parent_id in self.get_parent_ids():
2373
new_branch.fetch(self.branch, parent_id)
2374
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2375
if tree_transport.base != branch_transport.base:
2376
tree_bzrdir = format.initialize_on_transport(tree_transport)
2377
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2378
target_branch=new_branch)
2380
tree_bzrdir = branch_bzrdir
2381
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2382
wt.set_parent_ids(self.get_parent_ids())
2383
my_inv = self.inventory
2384
child_inv = inventory.Inventory(root_id=None)
2385
new_root = my_inv[file_id]
2386
my_inv.remove_recursive_id(file_id)
2387
new_root.parent_id = None
2388
child_inv.add(new_root)
2389
self._write_inventory(my_inv)
2390
wt._write_inventory(child_inv)
2393
def list_files(self, include_root=False, from_dir=None, recursive=True):
2394
"""List all files as (path, class, kind, id, entry).
2396
Lists, but does not descend into unversioned directories.
2397
This does not include files that have been deleted in this
2398
tree. Skips the control directory.
2400
:param include_root: if True, return an entry for the root
2401
:param from_dir: start from this directory or None for the root
2402
:param recursive: whether to recurse into subdirectories or not
2404
# list_files is an iterator, so @needs_read_lock doesn't work properly
2405
# with it. So callers should be careful to always read_lock the tree.
2406
if not self.is_locked():
2407
raise errors.ObjectNotLocked(self)
2409
inv = self.inventory
2410
if from_dir is None and include_root is True:
2411
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2412
# Convert these into local objects to save lookup times
2413
pathjoin = osutils.pathjoin
2414
file_kind = self._kind
2416
# transport.base ends in a slash, we want the piece
2417
# between the last two slashes
2418
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2420
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2422
# directory file_id, relative path, absolute path, reverse sorted children
2423
if from_dir is not None:
2424
from_dir_id = inv.path2id(from_dir)
2425
if from_dir_id is None:
2426
# Directory not versioned
2428
from_dir_abspath = pathjoin(self.basedir, from_dir)
2430
from_dir_id = inv.root.file_id
2431
from_dir_abspath = self.basedir
2432
children = os.listdir(from_dir_abspath)
2434
# jam 20060527 The kernel sized tree seems equivalent whether we
2435
# use a deque and popleft to keep them sorted, or if we use a plain
2436
# list and just reverse() them.
2437
children = collections.deque(children)
2438
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2440
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2443
f = children.popleft()
2444
## TODO: If we find a subdirectory with its own .bzr
2445
## directory, then that is a separate tree and we
2446
## should exclude it.
2448
# the bzrdir for this tree
2449
if transport_base_dir == f:
2452
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2453
# and 'f' doesn't begin with one, we can do a string op, rather
2454
# than the checks of pathjoin(), all relative paths will have an extra slash
2456
fp = from_dir_relpath + '/' + f
2459
fap = from_dir_abspath + '/' + f
2461
dir_ie = inv[from_dir_id]
2462
if dir_ie.kind == 'directory':
2463
f_ie = dir_ie.children.get(f)
2468
elif self.is_ignored(fp[1:]):
2471
# we may not have found this file, because of a unicode
2472
# issue, or because the directory was actually a symlink.
2473
f_norm, can_access = osutils.normalized_filename(f)
2474
if f == f_norm or not can_access:
2475
# No change, so treat this file normally
2478
# this file can be accessed by a normalized path
2479
# check again if it is versioned
2480
# these lines are repeated here for performance
2482
fp = from_dir_relpath + '/' + f
2483
fap = from_dir_abspath + '/' + f
2484
f_ie = inv.get_child(from_dir_id, f)
2487
elif self.is_ignored(fp[1:]):
2494
# make a last minute entry
2496
yield fp[1:], c, fk, f_ie.file_id, f_ie
2499
yield fp[1:], c, fk, None, fk_entries[fk]()
2501
yield fp[1:], c, fk, None, TreeEntry()
2504
if fk != 'directory':
2507
# But do this child first if recursing down
2509
new_children = os.listdir(fap)
2511
new_children = collections.deque(new_children)
2512
stack.append((f_ie.file_id, fp, fap, new_children))
2513
# Break out of inner loop,
2514
# so that we start outer loop with child
2517
# if we finished all children, pop it off the stack
2520
@needs_tree_write_lock
2521
def move(self, from_paths, to_dir=None, after=False):
2524
to_dir must exist in the inventory.
2526
If to_dir exists and is a directory, the files are moved into
2527
it, keeping their old names.
2529
Note that to_dir is only the last component of the new name;
2530
this doesn't change the directory.
2532
For each entry in from_paths the move mode will be determined
2535
The first mode moves the file in the filesystem and updates the
2536
inventory. The second mode only updates the inventory without
2537
touching the file on the filesystem.
2539
move uses the second mode if 'after == True' and the target is not
2540
versioned but present in the working tree.
2542
move uses the second mode if 'after == False' and the source is
2543
versioned but no longer in the working tree, and the target is not
2544
versioned but present in the working tree.
2546
move uses the first mode if 'after == False' and the source is
2547
versioned and present in the working tree, and the target is not
2548
versioned and not present in the working tree.
2550
Everything else results in an error.
2552
This returns a list of (from_path, to_path) pairs for each
2553
entry that is moved.
2558
# check for deprecated use of signature
2560
raise TypeError('You must supply a target directory')
2561
# check destination directory
2562
if isinstance(from_paths, basestring):
2564
inv = self.inventory
2565
to_abs = self.abspath(to_dir)
2566
if not isdir(to_abs):
2567
raise errors.BzrMoveFailedError('',to_dir,
2568
errors.NotADirectory(to_abs))
2569
if not self.has_filename(to_dir):
2570
raise errors.BzrMoveFailedError('',to_dir,
2571
errors.NotInWorkingDirectory(to_dir))
2572
to_dir_id = inv.path2id(to_dir)
2573
if to_dir_id is None:
2574
raise errors.BzrMoveFailedError('',to_dir,
2575
errors.NotVersionedError(path=to_dir))
2577
to_dir_ie = inv[to_dir_id]
2578
if to_dir_ie.kind != 'directory':
2579
raise errors.BzrMoveFailedError('',to_dir,
2580
errors.NotADirectory(to_abs))
2582
# create rename entries and tuples
2583
for from_rel in from_paths:
2584
from_tail = splitpath(from_rel)[-1]
2585
from_id = inv.path2id(from_rel)
2587
raise errors.BzrMoveFailedError(from_rel,to_dir,
2588
errors.NotVersionedError(path=from_rel))
2590
from_entry = inv[from_id]
2591
from_parent_id = from_entry.parent_id
2592
to_rel = pathjoin(to_dir, from_tail)
2593
rename_entry = InventoryWorkingTree._RenameEntry(
2596
from_tail=from_tail,
2597
from_parent_id=from_parent_id,
2598
to_rel=to_rel, to_tail=from_tail,
2599
to_parent_id=to_dir_id)
2600
rename_entries.append(rename_entry)
2601
rename_tuples.append((from_rel, to_rel))
2603
# determine which move mode to use. checks also for movability
2604
rename_entries = self._determine_mv_mode(rename_entries, after)
2606
original_modified = self._inventory_is_modified
2609
self._inventory_is_modified = True
2610
self._move(rename_entries)
2612
# restore the inventory on error
2613
self._inventory_is_modified = original_modified
2615
self._write_inventory(inv)
2616
return rename_tuples
2618
@needs_tree_write_lock
2619
def rename_one(self, from_rel, to_rel, after=False):
2622
This can change the directory or the filename or both.
2624
rename_one has several 'modes' to work. First, it can rename a physical
2625
file and change the file_id. That is the normal mode. Second, it can
2626
only change the file_id without touching any physical file.
2628
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2629
versioned but present in the working tree.
2631
rename_one uses the second mode if 'after == False' and 'from_rel' is
2632
versioned but no longer in the working tree, and 'to_rel' is not
2633
versioned but present in the working tree.
2635
rename_one uses the first mode if 'after == False' and 'from_rel' is
2636
versioned and present in the working tree, and 'to_rel' is not
2637
versioned and not present in the working tree.
2639
Everything else results in an error.
2641
inv = self.inventory
2644
# create rename entries and tuples
2645
from_tail = splitpath(from_rel)[-1]
2646
from_id = inv.path2id(from_rel)
2648
# if file is missing in the inventory maybe it's in the basis_tree
2649
basis_tree = self.branch.basis_tree()
2650
from_id = basis_tree.path2id(from_rel)
2652
raise errors.BzrRenameFailedError(from_rel,to_rel,
2653
errors.NotVersionedError(path=from_rel))
2654
# put entry back in the inventory so we can rename it
2655
from_entry = basis_tree.inventory[from_id].copy()
2658
from_entry = inv[from_id]
2659
from_parent_id = from_entry.parent_id
2660
to_dir, to_tail = os.path.split(to_rel)
2661
to_dir_id = inv.path2id(to_dir)
2662
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2664
from_tail=from_tail,
2665
from_parent_id=from_parent_id,
2666
to_rel=to_rel, to_tail=to_tail,
2667
to_parent_id=to_dir_id)
2668
rename_entries.append(rename_entry)
2670
# determine which move mode to use. checks also for movability
2671
rename_entries = self._determine_mv_mode(rename_entries, after)
2673
# check if the target changed directory and if the target directory is
2675
if to_dir_id is None:
2676
raise errors.BzrMoveFailedError(from_rel,to_rel,
2677
errors.NotVersionedError(path=to_dir))
2679
# all checks done. now we can continue with our actual work
2680
mutter('rename_one:\n'
2685
' to_dir_id {%s}\n',
2686
from_id, from_rel, to_rel, to_dir, to_dir_id)
2688
self._move(rename_entries)
2689
self._write_inventory(inv)
2691
class _RenameEntry(object):
2692
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2693
to_rel, to_tail, to_parent_id, only_change_inv=False):
2694
self.from_rel = from_rel
2695
self.from_id = from_id
2696
self.from_tail = from_tail
2697
self.from_parent_id = from_parent_id
2698
self.to_rel = to_rel
2699
self.to_tail = to_tail
2700
self.to_parent_id = to_parent_id
2701
self.only_change_inv = only_change_inv
2703
def _determine_mv_mode(self, rename_entries, after=False):
2704
"""Determines for each from-to pair if both inventory and working tree
2705
or only the inventory has to be changed.
2707
Also does basic plausability tests.
2709
inv = self.inventory
2711
for rename_entry in rename_entries:
2712
# store to local variables for easier reference
2713
from_rel = rename_entry.from_rel
2714
from_id = rename_entry.from_id
2715
to_rel = rename_entry.to_rel
2716
to_id = inv.path2id(to_rel)
2717
only_change_inv = False
2719
# check the inventory for source and destination
2721
raise errors.BzrMoveFailedError(from_rel,to_rel,
2722
errors.NotVersionedError(path=from_rel))
2723
if to_id is not None:
2724
raise errors.BzrMoveFailedError(from_rel,to_rel,
2725
errors.AlreadyVersionedError(path=to_rel))
2727
# try to determine the mode for rename (only change inv or change
2728
# inv and file system)
2730
if not self.has_filename(to_rel):
2731
raise errors.BzrMoveFailedError(from_id,to_rel,
2732
errors.NoSuchFile(path=to_rel,
2733
extra="New file has not been created yet"))
2734
only_change_inv = True
2735
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2736
only_change_inv = True
2737
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2738
only_change_inv = False
2739
elif (not self.case_sensitive
2740
and from_rel.lower() == to_rel.lower()
2741
and self.has_filename(from_rel)):
2742
only_change_inv = False
2744
# something is wrong, so lets determine what exactly
2745
if not self.has_filename(from_rel) and \
2746
not self.has_filename(to_rel):
2747
raise errors.BzrRenameFailedError(from_rel,to_rel,
2748
errors.PathsDoNotExist(paths=(str(from_rel),
2751
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2752
rename_entry.only_change_inv = only_change_inv
2753
return rename_entries
2755
def _move(self, rename_entries):
2756
"""Moves a list of files.
2758
Depending on the value of the flag 'only_change_inv', the
2759
file will be moved on the file system or not.
2761
inv = self.inventory
2764
for entry in rename_entries:
2766
self._move_entry(entry)
2768
self._rollback_move(moved)
2772
def _rollback_move(self, moved):
2773
"""Try to rollback a previous move in case of an filesystem error."""
2774
inv = self.inventory
2777
self._move_entry(WorkingTree._RenameEntry(
2778
entry.to_rel, entry.from_id,
2779
entry.to_tail, entry.to_parent_id, entry.from_rel,
2780
entry.from_tail, entry.from_parent_id,
2781
entry.only_change_inv))
2782
except errors.BzrMoveFailedError, e:
2783
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2784
" The working tree is in an inconsistent state."
2785
" Please consider doing a 'bzr revert'."
2786
" Error message is: %s" % e)
2788
def _move_entry(self, entry):
2789
inv = self.inventory
2790
from_rel_abs = self.abspath(entry.from_rel)
2791
to_rel_abs = self.abspath(entry.to_rel)
2792
if from_rel_abs == to_rel_abs:
2793
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2794
"Source and target are identical.")
2796
if not entry.only_change_inv:
2798
osutils.rename(from_rel_abs, to_rel_abs)
2800
raise errors.BzrMoveFailedError(entry.from_rel,
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 file_id not in self._inventory:
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
:prefix: is used as the directrory to start with.
2881
returns a generator which yields items in the form:
2882
((curren_directory_path, fileid),
2883
[(file1_path, file1_name, file1_kind, None, file1_id,
2886
_directory = 'directory'
2887
# get the root in the inventory
2888
inv = self.inventory
2889
top_id = inv.path2id(prefix)
2893
pending = [(prefix, '', _directory, None, top_id, None)]
2896
currentdir = pending.pop()
2897
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2898
top_id = currentdir[4]
2900
relroot = currentdir[0] + '/'
2903
# FIXME: stash the node in pending
2905
if entry.kind == 'directory':
2906
for name, child in entry.sorted_children():
2907
dirblock.append((relroot + name, name, child.kind, None,
2908
child.file_id, child.kind
2910
yield (currentdir[0], entry.file_id), dirblock
2911
# push the user specified dirs from dirblock
2912
for dir in reversed(dirblock):
2913
if dir[2] == _directory:
2917
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2918
"""Registry for working tree formats."""
2920
def __init__(self, other_registry=None):
2921
super(WorkingTreeFormatRegistry, self).__init__(other_registry)
2922
self._default_format = None
2924
def get_default(self):
2925
"""Return the current default format."""
2926
return self._default_format
2928
def set_default(self, format):
2929
self._default_format = format
2932
format_registry = WorkingTreeFormatRegistry()
2935
class WorkingTreeFormat(controldir.ControlComponentFormat):
2750
2936
"""An encapsulation of the initialization and open routines for a format.
2752
2938
Formats provide three things:
3033
@symbol_versioning.deprecated_method(
3034
symbol_versioning.deprecated_in((2, 4, 0)))
2827
3035
def register_format(klass, format):
2828
klass._formats[format.get_format_string()] = format
3036
format_registry.register(format)
3039
@symbol_versioning.deprecated_method(
3040
symbol_versioning.deprecated_in((2, 4, 0)))
3041
def register_extra_format(klass, format):
3042
format_registry.register_extra(format)
3045
@symbol_versioning.deprecated_method(
3046
symbol_versioning.deprecated_in((2, 4, 0)))
3047
def unregister_extra_format(klass, format):
3048
format_registry.unregister_extra(format)
3051
@symbol_versioning.deprecated_method(
3052
symbol_versioning.deprecated_in((2, 4, 0)))
3053
def get_formats(klass):
3054
return format_registry._get_all()
3057
@symbol_versioning.deprecated_method(
3058
symbol_versioning.deprecated_in((2, 4, 0)))
2831
3059
def set_default_format(klass, format):
2832
klass._default_format = format
3060
format_registry.set_default(format)
3063
@symbol_versioning.deprecated_method(
3064
symbol_versioning.deprecated_in((2, 4, 0)))
2835
3065
def unregister_format(klass, format):
2836
del klass._formats[format.get_format_string()]
2839
class WorkingTreeFormat2(WorkingTreeFormat):
2840
"""The second working tree format.
2842
This format modified the hash cache from the format 1 hash cache.
2845
upgrade_recommended = True
2847
def get_format_description(self):
2848
"""See WorkingTreeFormat.get_format_description()."""
2849
return "Working tree format 2"
2851
def _stub_initialize_on_transport(self, transport, file_mode):
2852
"""Workaround: create control files for a remote working tree.
2854
This ensures that it can later be updated and dealt with locally,
2855
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2856
no working tree. (See bug #43064).
2859
inv = inventory.Inventory()
2860
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2862
transport.put_file('inventory', sio, file_mode)
2863
transport.put_bytes('pending-merges', '', file_mode)
2865
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2866
accelerator_tree=None, hardlink=False):
2867
"""See WorkingTreeFormat.initialize()."""
2868
if not isinstance(a_bzrdir.transport, LocalTransport):
2869
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2870
if from_branch is not None:
2871
branch = from_branch
2873
branch = a_bzrdir.open_branch()
2874
if revision_id is None:
2875
revision_id = _mod_revision.ensure_null(branch.last_revision())
2878
branch.generate_revision_history(revision_id)
2881
inv = inventory.Inventory()
2882
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2888
basis_tree = branch.repository.revision_tree(revision_id)
2889
if basis_tree.inventory.root is not None:
2890
wt.set_root_id(basis_tree.get_root_id())
2891
# set the parent list and cache the basis tree.
2892
if _mod_revision.is_null(revision_id):
2895
parent_trees = [(revision_id, basis_tree)]
2896
wt.set_parent_trees(parent_trees)
2897
transform.build_tree(basis_tree, wt)
2901
super(WorkingTreeFormat2, self).__init__()
2902
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2904
def open(self, a_bzrdir, _found=False):
2905
"""Return the WorkingTree object for a_bzrdir
2907
_found is a private parameter, do not use it. It is used to indicate
2908
if format probing has already been done.
2911
# we are being called directly and must probe.
2912
raise NotImplementedError
2913
if not isinstance(a_bzrdir.transport, LocalTransport):
2914
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2915
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2921
class WorkingTreeFormat3(WorkingTreeFormat):
2922
"""The second working tree format updated to record a format marker.
2925
- exists within a metadir controlling .bzr
2926
- includes an explicit version marker for the workingtree control
2927
files, separate from the BzrDir format
2928
- modifies the hash cache format
2930
- uses a LockDir to guard access for writes.
2933
upgrade_recommended = True
2935
def get_format_string(self):
2936
"""See WorkingTreeFormat.get_format_string()."""
2937
return "Bazaar-NG Working Tree format 3"
2939
def get_format_description(self):
2940
"""See WorkingTreeFormat.get_format_description()."""
2941
return "Working tree format 3"
2943
_lock_file_name = 'lock'
2944
_lock_class = LockDir
2946
_tree_class = WorkingTree3
2948
def __get_matchingbzrdir(self):
2949
return bzrdir.BzrDirMetaFormat1()
2951
_matchingbzrdir = property(__get_matchingbzrdir)
2953
def _open_control_files(self, a_bzrdir):
2954
transport = a_bzrdir.get_workingtree_transport(None)
2955
return LockableFiles(transport, self._lock_file_name,
2958
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2959
accelerator_tree=None, hardlink=False):
2960
"""See WorkingTreeFormat.initialize().
2962
:param revision_id: if supplied, create a working tree at a different
2963
revision than the branch is at.
2964
:param accelerator_tree: A tree which can be used for retrieving file
2965
contents more quickly than the revision tree, i.e. a workingtree.
2966
The revision tree will be used for cases where accelerator_tree's
2967
content is different.
2968
:param hardlink: If true, hard-link files from accelerator_tree,
2971
if not isinstance(a_bzrdir.transport, LocalTransport):
2972
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2973
transport = a_bzrdir.get_workingtree_transport(self)
2974
control_files = self._open_control_files(a_bzrdir)
2975
control_files.create_lock()
2976
control_files.lock_write()
2977
transport.put_bytes('format', self.get_format_string(),
2978
mode=a_bzrdir._get_file_mode())
2979
if from_branch is not None:
2980
branch = from_branch
2982
branch = a_bzrdir.open_branch()
2983
if revision_id is None:
2984
revision_id = _mod_revision.ensure_null(branch.last_revision())
2985
# WorkingTree3 can handle an inventory which has a unique root id.
2986
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2987
# those trees. And because there isn't a format bump inbetween, we
2988
# are maintaining compatibility with older clients.
2989
# inv = Inventory(root_id=gen_root_id())
2990
inv = self._initial_inventory()
2991
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2997
_control_files=control_files)
2998
wt.lock_tree_write()
3000
basis_tree = branch.repository.revision_tree(revision_id)
3001
# only set an explicit root id if there is one to set.
3002
if basis_tree.inventory.root is not None:
3003
wt.set_root_id(basis_tree.get_root_id())
3004
if revision_id == _mod_revision.NULL_REVISION:
3005
wt.set_parent_trees([])
3007
wt.set_parent_trees([(revision_id, basis_tree)])
3008
transform.build_tree(basis_tree, wt)
3010
# Unlock in this order so that the unlock-triggers-flush in
3011
# WorkingTree is given a chance to fire.
3012
control_files.unlock()
3016
def _initial_inventory(self):
3017
return inventory.Inventory()
3020
super(WorkingTreeFormat3, self).__init__()
3022
def open(self, a_bzrdir, _found=False):
3023
"""Return the WorkingTree object for a_bzrdir
3025
_found is a private parameter, do not use it. It is used to indicate
3026
if format probing has already been done.
3029
# we are being called directly and must probe.
3030
raise NotImplementedError
3031
if not isinstance(a_bzrdir.transport, LocalTransport):
3032
raise errors.NotLocalUrl(a_bzrdir.transport.base)
3033
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
3036
def _open(self, a_bzrdir, control_files):
3037
"""Open the tree itself.
3039
:param a_bzrdir: the dir for the tree.
3040
:param control_files: the control files for the tree.
3042
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
3046
_control_files=control_files)
3049
return self.get_format_string()
3066
format_registry.remove(format)
3052
3069
__default_format = WorkingTreeFormat6()
3053
WorkingTreeFormat.register_format(__default_format)
3054
WorkingTreeFormat.register_format(WorkingTreeFormat5())
3055
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3056
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3057
WorkingTreeFormat.set_default_format(__default_format)
3058
# formats which have no format string are not discoverable
3059
# and not independently creatable, so are not registered.
3060
_legacy_formats = [WorkingTreeFormat2(),
3070
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3071
"bzrlib.workingtree_4", "WorkingTreeFormat4")
3072
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3073
"bzrlib.workingtree_4", "WorkingTreeFormat5")
3074
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3075
"bzrlib.workingtree_4", "WorkingTreeFormat6")
3076
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
3077
"bzrlib.workingtree_3", "WorkingTreeFormat3")
3078
format_registry.set_default(__default_format)