1
# Copyright (C) 2007-2011 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
53
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
54
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
55
from bzrlib.lock import LogicalLockResult
56
from bzrlib.lockable_files import LockableFiles
57
from bzrlib.lockdir import LockDir
56
import bzrlib.mutabletree
58
57
from bzrlib.mutabletree import needs_tree_write_lock
59
58
from bzrlib.osutils import (
65
from bzrlib.trace import mutter
66
66
from bzrlib.transport.local import LocalTransport
67
from bzrlib.tree import (
71
from bzrlib.workingtree import (
78
class DirStateWorkingTree(InventoryWorkingTree):
67
from bzrlib.tree import InterTree
68
from bzrlib.tree import Tree
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
72
class DirStateWorkingTree(WorkingTree3):
80
73
def __init__(self, basedir,
82
75
_control_files=None,
92
85
self._format = _format
93
86
self.bzrdir = _bzrdir
94
87
basedir = safe_unicode(basedir)
95
trace.mutter("opening working tree %r", basedir)
88
mutter("opening working tree %r", basedir)
96
89
self._branch = branch
97
90
self.basedir = realpath(basedir)
98
91
# if branch is at our basedir and is a format 6 or less
132
125
state.add(f, file_id, kind, None, '')
133
126
self._make_dirty(reset_inventory=True)
135
def _get_check_refs(self):
136
"""Return the references needed to perform a check of this tree."""
137
return [('trees', self.last_revision())]
139
128
def _make_dirty(self, reset_inventory):
140
129
"""Make the tree state dirty.
194
183
def _comparison_data(self, entry, path):
195
184
kind, executable, stat_value = \
196
WorkingTree._comparison_data(self, entry, path)
185
WorkingTree3._comparison_data(self, entry, path)
197
186
# it looks like a plain directory, but it's really a reference -- see
199
188
if (self._repo_supports_tree_reference and kind == 'directory'
205
194
def commit(self, message=None, revprops=None, *args, **kwargs):
206
195
# mark the tree as dirty post commit - commit
207
196
# can change the current versioned list by doing deletes.
208
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
197
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
209
198
self._make_dirty(reset_inventory=True)
230
219
local_path = self.bzrdir.get_workingtree_transport(None
231
220
).local_abspath('dirstate')
232
221
self._dirstate = dirstate.DirState.on_file(local_path,
233
self._sha1_provider(), self._worth_saving_limit())
222
self._sha1_provider())
234
223
return self._dirstate
236
225
def _sha1_provider(self):
248
def _worth_saving_limit(self):
249
"""How many hash changes are ok before we must save the dirstate.
251
:return: an integer. -1 means never save.
253
# FIXME: We want a WorkingTreeStack here -- vila 20110812
254
conf = config.BranchStack(self.branch)
255
return conf.get('bzr.workingtree.worth_saving_limit')
257
237
def filter_unversioned_files(self, paths):
258
238
"""Filter out paths that are versioned.
588
568
return _mod_revision.NULL_REVISION
590
570
def lock_read(self):
591
"""See Branch.lock_read, and WorkingTree.unlock.
593
:return: A bzrlib.lock.LogicalLockResult.
571
"""See Branch.lock_read, and WorkingTree.unlock."""
595
572
self.branch.lock_read()
597
574
self._control_files.lock_read()
632
608
self.branch.unlock()
634
return LogicalLockResult(self.unlock)
636
611
def lock_tree_write(self):
637
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
639
:return: A bzrlib.lock.LogicalLockResult.
612
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
641
613
self.branch.lock_read()
642
return self._lock_self_write()
614
self._lock_self_write()
644
616
def lock_write(self):
645
"""See MutableTree.lock_write, and WorkingTree.unlock.
647
:return: A bzrlib.lock.LogicalLockResult.
617
"""See MutableTree.lock_write, and WorkingTree.unlock."""
649
618
self.branch.lock_write()
650
return self._lock_self_write()
619
self._lock_self_write()
652
621
@needs_tree_write_lock
653
622
def move(self, from_paths, to_dir, after=False):
1133
1102
_mod_revision.NULL_REVISION)))
1134
1103
ghosts.append(rev_id)
1135
1104
accepted_revisions.add(rev_id)
1137
if (len(real_trees) == 1
1139
and self.branch.repository._format.fast_deltas
1140
and isinstance(real_trees[0][1],
1141
revisiontree.InventoryRevisionTree)
1142
and self.get_parent_ids()):
1143
rev_id, rev_tree = real_trees[0]
1144
basis_id = self.get_parent_ids()[0]
1145
# There are times when basis_tree won't be in
1146
# self.branch.repository, (switch, for example)
1148
basis_tree = self.branch.repository.revision_tree(basis_id)
1149
except errors.NoSuchRevision:
1150
# Fall back to the set_parent_trees(), since we can't use
1151
# _make_delta if we can't get the RevisionTree
1154
delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1155
dirstate.update_basis_by_delta(delta, rev_id)
1158
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1159
1106
self._make_dirty(reset_inventory=False)
1161
1108
def _set_root_id(self, file_id):
1182
1129
def unlock(self):
1183
1130
"""Unlock in format 4 trees needs to write the entire dirstate."""
1131
# do non-implementation specific cleanup
1184
1134
if self._control_files._lock_count == 1:
1185
# do non-implementation specific cleanup
1188
1135
# eventually we should do signature checking during read locks for
1189
1136
# dirstate updates.
1190
1137
if self._control_files._lock_mode == 'w':
1289
1236
# have to change the legacy inventory too.
1290
1237
if self._inventory is not None:
1291
1238
for file_id in file_ids:
1292
if self._inventory.has_id(file_id):
1293
self._inventory.remove_recursive_id(file_id)
1239
self._inventory.remove_recursive_id(file_id)
1295
1241
@needs_tree_write_lock
1296
1242
def rename_one(self, from_rel, to_rel, after=False):
1297
1243
"""See WorkingTree.rename_one"""
1299
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1245
WorkingTree.rename_one(self, from_rel, to_rel, after)
1301
1247
@needs_tree_write_lock
1302
1248
def apply_inventory_delta(self, changes):
1321
1267
if self._dirty:
1322
1268
raise AssertionError("attempting to write an inventory when the "
1323
1269
"dirstate is dirty will lose pending changes")
1324
had_inventory = self._inventory is not None
1325
# Setting self._inventory = None forces the dirstate to regenerate the
1326
# working inventory. We do this because self.inventory may be inv, or
1327
# may have been modified, and either case would prevent a clean delta
1329
self._inventory = None
1331
delta = inv._make_delta(self.inventory)
1333
self.apply_inventory_delta(delta)
1270
self.current_dirstate().set_state_from_inventory(inv)
1271
self._make_dirty(reset_inventory=False)
1272
if self._inventory is not None:
1335
1273
self._inventory = inv
1338
@needs_tree_write_lock
1339
def reset_state(self, revision_ids=None):
1340
"""Reset the state of the working tree.
1342
This does a hard-reset to a last-known-good state. This is a way to
1343
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1345
if revision_ids is None:
1346
revision_ids = self.get_parent_ids()
1347
if not revision_ids:
1348
base_tree = self.branch.repository.revision_tree(
1349
_mod_revision.NULL_REVISION)
1352
trees = zip(revision_ids,
1353
self.branch.repository.revision_trees(revision_ids))
1354
base_tree = trees[0][1]
1355
state = self.current_dirstate()
1356
# We don't support ghosts yet
1357
state.set_state_from_scratch(base_tree.inventory, trees, [])
1360
1277
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1366
1283
"""See dirstate.SHA1Provider.sha1()."""
1367
1284
filters = self.tree._content_filter_stack(
1368
1285
self.tree.relpath(osutils.safe_unicode(abspath)))
1369
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1286
return internal_size_sha_file_byname(abspath, filters)[1]
1371
1288
def stat_and_sha1(self, abspath):
1372
1289
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1377
1294
statvalue = os.fstat(file_obj.fileno())
1379
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1296
file_obj = filtered_input_file(file_obj, filters)
1380
1297
sha1 = osutils.size_sha_file(file_obj)[1]
1382
1299
file_obj.close()
1383
1300
return statvalue, sha1
1386
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1387
"""Dirstate working tree that supports content filtering.
1389
The dirstate holds the hash and size of the canonical form of the file,
1390
and most methods must return that.
1393
def _file_content_summary(self, path, stat_result):
1394
# This is to support the somewhat obsolete path_content_summary method
1395
# with content filtering: see
1396
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1398
# If the dirstate cache is up to date and knows the hash and size,
1400
# Otherwise if there are no content filters, return the on-disk size
1401
# and leave the hash blank.
1402
# Otherwise, read and filter the on-disk file and use its size and
1405
# The dirstate doesn't store the size of the canonical form so we
1406
# can't trust it for content-filtered trees. We just return None.
1407
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1408
executable = self._is_executable_from_path_and_stat(path, stat_result)
1409
return ('file', None, executable, dirstate_sha1)
1412
1303
class WorkingTree4(DirStateWorkingTree):
1413
1304
"""This is the Format 4 working tree.
1415
This differs from WorkingTree by:
1306
This differs from WorkingTree3 by:
1416
1307
- Having a consolidated internal dirstate, stored in a
1417
1308
randomly-accessible sorted file on disk.
1418
1309
- Not having a regular inventory attribute. One can be synthesized
1446
1337
return views.PathBasedViews(self)
1449
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1451
missing_parent_conflicts = True
1453
supports_versioned_directories = True
1455
_lock_class = LockDir
1456
_lock_file_name = 'lock'
1458
def _open_control_files(self, a_bzrdir):
1459
transport = a_bzrdir.get_workingtree_transport(None)
1460
return LockableFiles(transport, self._lock_file_name,
1340
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1463
1341
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1464
1342
accelerator_tree=None, hardlink=False):
1465
1343
"""See WorkingTreeFormat.initialize().
1467
1345
:param revision_id: allows creating a working tree at a different
1468
revision than the branch is at.
1346
revision than the branch is at.
1469
1347
:param accelerator_tree: A tree which can be used for retrieving file
1470
1348
contents more quickly than the revision tree, i.e. a workingtree.
1471
1349
The revision tree will be used for cases where accelerator_tree's
1535
1413
if basis_root_id is not None:
1536
1414
wt._set_root_id(basis_root_id)
1416
# If content filtering is supported, do not use the accelerator
1417
# tree - the cost of transforming the content both ways and
1418
# checking for changed content can outweight the gains it gives.
1419
# Note: do NOT move this logic up higher - using the basis from
1420
# the accelerator tree is still desirable because that can save
1421
# a minute or more of processing on large trees!
1422
# The original tree may not have the same content filters
1423
# applied so we can't safely build the inventory delta from
1538
1425
if wt.supports_content_filtering():
1539
# The original tree may not have the same content filters
1540
# applied so we can't safely build the inventory delta from
1426
accelerator_tree = None
1542
1427
delta_from_tree = False
1544
1429
delta_from_tree = True
1564
1449
:param wt: the WorkingTree object
1567
def open(self, a_bzrdir, _found=False):
1568
"""Return the WorkingTree object for a_bzrdir
1570
_found is a private parameter, do not use it. It is used to indicate
1571
if format probing has already been done.
1574
# we are being called directly and must probe.
1575
raise NotImplementedError
1576
if not isinstance(a_bzrdir.transport, LocalTransport):
1577
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1578
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1581
1452
def _open(self, a_bzrdir, control_files):
1582
1453
"""Open the tree itself.
1677
class DirStateRevisionTree(InventoryTree):
1678
"""A revision tree pulling the inventory from a dirstate.
1680
Note that this is one of the historical (ie revision) trees cached in the
1681
dirstate for easy access, not the workingtree.
1548
class DirStateRevisionTree(Tree):
1549
"""A revision tree pulling the inventory from a dirstate."""
1684
1551
def __init__(self, dirstate, revision_id, repository):
1685
1552
self._dirstate = dirstate
1699
1566
def annotate_iter(self, file_id,
1700
1567
default_revision=_mod_revision.CURRENT_REVISION):
1701
1568
"""See Tree.annotate_iter"""
1702
text_key = (file_id, self.get_file_revision(file_id))
1569
text_key = (file_id, self.inventory[file_id].revision)
1703
1570
annotations = self._repository.texts.annotate(text_key)
1704
1571
return [(key[-1], line) for (key, line) in annotations]
1573
def _get_ancestors(self, default_revision):
1574
return set(self._repository.get_ancestry(self._revision_id,
1706
1576
def _comparison_data(self, entry, path):
1707
1577
"""See Tree._comparison_data."""
1708
1578
if entry is None:
1824
1694
elif kind == 'directory':
1825
1695
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1826
1696
elif kind == 'symlink':
1697
inv_entry.executable = False
1698
inv_entry.text_size = None
1827
1699
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1828
1700
elif kind == 'tree-reference':
1829
1701
inv_entry.reference_revision = fingerprint or None
1853
1725
parent_index = self._get_parent_index()
1854
1726
last_changed_revision = entry[1][parent_index][4]
1856
rev = self._repository.get_revision(last_changed_revision)
1857
except errors.NoSuchRevision:
1858
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1859
return rev.timestamp
1727
return self._repository.get_revision(last_changed_revision).timestamp
1861
1729
def get_file_sha1(self, file_id, path=None, stat_value=None):
1862
1730
entry = self._get_entry(file_id=file_id, path=path)
1866
1734
return parent_details[1]
1870
def get_file_revision(self, file_id):
1871
return self.inventory[file_id].revision
1873
1737
def get_file(self, file_id, path=None):
1874
1738
return StringIO(self.get_file_text(file_id))
1899
1763
return self._repository.iter_files_bytes(repo_desired_files)
1901
def get_symlink_target(self, file_id, path=None):
1765
def get_symlink_target(self, file_id):
1902
1766
entry = self._get_entry(file_id=file_id)
1903
1767
parent_index = self._get_parent_index()
1904
1768
if entry[1][parent_index][0] != 'l':
1933
1797
entry = self._get_entry(file_id=file_id)[1]
1934
1798
if entry is None:
1935
1799
raise errors.NoSuchId(tree=self, file_id=file_id)
1936
parent_index = self._get_parent_index()
1937
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1800
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1939
1802
def stored_kind(self, file_id):
1940
1803
"""See Tree.stored_kind"""
1957
1820
def is_executable(self, file_id, path=None):
1958
1821
ie = self.inventory[file_id]
1959
1822
if ie.kind != "file":
1961
1824
return ie.executable
1963
def is_locked(self):
1966
1826
def list_files(self, include_root=False, from_dir=None, recursive=True):
1967
1827
# We use a standard implementation, because DirStateRevisionTree is
1968
1828
# dealing with one of the parents of the current state
1981
1841
yield path, 'V', entry.kind, entry.file_id, entry
1983
1843
def lock_read(self):
1984
"""Lock the tree for a set of operations.
1986
:return: A bzrlib.lock.LogicalLockResult.
1844
"""Lock the tree for a set of operations."""
1988
1845
if not self._locked:
1989
1846
self._repository.lock_read()
1990
1847
if self._dirstate._lock_token is None:
1991
1848
self._dirstate.lock_read()
1992
1849
self._dirstate_locked = True
1993
1850
self._locked += 1
1994
return LogicalLockResult(self.unlock)
1996
1852
def _must_be_locked(self):
1997
1853
if not self._locked:
2076
1932
def make_source_parent_tree(source, target):
2077
1933
"""Change the source tree into a parent of the target."""
2078
1934
revid = source.commit('record tree')
2079
target.branch.fetch(source.branch, revid)
1935
target.branch.repository.fetch(source.branch.repository, revid)
2080
1936
target.set_parent_ids([revid])
2081
1937
return target.basis_tree(), target
2090
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1946
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2092
1947
from bzrlib.tests.test__dirstate_helpers import \
2093
compiled_dirstate_helpers_feature
2094
test_case.requireFeature(compiled_dirstate_helpers_feature)
1948
CompiledDirstateHelpersFeature
1949
if not CompiledDirstateHelpersFeature.available():
1950
from bzrlib.tests import UnavailableFeature
1951
raise UnavailableFeature(CompiledDirstateHelpersFeature)
2095
1952
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2096
1953
result = klass.make_source_parent_tree(source, target)
2097
1954
result[1]._iter_changes = ProcessEntryC
2128
1985
output. An unversioned file is defined as one with (False, False)
2129
1986
for the versioned pair.
1988
# NB: show_status depends on being able to pass in non-versioned files
1989
# and report them as unknown
2131
1990
# TODO: handle extra trees in the dirstate.
2132
1991
if (extra_trees or specific_files == []):
2133
1992
# we can't fast-path these cases (yet)