132
132
self._format = _format
133
133
self.bzrdir = _bzrdir
134
assert isinstance(basedir, basestring), \
135
"base directory %r is not a string" % basedir
136
134
basedir = safe_unicode(basedir)
137
135
mutter("opening working tree %r", basedir)
138
136
self._branch = branch
139
assert isinstance(self.branch, bzrlib.branch.Branch), \
140
"branch %r is not a Branch" % self.branch
141
137
self.basedir = realpath(basedir)
142
138
# if branch is at our basedir and is a format 6 or less
143
139
# assume all other formats have their own control files.
144
assert isinstance(_control_files, LockableFiles), \
145
"_control_files must be a LockableFiles, not %r" % _control_files
146
140
self._control_files = _control_files
141
self._transport = self._control_files._transport
147
142
self._dirty = None
149
144
# during a read or write lock these objects are set, and are
317
312
state._read_dirblocks_if_needed()
318
313
root_key, current_entry = self._get_entry(path='')
319
314
current_id = root_key[2]
320
assert current_entry[0][0] == 'd' # directory
315
if not (current_entry[0][0] == 'd'): # directory
316
raise AssertionError(current_entry)
321
317
inv = Inventory(root_id=current_id)
322
318
# Turn some things into local variables
323
319
minikind_to_kind = dirstate.DirState._minikind_to_kind
356
352
# add this entry to the parent map.
357
353
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
358
354
elif kind == 'tree-reference':
359
assert self._repo_supports_tree_reference, \
360
"repository of %r " \
361
"doesn't support tree references " \
362
"required by entry %r" \
355
if not self._repo_supports_tree_reference:
356
raise AssertionError(
358
"doesn't support tree references "
359
"required by entry %r"
364
361
inv_entry.reference_revision = link_or_sha1 or None
365
362
elif kind != 'symlink':
366
363
raise AssertionError("unknown kind %r" % kind)
367
364
# These checks cost us around 40ms on a 55k entry tree
368
assert file_id not in inv_byid, ('file_id %s already in'
369
' inventory as %s' % (file_id, inv_byid[file_id]))
370
assert name_unicode not in parent_ie.children
365
if file_id in inv_byid:
366
raise AssertionError('file_id %s already in'
367
' inventory as %s' % (file_id, inv_byid[file_id]))
368
if name_unicode in parent_ie.children:
369
raise AssertionError('name %r already in parent'
371
371
inv_byid[file_id] = inv_entry
372
372
parent_ie.children[name_unicode] = inv_entry
373
373
self._inventory = inv
500
500
mode = os.lstat(self.abspath(path)).st_mode
501
501
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
503
def all_file_ids(self):
504
"""See Tree.iter_all_file_ids"""
505
self._must_be_locked()
507
for key, tree_details in self.current_dirstate()._iter_entries():
508
if tree_details[0][0] in ('a', 'r'): # relocated
504
514
def __iter__(self):
505
515
"""Iterate through file_ids for this tree.
542
552
Note: The caller is expected to take a read-lock before calling this.
544
554
relpath = self.id2path(file_id)
545
assert relpath != None, \
546
"path for id {%s} is None!" % file_id
556
raise AssertionError(
557
"path for id {%s} is None!" % file_id)
547
558
return self._kind(relpath)
549
560
def _kind(self, relpath):
622
633
if not from_paths:
625
635
state = self.current_dirstate()
627
assert not isinstance(from_paths, basestring)
636
if isinstance(from_paths, basestring):
628
638
to_dir_utf8 = to_dir.encode('utf8')
629
639
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
630
640
id_index = state._get_id_index()
785
795
if minikind == 'd':
786
796
def update_dirblock(from_dir, to_key, to_dir_utf8):
787
797
"""Recursively update all entries in this dirblock."""
788
assert from_dir != '', "renaming root not supported"
799
raise AssertionError("renaming root not supported")
789
800
from_key = (from_dir, '')
790
801
from_block_idx, present = \
791
802
state._find_block_index_from_key(from_key)
805
816
# Grab a copy since move_one may update the list.
806
817
for entry in from_block[1][:]:
807
assert entry[0][0] == from_dir
818
if not (entry[0][0] == from_dir):
819
raise AssertionError()
808
820
cur_details = entry[1][0]
809
821
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
810
822
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1023
1035
"""Change the last revision in the working tree."""
1024
1036
parents = self.get_parent_ids()
1025
1037
if new_revision in (NULL_REVISION, None):
1026
assert len(parents) < 2, (
1027
"setting the last parent to none with a pending merge is "
1038
if len(parents) >= 2:
1039
raise AssertionError(
1040
"setting the last parent to none with a pending merge is "
1029
1042
self.set_parent_ids([])
1031
1044
self.set_parent_ids([new_revision] + parents[1:],
1072
1085
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1073
1086
real_trees = []
1089
parent_ids = [rev_id for rev_id, tree in parents_list]
1090
graph = self.branch.repository.get_graph()
1091
heads = graph.heads(parent_ids)
1092
accepted_revisions = set()
1075
1094
# convert absent trees to the null tree, which we convert back to
1076
1095
# missing on access.
1077
1096
for rev_id, tree in parents_list:
1097
if len(accepted_revisions) > 0:
1098
# we always accept the first tree
1099
if rev_id in accepted_revisions or rev_id not in heads:
1100
# We have already included either this tree, or its
1101
# descendent, so we skip it.
1078
1103
_mod_revision.check_not_reserved_id(rev_id)
1079
1104
if tree is not None:
1080
1105
real_trees.append((rev_id, tree))
1082
1107
real_trees.append((rev_id,
1083
1108
self.branch.repository.revision_tree(None)))
1084
1109
ghosts.append(rev_id)
1110
accepted_revisions.add(rev_id)
1085
1111
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1086
1112
self._make_dirty(reset_inventory=False)
1215
1241
for file_id in file_ids:
1216
1242
self._inventory.remove_recursive_id(file_id)
1244
@needs_tree_write_lock
1245
def rename_one(self, from_rel, to_rel, after=False):
1246
"""See WorkingTree.rename_one"""
1248
WorkingTree.rename_one(self, from_rel, to_rel, after)
1250
@needs_tree_write_lock
1251
def apply_inventory_delta(self, changes):
1252
"""See MutableTree.apply_inventory_delta"""
1253
state = self.current_dirstate()
1254
state.update_by_delta(changes)
1255
self._make_dirty(reset_inventory=True)
1218
1257
def update_basis_by_delta(self, new_revid, delta):
1219
1258
"""See MutableTree.update_basis_by_delta."""
1220
assert self.last_revision() != new_revid
1259
if self.last_revision() == new_revid:
1260
raise AssertionError()
1221
1261
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1223
1263
@needs_read_lock
1227
1267
@needs_tree_write_lock
1228
1268
def _write_inventory(self, inv):
1229
1269
"""Write inventory as the current inventory."""
1230
assert not self._dirty, ("attempting to write an inventory when the "
1231
"dirstate is dirty will cause data loss")
1271
raise AssertionError("attempting to write an inventory when the "
1272
"dirstate is dirty will lose pending changes")
1232
1273
self.current_dirstate().set_state_from_inventory(inv)
1233
1274
self._make_dirty(reset_inventory=False)
1234
1275
if self._inventory is not None:
1259
1300
return "Working tree format 4"
1261
1302
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1262
accelerator_tree=None):
1303
accelerator_tree=None, hardlink=False):
1263
1304
"""See WorkingTreeFormat.initialize().
1265
1306
:param revision_id: allows creating a working tree at a different
1268
1309
contents more quickly than the revision tree, i.e. a workingtree.
1269
1310
The revision tree will be used for cases where accelerator_tree's
1270
1311
content is different.
1312
:param hardlink: If true, hard-link files from accelerator_tree,
1272
1315
These trees get an initial random root id, if their repository supports
1273
1316
rich root data, TREE_ROOT otherwise.
1278
1321
control_files = self._open_control_files(a_bzrdir)
1279
1322
control_files.create_lock()
1280
1323
control_files.lock_write()
1281
control_files.put_utf8('format', self.get_format_string())
1324
transport.put_bytes('format', self.get_format_string(),
1325
mode=a_bzrdir._get_file_mode())
1282
1326
if from_branch is not None:
1283
1327
branch = from_branch
1321
1365
parents_list = [(revision_id, basis)]
1322
1366
basis.lock_read()
1323
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1325
# if the basis has a root id we have to use that; otherwise we use
1327
basis_root_id = basis.get_root_id()
1328
if basis_root_id is not None:
1329
wt._set_root_id(basis_root_id)
1368
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1331
transform.build_tree(basis, wt, accelerator_tree)
1370
# if the basis has a root id we have to use that; otherwise we
1371
# use a new random one
1372
basis_root_id = basis.get_root_id()
1373
if basis_root_id is not None:
1374
wt._set_root_id(basis_root_id)
1376
# delta_from_tree is safe even for DirStateRevisionTrees,
1377
# because wt4.apply_inventory_delta does not mutate the input
1378
# inventory entries.
1379
transform.build_tree(basis, wt, accelerator_tree,
1380
hardlink=hardlink, delta_from_tree=True)
1334
1384
control_files.unlock()
1374
1424
default_revision=_mod_revision.CURRENT_REVISION):
1375
1425
"""See Tree.annotate_iter"""
1376
1426
w = self._get_weave(file_id)
1377
return w.annotate_iter(self.inventory[file_id].revision)
1427
return w.annotate(self.inventory[file_id].revision)
1379
1429
def _get_ancestors(self, default_revision):
1380
1430
return set(self._repository.get_ancestry(self._revision_id,
1440
1490
This is relatively expensive: we have to walk the entire dirstate.
1442
assert self._locked, 'cannot generate inventory of an unlocked '\
1443
'dirstate revision tree'
1492
if not self._locked:
1493
raise AssertionError(
1494
'cannot generate inventory of an unlocked '
1495
'dirstate revision tree')
1444
1496
# separate call for profiling - makes it clear where the costs are.
1445
1497
self._dirstate._read_dirblocks_if_needed()
1446
assert self._revision_id in self._dirstate.get_parent_ids(), \
1447
'parent %s has disappeared from %s' % (
1448
self._revision_id, self._dirstate.get_parent_ids())
1498
if self._revision_id not in self._dirstate.get_parent_ids():
1499
raise AssertionError(
1500
'parent %s has disappeared from %s' % (
1501
self._revision_id, self._dirstate.get_parent_ids()))
1449
1502
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1450
1503
# This is identical now to the WorkingTree _generate_inventory except
1451
1504
# for the tree index use.
1452
1505
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1453
1506
current_id = root_key[2]
1454
assert current_entry[parent_index][0] == 'd'
1507
if current_entry[parent_index][0] != 'd':
1508
raise AssertionError()
1455
1509
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1456
1510
inv.root.revision = current_entry[parent_index][4]
1457
1511
# Turn some things into local variables
1497
1551
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1499
1553
# These checks cost us around 40ms on a 55k entry tree
1500
assert file_id not in inv_byid
1501
assert name_unicode not in parent_ie.children
1554
if file_id in inv_byid:
1555
raise AssertionError('file_id %s already in'
1556
' inventory as %s' % (file_id, inv_byid[file_id]))
1557
if name_unicode in parent_ie.children:
1558
raise AssertionError('name %r already in parent'
1502
1560
inv_byid[file_id] = inv_entry
1503
1561
parent_ie.children[name_unicode] = inv_entry
1504
1562
self._inventory = inv
1524
1582
return parent_details[1]
1527
@symbol_versioning.deprecated_method(symbol_versioning.zero_ninety)
1528
def get_weave(self, file_id):
1529
return self._get_weave(file_id)
1531
1585
def _get_weave(self, file_id):
1532
1586
return self._repository.weave_store.get_weave(file_id,
1533
1587
self._repository.get_transaction())
1538
1592
def get_file_lines(self, file_id):
1539
1593
entry = self._get_entry(file_id=file_id)[1]
1541
1595
raise errors.NoSuchId(tree=self, file_id=file_id)
1542
1596
return self._get_weave(file_id).get_lines(entry[1][4])
1544
1598
def get_file_size(self, file_id):
1599
"""See Tree.get_file_size"""
1545
1600
return self.inventory[file_id].text_size
1547
1602
def get_file_text(self, file_id):
1599
1654
def kind(self, file_id):
1600
1655
entry = self._get_entry(file_id=file_id)[1]
1602
1657
raise errors.NoSuchId(tree=self, file_id=file_id)
1603
1658
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1660
def stored_kind(self, file_id):
1661
"""See Tree.stored_kind"""
1662
return self.kind(file_id)
1605
1664
def path_content_summary(self, path):
1606
1665
"""See Tree.path_content_summary."""
1607
1666
id = self.inventory.path2id(path)
1728
1787
_matching_to_tree_format = WorkingTreeFormat4()
1729
1788
_test_mutable_trees_to_test_trees = make_source_parent_tree
1731
def _iter_changes(self, include_unchanged=False,
1790
def iter_changes(self, include_unchanged=False,
1732
1791
specific_files=None, pb=None, extra_trees=[],
1733
1792
require_versioned=True, want_unversioned=False):
1734
1793
"""Return the changes from source to target.
1736
:return: An iterator that yields tuples. See InterTree._iter_changes
1795
:return: An iterator that yields tuples. See InterTree.iter_changes
1738
1797
:param specific_files: An optional list of file paths to restrict the
1739
1798
comparison to. When mapping filenames to ids, all matches in all
1758
1817
# TODO: handle extra trees in the dirstate.
1759
1818
if (extra_trees or specific_files == []):
1760
1819
# we can't fast-path these cases (yet)
1761
for f in super(InterDirStateTree, self)._iter_changes(
1820
for f in super(InterDirStateTree, self).iter_changes(
1762
1821
include_unchanged, specific_files, pb, extra_trees,
1763
1822
require_versioned, want_unversioned=want_unversioned):
1766
1825
parent_ids = self.target.get_parent_ids()
1767
assert (self.source._revision_id in parent_ids
1768
or self.source._revision_id == NULL_REVISION), \
1769
"revision {%s} is not stored in {%s}, but %s " \
1770
"can only be used for trees stored in the dirstate" \
1771
% (self.source._revision_id, self.target, self._iter_changes)
1826
if not (self.source._revision_id in parent_ids
1827
or self.source._revision_id == NULL_REVISION):
1828
raise AssertionError(
1829
"revision {%s} is not stored in {%s}, but %s "
1830
"can only be used for trees stored in the dirstate"
1831
% (self.source._revision_id, self.target, self.iter_changes))
1772
1832
target_index = 0
1773
1833
if self.source._revision_id == NULL_REVISION:
1774
1834
source_index = None
1775
1835
indices = (target_index,)
1777
assert (self.source._revision_id in parent_ids), \
1778
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1779
self.source._revision_id, parent_ids)
1837
if not (self.source._revision_id in parent_ids):
1838
raise AssertionError(
1839
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1840
self.source._revision_id, parent_ids))
1780
1841
source_index = 1 + parent_ids.index(self.source._revision_id)
1781
1842
indices = (source_index, target_index)
1782
1843
# -- make all specific_files utf8 --
1920
1981
target_details = entry[1][target_index]
1921
1982
target_minikind = target_details[0]
1922
1983
if path_info is not None and target_minikind in 'fdlt':
1923
assert target_index == 0
1984
if not (target_index == 0):
1985
raise AssertionError()
1924
1986
link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1925
1987
stat_value=path_info[3])
1926
1988
# The entry may have been modified by update_entry
1951
2013
path_utf8=old_path)
1952
2014
# update the source details variable to be the real
2016
if old_entry == (None, None):
2017
raise errors.CorruptDirstate(state._filename,
2018
"entry '%s/%s' is considered renamed from %r"
2019
" but source does not exist\n"
2020
"entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
1954
2021
source_details = old_entry[1][source_index]
1955
2022
source_minikind = source_details[0]
2034
2101
# parent entry will be the same as the source entry.
2035
2102
target_parent_entry = state._get_entry(target_index,
2036
2103
path_utf8=new_dirname)
2037
assert target_parent_entry != (None, None), (
2038
"Could not find target parent in wt: %s\nparent of: %s"
2039
% (new_dirname, entry))
2104
if target_parent_entry == (None, None):
2105
raise AssertionError(
2106
"Could not find target parent in wt: %s\nparent of: %s"
2107
% (new_dirname, entry))
2040
2108
target_parent_id = target_parent_entry[0][2]
2041
2109
if target_parent_id == entry[0][2]:
2042
2110
# This is the root, so the parent is None
2225
2293
if current_dir_info[0][0] == '':
2226
2294
# remove .bzr from iteration
2227
2295
bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2228
assert current_dir_info[1][bzr_index][0] == '.bzr'
2296
if current_dir_info[1][bzr_index][0] != '.bzr':
2297
raise AssertionError()
2229
2298
del current_dir_info[1][bzr_index]
2230
2299
# walk until both the directory listing and the versioned metadata
2231
2300
# are exhausted.
2483
2552
def update_format(self, tree):
2484
2553
"""Change the format marker."""
2485
tree._control_files.put_utf8('format',
2486
self.target_format.get_format_string())
2554
tree._transport.put_bytes('format',
2555
self.target_format.get_format_string(),
2556
mode=tree.bzrdir._get_file_mode())