29
29
from bzrlib.lazy_import import lazy_import
30
30
lazy_import(globals(), """
31
from bisect import bisect_left
33
from copy import deepcopy
34
42
from bzrlib import (
37
45
conflicts as _mod_conflicts,
41
filters as _mod_filters,
44
55
revision as _mod_revision,
66
from bzrlib.transport import get_transport
70
from bzrlib import symbol_versioning
52
71
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
54
from bzrlib.lock import LogicalLockResult
55
from bzrlib.lockable_files import LockableFiles
56
from bzrlib.lockdir import LockDir
72
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
73
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
74
import bzrlib.mutabletree
57
75
from bzrlib.mutabletree import needs_tree_write_lock
58
76
from bzrlib.osutils import (
86
from bzrlib.trace import mutter, note
65
87
from bzrlib.transport.local import LocalTransport
66
from bzrlib.tree import (
70
from bzrlib.workingtree import (
77
class DirStateWorkingTree(InventoryWorkingTree):
79
_DEFAULT_WORTH_SAVING_LIMIT = 10
88
from bzrlib.tree import InterTree
89
from bzrlib.progress import DummyProgress, ProgressPhase
90
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
91
from bzrlib.rio import RioReader, rio_file, Stanza
92
from bzrlib.symbol_versioning import (deprecated_passed,
97
from bzrlib.tree import Tree
98
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
101
class DirStateWorkingTree(WorkingTree3):
81
102
def __init__(self, basedir,
83
104
_control_files=None,
133
154
state.add(f, file_id, kind, None, '')
134
155
self._make_dirty(reset_inventory=True)
136
def _get_check_refs(self):
137
"""Return the references needed to perform a check of this tree."""
138
return [('trees', self.last_revision())]
140
157
def _make_dirty(self, reset_inventory):
141
158
"""Make the tree state dirty.
195
212
def _comparison_data(self, entry, path):
196
213
kind, executable, stat_value = \
197
WorkingTree._comparison_data(self, entry, path)
214
WorkingTree3._comparison_data(self, entry, path)
198
215
# it looks like a plain directory, but it's really a reference -- see
200
217
if (self._repo_supports_tree_reference and kind == 'directory'
206
223
def commit(self, message=None, revprops=None, *args, **kwargs):
207
224
# mark the tree as dirty post commit - commit
208
225
# can change the current versioned list by doing deletes.
209
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
226
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
210
227
self._make_dirty(reset_inventory=True)
249
def _worth_saving_limit(self):
250
"""How many hash changes are ok before we must save the dirstate.
252
:return: an integer. -1 means never save.
254
config = self.branch.get_config()
255
val = config.get_user_option('bzr.workingtree.worth_saving_limit')
257
val = self._DEFAULT_WORTH_SAVING_LIMIT
261
except ValueError, e:
262
trace.warning('Invalid config value for'
263
' "bzr.workingtree.worth_saving_limit"'
264
' value %r is not an integer.'
266
val = self._DEFAULT_WORTH_SAVING_LIMIT
269
266
def filter_unversioned_files(self, paths):
270
267
"""Filter out paths that are versioned.
467
464
return osutils.lexists(pathjoin(
468
465
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
470
def has_or_had_id(self, file_id):
471
state = self.current_dirstate()
472
row, parents = self._get_entry(file_id=file_id)
473
return row is not None
476
468
def id2path(self, file_id):
477
469
"Convert a file-id to a path."
644
632
self.branch.unlock()
646
return LogicalLockResult(self.unlock)
648
635
def lock_tree_write(self):
649
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
651
:return: A bzrlib.lock.LogicalLockResult.
636
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
653
637
self.branch.lock_read()
654
return self._lock_self_write()
638
self._lock_self_write()
656
640
def lock_write(self):
657
"""See MutableTree.lock_write, and WorkingTree.unlock.
659
:return: A bzrlib.lock.LogicalLockResult.
641
"""See MutableTree.lock_write, and WorkingTree.unlock."""
661
642
self.branch.lock_write()
662
return self._lock_self_write()
643
self._lock_self_write()
664
645
@needs_tree_write_lock
665
646
def move(self, from_paths, to_dir, after=False):
735
716
from_entry = self._get_entry(path=from_rel)
736
717
if from_entry == (None, None):
737
718
raise errors.BzrMoveFailedError(from_rel,to_dir,
738
errors.NotVersionedError(path=from_rel))
719
errors.NotVersionedError(path=str(from_rel)))
740
721
from_id = from_entry[0][2]
741
722
to_rel = pathjoin(to_dir, from_tail)
1070
1051
def set_last_revision(self, new_revision):
1071
1052
"""Change the last revision in the working tree."""
1072
1053
parents = self.get_parent_ids()
1073
if new_revision in (_mod_revision.NULL_REVISION, None):
1054
if new_revision in (NULL_REVISION, None):
1074
1055
if len(parents) >= 2:
1075
1056
raise AssertionError(
1076
1057
"setting the last parent to none with a pending merge is "
1145
1126
_mod_revision.NULL_REVISION)))
1146
1127
ghosts.append(rev_id)
1147
1128
accepted_revisions.add(rev_id)
1149
if (len(real_trees) == 1
1151
and self.branch.repository._format.fast_deltas
1152
and isinstance(real_trees[0][1],
1153
revisiontree.InventoryRevisionTree)
1154
and self.get_parent_ids()):
1155
rev_id, rev_tree = real_trees[0]
1156
basis_id = self.get_parent_ids()[0]
1157
# There are times when basis_tree won't be in
1158
# self.branch.repository, (switch, for example)
1160
basis_tree = self.branch.repository.revision_tree(basis_id)
1161
except errors.NoSuchRevision:
1162
# Fall back to the set_parent_trees(), since we can't use
1163
# _make_delta if we can't get the RevisionTree
1166
delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1167
dirstate.update_basis_by_delta(delta, rev_id)
1170
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1129
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1171
1130
self._make_dirty(reset_inventory=False)
1173
1132
def _set_root_id(self, file_id):
1265
1224
# just forget the whole block.
1266
1225
entry_index = 0
1267
1226
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1268
1228
entry = block[1][entry_index]
1269
if entry[1][0][0] in 'ar':
1270
# don't remove absent or renamed entries
1229
ids_to_unversion.discard(entry[0][2])
1230
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1232
or not state._make_absent(entry)):
1271
1233
entry_index += 1
1273
# Mark this file id as having been removed
1274
ids_to_unversion.discard(entry[0][2])
1275
if not state._make_absent(entry):
1276
# The block has not shrunk.
1278
1234
# go to the next block. (At the moment we dont delete empty
1280
1236
block_index += 1
1301
1257
# have to change the legacy inventory too.
1302
1258
if self._inventory is not None:
1303
1259
for file_id in file_ids:
1304
if self._inventory.has_id(file_id):
1305
self._inventory.remove_recursive_id(file_id)
1260
self._inventory.remove_recursive_id(file_id)
1307
1262
@needs_tree_write_lock
1308
1263
def rename_one(self, from_rel, to_rel, after=False):
1309
1264
"""See WorkingTree.rename_one"""
1311
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1266
WorkingTree.rename_one(self, from_rel, to_rel, after)
1313
1268
@needs_tree_write_lock
1314
1269
def apply_inventory_delta(self, changes):
1333
1288
if self._dirty:
1334
1289
raise AssertionError("attempting to write an inventory when the "
1335
1290
"dirstate is dirty will lose pending changes")
1336
had_inventory = self._inventory is not None
1337
# Setting self._inventory = None forces the dirstate to regenerate the
1338
# working inventory. We do this because self.inventory may be inv, or
1339
# may have been modified, and either case would prevent a clean delta
1341
self._inventory = None
1343
delta = inv._make_delta(self.inventory)
1345
self.apply_inventory_delta(delta)
1291
self.current_dirstate().set_state_from_inventory(inv)
1292
self._make_dirty(reset_inventory=False)
1293
if self._inventory is not None:
1347
1294
self._inventory = inv
1350
@needs_tree_write_lock
1351
def reset_state(self, revision_ids=None):
1352
"""Reset the state of the working tree.
1354
This does a hard-reset to a last-known-good state. This is a way to
1355
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1357
if revision_ids is None:
1358
revision_ids = self.get_parent_ids()
1359
if not revision_ids:
1360
base_tree = self.branch.repository.revision_tree(
1361
_mod_revision.NULL_REVISION)
1364
trees = zip(revision_ids,
1365
self.branch.repository.revision_trees(revision_ids))
1366
base_tree = trees[0][1]
1367
state = self.current_dirstate()
1368
# We don't support ghosts yet
1369
state.set_state_from_scratch(base_tree.inventory, trees, [])
1372
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1375
1301
self.tree = tree
1377
1303
def sha1(self, abspath):
1378
"""See dirstate.SHA1Provider.sha1()."""
1379
filters = self.tree._content_filter_stack(
1380
self.tree.relpath(osutils.safe_unicode(abspath)))
1381
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1304
"""Return the sha1 of a file given its absolute path."""
1305
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1306
return internal_size_sha_file_byname(abspath, filters)[1]
1383
1308
def stat_and_sha1(self, abspath):
1384
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1385
filters = self.tree._content_filter_stack(
1386
self.tree.relpath(osutils.safe_unicode(abspath)))
1309
"""Return the stat and sha1 of a file given its absolute path."""
1310
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1387
1311
file_obj = file(abspath, 'rb', 65000)
1389
1313
statvalue = os.fstat(file_obj.fileno())
1391
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1315
file_obj = filtered_input_file(file_obj, filters)
1392
1316
sha1 = osutils.size_sha_file(file_obj)[1]
1394
1318
file_obj.close()
1395
1319
return statvalue, sha1
1398
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1399
"""Dirstate working tree that supports content filtering.
1401
The dirstate holds the hash and size of the canonical form of the file,
1402
and most methods must return that.
1405
def _file_content_summary(self, path, stat_result):
1406
# This is to support the somewhat obsolete path_content_summary method
1407
# with content filtering: see
1408
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1410
# If the dirstate cache is up to date and knows the hash and size,
1412
# Otherwise if there are no content filters, return the on-disk size
1413
# and leave the hash blank.
1414
# Otherwise, read and filter the on-disk file and use its size and
1417
# The dirstate doesn't store the size of the canonical form so we
1418
# can't trust it for content-filtered trees. We just return None.
1419
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1420
executable = self._is_executable_from_path_and_stat(path, stat_result)
1421
return ('file', None, executable, dirstate_sha1)
1424
1322
class WorkingTree4(DirStateWorkingTree):
1425
1323
"""This is the Format 4 working tree.
1427
This differs from WorkingTree by:
1325
This differs from WorkingTree3 by:
1428
1326
- Having a consolidated internal dirstate, stored in a
1429
1327
randomly-accessible sorted file on disk.
1430
1328
- Not having a regular inventory attribute. One can be synthesized
1458
1356
return views.PathBasedViews(self)
1461
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1463
missing_parent_conflicts = True
1465
supports_versioned_directories = True
1467
_lock_class = LockDir
1468
_lock_file_name = 'lock'
1470
def _open_control_files(self, a_bzrdir):
1471
transport = a_bzrdir.get_workingtree_transport(None)
1472
return LockableFiles(transport, self._lock_file_name,
1359
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1475
1360
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1476
1361
accelerator_tree=None, hardlink=False):
1477
1362
"""See WorkingTreeFormat.initialize().
1479
1364
:param revision_id: allows creating a working tree at a different
1480
revision than the branch is at.
1365
revision than the branch is at.
1481
1366
:param accelerator_tree: A tree which can be used for retrieving file
1482
1367
contents more quickly than the revision tree, i.e. a workingtree.
1483
1368
The revision tree will be used for cases where accelerator_tree's
1547
1432
if basis_root_id is not None:
1548
1433
wt._set_root_id(basis_root_id)
1550
if wt.supports_content_filtering():
1551
# The original tree may not have the same content filters
1552
# applied so we can't safely build the inventory delta from
1554
delta_from_tree = False
1556
delta_from_tree = True
1557
1435
# delta_from_tree is safe even for DirStateRevisionTrees,
1558
1436
# because wt4.apply_inventory_delta does not mutate the input
1559
1437
# inventory entries.
1560
1438
transform.build_tree(basis, wt, accelerator_tree,
1562
delta_from_tree=delta_from_tree)
1439
hardlink=hardlink, delta_from_tree=True)
1576
1453
:param wt: the WorkingTree object
1579
def open(self, a_bzrdir, _found=False):
1580
"""Return the WorkingTree object for a_bzrdir
1582
_found is a private parameter, do not use it. It is used to indicate
1583
if format probing has already been done.
1586
# we are being called directly and must probe.
1587
raise NotImplementedError
1588
if not isinstance(a_bzrdir.transport, LocalTransport):
1589
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1590
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1593
1456
def _open(self, a_bzrdir, control_files):
1594
1457
"""Open the tree itself.
1689
class DirStateRevisionTree(InventoryTree):
1690
"""A revision tree pulling the inventory from a dirstate.
1692
Note that this is one of the historical (ie revision) trees cached in the
1693
dirstate for easy access, not the workingtree.
1552
class DirStateRevisionTree(Tree):
1553
"""A revision tree pulling the inventory from a dirstate."""
1696
1555
def __init__(self, dirstate, revision_id, repository):
1697
1556
self._dirstate = dirstate
1711
1570
def annotate_iter(self, file_id,
1712
1571
default_revision=_mod_revision.CURRENT_REVISION):
1713
1572
"""See Tree.annotate_iter"""
1714
text_key = (file_id, self.get_file_revision(file_id))
1573
text_key = (file_id, self.inventory[file_id].revision)
1715
1574
annotations = self._repository.texts.annotate(text_key)
1716
1575
return [(key[-1], line) for (key, line) in annotations]
1577
def _get_ancestors(self, default_revision):
1578
return set(self._repository.get_ancestry(self._revision_id,
1718
1580
def _comparison_data(self, entry, path):
1719
1581
"""See Tree._comparison_data."""
1720
1582
if entry is None:
1865
1729
parent_index = self._get_parent_index()
1866
1730
last_changed_revision = entry[1][parent_index][4]
1868
rev = self._repository.get_revision(last_changed_revision)
1869
except errors.NoSuchRevision:
1870
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1871
return rev.timestamp
1731
return self._repository.get_revision(last_changed_revision).timestamp
1873
1733
def get_file_sha1(self, file_id, path=None, stat_value=None):
1874
1734
entry = self._get_entry(file_id=file_id, path=path)
1911
1767
return self._repository.iter_files_bytes(repo_desired_files)
1913
def get_symlink_target(self, file_id, path=None):
1769
def get_symlink_target(self, file_id):
1914
1770
entry = self._get_entry(file_id=file_id)
1915
1771
parent_index = self._get_parent_index()
1916
1772
if entry[1][parent_index][0] != 'l':
1945
1801
entry = self._get_entry(file_id=file_id)[1]
1946
1802
if entry is None:
1947
1803
raise errors.NoSuchId(tree=self, file_id=file_id)
1948
parent_index = self._get_parent_index()
1949
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1804
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1951
1806
def stored_kind(self, file_id):
1952
1807
"""See Tree.stored_kind"""
1969
1824
def is_executable(self, file_id, path=None):
1970
1825
ie = self.inventory[file_id]
1971
1826
if ie.kind != "file":
1973
1828
return ie.executable
1975
def is_locked(self):
1978
def list_files(self, include_root=False, from_dir=None, recursive=True):
1830
def list_files(self, include_root=False):
1979
1831
# We use a standard implementation, because DirStateRevisionTree is
1980
1832
# dealing with one of the parents of the current state
1981
1833
inv = self._get_inventory()
1982
if from_dir is None:
1985
from_dir_id = inv.path2id(from_dir)
1986
if from_dir_id is None:
1987
# Directory not versioned
1989
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1990
if inv.root is not None and not include_root and from_dir is None:
1834
entries = inv.iter_entries()
1835
if self.inventory.root is not None and not include_root:
1992
1837
for path, entry in entries:
1993
1838
yield path, 'V', entry.kind, entry.file_id, entry
1995
1840
def lock_read(self):
1996
"""Lock the tree for a set of operations.
1998
:return: A bzrlib.lock.LogicalLockResult.
1841
"""Lock the tree for a set of operations."""
2000
1842
if not self._locked:
2001
1843
self._repository.lock_read()
2002
1844
if self._dirstate._lock_token is None:
2003
1845
self._dirstate.lock_read()
2004
1846
self._dirstate_locked = True
2005
1847
self._locked += 1
2006
return LogicalLockResult(self.unlock)
2008
1849
def _must_be_locked(self):
2009
1850
if not self._locked:
2088
1929
def make_source_parent_tree(source, target):
2089
1930
"""Change the source tree into a parent of the target."""
2090
1931
revid = source.commit('record tree')
2091
target.branch.fetch(source.branch, revid)
1932
target.branch.repository.fetch(source.branch.repository, revid)
2092
1933
target.set_parent_ids([revid])
2093
1934
return target.basis_tree(), target
2102
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1943
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2104
1944
from bzrlib.tests.test__dirstate_helpers import \
2105
compiled_dirstate_helpers_feature
2106
test_case.requireFeature(compiled_dirstate_helpers_feature)
2107
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1945
CompiledDirstateHelpersFeature
1946
if not CompiledDirstateHelpersFeature.available():
1947
from bzrlib.tests import UnavailableFeature
1948
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1949
from bzrlib._dirstate_helpers_c import ProcessEntryC
2108
1950
result = klass.make_source_parent_tree(source, target)
2109
1951
result[1]._iter_changes = ProcessEntryC
2148
1992
require_versioned, want_unversioned=want_unversioned)
2149
1993
parent_ids = self.target.get_parent_ids()
2150
1994
if not (self.source._revision_id in parent_ids
2151
or self.source._revision_id == _mod_revision.NULL_REVISION):
1995
or self.source._revision_id == NULL_REVISION):
2152
1996
raise AssertionError(
2153
1997
"revision {%s} is not stored in {%s}, but %s "
2154
1998
"can only be used for trees stored in the dirstate"
2155
1999
% (self.source._revision_id, self.target, self.iter_changes))
2156
2000
target_index = 0
2157
if self.source._revision_id == _mod_revision.NULL_REVISION:
2001
if self.source._revision_id == NULL_REVISION:
2158
2002
source_index = None
2159
2003
indices = (target_index,)
2176
2020
specific_files = set([''])
2177
2021
# -- specific_files is now a utf8 path set --
2022
search_specific_files = set()
2179
2023
# -- get the state object and prepare it.
2180
2024
state = self.target.current_dirstate()
2181
2025
state._read_dirblocks_if_needed()
2182
2026
if require_versioned:
2183
2027
# -- check all supplied paths are versioned in a search tree. --
2028
all_versioned = True
2185
2029
for path in specific_files:
2186
2030
path_entries = state._entries_for_path(path)
2187
2031
if not path_entries:
2188
2032
# this specified path is not present at all: error
2189
not_versioned.append(path)
2033
all_versioned = False
2191
2035
found_versioned = False
2192
2036
# for each id at this path
2193
2037
for entry in path_entries:
2200
2044
if not found_versioned:
2201
2045
# none of the indexes was not 'absent' at all ids for this
2203
not_versioned.append(path)
2204
if len(not_versioned) > 0:
2205
raise errors.PathsNotVersionedError(not_versioned)
2047
all_versioned = False
2049
if not all_versioned:
2050
raise errors.PathsNotVersionedError(specific_files)
2206
2051
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2207
search_specific_files = osutils.minimum_path_selection(specific_files)
2052
for path in specific_files:
2053
other_specific_files = specific_files.difference(set([path]))
2054
if not osutils.is_inside_any(other_specific_files, path):
2055
# this is a top level path, we must check it.
2056
search_specific_files.add(path)
2209
2058
use_filesystem_for_exec = (sys.platform != 'win32')
2210
2059
iter_changes = self.target._iter_changes(include_unchanged,
2222
2071
(revisiontree.RevisionTree, DirStateRevisionTree)):
2224
2073
# the source revid must be in the target dirstate
2225
if not (source._revision_id == _mod_revision.NULL_REVISION or
2074
if not (source._revision_id == NULL_REVISION or
2226
2075
source._revision_id in target.get_parent_ids()):
2227
2076
# TODO: what about ghosts? it may well need to
2228
2077
# check for them explicitly.