1
# Copyright (C) 2007-2010 Canonical Ltd
1
# Copyright (C) 2007-2012 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
WorkingTree.open(dir).
25
from cStringIO import StringIO
25
from __future__ import absolute_import
29
from bzrlib.lazy_import import lazy_import
30
from .lazy_import import lazy_import
30
31
lazy_import(globals(), """
40
conflicts as _mod_conflicts,
45
filters as _mod_filters,
43
48
revision as _mod_revision,
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
from bzrlib.mutabletree import needs_tree_write_lock
57
from bzrlib.osutils import (
56
from .decorators import needs_read_lock, needs_write_lock
57
from .inventory import Inventory, ROOT_ID, entry_factory
58
from .lock import LogicalLockResult
59
from .lockable_files import LockableFiles
60
from .lockdir import LockDir
61
from .mutabletree import (
63
needs_tree_write_lock,
65
from .osutils import (
64
from bzrlib.trace import mutter
65
from bzrlib.transport.local import LocalTransport
66
from bzrlib.tree import InterTree
67
from bzrlib.tree import Tree
68
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
71
class DirStateWorkingTree(WorkingTree3):
76
from .transport.local import LocalTransport
81
from .workingtree import (
84
from .bzrworkingtree import (
86
WorkingTreeFormatMetaDir,
90
class DirStateWorkingTree(InventoryWorkingTree):
72
92
def __init__(self, basedir,
74
94
_control_files=None,
84
104
self._format = _format
85
105
self.bzrdir = _bzrdir
86
106
basedir = safe_unicode(basedir)
87
mutter("opening working tree %r", basedir)
107
trace.mutter("opening working tree %r", basedir)
88
108
self._branch = branch
89
109
self.basedir = realpath(basedir)
90
110
# if branch is at our basedir and is a format 6 or less
124
144
state.add(f, file_id, kind, None, '')
125
145
self._make_dirty(reset_inventory=True)
147
def _get_check_refs(self):
148
"""Return the references needed to perform a check of this tree."""
149
return [('trees', self.last_revision())]
127
151
def _make_dirty(self, reset_inventory):
128
152
"""Make the tree state dirty.
182
206
def _comparison_data(self, entry, path):
183
207
kind, executable, stat_value = \
184
WorkingTree3._comparison_data(self, entry, path)
208
WorkingTree._comparison_data(self, entry, path)
185
209
# it looks like a plain directory, but it's really a reference -- see
187
211
if (self._repo_supports_tree_reference and kind == 'directory'
193
217
def commit(self, message=None, revprops=None, *args, **kwargs):
194
218
# mark the tree as dirty post commit - commit
195
219
# can change the current versioned list by doing deletes.
196
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
220
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
221
self._make_dirty(reset_inventory=True)
218
242
local_path = self.bzrdir.get_workingtree_transport(None
219
243
).local_abspath('dirstate')
220
244
self._dirstate = dirstate.DirState.on_file(local_path,
221
self._sha1_provider())
245
self._sha1_provider(), self._worth_saving_limit())
222
246
return self._dirstate
224
248
def _sha1_provider(self):
260
def _worth_saving_limit(self):
261
"""How many hash changes are ok before we must save the dirstate.
263
:return: an integer. -1 means never save.
265
conf = self.get_config_stack()
266
return conf.get('bzr.workingtree.worth_saving_limit')
236
268
def filter_unversioned_files(self, paths):
237
269
"""Filter out paths that are versioned.
400
432
self._generate_inventory()
401
433
return self._inventory
403
inventory = property(_get_inventory,
404
doc="Inventory of this Tree")
435
root_inventory = property(_get_root_inventory,
436
"Root inventory of this tree")
407
439
def get_parent_ids(self):
455
487
return False # Missing entries are not executable
456
488
return entry[1][0][3] # Executable?
458
if not osutils.supports_executable():
459
def is_executable(self, file_id, path=None):
460
"""Test if a file is executable or not.
490
def is_executable(self, file_id, path=None):
491
"""Test if a file is executable or not.
462
Note: The caller is expected to take a read-lock before calling this.
493
Note: The caller is expected to take a read-lock before calling this.
495
if not self._supports_executable():
464
496
entry = self._get_entry(file_id=file_id, path=path)
465
497
if entry == (None, None):
467
499
return entry[1][0][3]
469
_is_executable_from_path_and_stat = \
470
_is_executable_from_path_and_stat_from_basis
472
def is_executable(self, file_id, path=None):
473
"""Test if a file is executable or not.
475
Note: The caller is expected to take a read-lock before calling this.
477
501
self._must_be_locked()
479
503
path = self.id2path(file_id)
480
mode = os.lstat(self.abspath(path)).st_mode
504
mode = osutils.lstat(self.abspath(path)).st_mode
481
505
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
483
507
def all_file_ids(self):
527
551
# path is missing on disk.
530
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
554
def _observed_sha1(self, file_id, path, sha_and_stat):
531
555
"""See MutableTree._observed_sha1."""
532
556
state = self.current_dirstate()
533
557
entry = self._get_entry(file_id=file_id, path=path)
534
state._observed_sha1(entry, sha1, statvalue)
558
state._observed_sha1(entry, *sha_and_stat)
536
560
def kind(self, file_id):
537
561
"""Return the kind of a file.
567
591
return _mod_revision.NULL_REVISION
569
593
def lock_read(self):
570
"""See Branch.lock_read, and WorkingTree.unlock."""
594
"""See Branch.lock_read, and WorkingTree.unlock.
596
:return: A breezy.lock.LogicalLockResult.
571
598
self.branch.lock_read()
573
600
self._control_files.lock_read()
607
635
self.branch.unlock()
637
return LogicalLockResult(self.unlock)
610
639
def lock_tree_write(self):
611
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
640
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
642
:return: A breezy.lock.LogicalLockResult.
612
644
self.branch.lock_read()
613
self._lock_self_write()
645
return self._lock_self_write()
615
647
def lock_write(self):
616
"""See MutableTree.lock_write, and WorkingTree.unlock."""
648
"""See MutableTree.lock_write, and WorkingTree.unlock.
650
:return: A breezy.lock.LogicalLockResult.
617
652
self.branch.lock_write()
618
self._lock_self_write()
653
return self._lock_self_write()
620
655
@needs_tree_write_lock
621
656
def move(self, from_paths, to_dir, after=False):
653
688
if self._inventory is not None:
654
689
update_inventory = True
690
inv = self.root_inventory
656
691
to_dir_id = to_entry[0][2]
657
692
to_dir_ie = inv[to_dir_id]
659
694
update_inventory = False
696
# GZ 2017-03-28: The rollbacks variable was shadowed in the loop below
697
# missing those added here, but there's also no test coverage for this.
698
rollbacks = cleanup.ObjectWithCleanups()
662
699
def move_one(old_entry, from_path_utf8, minikind, executable,
663
700
fingerprint, packed_stat, size,
664
701
to_block, to_key, to_path_utf8):
665
702
state._make_absent(old_entry)
666
703
from_key = old_entry[0]
668
lambda:state.update_minimal(from_key,
670
executable=executable,
671
fingerprint=fingerprint,
672
packed_stat=packed_stat,
674
path_utf8=from_path_utf8))
704
rollbacks.add_cleanup(
705
state.update_minimal,
708
executable=executable,
709
fingerprint=fingerprint,
710
packed_stat=packed_stat,
712
path_utf8=from_path_utf8)
675
713
state.update_minimal(to_key,
677
715
executable=executable,
681
719
path_utf8=to_path_utf8)
682
720
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
683
721
new_entry = to_block[1][added_entry_index]
684
rollbacks.append(lambda:state._make_absent(new_entry))
722
rollbacks.add_cleanup(state._make_absent, new_entry)
686
724
for from_rel in from_paths:
687
725
# from_rel is 'pathinroot/foo/bar'
727
765
raise errors.RenameFailedFilesExist(from_rel, to_rel)
730
def rollback_rename():
731
"""A single rename has failed, roll it back."""
732
# roll back everything, even if we encounter trouble doing one
735
# TODO: at least log the other exceptions rather than just
736
# losing them mbp 20070307
738
for rollback in reversed(rollbacks):
742
exc_info = sys.exc_info()
744
raise exc_info[0], exc_info[1], exc_info[2]
746
767
# perform the disk move first - its the most likely failure point.
748
769
from_rel_abs = self.abspath(from_rel)
749
770
to_rel_abs = self.abspath(to_rel)
751
772
osutils.rename(from_rel_abs, to_rel_abs)
753
774
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
754
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
775
rollbacks.add_cleanup(osutils.rename, to_rel_abs, from_rel_abs)
756
777
# perform the rename in the inventory next if needed: its easy
760
781
from_entry = inv[from_id]
761
782
current_parent = from_entry.parent_id
762
783
inv.rename(from_id, to_dir_id, from_tail)
764
lambda: inv.rename(from_id, current_parent, from_tail))
784
rollbacks.add_cleanup(
785
inv.rename, from_id, current_parent, from_tail)
765
786
# finally do the rename in the dirstate, which is a little
766
787
# tricky to rollback, but least likely to need it.
767
788
old_block_index, old_entry_index, dir_present, file_present = \
836
857
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
859
rollbacks.cleanup_now()
840
861
result.append((from_rel, to_rel))
841
state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
862
state._mark_modified()
842
863
self._make_dirty(reset_inventory=False)
855
876
def path2id(self, path):
856
877
"""Return the id for path in this tree."""
878
if isinstance(path, list):
881
path = osutils.pathjoin(*path)
857
882
path = path.strip('/')
858
883
entry = self._get_entry(path=path)
859
884
if entry == (None, None):
937
962
all_versioned = False
939
964
if not all_versioned:
940
raise errors.PathsNotVersionedError(paths)
965
raise errors.PathsNotVersionedError(
966
[p.decode('utf-8') for p in paths])
941
967
# -- remove redundancy in supplied paths to prevent over-scanning --
942
968
search_paths = osutils.minimum_path_selection(paths)
992
1018
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
993
1019
for dir_name in split_paths:
994
1020
if dir_name not in found_dir_names:
995
raise errors.PathsNotVersionedError(paths)
1021
raise errors.PathsNotVersionedError(
1022
[p.decode('utf-8') for p in paths])
997
for dir_name_id, trees_info in found.iteritems():
1024
for dir_name_id, trees_info in viewitems(found):
998
1025
for index in search_indexes:
999
1026
if trees_info[index][0] not in ('r', 'a'):
1000
1027
found_ids.add(dir_name_id[2])
1101
1128
_mod_revision.NULL_REVISION)))
1102
1129
ghosts.append(rev_id)
1103
1130
accepted_revisions.add(rev_id)
1104
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1132
if (len(real_trees) == 1
1134
and self.branch.repository._format.fast_deltas
1135
and isinstance(real_trees[0][1],
1136
revisiontree.InventoryRevisionTree)
1137
and self.get_parent_ids()):
1138
rev_id, rev_tree = real_trees[0]
1139
basis_id = self.get_parent_ids()[0]
1140
# There are times when basis_tree won't be in
1141
# self.branch.repository, (switch, for example)
1143
basis_tree = self.branch.repository.revision_tree(basis_id)
1144
except errors.NoSuchRevision:
1145
# Fall back to the set_parent_trees(), since we can't use
1146
# _make_delta if we can't get the RevisionTree
1149
delta = rev_tree.root_inventory._make_delta(
1150
basis_tree.root_inventory)
1151
dirstate.update_basis_by_delta(delta, rev_id)
1154
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
1155
self._make_dirty(reset_inventory=False)
1107
1157
def _set_root_id(self, file_id):
1128
1178
def unlock(self):
1129
1179
"""Unlock in format 4 trees needs to write the entire dirstate."""
1130
# do non-implementation specific cleanup
1133
1180
if self._control_files._lock_count == 1:
1181
# do non-implementation specific cleanup
1134
1184
# eventually we should do signature checking during read locks for
1135
1185
# dirstate updates.
1136
1186
if self._control_files._lock_mode == 'w':
1230
1280
ids_to_unversion.remove(entry[0][2])
1231
1281
block_index += 1
1232
1282
if ids_to_unversion:
1233
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1283
raise errors.NoSuchId(self, next(iter(ids_to_unversion)))
1234
1284
self._make_dirty(reset_inventory=False)
1235
1285
# have to change the legacy inventory too.
1236
1286
if self._inventory is not None:
1237
1287
for file_id in file_ids:
1238
self._inventory.remove_recursive_id(file_id)
1288
if self._inventory.has_id(file_id):
1289
self._inventory.remove_recursive_id(file_id)
1240
1291
@needs_tree_write_lock
1241
1292
def rename_one(self, from_rel, to_rel, after=False):
1242
1293
"""See WorkingTree.rename_one"""
1244
WorkingTree.rename_one(self, from_rel, to_rel, after)
1295
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1246
1297
@needs_tree_write_lock
1247
1298
def apply_inventory_delta(self, changes):
1273
1324
# being created.
1274
1325
self._inventory = None
1275
1326
# generate a delta,
1276
delta = inv._make_delta(self.inventory)
1327
delta = inv._make_delta(self.root_inventory)
1277
1328
# and apply it.
1278
1329
self.apply_inventory_delta(delta)
1279
1330
if had_inventory:
1280
1331
self._inventory = inv
1334
@needs_tree_write_lock
1335
def reset_state(self, revision_ids=None):
1336
"""Reset the state of the working tree.
1338
This does a hard-reset to a last-known-good state. This is a way to
1339
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1341
if revision_ids is None:
1342
revision_ids = self.get_parent_ids()
1343
if not revision_ids:
1344
base_tree = self.branch.repository.revision_tree(
1345
_mod_revision.NULL_REVISION)
1348
trees = list(zip(revision_ids,
1349
self.branch.repository.revision_trees(revision_ids)))
1350
base_tree = trees[0][1]
1351
state = self.current_dirstate()
1352
# We don't support ghosts yet
1353
state.set_state_from_scratch(base_tree.root_inventory, trees, [])
1284
1356
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1290
1362
"""See dirstate.SHA1Provider.sha1()."""
1291
1363
filters = self.tree._content_filter_stack(
1292
1364
self.tree.relpath(osutils.safe_unicode(abspath)))
1293
return internal_size_sha_file_byname(abspath, filters)[1]
1365
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1295
1367
def stat_and_sha1(self, abspath):
1296
1368
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1301
1373
statvalue = os.fstat(file_obj.fileno())
1303
file_obj = filtered_input_file(file_obj, filters)
1375
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1304
1376
sha1 = osutils.size_sha_file(file_obj)[1]
1306
1378
file_obj.close()
1317
1389
def _file_content_summary(self, path, stat_result):
1318
1390
# This is to support the somewhat obsolete path_content_summary method
1319
1391
# with content filtering: see
1320
# <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1392
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1322
1394
# If the dirstate cache is up to date and knows the hash and size,
1336
1408
class WorkingTree4(DirStateWorkingTree):
1337
1409
"""This is the Format 4 working tree.
1339
This differs from WorkingTree3 by:
1411
This differs from WorkingTree by:
1340
1412
- Having a consolidated internal dirstate, stored in a
1341
1413
randomly-accessible sorted file on disk.
1342
1414
- Not having a regular inventory attribute. One can be synthesized
1370
1442
return views.PathBasedViews(self)
1373
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1445
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
1447
missing_parent_conflicts = True
1449
supports_versioned_directories = True
1451
_lock_class = LockDir
1452
_lock_file_name = 'lock'
1454
def _open_control_files(self, a_bzrdir):
1455
transport = a_bzrdir.get_workingtree_transport(None)
1456
return LockableFiles(transport, self._lock_file_name,
1375
1459
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1460
accelerator_tree=None, hardlink=False):
1377
1461
"""See WorkingTreeFormat.initialize().
1379
1463
:param revision_id: allows creating a working tree at a different
1380
revision than the branch is at.
1464
revision than the branch is at.
1381
1465
:param accelerator_tree: A tree which can be used for retrieving file
1382
1466
contents more quickly than the revision tree, i.e. a workingtree.
1383
1467
The revision tree will be used for cases where accelerator_tree's
1394
1478
control_files = self._open_control_files(a_bzrdir)
1395
1479
control_files.create_lock()
1396
1480
control_files.lock_write()
1397
transport.put_bytes('format', self.get_format_string(),
1481
transport.put_bytes('format', self.as_string(),
1398
1482
mode=a_bzrdir._get_file_mode())
1399
1483
if from_branch is not None:
1400
1484
branch = from_branch
1460
1544
transform.build_tree(basis, wt, accelerator_tree,
1461
1545
hardlink=hardlink,
1462
1546
delta_from_tree=delta_from_tree)
1547
for hook in MutableTree.hooks['post_build_tree']:
1476
1562
:param wt: the WorkingTree object
1565
def open(self, a_bzrdir, _found=False):
1566
"""Return the WorkingTree object for a_bzrdir
1568
_found is a private parameter, do not use it. It is used to indicate
1569
if format probing has already been done.
1572
# we are being called directly and must probe.
1573
raise NotImplementedError
1574
if not isinstance(a_bzrdir.transport, LocalTransport):
1575
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1576
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1479
1579
def _open(self, a_bzrdir, control_files):
1480
1580
"""Open the tree itself.
1494
1594
def _get_matchingbzrdir(self):
1495
1595
"""Overrideable method to get a bzrdir for testing."""
1496
1596
# please test against something that will let us do tree references
1497
return bzrdir.format_registry.make_bzrdir(
1498
'dirstate-with-subtree')
1597
return controldir.format_registry.make_bzrdir(
1598
'development-subtree')
1500
1600
_matchingbzrdir = property(__get_matchingbzrdir)
1507
1607
- exists within a metadir controlling .bzr
1508
1608
- includes an explicit version marker for the workingtree control
1509
files, separate from the BzrDir format
1609
files, separate from the ControlDir format
1510
1610
- modifies the hash cache format
1511
1611
- is new in bzr 0.15
1512
1612
- uses a LockDir to guard access to it.
1517
1617
_tree_class = WorkingTree4
1519
def get_format_string(self):
1620
def get_format_string(cls):
1520
1621
"""See WorkingTreeFormat.get_format_string()."""
1521
1622
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1534
1635
_tree_class = WorkingTree5
1536
def get_format_string(self):
1638
def get_format_string(cls):
1537
1639
"""See WorkingTreeFormat.get_format_string()."""
1538
1640
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1554
1656
_tree_class = WorkingTree6
1556
def get_format_string(self):
1659
def get_format_string(cls):
1557
1660
"""See WorkingTreeFormat.get_format_string()."""
1558
1661
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1571
1674
def supports_views(self):
1575
class DirStateRevisionTree(Tree):
1677
def _get_matchingbzrdir(self):
1678
"""Overrideable method to get a bzrdir for testing."""
1679
# We use 'development-subtree' instead of '2a', because we have a
1680
# few tests that want to test tree references
1681
return controldir.format_registry.make_bzrdir('development-subtree')
1684
class DirStateRevisionTree(InventoryTree):
1576
1685
"""A revision tree pulling the inventory from a dirstate.
1578
1687
Note that this is one of the historical (ie revision) trees cached in the
1597
1706
def annotate_iter(self, file_id,
1598
1707
default_revision=_mod_revision.CURRENT_REVISION):
1599
1708
"""See Tree.annotate_iter"""
1600
text_key = (file_id, self.inventory[file_id].revision)
1709
text_key = (file_id, self.get_file_revision(file_id))
1601
1710
annotations = self._repository.texts.annotate(text_key)
1602
1711
return [(key[-1], line) for (key, line) in annotations]
1604
def _get_ancestors(self, default_revision):
1605
return set(self._repository.get_ancestry(self._revision_id,
1607
1713
def _comparison_data(self, entry, path):
1608
1714
"""See Tree._comparison_data."""
1609
1715
if entry is None:
1662
1768
if path is not None:
1663
1769
path = path.encode('utf8')
1664
1770
parent_index = self._get_parent_index()
1665
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1771
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id,
1667
1774
def _generate_inventory(self):
1668
1775
"""Create and set self.inventory from the dirstate object.
1725
1832
elif kind == 'directory':
1726
1833
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1727
1834
elif kind == 'symlink':
1728
inv_entry.executable = False
1729
inv_entry.text_size = None
1730
1835
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1731
1836
elif kind == 'tree-reference':
1732
1837
inv_entry.reference_revision = fingerprint or None
1752
1857
# Make sure the file exists
1753
1858
entry = self._get_entry(file_id, path=path)
1754
1859
if entry == (None, None): # do we raise?
1860
raise errors.NoSuchId(self, file_id)
1756
1861
parent_index = self._get_parent_index()
1757
1862
last_changed_revision = entry[1][parent_index][4]
1769
1874
return parent_details[1]
1878
def get_file_revision(self, file_id):
1879
inv, inv_file_id = self._unpack_file_id(file_id)
1880
return inv[inv_file_id].revision
1772
1882
def get_file(self, file_id, path=None):
1773
return StringIO(self.get_file_text(file_id))
1883
return BytesIO(self.get_file_text(file_id))
1775
1885
def get_file_size(self, file_id):
1776
1886
"""See Tree.get_file_size"""
1777
return self.inventory[file_id].text_size
1887
inv, inv_file_id = self._unpack_file_id(file_id)
1888
return inv[inv_file_id].text_size
1779
1890
def get_file_text(self, file_id, path=None):
1780
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1781
return ''.join(content)
1892
for _, content_iter in self.iter_files_bytes([(file_id, None)]):
1893
if content is not None:
1894
raise AssertionError('iter_files_bytes returned'
1895
' too many entries')
1896
# For each entry returned by iter_files_bytes, we must consume the
1897
# content_iter before we step the files iterator.
1898
content = ''.join(content_iter)
1900
raise AssertionError('iter_files_bytes did not return'
1901
' the requested data')
1783
1904
def get_reference_revision(self, file_id, path=None):
1784
return self.inventory[file_id].reference_revision
1905
inv, inv_file_id = self._unpack_file_id(file_id)
1906
return inv[inv_file_id].reference_revision
1786
1908
def iter_files_bytes(self, desired_files):
1787
1909
"""See Tree.iter_files_bytes.
1798
1920
return self._repository.iter_files_bytes(repo_desired_files)
1800
def get_symlink_target(self, file_id):
1922
def get_symlink_target(self, file_id, path=None):
1801
1923
entry = self._get_entry(file_id=file_id)
1802
1924
parent_index = self._get_parent_index()
1803
1925
if entry[1][parent_index][0] != 'l':
1811
1933
"""Return the revision id for this tree."""
1812
1934
return self._revision_id
1814
def _get_inventory(self):
1936
def _get_root_inventory(self):
1815
1937
if self._inventory is not None:
1816
1938
return self._inventory
1817
1939
self._must_be_locked()
1818
1940
self._generate_inventory()
1819
1941
return self._inventory
1821
inventory = property(_get_inventory,
1943
root_inventory = property(_get_root_inventory,
1822
1944
doc="Inventory of this Tree")
1824
1946
def get_parent_ids(self):
1842
1964
def path_content_summary(self, path):
1843
1965
"""See Tree.path_content_summary."""
1844
id = self.inventory.path2id(path)
1966
inv, inv_file_id = self._path2inv_file_id(path)
1967
if inv_file_id is None:
1846
1968
return ('missing', None, None, None)
1847
entry = self._inventory[id]
1969
entry = inv[inv_file_id]
1848
1970
kind = entry.kind
1849
1971
if kind == 'file':
1850
1972
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1854
1976
return (kind, None, None, None)
1856
1978
def is_executable(self, file_id, path=None):
1857
ie = self.inventory[file_id]
1979
inv, inv_file_id = self._unpack_file_id(file_id)
1980
ie = inv[inv_file_id]
1858
1981
if ie.kind != "file":
1860
1983
return ie.executable
1985
def is_locked(self):
1862
1988
def list_files(self, include_root=False, from_dir=None, recursive=True):
1863
1989
# We use a standard implementation, because DirStateRevisionTree is
1864
1990
# dealing with one of the parents of the current state
1865
inv = self._get_inventory()
1866
1991
if from_dir is None:
1992
inv = self.root_inventory
1867
1993
from_dir_id = None
1869
from_dir_id = inv.path2id(from_dir)
1995
inv, from_dir_id = self._path2inv_file_id(from_dir)
1870
1996
if from_dir_id is None:
1871
1997
# Directory not versioned
1999
# FIXME: Support nested trees
1873
2000
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1874
2001
if inv.root is not None and not include_root and from_dir is None:
1876
2003
for path, entry in entries:
1877
2004
yield path, 'V', entry.kind, entry.file_id, entry
1879
2006
def lock_read(self):
1880
"""Lock the tree for a set of operations."""
2007
"""Lock the tree for a set of operations.
2009
:return: A breezy.lock.LogicalLockResult.
1881
2011
if not self._locked:
1882
2012
self._repository.lock_read()
1883
2013
if self._dirstate._lock_token is None:
1884
2014
self._dirstate.lock_read()
1885
2015
self._dirstate_locked = True
1886
2016
self._locked += 1
2017
return LogicalLockResult(self.unlock)
1888
2019
def _must_be_locked(self):
1889
2020
if not self._locked:
1893
2024
def path2id(self, path):
1894
2025
"""Return the id for path in this tree."""
1895
2026
# lookup by path: faster than splitting and walking the ivnentory.
2027
if isinstance(path, list):
2030
path = osutils.pathjoin(*path)
1896
2031
entry = self._get_entry(path=path)
1897
2032
if entry == (None, None):
1921
2056
# So for now, we just build up the parent inventory, and extract
1922
2057
# it the same way RevisionTree does.
1923
2058
_directory = 'directory'
1924
inv = self._get_inventory()
2059
inv = self._get_root_inventory()
1925
2060
top_id = inv.path2id(prefix)
1926
2061
if top_id is None:
1962
2097
def __init__(self, source, target):
1963
2098
super(InterDirStateTree, self).__init__(source, target)
1964
2099
if not InterDirStateTree.is_compatible(source, target):
1965
raise Exception, "invalid source %r and target %r" % (source, target)
2100
raise Exception("invalid source %r and target %r" % (source, target))
1968
2103
def make_source_parent_tree(source, target):
1969
2104
"""Change the source tree into a parent of the target."""
1970
2105
revid = source.commit('record tree')
1971
target.branch.repository.fetch(source.branch.repository, revid)
2106
target.branch.fetch(source.branch, revid)
1972
2107
target.set_parent_ids([revid])
1973
2108
return target.basis_tree(), target
1982
2117
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1984
from bzrlib.tests.test__dirstate_helpers import \
2119
from .tests.test__dirstate_helpers import \
1985
2120
compiled_dirstate_helpers_feature
1986
2121
test_case.requireFeature(compiled_dirstate_helpers_feature)
1987
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2122
from ._dirstate_helpers_pyx import ProcessEntryC
1988
2123
result = klass.make_source_parent_tree(source, target)
1989
2124
result[1]._iter_changes = ProcessEntryC
2053
2188
specific_files_utf8.add(path.encode('utf8'))
2054
2189
specific_files = specific_files_utf8
2056
specific_files = set([''])
2191
specific_files = {''}
2057
2192
# -- specific_files is now a utf8 path set --
2059
2194
# -- get the state object and prepare it.
2066
2201
path_entries = state._entries_for_path(path)
2067
2202
if not path_entries:
2068
2203
# this specified path is not present at all: error
2069
not_versioned.append(path)
2204
not_versioned.append(path.decode('utf-8'))
2071
2206
found_versioned = False
2072
2207
# for each id at this path
2080
2215
if not found_versioned:
2081
2216
# none of the indexes was not 'absent' at all ids for this
2083
not_versioned.append(path)
2218
not_versioned.append(path.decode('utf-8'))
2084
2219
if len(not_versioned) > 0:
2085
2220
raise errors.PathsNotVersionedError(not_versioned)
2086
2221
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2153
2288
def update_format(self, tree):
2154
2289
"""Change the format marker."""
2155
2290
tree._transport.put_bytes('format',
2156
self.target_format.get_format_string(),
2291
self.target_format.as_string(),
2157
2292
mode=tree.bzrdir._get_file_mode())
2176
2311
def update_format(self, tree):
2177
2312
"""Change the format marker."""
2178
2313
tree._transport.put_bytes('format',
2179
self.target_format.get_format_string(),
2314
self.target_format.as_string(),
2180
2315
mode=tree.bzrdir._get_file_mode())
2205
2340
def update_format(self, tree):
2206
2341
"""Change the format marker."""
2207
2342
tree._transport.put_bytes('format',
2208
self.target_format.get_format_string(),
2343
self.target_format.as_string(),
2209
2344
mode=tree.bzrdir._get_file_mode())