823
874
self.add(path, file_id, 'directory')
826
def get_symlink_target(self, file_id, path=None):
828
abspath = self.abspath(path)
830
abspath = self.id2abspath(file_id)
831
target = osutils.readlink(abspath)
877
def get_symlink_target(self, file_id):
878
file_id = osutils.safe_file_id(file_id)
879
return os.readlink(self.id2abspath(file_id))
834
882
def subsume(self, other_tree):
835
raise NotImplementedError(self.subsume)
837
def _setup_directory_is_tree_reference(self):
838
if self._branch.repository._format.supports_tree_reference:
839
self._directory_is_tree_reference = \
840
self._directory_may_be_tree_reference
842
self._directory_is_tree_reference = \
843
self._directory_is_never_tree_reference
845
def _directory_is_never_tree_reference(self, relpath):
848
def _directory_may_be_tree_reference(self, relpath):
849
# as a special case, if a directory contains control files then
850
# it's a tree reference, except that the root of the tree is not
851
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
852
# TODO: We could ask all the control formats whether they
853
# recognize this directory, but at the moment there's no cheap api
854
# to do that. Since we probably can only nest bzr checkouts and
855
# they always use this name it's ok for now. -- mbp 20060306
857
# FIXME: There is an unhandled case here of a subdirectory
858
# containing .bzr but not a branch; that will probably blow up
859
# when you try to commit it. It might happen if there is a
860
# checkout in a subdirectory. This can be avoided by not adding
883
def add_children(inventory, entry):
884
for child_entry in entry.children.values():
885
inventory._byid[child_entry.file_id] = child_entry
886
if child_entry.kind == 'directory':
887
add_children(inventory, child_entry)
888
if other_tree.get_root_id() == self.get_root_id():
889
raise errors.BadSubsumeSource(self, other_tree,
890
'Trees have the same root')
892
other_tree_path = self.relpath(other_tree.basedir)
893
except errors.PathNotChild:
894
raise errors.BadSubsumeSource(self, other_tree,
895
'Tree is not contained by the other')
896
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
897
if new_root_parent is None:
898
raise errors.BadSubsumeSource(self, other_tree,
899
'Parent directory is not versioned.')
900
# We need to ensure that the result of a fetch will have a
901
# versionedfile for the other_tree root, and only fetching into
902
# RepositoryKnit2 guarantees that.
903
if not self.branch.repository.supports_rich_root():
904
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
905
other_tree.lock_tree_write()
907
new_parents = other_tree.get_parent_ids()
908
other_root = other_tree.inventory.root
909
other_root.parent_id = new_root_parent
910
other_root.name = osutils.basename(other_tree_path)
911
self.inventory.add(other_root)
912
add_children(self.inventory, other_root)
913
self._write_inventory(self.inventory)
914
# normally we don't want to fetch whole repositories, but i think
915
# here we really do want to consolidate the whole thing.
916
for parent_id in other_tree.get_parent_ids():
917
self.branch.fetch(other_tree.branch, parent_id)
918
self.add_parent_tree_id(parent_id)
921
other_tree.bzrdir.retire_bzrdir()
923
@needs_tree_write_lock
863
924
def extract(self, file_id, format=None):
864
925
"""Extract a subtree from this tree.
866
927
A new branch will be created, relative to the path for this tree.
868
raise NotImplementedError(self.extract)
931
segments = osutils.splitpath(path)
932
transport = self.branch.bzrdir.root_transport
933
for name in segments:
934
transport = transport.clone(name)
937
except errors.FileExists:
941
sub_path = self.id2path(file_id)
942
branch_transport = mkdirs(sub_path)
944
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
946
branch_transport.mkdir('.')
947
except errors.FileExists:
949
branch_bzrdir = format.initialize_on_transport(branch_transport)
951
repo = branch_bzrdir.find_repository()
952
except errors.NoRepositoryPresent:
953
repo = branch_bzrdir.create_repository()
954
assert repo.supports_rich_root()
956
if not repo.supports_rich_root():
957
raise errors.RootNotRich()
958
new_branch = branch_bzrdir.create_branch()
959
new_branch.pull(self.branch)
960
for parent_id in self.get_parent_ids():
961
new_branch.fetch(self.branch, parent_id)
962
tree_transport = self.bzrdir.root_transport.clone(sub_path)
963
if tree_transport.base != branch_transport.base:
964
tree_bzrdir = format.initialize_on_transport(tree_transport)
965
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
967
tree_bzrdir = branch_bzrdir
968
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
969
wt.set_parent_ids(self.get_parent_ids())
970
my_inv = self.inventory
971
child_inv = Inventory(root_id=None)
972
new_root = my_inv[file_id]
973
my_inv.remove_recursive_id(file_id)
974
new_root.parent_id = None
975
child_inv.add(new_root)
976
self._write_inventory(my_inv)
977
wt._write_inventory(child_inv)
980
def _serialize(self, inventory, out_file):
981
xml5.serializer_v5.write_inventory(self._inventory, out_file)
983
def _deserialize(selt, in_file):
984
return xml5.serializer_v5.read_inventory(in_file)
871
"""Write the in memory meta data to disk."""
872
raise NotImplementedError(self.flush)
987
"""Write the in memory inventory to disk."""
988
# TODO: Maybe this should only write on dirty ?
989
if self._control_files._lock_mode != 'w':
990
raise errors.NotWriteLocked(self)
992
self._serialize(self._inventory, sio)
994
self._control_files.put('inventory', sio)
995
self._inventory_is_modified = False
874
997
def _kind(self, relpath):
875
998
return osutils.file_kind(self.abspath(relpath))
877
def list_files(self, include_root=False, from_dir=None, recursive=True):
878
"""List all files as (path, class, kind, id, entry).
1000
def list_files(self, include_root=False):
1001
"""Recursively list all files as (path, class, kind, id, entry).
880
1003
Lists, but does not descend into unversioned directories.
881
1005
This does not include files that have been deleted in this
882
tree. Skips the control directory.
884
:param include_root: if True, return an entry for the root
885
:param from_dir: start from this directory or None for the root
886
:param recursive: whether to recurse into subdirectories or not
1008
Skips the control directory.
888
raise NotImplementedError(self.list_files)
890
def move(self, from_paths, to_dir=None, after=False):
1010
# list_files is an iterator, so @needs_read_lock doesn't work properly
1011
# with it. So callers should be careful to always read_lock the tree.
1012
if not self.is_locked():
1013
raise errors.ObjectNotLocked(self)
1015
inv = self.inventory
1016
if include_root is True:
1017
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1018
# Convert these into local objects to save lookup times
1019
pathjoin = osutils.pathjoin
1020
file_kind = self._kind
1022
# transport.base ends in a slash, we want the piece
1023
# between the last two slashes
1024
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
1026
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1028
# directory file_id, relative path, absolute path, reverse sorted children
1029
children = os.listdir(self.basedir)
1031
# jam 20060527 The kernel sized tree seems equivalent whether we
1032
# use a deque and popleft to keep them sorted, or if we use a plain
1033
# list and just reverse() them.
1034
children = collections.deque(children)
1035
stack = [(inv.root.file_id, u'', self.basedir, children)]
1037
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1040
f = children.popleft()
1041
## TODO: If we find a subdirectory with its own .bzr
1042
## directory, then that is a separate tree and we
1043
## should exclude it.
1045
# the bzrdir for this tree
1046
if transport_base_dir == f:
1049
# we know that from_dir_relpath and from_dir_abspath never end in a slash
1050
# and 'f' doesn't begin with one, we can do a string op, rather
1051
# than the checks of pathjoin(), all relative paths will have an extra slash
1053
fp = from_dir_relpath + '/' + f
1056
fap = from_dir_abspath + '/' + f
1058
f_ie = inv.get_child(from_dir_id, f)
1061
elif self.is_ignored(fp[1:]):
1064
# we may not have found this file, because of a unicode issue
1065
f_norm, can_access = osutils.normalized_filename(f)
1066
if f == f_norm or not can_access:
1067
# No change, so treat this file normally
1070
# this file can be accessed by a normalized path
1071
# check again if it is versioned
1072
# these lines are repeated here for performance
1074
fp = from_dir_relpath + '/' + f
1075
fap = from_dir_abspath + '/' + f
1076
f_ie = inv.get_child(from_dir_id, f)
1079
elif self.is_ignored(fp[1:]):
1086
# make a last minute entry
1088
yield fp[1:], c, fk, f_ie.file_id, f_ie
1091
yield fp[1:], c, fk, None, fk_entries[fk]()
1093
yield fp[1:], c, fk, None, TreeEntry()
1096
if fk != 'directory':
1099
# But do this child first
1100
new_children = os.listdir(fap)
1102
new_children = collections.deque(new_children)
1103
stack.append((f_ie.file_id, fp, fap, new_children))
1104
# Break out of inner loop,
1105
# so that we start outer loop with child
1108
# if we finished all children, pop it off the stack
1111
@needs_tree_write_lock
1112
def move(self, from_paths, to_dir=None, after=False, **kwargs):
891
1113
"""Rename files.
893
to_dir must be known to the working tree.
1115
to_dir must exist in the inventory.
895
1117
If to_dir exists and is a directory, the files are moved into
896
it, keeping their old names.
1118
it, keeping their old names.
898
1120
Note that to_dir is only the last component of the new name;
899
1121
this doesn't change the directory.
1196
1670
def get_physical_lock_status(self):
1197
1671
return self._control_files.get_physical_lock_status()
1673
def _basis_inventory_name(self):
1674
return 'basis-inventory-cache'
1199
1676
def _reset_data(self):
1200
1677
"""Reset transient data that cannot be revalidated."""
1201
raise NotImplementedError(self._reset_data)
1678
self._inventory_is_modified = False
1679
result = self._deserialize(self._control_files.get('inventory'))
1680
self._set_inventory(result, dirty=False)
1682
@needs_tree_write_lock
1203
1683
def set_last_revision(self, new_revision):
1204
1684
"""Change the last revision in the working tree."""
1205
raise NotImplementedError(self.set_last_revision)
1685
new_revision = osutils.safe_revision_id(new_revision)
1686
if self._change_last_revision(new_revision):
1687
self._cache_basis_inventory(new_revision)
1207
1689
def _change_last_revision(self, new_revision):
1208
1690
"""Template method part of set_last_revision to perform the change.
1210
1692
This is used to allow WorkingTree3 instances to not affect branch
1211
1693
when their last revision is set.
1213
if _mod_revision.is_null(new_revision):
1214
self.branch.set_last_revision_info(0, new_revision)
1695
if new_revision is None:
1696
self.branch.set_revision_history([])
1216
_mod_revision.check_not_reserved_id(new_revision)
1218
1699
self.branch.generate_revision_history(new_revision)
1219
1700
except errors.NoSuchRevision:
1220
1701
# not present in the repo - dont try to set it deeper than the tip
1221
self.branch._set_revision_history([new_revision])
1702
self.branch.set_revision_history([new_revision])
1705
def _write_basis_inventory(self, xml):
1706
"""Write the basis inventory XML to the basis-inventory file"""
1707
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1708
path = self._basis_inventory_name()
1710
self._control_files.put(path, sio)
1712
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1713
"""Create the text that will be saved in basis-inventory"""
1714
# TODO: jam 20070209 This should be redundant, as the revision_id
1715
# as all callers should have already converted the revision_id to
1717
inventory.revision_id = osutils.safe_revision_id(revision_id)
1718
return xml7.serializer_v7.write_inventory_to_string(inventory)
1720
def _cache_basis_inventory(self, new_revision):
1721
"""Cache new_revision as the basis inventory."""
1722
# TODO: this should allow the ready-to-use inventory to be passed in,
1723
# as commit already has that ready-to-use [while the format is the
1726
# this double handles the inventory - unpack and repack -
1727
# but is easier to understand. We can/should put a conditional
1728
# in here based on whether the inventory is in the latest format
1729
# - perhaps we should repack all inventories on a repository
1731
# the fast path is to copy the raw xml from the repository. If the
1732
# xml contains 'revision_id="', then we assume the right
1733
# revision_id is set. We must check for this full string, because a
1734
# root node id can legitimately look like 'revision_id' but cannot
1736
xml = self.branch.repository.get_inventory_xml(new_revision)
1737
firstline = xml.split('\n', 1)[0]
1738
if (not 'revision_id="' in firstline or
1739
'format="7"' not in firstline):
1740
inv = self.branch.repository.deserialise_inventory(
1742
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1743
self._write_basis_inventory(xml)
1744
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1747
def read_basis_inventory(self):
1748
"""Read the cached basis inventory."""
1749
path = self._basis_inventory_name()
1750
return self._control_files.get(path).read()
1753
def read_working_inventory(self):
1754
"""Read the working inventory.
1756
:raises errors.InventoryModified: read_working_inventory will fail
1757
when the current in memory inventory has been modified.
1759
# conceptually this should be an implementation detail of the tree.
1760
# XXX: Deprecate this.
1761
# ElementTree does its own conversion from UTF-8, so open in
1763
if self._inventory_is_modified:
1764
raise errors.InventoryModified(self)
1765
result = self._deserialize(self._control_files.get('inventory'))
1766
self._set_inventory(result, dirty=False)
1224
1769
@needs_tree_write_lock
1225
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1227
"""Remove nominated files from the working tree metadata.
1229
:files: File paths relative to the basedir.
1230
:keep_files: If true, the files will also be kept.
1231
:force: Delete files and directories, even if they are changed and
1232
even if the directories are not empty.
1770
def remove(self, files, verbose=False, to_file=None):
1771
"""Remove nominated files from the working inventory..
1773
This does not remove their text. This does not run on XXX on what? RBC
1775
TODO: Refuse to remove modified files unless --force is given?
1777
TODO: Do something useful with directories.
1779
TODO: Should this remove the text or not? Tough call; not
1780
removing may be useful and the user can just use use rm, and
1781
is the opposite of add. Removing it is consistent with most
1782
other tools. Maybe an option.
1784
## TODO: Normalize names
1785
## TODO: Remove nested loops; better scalability
1234
1786
if isinstance(files, basestring):
1235
1787
files = [files]
1239
all_files = set() # specified and nested files
1240
unknown_nested_files=set()
1242
to_file = sys.stdout
1244
files_to_backup = []
1246
def recurse_directory_to_add_files(directory):
1247
# Recurse directory and add all files
1248
# so we can check if they have changed.
1249
for parent_info, file_infos in self.walkdirs(directory):
1250
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1251
# Is it versioned or ignored?
1252
if self.path2id(relpath):
1253
# Add nested content for deletion.
1254
all_files.add(relpath)
1256
# Files which are not versioned
1257
# should be treated as unknown.
1258
files_to_backup.append(relpath)
1260
for filename in files:
1261
# Get file name into canonical form.
1262
abspath = self.abspath(filename)
1263
filename = self.relpath(abspath)
1264
if len(filename) > 0:
1265
all_files.add(filename)
1266
recurse_directory_to_add_files(filename)
1268
files = list(all_files)
1271
return # nothing to do
1273
# Sort needed to first handle directory content before the directory
1274
files.sort(reverse=True)
1276
# Bail out if we are going to delete files we shouldn't
1277
if not keep_files and not force:
1278
for (file_id, path, content_change, versioned, parent_id, name,
1279
kind, executable) in self.iter_changes(self.basis_tree(),
1280
include_unchanged=True, require_versioned=False,
1281
want_unversioned=True, specific_files=files):
1282
if versioned[0] == False:
1283
# The record is unknown or newly added
1284
files_to_backup.append(path[1])
1285
elif (content_change and (kind[1] is not None) and
1286
osutils.is_inside_any(files, path[1])):
1287
# Versioned and changed, but not deleted, and still
1288
# in one of the dirs to be deleted.
1289
files_to_backup.append(path[1])
1291
def backup(file_to_backup):
1292
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1293
osutils.rename(abs_path, self.abspath(backup_name))
1294
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1297
# Build inv_delta and delete files where applicable,
1298
# do this before any modifications to meta data.
1789
inv = self.inventory
1791
# do this before any modifications
1299
1792
for f in files:
1300
fid = self.path2id(f)
1793
fid = inv.path2id(f)
1303
message = "%s is not versioned." % (f,)
1795
note("%s is not versioned."%f)
1306
# having removed it, it must be either ignored or unknown
1798
# having remove it, it must be either ignored or unknown
1307
1799
if self.is_ignored(f):
1308
1800
new_status = 'I'
1310
1802
new_status = '?'
1311
# XXX: Really should be a more abstract reporter interface
1312
kind_ch = osutils.kind_marker(self.kind(fid))
1313
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1315
inv_delta.append((f, None, fid, None))
1316
message = "removed %s" % (f,)
1319
abs_path = self.abspath(f)
1320
if osutils.lexists(abs_path):
1321
if (osutils.isdir(abs_path) and
1322
len(os.listdir(abs_path)) > 0):
1324
osutils.rmtree(abs_path)
1325
message = "deleted %s" % (f,)
1329
if f in files_to_backup:
1332
osutils.delete_any(abs_path)
1333
message = "deleted %s" % (f,)
1334
elif message is not None:
1335
# Only care if we haven't done anything yet.
1336
message = "%s does not exist." % (f,)
1338
# Print only one message (if any) per file.
1339
if message is not None:
1341
self.apply_inventory_delta(inv_delta)
1803
textui.show_status(new_status, inv[fid].kind, f,
1807
self._write_inventory(inv)
1343
1809
@needs_tree_write_lock
1344
def revert(self, filenames=None, old_tree=None, backups=True,
1345
pb=None, report_changes=False):
1810
def revert(self, filenames, old_tree=None, backups=True,
1811
pb=DummyProgress(), report_changes=False):
1346
1812
from bzrlib.conflicts import resolve
1347
1813
if old_tree is None:
1348
basis_tree = self.basis_tree()
1349
basis_tree.lock_read()
1350
old_tree = basis_tree
1814
old_tree = self.basis_tree()
1815
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1817
if not len(filenames):
1818
self.set_parent_ids(self.get_parent_ids()[:1])
1354
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1356
if filenames is None and len(self.get_parent_ids()) > 1:
1358
last_revision = self.last_revision()
1359
if last_revision != _mod_revision.NULL_REVISION:
1360
if basis_tree is None:
1361
basis_tree = self.basis_tree()
1362
basis_tree.lock_read()
1363
parent_trees.append((last_revision, basis_tree))
1364
self.set_parent_trees(parent_trees)
1367
resolve(self, filenames, ignore_misses=True, recursive=True)
1369
if basis_tree is not None:
1821
resolve(self, filenames, ignore_misses=True)
1371
1822
return conflicts
1373
1824
def revision_tree(self, revision_id):
1735
def check_state(self):
1736
"""Check that the working state is/isn't valid."""
1737
raise NotImplementedError(self.check_state)
1739
def reset_state(self, revision_ids=None):
1740
"""Reset the state of the working tree.
1742
This does a hard-reset to a last-known-good state. This is a way to
1743
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1745
raise NotImplementedError(self.reset_state)
1747
def _get_rules_searcher(self, default_searcher):
1748
"""See Tree._get_rules_searcher."""
1749
if self._rules_searcher is None:
1750
self._rules_searcher = super(WorkingTree,
1751
self)._get_rules_searcher(default_searcher)
1752
return self._rules_searcher
1754
def get_shelf_manager(self):
1755
"""Return the ShelfManager for this WorkingTree."""
1756
from bzrlib.shelf import ShelfManager
1757
return ShelfManager(self, self._transport)
1760
class InventoryWorkingTree(WorkingTree,
1761
bzrlib.mutabletree.MutableInventoryTree):
1762
"""Base class for working trees that are inventory-oriented.
1764
The inventory is held in the `Branch` working-inventory, and the
1765
files are in a directory on disk.
1767
It is possible for a `WorkingTree` to have a filename which is
1768
not listed in the Inventory and vice versa.
1771
def __init__(self, basedir='.',
1772
branch=DEPRECATED_PARAMETER,
1774
_control_files=None,
1778
"""Construct a InventoryWorkingTree instance. This is not a public API.
1780
:param branch: A branch to override probing for the branch.
1782
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1783
branch=branch, _control_files=_control_files, _internal=_internal,
1784
_format=_format, _bzrdir=_bzrdir)
1786
self._detect_case_handling()
1788
if _inventory is None:
1789
# This will be acquired on lock_read() or lock_write()
1790
self._inventory_is_modified = False
1791
self._inventory = None
1793
# the caller of __init__ has provided an inventory,
1794
# we assume they know what they are doing - as its only
1795
# the Format factory and creation methods that are
1796
# permitted to do this.
1797
self._set_inventory(_inventory, dirty=False)
1799
def _set_inventory(self, inv, dirty):
1800
"""Set the internal cached inventory.
1802
:param inv: The inventory to set.
1803
:param dirty: A boolean indicating whether the inventory is the same
1804
logical inventory as whats on disk. If True the inventory is not
1805
the same and should be written to disk or data will be lost, if
1806
False then the inventory is the same as that on disk and any
1807
serialisation would be unneeded overhead.
1809
self._inventory = inv
1810
self._inventory_is_modified = dirty
1812
def _detect_case_handling(self):
1813
wt_trans = self.bzrdir.get_workingtree_transport(None)
1815
wt_trans.stat(self._format.case_sensitive_filename)
2284
class WorkingTree2(WorkingTree):
2285
"""This is the Format 2 working tree.
2287
This was the first weave based working tree.
2288
- uses os locks for locking.
2289
- uses the branch last-revision.
2292
def __init__(self, *args, **kwargs):
2293
super(WorkingTree2, self).__init__(*args, **kwargs)
2294
# WorkingTree2 has more of a constraint that self._inventory must
2295
# exist. Because this is an older format, we don't mind the overhead
2296
# caused by the extra computation here.
2298
# Newer WorkingTree's should only have self._inventory set when they
2300
if self._inventory is None:
2301
self.read_working_inventory()
2303
def lock_tree_write(self):
2304
"""See WorkingTree.lock_tree_write().
2306
In Format2 WorkingTrees we have a single lock for the branch and tree
2307
so lock_tree_write() degrades to lock_write().
2309
self.branch.lock_write()
2311
return self._control_files.lock_write()
2313
self.branch.unlock()
2317
# we share control files:
2318
if self._control_files._lock_count == 3:
2319
# _inventory_is_modified is always False during a read lock.
2320
if self._inventory_is_modified:
2322
self._write_hashcache_if_dirty()
2324
# reverse order of locking.
2326
return self._control_files.unlock()
2328
self.branch.unlock()
2331
class WorkingTree3(WorkingTree):
2332
"""This is the Format 3 working tree.
2334
This differs from the base WorkingTree by:
2335
- having its own file lock
2336
- having its own last-revision property.
2338
This is new in bzr 0.8
2342
def _last_revision(self):
2343
"""See Mutable.last_revision."""
2345
return osutils.safe_revision_id(
2346
self._control_files.get('last-revision').read())
1816
2347
except errors.NoSuchFile:
1817
self.case_sensitive = True
1819
self.case_sensitive = False
1821
self._setup_directory_is_tree_reference()
1823
def _serialize(self, inventory, out_file):
1824
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1827
def _deserialize(selt, in_file):
1828
return xml5.serializer_v5.read_inventory(in_file)
1830
@needs_tree_write_lock
1831
def _write_inventory(self, inv):
1832
"""Write inventory as the current inventory."""
1833
self._set_inventory(inv, dirty=True)
1836
# XXX: This method should be deprecated in favour of taking in a proper
1837
# new Inventory object.
1838
@needs_tree_write_lock
1839
def set_inventory(self, new_inventory_list):
1840
from bzrlib.inventory import (Inventory,
1844
inv = Inventory(self.get_root_id())
1845
for path, file_id, parent, kind in new_inventory_list:
1846
name = os.path.basename(path)
1849
# fixme, there should be a factory function inv,add_??
1850
if kind == 'directory':
1851
inv.add(InventoryDirectory(file_id, name, parent))
1852
elif kind == 'file':
1853
inv.add(InventoryFile(file_id, name, parent))
1854
elif kind == 'symlink':
1855
inv.add(InventoryLink(file_id, name, parent))
1857
raise errors.BzrError("unknown kind %r" % kind)
1858
self._write_inventory(inv)
1860
def _write_basis_inventory(self, xml):
1861
"""Write the basis inventory XML to the basis-inventory file"""
1862
path = self._basis_inventory_name()
1864
self._transport.put_file(path, sio,
1865
mode=self.bzrdir._get_file_mode())
1867
def _reset_data(self):
1868
"""Reset transient data that cannot be revalidated."""
1869
self._inventory_is_modified = False
1870
f = self._transport.get('inventory')
1872
result = self._deserialize(f)
1875
self._set_inventory(result, dirty=False)
1877
def _set_root_id(self, file_id):
1878
"""Set the root id for this tree, in a format specific manner.
1880
:param file_id: The file id to assign to the root. It must not be
1881
present in the current inventory or an error will occur. It must
1882
not be None, but rather a valid file id.
1884
inv = self._inventory
1885
orig_root_id = inv.root.file_id
1886
# TODO: it might be nice to exit early if there was nothing
1887
# to do, saving us from trigger a sync on unlock.
1888
self._inventory_is_modified = True
1889
# we preserve the root inventory entry object, but
1890
# unlinkit from the byid index
1891
del inv._byid[inv.root.file_id]
1892
inv.root.file_id = file_id
1893
# and link it into the index with the new changed id.
1894
inv._byid[inv.root.file_id] = inv.root
1895
# and finally update all children to reference the new id.
1896
# XXX: this should be safe to just look at the root.children
1897
# list, not the WHOLE INVENTORY.
1900
if entry.parent_id == orig_root_id:
1901
entry.parent_id = inv.root.file_id
1903
def all_file_ids(self):
1904
"""See Tree.iter_all_file_ids"""
1905
return set(self.inventory)
1907
@needs_tree_write_lock
1908
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1909
"""See MutableTree.set_parent_trees."""
1910
parent_ids = [rev for (rev, tree) in parents_list]
1911
for revision_id in parent_ids:
1912
_mod_revision.check_not_reserved_id(revision_id)
1914
self._check_parents_for_ghosts(parent_ids,
1915
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1917
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
1919
if len(parent_ids) == 0:
1920
leftmost_parent_id = _mod_revision.NULL_REVISION
1921
leftmost_parent_tree = None
1923
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1925
if self._change_last_revision(leftmost_parent_id):
1926
if leftmost_parent_tree is None:
1927
# If we don't have a tree, fall back to reading the
1928
# parent tree from the repository.
1929
self._cache_basis_inventory(leftmost_parent_id)
1931
inv = leftmost_parent_tree.inventory
1932
xml = self._create_basis_xml_from_inventory(
1933
leftmost_parent_id, inv)
1934
self._write_basis_inventory(xml)
1935
self._set_merges_from_parent_ids(parent_ids)
1937
def _cache_basis_inventory(self, new_revision):
1938
"""Cache new_revision as the basis inventory."""
1939
# TODO: this should allow the ready-to-use inventory to be passed in,
1940
# as commit already has that ready-to-use [while the format is the
1943
# this double handles the inventory - unpack and repack -
1944
# but is easier to understand. We can/should put a conditional
1945
# in here based on whether the inventory is in the latest format
1946
# - perhaps we should repack all inventories on a repository
1948
# the fast path is to copy the raw xml from the repository. If the
1949
# xml contains 'revision_id="', then we assume the right
1950
# revision_id is set. We must check for this full string, because a
1951
# root node id can legitimately look like 'revision_id' but cannot
1953
xml = self.branch.repository._get_inventory_xml(new_revision)
1954
firstline = xml.split('\n', 1)[0]
1955
if (not 'revision_id="' in firstline or
1956
'format="7"' not in firstline):
1957
inv = self.branch.repository._serializer.read_inventory_from_string(
1959
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1960
self._write_basis_inventory(xml)
1961
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1964
def _basis_inventory_name(self):
1965
return 'basis-inventory-cache'
1967
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1968
"""Create the text that will be saved in basis-inventory"""
1969
inventory.revision_id = revision_id
1970
return xml7.serializer_v7.write_inventory_to_string(inventory)
2350
def _change_last_revision(self, revision_id):
2351
"""See WorkingTree._change_last_revision."""
2352
if revision_id is None or revision_id == NULL_REVISION:
2354
self._control_files._transport.delete('last-revision')
2355
except errors.NoSuchFile:
2359
self._control_files.put_bytes('last-revision', revision_id)
1972
2362
@needs_tree_write_lock
1973
2363
def set_conflicts(self, conflicts):
1974
self._put_rio('conflicts', conflicts.to_stanzas(),
2364
self._put_rio('conflicts', conflicts.to_stanzas(),
1975
2365
CONFLICT_HEADER_1)
1977
2367
@needs_tree_write_lock
1984
2374
@needs_read_lock
1985
2375
def conflicts(self):
1987
confile = self._transport.get('conflicts')
2377
confile = self._control_files.get('conflicts')
1988
2378
except errors.NoSuchFile:
1989
2379
return _mod_conflicts.ConflictList()
1992
if confile.next() != CONFLICT_HEADER_1 + '\n':
1993
raise errors.ConflictFormatError()
1994
except StopIteration:
2381
if confile.next() != CONFLICT_HEADER_1 + '\n':
1995
2382
raise errors.ConflictFormatError()
1996
reader = _mod_rio.RioReader(confile)
1997
return _mod_conflicts.ConflictList.from_stanzas(reader)
2001
def read_basis_inventory(self):
2002
"""Read the cached basis inventory."""
2003
path = self._basis_inventory_name()
2004
return self._transport.get_bytes(path)
2007
def read_working_inventory(self):
2008
"""Read the working inventory.
2010
:raises errors.InventoryModified: read_working_inventory will fail
2011
when the current in memory inventory has been modified.
2013
# conceptually this should be an implementation detail of the tree.
2014
# XXX: Deprecate this.
2015
# ElementTree does its own conversion from UTF-8, so open in
2017
if self._inventory_is_modified:
2018
raise errors.InventoryModified(self)
2019
f = self._transport.get('inventory')
2383
except StopIteration:
2384
raise errors.ConflictFormatError()
2385
return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2388
if self._control_files._lock_count == 1:
2389
# _inventory_is_modified is always False during a read lock.
2390
if self._inventory_is_modified:
2392
self._write_hashcache_if_dirty()
2393
# reverse order of locking.
2021
result = self._deserialize(f)
2395
return self._control_files.unlock()
2024
self._set_inventory(result, dirty=False)
2028
def get_root_id(self):
2029
"""Return the id of this trees root"""
2030
return self._inventory.root.file_id
2032
def has_id(self, file_id):
2033
# files that have been deleted are excluded
2034
inv = self.inventory
2035
if not inv.has_id(file_id):
2037
path = inv.id2path(file_id)
2038
return osutils.lexists(self.abspath(path))
2040
def has_or_had_id(self, file_id):
2041
if file_id == self.inventory.root.file_id:
2397
self.branch.unlock()
2400
def get_conflicted_stem(path):
2401
for suffix in _mod_conflicts.CONFLICT_SUFFIXES:
2402
if path.endswith(suffix):
2403
return path[:-len(suffix)]
2406
@deprecated_function(zero_eight)
2407
def is_control_file(filename):
2408
"""See WorkingTree.is_control_filename(filename)."""
2409
## FIXME: better check
2410
filename = normpath(filename)
2411
while filename != '':
2412
head, tail = os.path.split(filename)
2413
## mutter('check %r for control file' % ((head, tail),))
2043
return self.inventory.has_id(file_id)
2045
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2047
"""Iterate through file_ids for this tree.
2049
file_ids are in a WorkingTree if they are in the working inventory
2050
and the working file exists.
2052
inv = self._inventory
2053
for path, ie in inv.iter_entries():
2054
if osutils.lexists(self.abspath(path)):
2057
@needs_tree_write_lock
2058
def set_last_revision(self, new_revision):
2059
"""Change the last revision in the working tree."""
2060
if self._change_last_revision(new_revision):
2061
self._cache_basis_inventory(new_revision)
2063
def _get_check_refs(self):
2064
"""Return the references needed to perform a check of this tree.
2066
The default implementation returns no refs, and is only suitable for
2067
trees that have no local caching and can commit on ghosts at any time.
2069
:seealso: bzrlib.check for details about check_refs.
2074
def _check(self, references):
2075
"""Check the tree for consistency.
2077
:param references: A dict with keys matching the items returned by
2078
self._get_check_refs(), and values from looking those keys up in
2081
tree_basis = self.basis_tree()
2082
tree_basis.lock_read()
2084
repo_basis = references[('trees', self.last_revision())]
2085
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2086
raise errors.BzrCheckError(
2087
"Mismatched basis inventory content.")
2093
def check_state(self):
2094
"""Check that the working state is/isn't valid."""
2095
check_refs = self._get_check_refs()
2097
for ref in check_refs:
2100
refs[ref] = self.branch.repository.revision_tree(value)
2103
@needs_tree_write_lock
2104
def reset_state(self, revision_ids=None):
2105
"""Reset the state of the working tree.
2107
This does a hard-reset to a last-known-good state. This is a way to
2108
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2110
if revision_ids is None:
2111
revision_ids = self.get_parent_ids()
2112
if not revision_ids:
2113
rt = self.branch.repository.revision_tree(
2114
_mod_revision.NULL_REVISION)
2116
rt = self.branch.repository.revision_tree(revision_ids[0])
2117
self._write_inventory(rt.inventory)
2118
self.set_parent_ids(revision_ids)
2121
"""Write the in memory inventory to disk."""
2122
# TODO: Maybe this should only write on dirty ?
2123
if self._control_files._lock_mode != 'w':
2124
raise errors.NotWriteLocked(self)
2126
self._serialize(self._inventory, sio)
2128
self._transport.put_file('inventory', sio,
2129
mode=self.bzrdir._get_file_mode())
2130
self._inventory_is_modified = False
2132
def get_file_mtime(self, file_id, path=None):
2133
"""See Tree.get_file_mtime."""
2135
path = self.inventory.id2path(file_id)
2136
return os.lstat(self.abspath(path)).st_mtime
2138
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2139
file_id = self.path2id(path)
2141
# For unversioned files on win32, we just assume they are not
2144
return self._inventory[file_id].executable
2146
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2147
mode = stat_result.st_mode
2148
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2150
if not supports_executable():
2151
def is_executable(self, file_id, path=None):
2152
return self._inventory[file_id].executable
2154
_is_executable_from_path_and_stat = \
2155
_is_executable_from_path_and_stat_from_basis
2157
def is_executable(self, file_id, path=None):
2159
path = self.id2path(file_id)
2160
mode = os.lstat(self.abspath(path)).st_mode
2161
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2163
_is_executable_from_path_and_stat = \
2164
_is_executable_from_path_and_stat_from_stat
2166
@needs_tree_write_lock
2167
def _add(self, files, ids, kinds):
2168
"""See MutableTree._add."""
2169
# TODO: Re-adding a file that is removed in the working copy
2170
# should probably put it back with the previous ID.
2171
# the read and write working inventory should not occur in this
2172
# function - they should be part of lock_write and unlock.
2173
inv = self.inventory
2174
for f, file_id, kind in zip(files, ids, kinds):
2176
inv.add_path(f, kind=kind)
2178
inv.add_path(f, kind=kind, file_id=file_id)
2179
self._inventory_is_modified = True
2181
def revision_tree(self, revision_id):
2182
"""See WorkingTree.revision_id."""
2183
if revision_id == self.last_revision():
2185
xml = self.read_basis_inventory()
2186
except errors.NoSuchFile:
2190
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2191
# dont use the repository revision_tree api because we want
2192
# to supply the inventory.
2193
if inv.revision_id == revision_id:
2194
return revisiontree.InventoryRevisionTree(
2195
self.branch.repository, inv, revision_id)
2196
except errors.BadInventoryFormat:
2198
# raise if there was no inventory, or if we read the wrong inventory.
2199
raise errors.NoSuchRevisionInTree(self, revision_id)
2202
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2203
"""See Tree.annotate_iter
2205
This implementation will use the basis tree implementation if possible.
2206
Lines not in the basis are attributed to CURRENT_REVISION
2208
If there are pending merges, lines added by those merges will be
2209
incorrectly attributed to CURRENT_REVISION (but after committing, the
2210
attribution will be correct).
2212
maybe_file_parent_keys = []
2213
for parent_id in self.get_parent_ids():
2215
parent_tree = self.revision_tree(parent_id)
2216
except errors.NoSuchRevisionInTree:
2217
parent_tree = self.branch.repository.revision_tree(parent_id)
2218
parent_tree.lock_read()
2220
if not parent_tree.has_id(file_id):
2222
ie = parent_tree.inventory[file_id]
2223
if ie.kind != 'file':
2224
# Note: this is slightly unnecessary, because symlinks and
2225
# directories have a "text" which is the empty text, and we
2226
# know that won't mess up annotations. But it seems cleaner
2228
parent_text_key = (file_id, ie.revision)
2229
if parent_text_key not in maybe_file_parent_keys:
2230
maybe_file_parent_keys.append(parent_text_key)
2232
parent_tree.unlock()
2233
graph = _mod_graph.Graph(self.branch.repository.texts)
2234
heads = graph.heads(maybe_file_parent_keys)
2235
file_parent_keys = []
2236
for key in maybe_file_parent_keys:
2238
file_parent_keys.append(key)
2240
# Now we have the parents of this content
2241
annotator = self.branch.repository.texts.get_annotator()
2242
text = self.get_file_text(file_id)
2243
this_key =(file_id, default_revision)
2244
annotator.add_special_text(this_key, file_parent_keys, text)
2245
annotations = [(key[-1], line)
2246
for key, line in annotator.annotate_flat(this_key)]
2250
def merge_modified(self):
2251
"""Return a dictionary of files modified by a merge.
2253
The list is initialized by WorkingTree.set_merge_modified, which is
2254
typically called after we make some automatic updates to the tree
2257
This returns a map of file_id->sha1, containing only files which are
2258
still in the working inventory and have that text hash.
2261
hashfile = self._transport.get('merge-hashes')
2262
except errors.NoSuchFile:
2267
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2268
raise errors.MergeModifiedFormatError()
2269
except StopIteration:
2270
raise errors.MergeModifiedFormatError()
2271
for s in _mod_rio.RioReader(hashfile):
2272
# RioReader reads in Unicode, so convert file_ids back to utf8
2273
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2274
if not self.inventory.has_id(file_id):
2276
text_hash = s.get("hash")
2277
if text_hash == self.get_file_sha1(file_id):
2278
merge_hashes[file_id] = text_hash
2284
def subsume(self, other_tree):
2285
def add_children(inventory, entry):
2286
for child_entry in entry.children.values():
2287
inventory._byid[child_entry.file_id] = child_entry
2288
if child_entry.kind == 'directory':
2289
add_children(inventory, child_entry)
2290
if other_tree.get_root_id() == self.get_root_id():
2291
raise errors.BadSubsumeSource(self, other_tree,
2292
'Trees have the same root')
2294
other_tree_path = self.relpath(other_tree.basedir)
2295
except errors.PathNotChild:
2296
raise errors.BadSubsumeSource(self, other_tree,
2297
'Tree is not contained by the other')
2298
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2299
if new_root_parent is None:
2300
raise errors.BadSubsumeSource(self, other_tree,
2301
'Parent directory is not versioned.')
2302
# We need to ensure that the result of a fetch will have a
2303
# versionedfile for the other_tree root, and only fetching into
2304
# RepositoryKnit2 guarantees that.
2305
if not self.branch.repository.supports_rich_root():
2306
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2307
other_tree.lock_tree_write()
2309
new_parents = other_tree.get_parent_ids()
2310
other_root = other_tree.inventory.root
2311
other_root.parent_id = new_root_parent
2312
other_root.name = osutils.basename(other_tree_path)
2313
self.inventory.add(other_root)
2314
add_children(self.inventory, other_root)
2315
self._write_inventory(self.inventory)
2316
# normally we don't want to fetch whole repositories, but i think
2317
# here we really do want to consolidate the whole thing.
2318
for parent_id in other_tree.get_parent_ids():
2319
self.branch.fetch(other_tree.branch, parent_id)
2320
self.add_parent_tree_id(parent_id)
2323
other_tree.bzrdir.retire_bzrdir()
2325
@needs_tree_write_lock
2326
def extract(self, file_id, format=None):
2327
"""Extract a subtree from this tree.
2329
A new branch will be created, relative to the path for this tree.
2333
segments = osutils.splitpath(path)
2334
transport = self.branch.bzrdir.root_transport
2335
for name in segments:
2336
transport = transport.clone(name)
2337
transport.ensure_base()
2340
sub_path = self.id2path(file_id)
2341
branch_transport = mkdirs(sub_path)
2343
format = self.bzrdir.cloning_metadir()
2344
branch_transport.ensure_base()
2345
branch_bzrdir = format.initialize_on_transport(branch_transport)
2347
repo = branch_bzrdir.find_repository()
2348
except errors.NoRepositoryPresent:
2349
repo = branch_bzrdir.create_repository()
2350
if not repo.supports_rich_root():
2351
raise errors.RootNotRich()
2352
new_branch = branch_bzrdir.create_branch()
2353
new_branch.pull(self.branch)
2354
for parent_id in self.get_parent_ids():
2355
new_branch.fetch(self.branch, parent_id)
2356
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2357
if tree_transport.base != branch_transport.base:
2358
tree_bzrdir = format.initialize_on_transport(tree_transport)
2359
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2360
target_branch=new_branch)
2362
tree_bzrdir = branch_bzrdir
2363
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2364
wt.set_parent_ids(self.get_parent_ids())
2365
my_inv = self.inventory
2366
child_inv = inventory.Inventory(root_id=None)
2367
new_root = my_inv[file_id]
2368
my_inv.remove_recursive_id(file_id)
2369
new_root.parent_id = None
2370
child_inv.add(new_root)
2371
self._write_inventory(my_inv)
2372
wt._write_inventory(child_inv)
2375
def list_files(self, include_root=False, from_dir=None, recursive=True):
2376
"""List all files as (path, class, kind, id, entry).
2378
Lists, but does not descend into unversioned directories.
2379
This does not include files that have been deleted in this
2380
tree. Skips the control directory.
2382
:param include_root: if True, return an entry for the root
2383
:param from_dir: start from this directory or None for the root
2384
:param recursive: whether to recurse into subdirectories or not
2386
# list_files is an iterator, so @needs_read_lock doesn't work properly
2387
# with it. So callers should be careful to always read_lock the tree.
2388
if not self.is_locked():
2389
raise errors.ObjectNotLocked(self)
2391
inv = self.inventory
2392
if from_dir is None and include_root is True:
2393
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2394
# Convert these into local objects to save lookup times
2395
pathjoin = osutils.pathjoin
2396
file_kind = self._kind
2398
# transport.base ends in a slash, we want the piece
2399
# between the last two slashes
2400
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2402
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2404
# directory file_id, relative path, absolute path, reverse sorted children
2405
if from_dir is not None:
2406
from_dir_id = inv.path2id(from_dir)
2407
if from_dir_id is None:
2408
# Directory not versioned
2410
from_dir_abspath = pathjoin(self.basedir, from_dir)
2412
from_dir_id = inv.root.file_id
2413
from_dir_abspath = self.basedir
2414
children = os.listdir(from_dir_abspath)
2416
# jam 20060527 The kernel sized tree seems equivalent whether we
2417
# use a deque and popleft to keep them sorted, or if we use a plain
2418
# list and just reverse() them.
2419
children = collections.deque(children)
2420
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2422
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2425
f = children.popleft()
2426
## TODO: If we find a subdirectory with its own .bzr
2427
## directory, then that is a separate tree and we
2428
## should exclude it.
2430
# the bzrdir for this tree
2431
if transport_base_dir == f:
2434
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2435
# and 'f' doesn't begin with one, we can do a string op, rather
2436
# than the checks of pathjoin(), all relative paths will have an extra slash
2438
fp = from_dir_relpath + '/' + f
2441
fap = from_dir_abspath + '/' + f
2443
dir_ie = inv[from_dir_id]
2444
if dir_ie.kind == 'directory':
2445
f_ie = dir_ie.children.get(f)
2450
elif self.is_ignored(fp[1:]):
2453
# we may not have found this file, because of a unicode
2454
# issue, or because the directory was actually a symlink.
2455
f_norm, can_access = osutils.normalized_filename(f)
2456
if f == f_norm or not can_access:
2457
# No change, so treat this file normally
2460
# this file can be accessed by a normalized path
2461
# check again if it is versioned
2462
# these lines are repeated here for performance
2464
fp = from_dir_relpath + '/' + f
2465
fap = from_dir_abspath + '/' + f
2466
f_ie = inv.get_child(from_dir_id, f)
2469
elif self.is_ignored(fp[1:]):
2476
# make a last minute entry
2478
yield fp[1:], c, fk, f_ie.file_id, f_ie
2481
yield fp[1:], c, fk, None, fk_entries[fk]()
2483
yield fp[1:], c, fk, None, TreeEntry()
2486
if fk != 'directory':
2489
# But do this child first if recursing down
2491
new_children = os.listdir(fap)
2493
new_children = collections.deque(new_children)
2494
stack.append((f_ie.file_id, fp, fap, new_children))
2495
# Break out of inner loop,
2496
# so that we start outer loop with child
2499
# if we finished all children, pop it off the stack
2502
@needs_tree_write_lock
2503
def move(self, from_paths, to_dir=None, after=False):
2506
to_dir must exist in the inventory.
2508
If to_dir exists and is a directory, the files are moved into
2509
it, keeping their old names.
2511
Note that to_dir is only the last component of the new name;
2512
this doesn't change the directory.
2514
For each entry in from_paths the move mode will be determined
2517
The first mode moves the file in the filesystem and updates the
2518
inventory. The second mode only updates the inventory without
2519
touching the file on the filesystem.
2521
move uses the second mode if 'after == True' and the target is
2522
either not versioned or newly added, and present in the working tree.
2524
move uses the second mode if 'after == False' and the source is
2525
versioned but no longer in the working tree, and the target is not
2526
versioned but present in the working tree.
2528
move uses the first mode if 'after == False' and the source is
2529
versioned and present in the working tree, and the target is not
2530
versioned and not present in the working tree.
2532
Everything else results in an error.
2534
This returns a list of (from_path, to_path) pairs for each
2535
entry that is moved.
2540
# check for deprecated use of signature
2542
raise TypeError('You must supply a target directory')
2543
# check destination directory
2544
if isinstance(from_paths, basestring):
2546
inv = self.inventory
2547
to_abs = self.abspath(to_dir)
2548
if not isdir(to_abs):
2549
raise errors.BzrMoveFailedError('',to_dir,
2550
errors.NotADirectory(to_abs))
2551
if not self.has_filename(to_dir):
2552
raise errors.BzrMoveFailedError('',to_dir,
2553
errors.NotInWorkingDirectory(to_dir))
2554
to_dir_id = inv.path2id(to_dir)
2555
if to_dir_id is None:
2556
raise errors.BzrMoveFailedError('',to_dir,
2557
errors.NotVersionedError(path=to_dir))
2559
to_dir_ie = inv[to_dir_id]
2560
if to_dir_ie.kind != 'directory':
2561
raise errors.BzrMoveFailedError('',to_dir,
2562
errors.NotADirectory(to_abs))
2564
# create rename entries and tuples
2565
for from_rel in from_paths:
2566
from_tail = splitpath(from_rel)[-1]
2567
from_id = inv.path2id(from_rel)
2569
raise errors.BzrMoveFailedError(from_rel,to_dir,
2570
errors.NotVersionedError(path=from_rel))
2572
from_entry = inv[from_id]
2573
from_parent_id = from_entry.parent_id
2574
to_rel = pathjoin(to_dir, from_tail)
2575
rename_entry = InventoryWorkingTree._RenameEntry(
2578
from_tail=from_tail,
2579
from_parent_id=from_parent_id,
2580
to_rel=to_rel, to_tail=from_tail,
2581
to_parent_id=to_dir_id)
2582
rename_entries.append(rename_entry)
2583
rename_tuples.append((from_rel, to_rel))
2585
# determine which move mode to use. checks also for movability
2586
rename_entries = self._determine_mv_mode(rename_entries, after)
2588
original_modified = self._inventory_is_modified
2591
self._inventory_is_modified = True
2592
self._move(rename_entries)
2594
# restore the inventory on error
2595
self._inventory_is_modified = original_modified
2597
self._write_inventory(inv)
2598
return rename_tuples
2600
@needs_tree_write_lock
2601
def rename_one(self, from_rel, to_rel, after=False):
2604
This can change the directory or the filename or both.
2606
rename_one has several 'modes' to work. First, it can rename a physical
2607
file and change the file_id. That is the normal mode. Second, it can
2608
only change the file_id without touching any physical file.
2610
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2611
versioned but present in the working tree.
2613
rename_one uses the second mode if 'after == False' and 'from_rel' is
2614
versioned but no longer in the working tree, and 'to_rel' is not
2615
versioned but present in the working tree.
2617
rename_one uses the first mode if 'after == False' and 'from_rel' is
2618
versioned and present in the working tree, and 'to_rel' is not
2619
versioned and not present in the working tree.
2621
Everything else results in an error.
2623
inv = self.inventory
2626
# create rename entries and tuples
2627
from_tail = splitpath(from_rel)[-1]
2628
from_id = inv.path2id(from_rel)
2630
# if file is missing in the inventory maybe it's in the basis_tree
2631
basis_tree = self.branch.basis_tree()
2632
from_id = basis_tree.path2id(from_rel)
2634
raise errors.BzrRenameFailedError(from_rel,to_rel,
2635
errors.NotVersionedError(path=from_rel))
2636
# put entry back in the inventory so we can rename it
2637
from_entry = basis_tree.inventory[from_id].copy()
2640
from_entry = inv[from_id]
2641
from_parent_id = from_entry.parent_id
2642
to_dir, to_tail = os.path.split(to_rel)
2643
to_dir_id = inv.path2id(to_dir)
2644
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2646
from_tail=from_tail,
2647
from_parent_id=from_parent_id,
2648
to_rel=to_rel, to_tail=to_tail,
2649
to_parent_id=to_dir_id)
2650
rename_entries.append(rename_entry)
2652
# determine which move mode to use. checks also for movability
2653
rename_entries = self._determine_mv_mode(rename_entries, after)
2655
# check if the target changed directory and if the target directory is
2657
if to_dir_id is None:
2658
raise errors.BzrMoveFailedError(from_rel,to_rel,
2659
errors.NotVersionedError(path=to_dir))
2661
# all checks done. now we can continue with our actual work
2662
mutter('rename_one:\n'
2667
' to_dir_id {%s}\n',
2668
from_id, from_rel, to_rel, to_dir, to_dir_id)
2670
self._move(rename_entries)
2671
self._write_inventory(inv)
2673
class _RenameEntry(object):
2674
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2675
to_rel, to_tail, to_parent_id, only_change_inv=False,
2677
self.from_rel = from_rel
2678
self.from_id = from_id
2679
self.from_tail = from_tail
2680
self.from_parent_id = from_parent_id
2681
self.to_rel = to_rel
2682
self.to_tail = to_tail
2683
self.to_parent_id = to_parent_id
2684
self.change_id = change_id
2685
self.only_change_inv = only_change_inv
2687
def _determine_mv_mode(self, rename_entries, after=False):
2688
"""Determines for each from-to pair if both inventory and working tree
2689
or only the inventory has to be changed.
2691
Also does basic plausability tests.
2693
inv = self.inventory
2695
for rename_entry in rename_entries:
2696
# store to local variables for easier reference
2697
from_rel = rename_entry.from_rel
2698
from_id = rename_entry.from_id
2699
to_rel = rename_entry.to_rel
2700
to_id = inv.path2id(to_rel)
2701
only_change_inv = False
2704
# check the inventory for source and destination
2706
raise errors.BzrMoveFailedError(from_rel,to_rel,
2707
errors.NotVersionedError(path=from_rel))
2708
if to_id is not None:
2710
# allow it with --after but only if dest is newly added
2712
basis = self.basis_tree()
2715
if not basis.has_id(to_id):
2716
rename_entry.change_id = True
2721
raise errors.BzrMoveFailedError(from_rel,to_rel,
2722
errors.AlreadyVersionedError(path=to_rel))
2724
# try to determine the mode for rename (only change inv or change
2725
# inv and file system)
2727
if not self.has_filename(to_rel):
2728
raise errors.BzrMoveFailedError(from_id,to_rel,
2729
errors.NoSuchFile(path=to_rel,
2730
extra="New file has not been created yet"))
2731
only_change_inv = True
2732
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2733
only_change_inv = True
2734
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2735
only_change_inv = False
2736
elif (not self.case_sensitive
2737
and from_rel.lower() == to_rel.lower()
2738
and self.has_filename(from_rel)):
2739
only_change_inv = False
2741
# something is wrong, so lets determine what exactly
2742
if not self.has_filename(from_rel) and \
2743
not self.has_filename(to_rel):
2744
raise errors.BzrRenameFailedError(from_rel,to_rel,
2745
errors.PathsDoNotExist(paths=(str(from_rel),
2748
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2749
rename_entry.only_change_inv = only_change_inv
2750
return rename_entries
2752
def _move(self, rename_entries):
2753
"""Moves a list of files.
2755
Depending on the value of the flag 'only_change_inv', the
2756
file will be moved on the file system or not.
2758
inv = self.inventory
2761
for entry in rename_entries:
2763
self._move_entry(entry)
2765
self._rollback_move(moved)
2769
def _rollback_move(self, moved):
2770
"""Try to rollback a previous move in case of an filesystem error."""
2771
inv = self.inventory
2774
self._move_entry(WorkingTree._RenameEntry(
2775
entry.to_rel, entry.from_id,
2776
entry.to_tail, entry.to_parent_id, entry.from_rel,
2777
entry.from_tail, entry.from_parent_id,
2778
entry.only_change_inv))
2779
except errors.BzrMoveFailedError, e:
2780
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2781
" The working tree is in an inconsistent state."
2782
" Please consider doing a 'bzr revert'."
2783
" Error message is: %s" % e)
2785
def _move_entry(self, entry):
2786
inv = self.inventory
2787
from_rel_abs = self.abspath(entry.from_rel)
2788
to_rel_abs = self.abspath(entry.to_rel)
2789
if from_rel_abs == to_rel_abs:
2790
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2791
"Source and target are identical.")
2793
if not entry.only_change_inv:
2795
osutils.rename(from_rel_abs, to_rel_abs)
2797
raise errors.BzrMoveFailedError(entry.from_rel,
2800
to_id = inv.path2id(entry.to_rel)
2801
inv.remove_recursive_id(to_id)
2802
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2804
@needs_tree_write_lock
2805
def unversion(self, file_ids):
2806
"""Remove the file ids in file_ids from the current versioned set.
2808
When a file_id is unversioned, all of its children are automatically
2811
:param file_ids: The file ids to stop versioning.
2812
:raises: NoSuchId if any fileid is not currently versioned.
2814
for file_id in file_ids:
2815
if not self._inventory.has_id(file_id):
2816
raise errors.NoSuchId(self, file_id)
2817
for file_id in file_ids:
2818
if self._inventory.has_id(file_id):
2819
self._inventory.remove_recursive_id(file_id)
2821
# in the future this should just set a dirty bit to wait for the
2822
# final unlock. However, until all methods of workingtree start
2823
# with the current in -memory inventory rather than triggering
2824
# a read, it is more complex - we need to teach read_inventory
2825
# to know when to read, and when to not read first... and possibly
2826
# to save first when the in memory one may be corrupted.
2827
# so for now, we just only write it if it is indeed dirty.
2829
self._write_inventory(self._inventory)
2831
def stored_kind(self, file_id):
2832
"""See Tree.stored_kind"""
2833
return self.inventory[file_id].kind
2836
"""Yield all unversioned files in this WorkingTree.
2838
If there are any unversioned directories then only the directory is
2839
returned, not all its children. But if there are unversioned files
2840
under a versioned subdirectory, they are returned.
2842
Currently returned depth-first, sorted by name within directories.
2843
This is the same order used by 'osutils.walkdirs'.
2845
## TODO: Work from given directory downwards
2846
for path, dir_entry in self.inventory.directories():
2847
# mutter("search for unknowns in %r", path)
2848
dirabs = self.abspath(path)
2849
if not isdir(dirabs):
2850
# e.g. directory deleted
2854
for subf in os.listdir(dirabs):
2855
if self.bzrdir.is_control_filename(subf):
2857
if subf not in dir_entry.children:
2860
can_access) = osutils.normalized_filename(subf)
2861
except UnicodeDecodeError:
2862
path_os_enc = path.encode(osutils._fs_enc)
2863
relpath = path_os_enc + '/' + subf
2864
raise errors.BadFilenameEncoding(relpath,
2866
if subf_norm != subf and can_access:
2867
if subf_norm not in dir_entry.children:
2868
fl.append(subf_norm)
2874
subp = pathjoin(path, subf)
2877
def _walkdirs(self, prefix=""):
2878
"""Walk the directories of this tree.
2880
:param prefix: is used as the directrory to start with.
2881
:returns: a generator which yields items in the form::
2883
((curren_directory_path, fileid),
2884
[(file1_path, file1_name, file1_kind, None, file1_id,
2887
_directory = 'directory'
2888
# get the root in the inventory
2889
inv = self.inventory
2890
top_id = inv.path2id(prefix)
2894
pending = [(prefix, '', _directory, None, top_id, None)]
2897
currentdir = pending.pop()
2898
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2899
top_id = currentdir[4]
2901
relroot = currentdir[0] + '/'
2904
# FIXME: stash the node in pending
2906
if entry.kind == 'directory':
2907
for name, child in entry.sorted_children():
2908
dirblock.append((relroot + name, name, child.kind, None,
2909
child.file_id, child.kind
2911
yield (currentdir[0], entry.file_id), dirblock
2912
# push the user specified dirs from dirblock
2913
for dir in reversed(dirblock):
2914
if dir[2] == _directory:
2918
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2919
"""Registry for working tree formats."""
2921
def __init__(self, other_registry=None):
2922
super(WorkingTreeFormatRegistry, self).__init__(other_registry)
2923
self._default_format = None
2924
self._default_format_key = None
2926
def get_default(self):
2927
"""Return the current default format."""
2928
if (self._default_format_key is not None and
2929
self._default_format is None):
2930
self._default_format = self.get(self._default_format_key)
2931
return self._default_format
2933
def set_default(self, format):
2934
"""Set the default format."""
2935
self._default_format = format
2936
self._default_format_key = None
2938
def set_default_key(self, format_string):
2939
"""Set the default format by its format string."""
2940
self._default_format_key = format_string
2941
self._default_format = None
2944
format_registry = WorkingTreeFormatRegistry()
2947
class WorkingTreeFormat(controldir.ControlComponentFormat):
2416
if filename == head:
2422
class WorkingTreeFormat(object):
2948
2423
"""An encapsulation of the initialization and open routines for a format.
2950
2425
Formats provide three things:
3036
2482
"""Is this format supported?
3038
2484
Supported formats can be initialized and opened.
3039
Unsupported formats may not support initialization or committing or
2485
Unsupported formats may not support initialization or committing or
3040
2486
some other features depending on the reason for not being supported.
3044
def supports_content_filtering(self):
3045
"""True if this format supports content filtering."""
3048
def supports_views(self):
3049
"""True if this format supports stored views."""
3053
@symbol_versioning.deprecated_method(
3054
symbol_versioning.deprecated_in((2, 4, 0)))
3055
2491
def register_format(klass, format):
3056
format_registry.register(format)
3059
@symbol_versioning.deprecated_method(
3060
symbol_versioning.deprecated_in((2, 4, 0)))
3061
def register_extra_format(klass, format):
3062
format_registry.register_extra(format)
3065
@symbol_versioning.deprecated_method(
3066
symbol_versioning.deprecated_in((2, 4, 0)))
3067
def unregister_extra_format(klass, format):
3068
format_registry.unregister_extra(format)
3071
@symbol_versioning.deprecated_method(
3072
symbol_versioning.deprecated_in((2, 4, 0)))
3073
def get_formats(klass):
3074
return format_registry._get_all()
3077
@symbol_versioning.deprecated_method(
3078
symbol_versioning.deprecated_in((2, 4, 0)))
2492
klass._formats[format.get_format_string()] = format
3079
2495
def set_default_format(klass, format):
3080
format_registry.set_default(format)
2496
klass._default_format = format
3083
@symbol_versioning.deprecated_method(
3084
symbol_versioning.deprecated_in((2, 4, 0)))
3085
2499
def unregister_format(klass, format):
3086
format_registry.remove(format)
3089
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3090
"bzrlib.workingtree_4", "WorkingTreeFormat4")
3091
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3092
"bzrlib.workingtree_4", "WorkingTreeFormat5")
3093
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3094
"bzrlib.workingtree_4", "WorkingTreeFormat6")
3095
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
3096
"bzrlib.workingtree_3", "WorkingTreeFormat3")
3097
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")
2500
assert klass._formats[format.get_format_string()] is format
2501
del klass._formats[format.get_format_string()]
2504
class WorkingTreeFormat2(WorkingTreeFormat):
2505
"""The second working tree format.
2507
This format modified the hash cache from the format 1 hash cache.
2510
upgrade_recommended = True
2512
def get_format_description(self):
2513
"""See WorkingTreeFormat.get_format_description()."""
2514
return "Working tree format 2"
2516
def stub_initialize_remote(self, control_files):
2517
"""As a special workaround create critical control files for a remote working tree
2519
This ensures that it can later be updated and dealt with locally,
2520
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2521
no working tree. (See bug #43064).
2525
xml5.serializer_v5.write_inventory(inv, sio)
2527
control_files.put('inventory', sio)
2529
control_files.put_bytes('pending-merges', '')
2532
def initialize(self, a_bzrdir, revision_id=None):
2533
"""See WorkingTreeFormat.initialize()."""
2534
if not isinstance(a_bzrdir.transport, LocalTransport):
2535
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2536
branch = a_bzrdir.open_branch()
2537
if revision_id is not None:
2538
revision_id = osutils.safe_revision_id(revision_id)
2541
revision_history = branch.revision_history()
2543
position = revision_history.index(revision_id)
2545
raise errors.NoSuchRevision(branch, revision_id)
2546
branch.set_revision_history(revision_history[:position + 1])
2549
revision = branch.last_revision()
2551
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2557
basis_tree = branch.repository.revision_tree(revision)
2558
if basis_tree.inventory.root is not None:
2559
wt.set_root_id(basis_tree.inventory.root.file_id)
2560
# set the parent list and cache the basis tree.
2561
wt.set_parent_trees([(revision, basis_tree)])
2562
transform.build_tree(basis_tree, wt)
2566
super(WorkingTreeFormat2, self).__init__()
2567
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2569
def open(self, a_bzrdir, _found=False):
2570
"""Return the WorkingTree object for a_bzrdir
2572
_found is a private parameter, do not use it. It is used to indicate
2573
if format probing has already been done.
2576
# we are being called directly and must probe.
2577
raise NotImplementedError
2578
if not isinstance(a_bzrdir.transport, LocalTransport):
2579
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2580
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2586
class WorkingTreeFormat3(WorkingTreeFormat):
2587
"""The second working tree format updated to record a format marker.
2590
- exists within a metadir controlling .bzr
2591
- includes an explicit version marker for the workingtree control
2592
files, separate from the BzrDir format
2593
- modifies the hash cache format
2595
- uses a LockDir to guard access for writes.
2598
upgrade_recommended = True
2600
def get_format_string(self):
2601
"""See WorkingTreeFormat.get_format_string()."""
2602
return "Bazaar-NG Working Tree format 3"
2604
def get_format_description(self):
2605
"""See WorkingTreeFormat.get_format_description()."""
2606
return "Working tree format 3"
2608
_lock_file_name = 'lock'
2609
_lock_class = LockDir
2611
_tree_class = WorkingTree3
2613
def __get_matchingbzrdir(self):
2614
return bzrdir.BzrDirMetaFormat1()
2616
_matchingbzrdir = property(__get_matchingbzrdir)
2618
def _open_control_files(self, a_bzrdir):
2619
transport = a_bzrdir.get_workingtree_transport(None)
2620
return LockableFiles(transport, self._lock_file_name,
2623
def initialize(self, a_bzrdir, revision_id=None):
2624
"""See WorkingTreeFormat.initialize().
2626
revision_id allows creating a working tree at a different
2627
revision than the branch is at.
2629
if not isinstance(a_bzrdir.transport, LocalTransport):
2630
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2631
transport = a_bzrdir.get_workingtree_transport(self)
2632
control_files = self._open_control_files(a_bzrdir)
2633
control_files.create_lock()
2634
control_files.lock_write()
2635
control_files.put_utf8('format', self.get_format_string())
2636
branch = a_bzrdir.open_branch()
2637
if revision_id is None:
2638
revision_id = branch.last_revision()
2640
revision_id = osutils.safe_revision_id(revision_id)
2641
# WorkingTree3 can handle an inventory which has a unique root id.
2642
# as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
2643
# those trees. And because there isn't a format bump inbetween, we
2644
# are maintaining compatibility with older clients.
2645
# inv = Inventory(root_id=gen_root_id())
2646
inv = self._initial_inventory()
2647
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2653
_control_files=control_files)
2654
wt.lock_tree_write()
2656
basis_tree = branch.repository.revision_tree(revision_id)
2657
# only set an explicit root id if there is one to set.
2658
if basis_tree.inventory.root is not None:
2659
wt.set_root_id(basis_tree.inventory.root.file_id)
2660
if revision_id == NULL_REVISION:
2661
wt.set_parent_trees([])
2663
wt.set_parent_trees([(revision_id, basis_tree)])
2664
transform.build_tree(basis_tree, wt)
2666
# Unlock in this order so that the unlock-triggers-flush in
2667
# WorkingTree is given a chance to fire.
2668
control_files.unlock()
2672
def _initial_inventory(self):
2676
super(WorkingTreeFormat3, self).__init__()
2678
def open(self, a_bzrdir, _found=False):
2679
"""Return the WorkingTree object for a_bzrdir
2681
_found is a private parameter, do not use it. It is used to indicate
2682
if format probing has already been done.
2685
# we are being called directly and must probe.
2686
raise NotImplementedError
2687
if not isinstance(a_bzrdir.transport, LocalTransport):
2688
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2689
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
2692
def _open(self, a_bzrdir, control_files):
2693
"""Open the tree itself.
2695
:param a_bzrdir: the dir for the tree.
2696
:param control_files: the control files for the tree.
2698
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2702
_control_files=control_files)
2705
return self.get_format_string()
2708
__default_format = WorkingTreeFormat4()
2709
WorkingTreeFormat.register_format(__default_format)
2710
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2711
WorkingTreeFormat.set_default_format(__default_format)
2712
# formats which have no format string are not discoverable
2713
# and not independently creatable, so are not registered.
2714
_legacy_formats = [WorkingTreeFormat2(),
2718
class WorkingTreeTestProviderAdapter(object):
2719
"""A tool to generate a suite testing multiple workingtree formats at once.
2721
This is done by copying the test once for each transport and injecting
2722
the transport_server, transport_readonly_server, and workingtree_format
2723
classes into each copy. Each copy is also given a new id() to make it
2727
def __init__(self, transport_server, transport_readonly_server, formats):
2728
self._transport_server = transport_server
2729
self._transport_readonly_server = transport_readonly_server
2730
self._formats = formats
2732
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2733
"""Clone test for adaption."""
2734
new_test = deepcopy(test)
2735
new_test.transport_server = self._transport_server
2736
new_test.transport_readonly_server = self._transport_readonly_server
2737
new_test.bzrdir_format = bzrdir_format
2738
new_test.workingtree_format = workingtree_format
2739
def make_new_test_id():
2740
new_id = "%s(%s)" % (test.id(), variation)
2741
return lambda: new_id
2742
new_test.id = make_new_test_id()
2745
def adapt(self, test):
2746
from bzrlib.tests import TestSuite
2747
result = TestSuite()
2748
for workingtree_format, bzrdir_format in self._formats:
2749
new_test = self._clone_test(
2752
workingtree_format, workingtree_format.__class__.__name__)
2753
result.addTest(new_test)