1539
1226
def get_physical_lock_status(self):
1540
1227
return self._control_files.get_physical_lock_status()
1542
def _basis_inventory_name(self):
1543
return 'basis-inventory-cache'
1229
def _reset_data(self):
1230
"""Reset transient data that cannot be revalidated."""
1231
raise NotImplementedError(self._reset_data)
1545
@needs_tree_write_lock
1546
1233
def set_last_revision(self, new_revision):
1547
1234
"""Change the last revision in the working tree."""
1548
new_revision = osutils.safe_revision_id(new_revision)
1549
if self._change_last_revision(new_revision):
1550
self._cache_basis_inventory(new_revision)
1235
raise NotImplementedError(self.set_last_revision)
1552
1237
def _change_last_revision(self, new_revision):
1553
1238
"""Template method part of set_last_revision to perform the change.
1555
1240
This is used to allow WorkingTree3 instances to not affect branch
1556
1241
when their last revision is set.
1558
if new_revision is None:
1559
self.branch.set_revision_history([])
1243
if _mod_revision.is_null(new_revision):
1244
self.branch.set_last_revision_info(0, new_revision)
1246
_mod_revision.check_not_reserved_id(new_revision)
1562
1248
self.branch.generate_revision_history(new_revision)
1563
1249
except errors.NoSuchRevision:
1564
1250
# not present in the repo - dont try to set it deeper than the tip
1565
self.branch.set_revision_history([new_revision])
1251
self.branch._set_revision_history([new_revision])
1568
def _write_basis_inventory(self, xml):
1569
"""Write the basis inventory XML to the basis-inventory file"""
1570
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1571
path = self._basis_inventory_name()
1573
self._control_files.put(path, sio)
1575
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1576
"""Create the text that will be saved in basis-inventory"""
1577
# TODO: jam 20070209 This should be redundant, as the revision_id
1578
# as all callers should have already converted the revision_id to
1580
inventory.revision_id = osutils.safe_revision_id(revision_id)
1581
return xml6.serializer_v6.write_inventory_to_string(inventory)
1583
def _cache_basis_inventory(self, new_revision):
1584
"""Cache new_revision as the basis inventory."""
1585
# TODO: this should allow the ready-to-use inventory to be passed in,
1586
# as commit already has that ready-to-use [while the format is the
1589
# this double handles the inventory - unpack and repack -
1590
# but is easier to understand. We can/should put a conditional
1591
# in here based on whether the inventory is in the latest format
1592
# - perhaps we should repack all inventories on a repository
1594
# the fast path is to copy the raw xml from the repository. If the
1595
# xml contains 'revision_id="', then we assume the right
1596
# revision_id is set. We must check for this full string, because a
1597
# root node id can legitimately look like 'revision_id' but cannot
1599
xml = self.branch.repository.get_inventory_xml(new_revision)
1600
firstline = xml.split('\n', 1)[0]
1601
if (not 'revision_id="' in firstline or
1602
'format="6"' not in firstline):
1603
inv = self.branch.repository.deserialise_inventory(
1605
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1606
self._write_basis_inventory(xml)
1607
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1610
def read_basis_inventory(self):
1611
"""Read the cached basis inventory."""
1612
path = self._basis_inventory_name()
1613
return self._control_files.get(path).read()
1616
def read_working_inventory(self):
1617
"""Read the working inventory.
1619
:raises errors.InventoryModified: read_working_inventory will fail
1620
when the current in memory inventory has been modified.
1622
# conceptually this should be an implementation detail of the tree.
1623
# XXX: Deprecate this.
1624
# ElementTree does its own conversion from UTF-8, so open in
1626
if self._inventory_is_modified:
1627
raise errors.InventoryModified(self)
1628
result = xml5.serializer_v5.read_inventory(
1629
self._control_files.get('inventory'))
1630
self._set_inventory(result, dirty=False)
1633
1254
@needs_tree_write_lock
1634
def remove(self, files, verbose=False, to_file=None):
1635
"""Remove nominated files from the working inventory..
1637
This does not remove their text. This does not run on XXX on what? RBC
1639
TODO: Refuse to remove modified files unless --force is given?
1641
TODO: Do something useful with directories.
1643
TODO: Should this remove the text or not? Tough call; not
1644
removing may be useful and the user can just use use rm, and
1645
is the opposite of add. Removing it is consistent with most
1646
other tools. Maybe an option.
1255
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1257
"""Remove nominated files from the working tree metadata.
1259
:files: File paths relative to the basedir.
1260
:keep_files: If true, the files will also be kept.
1261
:force: Delete files and directories, even if they are changed and
1262
even if the directories are not empty.
1648
## TODO: Normalize names
1649
## TODO: Remove nested loops; better scalability
1650
1264
if isinstance(files, basestring):
1651
1265
files = [files]
1653
inv = self.inventory
1655
# do this before any modifications
1269
all_files = set() # specified and nested files
1270
unknown_nested_files=set()
1272
to_file = sys.stdout
1274
files_to_backup = []
1276
def recurse_directory_to_add_files(directory):
1277
# Recurse directory and add all files
1278
# so we can check if they have changed.
1279
for parent_info, file_infos in self.walkdirs(directory):
1280
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1281
# Is it versioned or ignored?
1282
if self.path2id(relpath):
1283
# Add nested content for deletion.
1284
all_files.add(relpath)
1286
# Files which are not versioned
1287
# should be treated as unknown.
1288
files_to_backup.append(relpath)
1290
for filename in files:
1291
# Get file name into canonical form.
1292
abspath = self.abspath(filename)
1293
filename = self.relpath(abspath)
1294
if len(filename) > 0:
1295
all_files.add(filename)
1296
recurse_directory_to_add_files(filename)
1298
files = list(all_files)
1301
return # nothing to do
1303
# Sort needed to first handle directory content before the directory
1304
files.sort(reverse=True)
1306
# Bail out if we are going to delete files we shouldn't
1307
if not keep_files and not force:
1308
for (file_id, path, content_change, versioned, parent_id, name,
1309
kind, executable) in self.iter_changes(self.basis_tree(),
1310
include_unchanged=True, require_versioned=False,
1311
want_unversioned=True, specific_files=files):
1312
if versioned[0] == False:
1313
# The record is unknown or newly added
1314
files_to_backup.append(path[1])
1315
elif (content_change and (kind[1] is not None) and
1316
osutils.is_inside_any(files, path[1])):
1317
# Versioned and changed, but not deleted, and still
1318
# in one of the dirs to be deleted.
1319
files_to_backup.append(path[1])
1321
def backup(file_to_backup):
1322
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1323
osutils.rename(abs_path, self.abspath(backup_name))
1324
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1327
# Build inv_delta and delete files where applicable,
1328
# do this before any modifications to meta data.
1656
1329
for f in files:
1657
fid = inv.path2id(f)
1330
fid = self.path2id(f)
1659
note("%s is not versioned."%f)
1333
message = "%s is not versioned." % (f,)
1662
# having remove it, it must be either ignored or unknown
1336
# having removed it, it must be either ignored or unknown
1663
1337
if self.is_ignored(f):
1664
1338
new_status = 'I'
1666
1340
new_status = '?'
1667
textui.show_status(new_status, inv[fid].kind, f,
1671
self._write_inventory(inv)
1341
# XXX: Really should be a more abstract reporter interface
1342
kind_ch = osutils.kind_marker(self.kind(fid))
1343
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1345
inv_delta.append((f, None, fid, None))
1346
message = "removed %s" % (f,)
1349
abs_path = self.abspath(f)
1350
if osutils.lexists(abs_path):
1351
if (osutils.isdir(abs_path) and
1352
len(os.listdir(abs_path)) > 0):
1354
osutils.rmtree(abs_path)
1355
message = "deleted %s" % (f,)
1359
if f in files_to_backup:
1362
osutils.delete_any(abs_path)
1363
message = "deleted %s" % (f,)
1364
elif message is not None:
1365
# Only care if we haven't done anything yet.
1366
message = "%s does not exist." % (f,)
1368
# Print only one message (if any) per file.
1369
if message is not None:
1371
self.apply_inventory_delta(inv_delta)
1673
1373
@needs_tree_write_lock
1674
def revert(self, filenames, old_tree=None, backups=True,
1675
pb=DummyProgress(), report_changes=False):
1374
def revert(self, filenames=None, old_tree=None, backups=True,
1375
pb=None, report_changes=False):
1676
1376
from bzrlib.conflicts import resolve
1677
1377
if old_tree is None:
1678
old_tree = self.basis_tree()
1679
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1681
if not len(filenames):
1682
self.set_parent_ids(self.get_parent_ids()[:1])
1378
basis_tree = self.basis_tree()
1379
basis_tree.lock_read()
1380
old_tree = basis_tree
1685
resolve(self, filenames, ignore_misses=True)
1384
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1386
if filenames is None and len(self.get_parent_ids()) > 1:
1388
last_revision = self.last_revision()
1389
if last_revision != _mod_revision.NULL_REVISION:
1390
if basis_tree is None:
1391
basis_tree = self.basis_tree()
1392
basis_tree.lock_read()
1393
parent_trees.append((last_revision, basis_tree))
1394
self.set_parent_trees(parent_trees)
1397
resolve(self, filenames, ignore_misses=True, recursive=True)
1399
if basis_tree is not None:
1686
1401
return conflicts
1688
1403
def revision_tree(self, revision_id):
2068
1721
disk_finished = True
2070
1723
def _walkdirs(self, prefix=""):
1724
"""Walk the directories of this tree.
1726
:param prefix: is used as the directrory to start with.
1727
:returns: a generator which yields items in the form::
1729
((curren_directory_path, fileid),
1730
[(file1_path, file1_name, file1_kind, None, file1_id,
1733
raise NotImplementedError(self._walkdirs)
1735
@needs_tree_write_lock
1736
def auto_resolve(self):
1737
"""Automatically resolve text conflicts according to contents.
1739
Only text conflicts are auto_resolvable. Files with no conflict markers
1740
are considered 'resolved', because bzr always puts conflict markers
1741
into files that have text conflicts. The corresponding .THIS .BASE and
1742
.OTHER files are deleted, as per 'resolve'.
1744
:return: a tuple of ConflictLists: (un_resolved, resolved).
1746
un_resolved = _mod_conflicts.ConflictList()
1747
resolved = _mod_conflicts.ConflictList()
1748
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1749
for conflict in self.conflicts():
1750
if (conflict.typestring != 'text conflict' or
1751
self.kind(conflict.file_id) != 'file'):
1752
un_resolved.append(conflict)
1754
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1756
for line in my_file:
1757
if conflict_re.search(line):
1758
un_resolved.append(conflict)
1761
resolved.append(conflict)
1764
resolved.remove_files(self)
1765
self.set_conflicts(un_resolved)
1766
return un_resolved, resolved
1768
def _validate(self):
1769
"""Validate internal structures.
1771
This is meant mostly for the test suite. To give it a chance to detect
1772
corruption after actions have occurred. The default implementation is a
1775
:return: None. An exception should be raised if there is an error.
1779
def check_state(self):
1780
"""Check that the working state is/isn't valid."""
1781
raise NotImplementedError(self.check_state)
1783
def reset_state(self, revision_ids=None):
1784
"""Reset the state of the working tree.
1786
This does a hard-reset to a last-known-good state. This is a way to
1787
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1789
raise NotImplementedError(self.reset_state)
1791
def _get_rules_searcher(self, default_searcher):
1792
"""See Tree._get_rules_searcher."""
1793
if self._rules_searcher is None:
1794
self._rules_searcher = super(WorkingTree,
1795
self)._get_rules_searcher(default_searcher)
1796
return self._rules_searcher
1798
def get_shelf_manager(self):
1799
"""Return the ShelfManager for this WorkingTree."""
1800
from bzrlib.shelf import ShelfManager
1801
return ShelfManager(self, self._transport)
1804
class InventoryWorkingTree(WorkingTree,
1805
bzrlib.mutabletree.MutableInventoryTree):
1806
"""Base class for working trees that are inventory-oriented.
1808
The inventory is held in the `Branch` working-inventory, and the
1809
files are in a directory on disk.
1811
It is possible for a `WorkingTree` to have a filename which is
1812
not listed in the Inventory and vice versa.
1815
def __init__(self, basedir='.',
1816
branch=DEPRECATED_PARAMETER,
1818
_control_files=None,
1822
"""Construct a InventoryWorkingTree instance. This is not a public API.
1824
:param branch: A branch to override probing for the branch.
1826
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1827
branch=branch, _control_files=_control_files, _internal=_internal,
1828
_format=_format, _bzrdir=_bzrdir)
1830
if _inventory is None:
1831
# This will be acquired on lock_read() or lock_write()
1832
self._inventory_is_modified = False
1833
self._inventory = None
1835
# the caller of __init__ has provided an inventory,
1836
# we assume they know what they are doing - as its only
1837
# the Format factory and creation methods that are
1838
# permitted to do this.
1839
self._set_inventory(_inventory, dirty=False)
1841
def _set_inventory(self, inv, dirty):
1842
"""Set the internal cached inventory.
1844
:param inv: The inventory to set.
1845
:param dirty: A boolean indicating whether the inventory is the same
1846
logical inventory as whats on disk. If True the inventory is not
1847
the same and should be written to disk or data will be lost, if
1848
False then the inventory is the same as that on disk and any
1849
serialisation would be unneeded overhead.
1851
self._inventory = inv
1852
self._inventory_is_modified = dirty
1854
def _serialize(self, inventory, out_file):
1855
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1858
def _deserialize(selt, in_file):
1859
return xml5.serializer_v5.read_inventory(in_file)
1861
@needs_tree_write_lock
1862
def _write_inventory(self, inv):
1863
"""Write inventory as the current inventory."""
1864
self._set_inventory(inv, dirty=True)
1867
# XXX: This method should be deprecated in favour of taking in a proper
1868
# new Inventory object.
1869
@needs_tree_write_lock
1870
def set_inventory(self, new_inventory_list):
1871
from bzrlib.inventory import (Inventory,
1875
inv = Inventory(self.get_root_id())
1876
for path, file_id, parent, kind in new_inventory_list:
1877
name = os.path.basename(path)
1880
# fixme, there should be a factory function inv,add_??
1881
if kind == 'directory':
1882
inv.add(InventoryDirectory(file_id, name, parent))
1883
elif kind == 'file':
1884
inv.add(InventoryFile(file_id, name, parent))
1885
elif kind == 'symlink':
1886
inv.add(InventoryLink(file_id, name, parent))
1888
raise errors.BzrError("unknown kind %r" % kind)
1889
self._write_inventory(inv)
1891
def _write_basis_inventory(self, xml):
1892
"""Write the basis inventory XML to the basis-inventory file"""
1893
path = self._basis_inventory_name()
1895
self._transport.put_file(path, sio,
1896
mode=self.bzrdir._get_file_mode())
1898
def _reset_data(self):
1899
"""Reset transient data that cannot be revalidated."""
1900
self._inventory_is_modified = False
1901
f = self._transport.get('inventory')
1903
result = self._deserialize(f)
1906
self._set_inventory(result, dirty=False)
1908
def _set_root_id(self, file_id):
1909
"""Set the root id for this tree, in a format specific manner.
1911
:param file_id: The file id to assign to the root. It must not be
1912
present in the current inventory or an error will occur. It must
1913
not be None, but rather a valid file id.
1915
inv = self._inventory
1916
orig_root_id = inv.root.file_id
1917
# TODO: it might be nice to exit early if there was nothing
1918
# to do, saving us from trigger a sync on unlock.
1919
self._inventory_is_modified = True
1920
# we preserve the root inventory entry object, but
1921
# unlinkit from the byid index
1922
del inv._byid[inv.root.file_id]
1923
inv.root.file_id = file_id
1924
# and link it into the index with the new changed id.
1925
inv._byid[inv.root.file_id] = inv.root
1926
# and finally update all children to reference the new id.
1927
# XXX: this should be safe to just look at the root.children
1928
# list, not the WHOLE INVENTORY.
1931
if entry.parent_id == orig_root_id:
1932
entry.parent_id = inv.root.file_id
1934
def all_file_ids(self):
1935
"""See Tree.iter_all_file_ids"""
1936
return set(self.inventory)
1938
@needs_tree_write_lock
1939
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1940
"""See MutableTree.set_parent_trees."""
1941
parent_ids = [rev for (rev, tree) in parents_list]
1942
for revision_id in parent_ids:
1943
_mod_revision.check_not_reserved_id(revision_id)
1945
self._check_parents_for_ghosts(parent_ids,
1946
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1948
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
1950
if len(parent_ids) == 0:
1951
leftmost_parent_id = _mod_revision.NULL_REVISION
1952
leftmost_parent_tree = None
1954
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1956
if self._change_last_revision(leftmost_parent_id):
1957
if leftmost_parent_tree is None:
1958
# If we don't have a tree, fall back to reading the
1959
# parent tree from the repository.
1960
self._cache_basis_inventory(leftmost_parent_id)
1962
inv = leftmost_parent_tree.inventory
1963
xml = self._create_basis_xml_from_inventory(
1964
leftmost_parent_id, inv)
1965
self._write_basis_inventory(xml)
1966
self._set_merges_from_parent_ids(parent_ids)
1968
def _cache_basis_inventory(self, new_revision):
1969
"""Cache new_revision as the basis inventory."""
1970
# TODO: this should allow the ready-to-use inventory to be passed in,
1971
# as commit already has that ready-to-use [while the format is the
1974
# this double handles the inventory - unpack and repack -
1975
# but is easier to understand. We can/should put a conditional
1976
# in here based on whether the inventory is in the latest format
1977
# - perhaps we should repack all inventories on a repository
1979
# the fast path is to copy the raw xml from the repository. If the
1980
# xml contains 'revision_id="', then we assume the right
1981
# revision_id is set. We must check for this full string, because a
1982
# root node id can legitimately look like 'revision_id' but cannot
1984
xml = self.branch.repository._get_inventory_xml(new_revision)
1985
firstline = xml.split('\n', 1)[0]
1986
if (not 'revision_id="' in firstline or
1987
'format="7"' not in firstline):
1988
inv = self.branch.repository._serializer.read_inventory_from_string(
1990
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1991
self._write_basis_inventory(xml)
1992
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1995
def _basis_inventory_name(self):
1996
return 'basis-inventory-cache'
1998
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1999
"""Create the text that will be saved in basis-inventory"""
2000
inventory.revision_id = revision_id
2001
return xml7.serializer_v7.write_inventory_to_string(inventory)
2003
@needs_tree_write_lock
2004
def set_conflicts(self, conflicts):
2005
self._put_rio('conflicts', conflicts.to_stanzas(),
2008
@needs_tree_write_lock
2009
def add_conflicts(self, new_conflicts):
2010
conflict_set = set(self.conflicts())
2011
conflict_set.update(set(list(new_conflicts)))
2012
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
2013
key=_mod_conflicts.Conflict.sort_key)))
2016
def conflicts(self):
2018
confile = self._transport.get('conflicts')
2019
except errors.NoSuchFile:
2020
return _mod_conflicts.ConflictList()
2023
if confile.next() != CONFLICT_HEADER_1 + '\n':
2024
raise errors.ConflictFormatError()
2025
except StopIteration:
2026
raise errors.ConflictFormatError()
2027
reader = _mod_rio.RioReader(confile)
2028
return _mod_conflicts.ConflictList.from_stanzas(reader)
2032
def read_basis_inventory(self):
2033
"""Read the cached basis inventory."""
2034
path = self._basis_inventory_name()
2035
return self._transport.get_bytes(path)
2038
def read_working_inventory(self):
2039
"""Read the working inventory.
2041
:raises errors.InventoryModified: read_working_inventory will fail
2042
when the current in memory inventory has been modified.
2044
# conceptually this should be an implementation detail of the tree.
2045
# XXX: Deprecate this.
2046
# ElementTree does its own conversion from UTF-8, so open in
2048
if self._inventory_is_modified:
2049
raise errors.InventoryModified(self)
2050
f = self._transport.get('inventory')
2052
result = self._deserialize(f)
2055
self._set_inventory(result, dirty=False)
2059
def get_root_id(self):
2060
"""Return the id of this trees root"""
2061
return self._inventory.root.file_id
2063
def has_id(self, file_id):
2064
# files that have been deleted are excluded
2065
inv = self.inventory
2066
if not inv.has_id(file_id):
2068
path = inv.id2path(file_id)
2069
return osutils.lexists(self.abspath(path))
2071
def has_or_had_id(self, file_id):
2072
if file_id == self.inventory.root.file_id:
2074
return self.inventory.has_id(file_id)
2076
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2078
"""Iterate through file_ids for this tree.
2080
file_ids are in a WorkingTree if they are in the working inventory
2081
and the working file exists.
2083
inv = self._inventory
2084
for path, ie in inv.iter_entries():
2085
if osutils.lexists(self.abspath(path)):
2088
@needs_tree_write_lock
2089
def set_last_revision(self, new_revision):
2090
"""Change the last revision in the working tree."""
2091
if self._change_last_revision(new_revision):
2092
self._cache_basis_inventory(new_revision)
2094
def _get_check_refs(self):
2095
"""Return the references needed to perform a check of this tree.
2097
The default implementation returns no refs, and is only suitable for
2098
trees that have no local caching and can commit on ghosts at any time.
2100
:seealso: bzrlib.check for details about check_refs.
2105
def _check(self, references):
2106
"""Check the tree for consistency.
2108
:param references: A dict with keys matching the items returned by
2109
self._get_check_refs(), and values from looking those keys up in
2112
tree_basis = self.basis_tree()
2113
tree_basis.lock_read()
2115
repo_basis = references[('trees', self.last_revision())]
2116
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2117
raise errors.BzrCheckError(
2118
"Mismatched basis inventory content.")
2124
def check_state(self):
2125
"""Check that the working state is/isn't valid."""
2126
check_refs = self._get_check_refs()
2128
for ref in check_refs:
2131
refs[ref] = self.branch.repository.revision_tree(value)
2134
@needs_tree_write_lock
2135
def reset_state(self, revision_ids=None):
2136
"""Reset the state of the working tree.
2138
This does a hard-reset to a last-known-good state. This is a way to
2139
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2141
if revision_ids is None:
2142
revision_ids = self.get_parent_ids()
2143
if not revision_ids:
2144
rt = self.branch.repository.revision_tree(
2145
_mod_revision.NULL_REVISION)
2147
rt = self.branch.repository.revision_tree(revision_ids[0])
2148
self._write_inventory(rt.inventory)
2149
self.set_parent_ids(revision_ids)
2152
"""Write the in memory inventory to disk."""
2153
# TODO: Maybe this should only write on dirty ?
2154
if self._control_files._lock_mode != 'w':
2155
raise errors.NotWriteLocked(self)
2157
self._serialize(self._inventory, sio)
2159
self._transport.put_file('inventory', sio,
2160
mode=self.bzrdir._get_file_mode())
2161
self._inventory_is_modified = False
2164
def get_file_sha1(self, file_id, path=None, stat_value=None):
2166
path = self._inventory.id2path(file_id)
2167
return self._hashcache.get_sha1(path, stat_value)
2169
def get_file_mtime(self, file_id, path=None):
2170
"""See Tree.get_file_mtime."""
2172
path = self.inventory.id2path(file_id)
2173
return os.lstat(self.abspath(path)).st_mtime
2175
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2176
file_id = self.path2id(path)
2178
# For unversioned files on win32, we just assume they are not
2181
return self._inventory[file_id].executable
2183
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2184
mode = stat_result.st_mode
2185
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2187
if not supports_executable():
2188
def is_executable(self, file_id, path=None):
2189
return self._inventory[file_id].executable
2191
_is_executable_from_path_and_stat = \
2192
_is_executable_from_path_and_stat_from_basis
2194
def is_executable(self, file_id, path=None):
2196
path = self.id2path(file_id)
2197
mode = os.lstat(self.abspath(path)).st_mode
2198
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2200
_is_executable_from_path_and_stat = \
2201
_is_executable_from_path_and_stat_from_stat
2203
@needs_tree_write_lock
2204
def _add(self, files, ids, kinds):
2205
"""See MutableTree._add."""
2206
# TODO: Re-adding a file that is removed in the working copy
2207
# should probably put it back with the previous ID.
2208
# the read and write working inventory should not occur in this
2209
# function - they should be part of lock_write and unlock.
2210
inv = self.inventory
2211
for f, file_id, kind in zip(files, ids, kinds):
2213
inv.add_path(f, kind=kind)
2215
inv.add_path(f, kind=kind, file_id=file_id)
2216
self._inventory_is_modified = True
2218
def revision_tree(self, revision_id):
2219
"""See WorkingTree.revision_id."""
2220
if revision_id == self.last_revision():
2222
xml = self.read_basis_inventory()
2223
except errors.NoSuchFile:
2227
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2228
# dont use the repository revision_tree api because we want
2229
# to supply the inventory.
2230
if inv.revision_id == revision_id:
2231
return revisiontree.InventoryRevisionTree(
2232
self.branch.repository, inv, revision_id)
2233
except errors.BadInventoryFormat:
2235
# raise if there was no inventory, or if we read the wrong inventory.
2236
raise errors.NoSuchRevisionInTree(self, revision_id)
2239
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2240
"""See Tree.annotate_iter
2242
This implementation will use the basis tree implementation if possible.
2243
Lines not in the basis are attributed to CURRENT_REVISION
2245
If there are pending merges, lines added by those merges will be
2246
incorrectly attributed to CURRENT_REVISION (but after committing, the
2247
attribution will be correct).
2249
maybe_file_parent_keys = []
2250
for parent_id in self.get_parent_ids():
2252
parent_tree = self.revision_tree(parent_id)
2253
except errors.NoSuchRevisionInTree:
2254
parent_tree = self.branch.repository.revision_tree(parent_id)
2255
parent_tree.lock_read()
2257
if not parent_tree.has_id(file_id):
2259
ie = parent_tree.inventory[file_id]
2260
if ie.kind != 'file':
2261
# Note: this is slightly unnecessary, because symlinks and
2262
# directories have a "text" which is the empty text, and we
2263
# know that won't mess up annotations. But it seems cleaner
2265
parent_text_key = (file_id, ie.revision)
2266
if parent_text_key not in maybe_file_parent_keys:
2267
maybe_file_parent_keys.append(parent_text_key)
2269
parent_tree.unlock()
2270
graph = _mod_graph.Graph(self.branch.repository.texts)
2271
heads = graph.heads(maybe_file_parent_keys)
2272
file_parent_keys = []
2273
for key in maybe_file_parent_keys:
2275
file_parent_keys.append(key)
2277
# Now we have the parents of this content
2278
annotator = self.branch.repository.texts.get_annotator()
2279
text = self.get_file_text(file_id)
2280
this_key =(file_id, default_revision)
2281
annotator.add_special_text(this_key, file_parent_keys, text)
2282
annotations = [(key[-1], line)
2283
for key, line in annotator.annotate_flat(this_key)]
2287
def merge_modified(self):
2288
"""Return a dictionary of files modified by a merge.
2290
The list is initialized by WorkingTree.set_merge_modified, which is
2291
typically called after we make some automatic updates to the tree
2294
This returns a map of file_id->sha1, containing only files which are
2295
still in the working inventory and have that text hash.
2298
hashfile = self._transport.get('merge-hashes')
2299
except errors.NoSuchFile:
2304
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2305
raise errors.MergeModifiedFormatError()
2306
except StopIteration:
2307
raise errors.MergeModifiedFormatError()
2308
for s in _mod_rio.RioReader(hashfile):
2309
# RioReader reads in Unicode, so convert file_ids back to utf8
2310
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2311
if not self.inventory.has_id(file_id):
2313
text_hash = s.get("hash")
2314
if text_hash == self.get_file_sha1(file_id):
2315
merge_hashes[file_id] = text_hash
2321
def subsume(self, other_tree):
2322
def add_children(inventory, entry):
2323
for child_entry in entry.children.values():
2324
inventory._byid[child_entry.file_id] = child_entry
2325
if child_entry.kind == 'directory':
2326
add_children(inventory, child_entry)
2327
if other_tree.get_root_id() == self.get_root_id():
2328
raise errors.BadSubsumeSource(self, other_tree,
2329
'Trees have the same root')
2331
other_tree_path = self.relpath(other_tree.basedir)
2332
except errors.PathNotChild:
2333
raise errors.BadSubsumeSource(self, other_tree,
2334
'Tree is not contained by the other')
2335
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2336
if new_root_parent is None:
2337
raise errors.BadSubsumeSource(self, other_tree,
2338
'Parent directory is not versioned.')
2339
# We need to ensure that the result of a fetch will have a
2340
# versionedfile for the other_tree root, and only fetching into
2341
# RepositoryKnit2 guarantees that.
2342
if not self.branch.repository.supports_rich_root():
2343
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2344
other_tree.lock_tree_write()
2346
new_parents = other_tree.get_parent_ids()
2347
other_root = other_tree.inventory.root
2348
other_root.parent_id = new_root_parent
2349
other_root.name = osutils.basename(other_tree_path)
2350
self.inventory.add(other_root)
2351
add_children(self.inventory, other_root)
2352
self._write_inventory(self.inventory)
2353
# normally we don't want to fetch whole repositories, but i think
2354
# here we really do want to consolidate the whole thing.
2355
for parent_id in other_tree.get_parent_ids():
2356
self.branch.fetch(other_tree.branch, parent_id)
2357
self.add_parent_tree_id(parent_id)
2360
other_tree.bzrdir.retire_bzrdir()
2362
@needs_tree_write_lock
2363
def extract(self, file_id, format=None):
2364
"""Extract a subtree from this tree.
2366
A new branch will be created, relative to the path for this tree.
2370
segments = osutils.splitpath(path)
2371
transport = self.branch.bzrdir.root_transport
2372
for name in segments:
2373
transport = transport.clone(name)
2374
transport.ensure_base()
2377
sub_path = self.id2path(file_id)
2378
branch_transport = mkdirs(sub_path)
2380
format = self.bzrdir.cloning_metadir()
2381
branch_transport.ensure_base()
2382
branch_bzrdir = format.initialize_on_transport(branch_transport)
2384
repo = branch_bzrdir.find_repository()
2385
except errors.NoRepositoryPresent:
2386
repo = branch_bzrdir.create_repository()
2387
if not repo.supports_rich_root():
2388
raise errors.RootNotRich()
2389
new_branch = branch_bzrdir.create_branch()
2390
new_branch.pull(self.branch)
2391
for parent_id in self.get_parent_ids():
2392
new_branch.fetch(self.branch, parent_id)
2393
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2394
if tree_transport.base != branch_transport.base:
2395
tree_bzrdir = format.initialize_on_transport(tree_transport)
2396
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2397
target_branch=new_branch)
2399
tree_bzrdir = branch_bzrdir
2400
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2401
wt.set_parent_ids(self.get_parent_ids())
2402
my_inv = self.inventory
2403
child_inv = inventory.Inventory(root_id=None)
2404
new_root = my_inv[file_id]
2405
my_inv.remove_recursive_id(file_id)
2406
new_root.parent_id = None
2407
child_inv.add(new_root)
2408
self._write_inventory(my_inv)
2409
wt._write_inventory(child_inv)
2412
def list_files(self, include_root=False, from_dir=None, recursive=True):
2413
"""List all files as (path, class, kind, id, entry).
2415
Lists, but does not descend into unversioned directories.
2416
This does not include files that have been deleted in this
2417
tree. Skips the control directory.
2419
:param include_root: if True, return an entry for the root
2420
:param from_dir: start from this directory or None for the root
2421
:param recursive: whether to recurse into subdirectories or not
2423
# list_files is an iterator, so @needs_read_lock doesn't work properly
2424
# with it. So callers should be careful to always read_lock the tree.
2425
if not self.is_locked():
2426
raise errors.ObjectNotLocked(self)
2428
inv = self.inventory
2429
if from_dir is None and include_root is True:
2430
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2431
# Convert these into local objects to save lookup times
2432
pathjoin = osutils.pathjoin
2433
file_kind = self._kind
2435
# transport.base ends in a slash, we want the piece
2436
# between the last two slashes
2437
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2439
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2441
# directory file_id, relative path, absolute path, reverse sorted children
2442
if from_dir is not None:
2443
from_dir_id = inv.path2id(from_dir)
2444
if from_dir_id is None:
2445
# Directory not versioned
2447
from_dir_abspath = pathjoin(self.basedir, from_dir)
2449
from_dir_id = inv.root.file_id
2450
from_dir_abspath = self.basedir
2451
children = os.listdir(from_dir_abspath)
2453
# jam 20060527 The kernel sized tree seems equivalent whether we
2454
# use a deque and popleft to keep them sorted, or if we use a plain
2455
# list and just reverse() them.
2456
children = collections.deque(children)
2457
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2459
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2462
f = children.popleft()
2463
## TODO: If we find a subdirectory with its own .bzr
2464
## directory, then that is a separate tree and we
2465
## should exclude it.
2467
# the bzrdir for this tree
2468
if transport_base_dir == f:
2471
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2472
# and 'f' doesn't begin with one, we can do a string op, rather
2473
# than the checks of pathjoin(), all relative paths will have an extra slash
2475
fp = from_dir_relpath + '/' + f
2478
fap = from_dir_abspath + '/' + f
2480
dir_ie = inv[from_dir_id]
2481
if dir_ie.kind == 'directory':
2482
f_ie = dir_ie.children.get(f)
2487
elif self.is_ignored(fp[1:]):
2490
# we may not have found this file, because of a unicode
2491
# issue, or because the directory was actually a symlink.
2492
f_norm, can_access = osutils.normalized_filename(f)
2493
if f == f_norm or not can_access:
2494
# No change, so treat this file normally
2497
# this file can be accessed by a normalized path
2498
# check again if it is versioned
2499
# these lines are repeated here for performance
2501
fp = from_dir_relpath + '/' + f
2502
fap = from_dir_abspath + '/' + f
2503
f_ie = inv.get_child(from_dir_id, f)
2506
elif self.is_ignored(fp[1:]):
2513
# make a last minute entry
2515
yield fp[1:], c, fk, f_ie.file_id, f_ie
2518
yield fp[1:], c, fk, None, fk_entries[fk]()
2520
yield fp[1:], c, fk, None, TreeEntry()
2523
if fk != 'directory':
2526
# But do this child first if recursing down
2528
new_children = os.listdir(fap)
2530
new_children = collections.deque(new_children)
2531
stack.append((f_ie.file_id, fp, fap, new_children))
2532
# Break out of inner loop,
2533
# so that we start outer loop with child
2536
# if we finished all children, pop it off the stack
2539
@needs_tree_write_lock
2540
def move(self, from_paths, to_dir=None, after=False):
2543
to_dir must exist in the inventory.
2545
If to_dir exists and is a directory, the files are moved into
2546
it, keeping their old names.
2548
Note that to_dir is only the last component of the new name;
2549
this doesn't change the directory.
2551
For each entry in from_paths the move mode will be determined
2554
The first mode moves the file in the filesystem and updates the
2555
inventory. The second mode only updates the inventory without
2556
touching the file on the filesystem.
2558
move uses the second mode if 'after == True' and the target is
2559
either not versioned or newly added, and present in the working tree.
2561
move uses the second mode if 'after == False' and the source is
2562
versioned but no longer in the working tree, and the target is not
2563
versioned but present in the working tree.
2565
move uses the first mode if 'after == False' and the source is
2566
versioned and present in the working tree, and the target is not
2567
versioned and not present in the working tree.
2569
Everything else results in an error.
2571
This returns a list of (from_path, to_path) pairs for each
2572
entry that is moved.
2577
# check for deprecated use of signature
2579
raise TypeError('You must supply a target directory')
2580
# check destination directory
2581
if isinstance(from_paths, basestring):
2583
inv = self.inventory
2584
to_abs = self.abspath(to_dir)
2585
if not isdir(to_abs):
2586
raise errors.BzrMoveFailedError('',to_dir,
2587
errors.NotADirectory(to_abs))
2588
if not self.has_filename(to_dir):
2589
raise errors.BzrMoveFailedError('',to_dir,
2590
errors.NotInWorkingDirectory(to_dir))
2591
to_dir_id = inv.path2id(to_dir)
2592
if to_dir_id is None:
2593
raise errors.BzrMoveFailedError('',to_dir,
2594
errors.NotVersionedError(path=to_dir))
2596
to_dir_ie = inv[to_dir_id]
2597
if to_dir_ie.kind != 'directory':
2598
raise errors.BzrMoveFailedError('',to_dir,
2599
errors.NotADirectory(to_abs))
2601
# create rename entries and tuples
2602
for from_rel in from_paths:
2603
from_tail = splitpath(from_rel)[-1]
2604
from_id = inv.path2id(from_rel)
2606
raise errors.BzrMoveFailedError(from_rel,to_dir,
2607
errors.NotVersionedError(path=from_rel))
2609
from_entry = inv[from_id]
2610
from_parent_id = from_entry.parent_id
2611
to_rel = pathjoin(to_dir, from_tail)
2612
rename_entry = InventoryWorkingTree._RenameEntry(
2615
from_tail=from_tail,
2616
from_parent_id=from_parent_id,
2617
to_rel=to_rel, to_tail=from_tail,
2618
to_parent_id=to_dir_id)
2619
rename_entries.append(rename_entry)
2620
rename_tuples.append((from_rel, to_rel))
2622
# determine which move mode to use. checks also for movability
2623
rename_entries = self._determine_mv_mode(rename_entries, after)
2625
original_modified = self._inventory_is_modified
2628
self._inventory_is_modified = True
2629
self._move(rename_entries)
2631
# restore the inventory on error
2632
self._inventory_is_modified = original_modified
2634
self._write_inventory(inv)
2635
return rename_tuples
2637
@needs_tree_write_lock
2638
def rename_one(self, from_rel, to_rel, after=False):
2641
This can change the directory or the filename or both.
2643
rename_one has several 'modes' to work. First, it can rename a physical
2644
file and change the file_id. That is the normal mode. Second, it can
2645
only change the file_id without touching any physical file.
2647
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2648
versioned but present in the working tree.
2650
rename_one uses the second mode if 'after == False' and 'from_rel' is
2651
versioned but no longer in the working tree, and 'to_rel' is not
2652
versioned but present in the working tree.
2654
rename_one uses the first mode if 'after == False' and 'from_rel' is
2655
versioned and present in the working tree, and 'to_rel' is not
2656
versioned and not present in the working tree.
2658
Everything else results in an error.
2660
inv = self.inventory
2663
# create rename entries and tuples
2664
from_tail = splitpath(from_rel)[-1]
2665
from_id = inv.path2id(from_rel)
2667
# if file is missing in the inventory maybe it's in the basis_tree
2668
basis_tree = self.branch.basis_tree()
2669
from_id = basis_tree.path2id(from_rel)
2671
raise errors.BzrRenameFailedError(from_rel,to_rel,
2672
errors.NotVersionedError(path=from_rel))
2673
# put entry back in the inventory so we can rename it
2674
from_entry = basis_tree.inventory[from_id].copy()
2677
from_entry = inv[from_id]
2678
from_parent_id = from_entry.parent_id
2679
to_dir, to_tail = os.path.split(to_rel)
2680
to_dir_id = inv.path2id(to_dir)
2681
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2683
from_tail=from_tail,
2684
from_parent_id=from_parent_id,
2685
to_rel=to_rel, to_tail=to_tail,
2686
to_parent_id=to_dir_id)
2687
rename_entries.append(rename_entry)
2689
# determine which move mode to use. checks also for movability
2690
rename_entries = self._determine_mv_mode(rename_entries, after)
2692
# check if the target changed directory and if the target directory is
2694
if to_dir_id is None:
2695
raise errors.BzrMoveFailedError(from_rel,to_rel,
2696
errors.NotVersionedError(path=to_dir))
2698
# all checks done. now we can continue with our actual work
2699
mutter('rename_one:\n'
2704
' to_dir_id {%s}\n',
2705
from_id, from_rel, to_rel, to_dir, to_dir_id)
2707
self._move(rename_entries)
2708
self._write_inventory(inv)
2710
class _RenameEntry(object):
2711
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2712
to_rel, to_tail, to_parent_id, only_change_inv=False,
2714
self.from_rel = from_rel
2715
self.from_id = from_id
2716
self.from_tail = from_tail
2717
self.from_parent_id = from_parent_id
2718
self.to_rel = to_rel
2719
self.to_tail = to_tail
2720
self.to_parent_id = to_parent_id
2721
self.change_id = change_id
2722
self.only_change_inv = only_change_inv
2724
def _determine_mv_mode(self, rename_entries, after=False):
2725
"""Determines for each from-to pair if both inventory and working tree
2726
or only the inventory has to be changed.
2728
Also does basic plausability tests.
2730
inv = self.inventory
2732
for rename_entry in rename_entries:
2733
# store to local variables for easier reference
2734
from_rel = rename_entry.from_rel
2735
from_id = rename_entry.from_id
2736
to_rel = rename_entry.to_rel
2737
to_id = inv.path2id(to_rel)
2738
only_change_inv = False
2741
# check the inventory for source and destination
2743
raise errors.BzrMoveFailedError(from_rel,to_rel,
2744
errors.NotVersionedError(path=from_rel))
2745
if to_id is not None:
2747
# allow it with --after but only if dest is newly added
2749
basis = self.basis_tree()
2752
if not basis.has_id(to_id):
2753
rename_entry.change_id = True
2758
raise errors.BzrMoveFailedError(from_rel,to_rel,
2759
errors.AlreadyVersionedError(path=to_rel))
2761
# try to determine the mode for rename (only change inv or change
2762
# inv and file system)
2764
if not self.has_filename(to_rel):
2765
raise errors.BzrMoveFailedError(from_id,to_rel,
2766
errors.NoSuchFile(path=to_rel,
2767
extra="New file has not been created yet"))
2768
only_change_inv = True
2769
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2770
only_change_inv = True
2771
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2772
only_change_inv = False
2773
elif (not self.case_sensitive
2774
and from_rel.lower() == to_rel.lower()
2775
and self.has_filename(from_rel)):
2776
only_change_inv = False
2778
# something is wrong, so lets determine what exactly
2779
if not self.has_filename(from_rel) and \
2780
not self.has_filename(to_rel):
2781
raise errors.BzrRenameFailedError(from_rel,to_rel,
2782
errors.PathsDoNotExist(paths=(str(from_rel),
2785
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2786
rename_entry.only_change_inv = only_change_inv
2787
return rename_entries
2789
def _move(self, rename_entries):
2790
"""Moves a list of files.
2792
Depending on the value of the flag 'only_change_inv', the
2793
file will be moved on the file system or not.
2795
inv = self.inventory
2798
for entry in rename_entries:
2800
self._move_entry(entry)
2802
self._rollback_move(moved)
2806
def _rollback_move(self, moved):
2807
"""Try to rollback a previous move in case of an filesystem error."""
2808
inv = self.inventory
2811
self._move_entry(WorkingTree._RenameEntry(
2812
entry.to_rel, entry.from_id,
2813
entry.to_tail, entry.to_parent_id, entry.from_rel,
2814
entry.from_tail, entry.from_parent_id,
2815
entry.only_change_inv))
2816
except errors.BzrMoveFailedError, e:
2817
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2818
" The working tree is in an inconsistent state."
2819
" Please consider doing a 'bzr revert'."
2820
" Error message is: %s" % e)
2822
def _move_entry(self, entry):
2823
inv = self.inventory
2824
from_rel_abs = self.abspath(entry.from_rel)
2825
to_rel_abs = self.abspath(entry.to_rel)
2826
if from_rel_abs == to_rel_abs:
2827
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2828
"Source and target are identical.")
2830
if not entry.only_change_inv:
2832
osutils.rename(from_rel_abs, to_rel_abs)
2834
raise errors.BzrMoveFailedError(entry.from_rel,
2837
to_id = inv.path2id(entry.to_rel)
2838
inv.remove_recursive_id(to_id)
2839
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2841
@needs_tree_write_lock
2842
def unversion(self, file_ids):
2843
"""Remove the file ids in file_ids from the current versioned set.
2845
When a file_id is unversioned, all of its children are automatically
2848
:param file_ids: The file ids to stop versioning.
2849
:raises: NoSuchId if any fileid is not currently versioned.
2851
for file_id in file_ids:
2852
if not self._inventory.has_id(file_id):
2853
raise errors.NoSuchId(self, file_id)
2854
for file_id in file_ids:
2855
if self._inventory.has_id(file_id):
2856
self._inventory.remove_recursive_id(file_id)
2858
# in the future this should just set a dirty bit to wait for the
2859
# final unlock. However, until all methods of workingtree start
2860
# with the current in -memory inventory rather than triggering
2861
# a read, it is more complex - we need to teach read_inventory
2862
# to know when to read, and when to not read first... and possibly
2863
# to save first when the in memory one may be corrupted.
2864
# so for now, we just only write it if it is indeed dirty.
2866
self._write_inventory(self._inventory)
2868
def stored_kind(self, file_id):
2869
"""See Tree.stored_kind"""
2870
return self.inventory[file_id].kind
2873
"""Yield all unversioned files in this WorkingTree.
2875
If there are any unversioned directories then only the directory is
2876
returned, not all its children. But if there are unversioned files
2877
under a versioned subdirectory, they are returned.
2879
Currently returned depth-first, sorted by name within directories.
2880
This is the same order used by 'osutils.walkdirs'.
2882
## TODO: Work from given directory downwards
2883
for path, dir_entry in self.inventory.directories():
2884
# mutter("search for unknowns in %r", path)
2885
dirabs = self.abspath(path)
2886
if not isdir(dirabs):
2887
# e.g. directory deleted
2891
for subf in os.listdir(dirabs):
2892
if self.bzrdir.is_control_filename(subf):
2894
if subf not in dir_entry.children:
2897
can_access) = osutils.normalized_filename(subf)
2898
except UnicodeDecodeError:
2899
path_os_enc = path.encode(osutils._fs_enc)
2900
relpath = path_os_enc + '/' + subf
2901
raise errors.BadFilenameEncoding(relpath,
2903
if subf_norm != subf and can_access:
2904
if subf_norm not in dir_entry.children:
2905
fl.append(subf_norm)
2911
subp = pathjoin(path, subf)
2914
def _walkdirs(self, prefix=""):
2915
"""Walk the directories of this tree.
2917
:param prefix: is used as the directrory to start with.
2918
:returns: a generator which yields items in the form::
2920
((curren_directory_path, fileid),
2921
[(file1_path, file1_name, file1_kind, None, file1_id,
2071
2924
_directory = 'directory'
2072
2925
# get the root in the inventory
2073
2926
inv = self.inventory
2278
3073
"""Is this format supported?
2280
3075
Supported formats can be initialized and opened.
2281
Unsupported formats may not support initialization or committing or
3076
Unsupported formats may not support initialization or committing or
2282
3077
some other features depending on the reason for not being supported.
3081
def supports_content_filtering(self):
3082
"""True if this format supports content filtering."""
3085
def supports_views(self):
3086
"""True if this format supports stored views."""
3090
@symbol_versioning.deprecated_method(
3091
symbol_versioning.deprecated_in((2, 4, 0)))
2287
3092
def register_format(klass, format):
2288
klass._formats[format.get_format_string()] = format
3093
format_registry.register(format)
3096
@symbol_versioning.deprecated_method(
3097
symbol_versioning.deprecated_in((2, 4, 0)))
3098
def register_extra_format(klass, format):
3099
format_registry.register_extra(format)
3102
@symbol_versioning.deprecated_method(
3103
symbol_versioning.deprecated_in((2, 4, 0)))
3104
def unregister_extra_format(klass, format):
3105
format_registry.unregister_extra(format)
3108
@symbol_versioning.deprecated_method(
3109
symbol_versioning.deprecated_in((2, 4, 0)))
3110
def get_formats(klass):
3111
return format_registry._get_all()
3114
@symbol_versioning.deprecated_method(
3115
symbol_versioning.deprecated_in((2, 4, 0)))
2291
3116
def set_default_format(klass, format):
2292
klass._default_format = format
3117
format_registry.set_default(format)
3120
@symbol_versioning.deprecated_method(
3121
symbol_versioning.deprecated_in((2, 4, 0)))
2295
3122
def unregister_format(klass, format):
2296
assert klass._formats[format.get_format_string()] is format
2297
del klass._formats[format.get_format_string()]
2301
class WorkingTreeFormat2(WorkingTreeFormat):
2302
"""The second working tree format.
2304
This format modified the hash cache from the format 1 hash cache.
2307
def get_format_description(self):
2308
"""See WorkingTreeFormat.get_format_description()."""
2309
return "Working tree format 2"
2311
def stub_initialize_remote(self, control_files):
2312
"""As a special workaround create critical control files for a remote working tree
2314
This ensures that it can later be updated and dealt with locally,
2315
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2316
no working tree. (See bug #43064).
2320
xml5.serializer_v5.write_inventory(inv, sio)
2322
control_files.put('inventory', sio)
2324
control_files.put_utf8('pending-merges', '')
2327
def initialize(self, a_bzrdir, revision_id=None):
2328
"""See WorkingTreeFormat.initialize()."""
2329
if not isinstance(a_bzrdir.transport, LocalTransport):
2330
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2331
branch = a_bzrdir.open_branch()
2332
if revision_id is not None:
2333
revision_id = osutils.safe_revision_id(revision_id)
2336
revision_history = branch.revision_history()
2338
position = revision_history.index(revision_id)
2340
raise errors.NoSuchRevision(branch, revision_id)
2341
branch.set_revision_history(revision_history[:position + 1])
2344
revision = branch.last_revision()
2346
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2352
basis_tree = branch.repository.revision_tree(revision)
2353
if basis_tree.inventory.root is not None:
2354
wt.set_root_id(basis_tree.inventory.root.file_id)
2355
# set the parent list and cache the basis tree.
2356
wt.set_parent_trees([(revision, basis_tree)])
2357
transform.build_tree(basis_tree, wt)
2361
super(WorkingTreeFormat2, self).__init__()
2362
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2364
def open(self, a_bzrdir, _found=False):
2365
"""Return the WorkingTree object for a_bzrdir
2367
_found is a private parameter, do not use it. It is used to indicate
2368
if format probing has already been done.
2371
# we are being called directly and must probe.
2372
raise NotImplementedError
2373
if not isinstance(a_bzrdir.transport, LocalTransport):
2374
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2375
return WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2381
class WorkingTreeFormat3(WorkingTreeFormat):
2382
"""The second working tree format updated to record a format marker.
2385
- exists within a metadir controlling .bzr
2386
- includes an explicit version marker for the workingtree control
2387
files, separate from the BzrDir format
2388
- modifies the hash cache format
2390
- uses a LockDir to guard access for writes.
2393
def get_format_string(self):
2394
"""See WorkingTreeFormat.get_format_string()."""
2395
return "Bazaar-NG Working Tree format 3"
2397
def get_format_description(self):
2398
"""See WorkingTreeFormat.get_format_description()."""
2399
return "Working tree format 3"
2401
_lock_file_name = 'lock'
2402
_lock_class = LockDir
2404
def _open_control_files(self, a_bzrdir):
2405
transport = a_bzrdir.get_workingtree_transport(None)
2406
return LockableFiles(transport, self._lock_file_name,
2409
def initialize(self, a_bzrdir, revision_id=None):
2410
"""See WorkingTreeFormat.initialize().
2412
revision_id allows creating a working tree at a different
2413
revision than the branch is at.
2415
if not isinstance(a_bzrdir.transport, LocalTransport):
2416
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2417
transport = a_bzrdir.get_workingtree_transport(self)
2418
control_files = self._open_control_files(a_bzrdir)
2419
control_files.create_lock()
2420
control_files.lock_write()
2421
control_files.put_utf8('format', self.get_format_string())
2422
branch = a_bzrdir.open_branch()
2423
if revision_id is None:
2424
revision_id = branch.last_revision()
2426
revision_id = osutils.safe_revision_id(revision_id)
2427
# WorkingTree3 can handle an inventory which has a unique root id.
2428
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2429
# those trees. And because there isn't a format bump inbetween, we
2430
# are maintaining compatibility with older clients.
2431
# inv = Inventory(root_id=gen_root_id())
2433
wt = WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2439
_control_files=control_files)
2440
wt.lock_tree_write()
2442
basis_tree = branch.repository.revision_tree(revision_id)
2443
# only set an explicit root id if there is one to set.
2444
if basis_tree.inventory.root is not None:
2445
wt.set_root_id(basis_tree.inventory.root.file_id)
2446
if revision_id == NULL_REVISION:
2447
wt.set_parent_trees([])
2449
wt.set_parent_trees([(revision_id, basis_tree)])
2450
transform.build_tree(basis_tree, wt)
2452
# Unlock in this order so that the unlock-triggers-flush in
2453
# WorkingTree is given a chance to fire.
2454
control_files.unlock()
2459
super(WorkingTreeFormat3, self).__init__()
2460
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2462
def open(self, a_bzrdir, _found=False):
2463
"""Return the WorkingTree object for a_bzrdir
2465
_found is a private parameter, do not use it. It is used to indicate
2466
if format probing has already been done.
2469
# we are being called directly and must probe.
2470
raise NotImplementedError
2471
if not isinstance(a_bzrdir.transport, LocalTransport):
2472
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2473
return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2475
def _open(self, a_bzrdir, control_files):
2476
"""Open the tree itself.
2478
:param a_bzrdir: the dir for the tree.
2479
:param control_files: the control files for the tree.
2481
return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2485
_control_files=control_files)
2488
return self.get_format_string()
2491
__default_format = WorkingTreeFormat3()
2492
WorkingTreeFormat.register_format(__default_format)
2493
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2494
WorkingTreeFormat.set_default_format(__default_format)
2495
# formats which have no format string are not discoverable
2496
# and not independently creatable, so are not registered.
2497
_legacy_formats = [WorkingTreeFormat2(),
2501
class WorkingTreeTestProviderAdapter(object):
2502
"""A tool to generate a suite testing multiple workingtree formats at once.
2504
This is done by copying the test once for each transport and injecting
2505
the transport_server, transport_readonly_server, and workingtree_format
2506
classes into each copy. Each copy is also given a new id() to make it
2510
def __init__(self, transport_server, transport_readonly_server, formats):
2511
self._transport_server = transport_server
2512
self._transport_readonly_server = transport_readonly_server
2513
self._formats = formats
2515
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2516
"""Clone test for adaption."""
2517
new_test = deepcopy(test)
2518
new_test.transport_server = self._transport_server
2519
new_test.transport_readonly_server = self._transport_readonly_server
2520
new_test.bzrdir_format = bzrdir_format
2521
new_test.workingtree_format = workingtree_format
2522
def make_new_test_id():
2523
new_id = "%s(%s)" % (test.id(), variation)
2524
return lambda: new_id
2525
new_test.id = make_new_test_id()
2528
def adapt(self, test):
2529
from bzrlib.tests import TestSuite
2530
result = TestSuite()
2531
for workingtree_format, bzrdir_format in self._formats:
2532
new_test = self._clone_test(
2535
workingtree_format, workingtree_format.__class__.__name__)
2536
result.addTest(new_test)
3123
format_registry.remove(format)
3126
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3127
"bzrlib.workingtree_4", "WorkingTreeFormat4")
3128
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3129
"bzrlib.workingtree_4", "WorkingTreeFormat5")
3130
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3131
"bzrlib.workingtree_4", "WorkingTreeFormat6")
3132
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
3133
"bzrlib.workingtree_3", "WorkingTreeFormat3")
3134
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")