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
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):
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):
79
102
def __init__(self, basedir,
81
104
_control_files=None,
91
114
self._format = _format
92
115
self.bzrdir = _bzrdir
93
116
basedir = safe_unicode(basedir)
94
trace.mutter("opening working tree %r", basedir)
117
mutter("opening working tree %r", basedir)
95
118
self._branch = branch
96
119
self.basedir = realpath(basedir)
97
120
# if branch is at our basedir and is a format 6 or less
131
154
state.add(f, file_id, kind, None, '')
132
155
self._make_dirty(reset_inventory=True)
134
def _get_check_refs(self):
135
"""Return the references needed to perform a check of this tree."""
136
return [('trees', self.last_revision())]
138
157
def _make_dirty(self, reset_inventory):
139
158
"""Make the tree state dirty.
193
212
def _comparison_data(self, entry, path):
194
213
kind, executable, stat_value = \
195
WorkingTree._comparison_data(self, entry, path)
214
WorkingTree3._comparison_data(self, entry, path)
196
215
# it looks like a plain directory, but it's really a reference -- see
198
217
if (self._repo_supports_tree_reference and kind == 'directory'
204
223
def commit(self, message=None, revprops=None, *args, **kwargs):
205
224
# mark the tree as dirty post commit - commit
206
225
# can change the current versioned list by doing deletes.
207
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
226
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
208
227
self._make_dirty(reset_inventory=True)
445
464
return osutils.lexists(pathjoin(
446
465
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
448
def has_or_had_id(self, file_id):
449
state = self.current_dirstate()
450
row, parents = self._get_entry(file_id=file_id)
451
return row is not None
454
468
def id2path(self, file_id):
455
469
"Convert a file-id to a path."
578
592
return _mod_revision.NULL_REVISION
580
594
def lock_read(self):
581
"""See Branch.lock_read, and WorkingTree.unlock.
583
:return: A bzrlib.lock.LogicalLockResult.
595
"""See Branch.lock_read, and WorkingTree.unlock."""
585
596
self.branch.lock_read()
587
598
self._control_files.lock_read()
622
632
self.branch.unlock()
624
return LogicalLockResult(self.unlock)
626
635
def lock_tree_write(self):
627
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
629
:return: A bzrlib.lock.LogicalLockResult.
636
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
631
637
self.branch.lock_read()
632
return self._lock_self_write()
638
self._lock_self_write()
634
640
def lock_write(self):
635
"""See MutableTree.lock_write, and WorkingTree.unlock.
637
:return: A bzrlib.lock.LogicalLockResult.
641
"""See MutableTree.lock_write, and WorkingTree.unlock."""
639
642
self.branch.lock_write()
640
return self._lock_self_write()
643
self._lock_self_write()
642
645
@needs_tree_write_lock
643
646
def move(self, from_paths, to_dir, after=False):
713
716
from_entry = self._get_entry(path=from_rel)
714
717
if from_entry == (None, None):
715
718
raise errors.BzrMoveFailedError(from_rel,to_dir,
716
errors.NotVersionedError(path=from_rel))
719
errors.NotVersionedError(path=str(from_rel)))
718
721
from_id = from_entry[0][2]
719
722
to_rel = pathjoin(to_dir, from_tail)
1048
1051
def set_last_revision(self, new_revision):
1049
1052
"""Change the last revision in the working tree."""
1050
1053
parents = self.get_parent_ids()
1051
if new_revision in (_mod_revision.NULL_REVISION, None):
1054
if new_revision in (NULL_REVISION, None):
1052
1055
if len(parents) >= 2:
1053
1056
raise AssertionError(
1054
1057
"setting the last parent to none with a pending merge is "
1221
1224
# just forget the whole block.
1222
1225
entry_index = 0
1223
1226
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1224
1228
entry = block[1][entry_index]
1225
if entry[1][0][0] in 'ar':
1226
# 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)):
1227
1233
entry_index += 1
1229
# Mark this file id as having been removed
1230
ids_to_unversion.discard(entry[0][2])
1231
if not state._make_absent(entry):
1232
# The block has not shrunk.
1234
1234
# go to the next block. (At the moment we dont delete empty
1236
1236
block_index += 1
1257
1257
# have to change the legacy inventory too.
1258
1258
if self._inventory is not None:
1259
1259
for file_id in file_ids:
1260
if self._inventory.has_id(file_id):
1261
self._inventory.remove_recursive_id(file_id)
1260
self._inventory.remove_recursive_id(file_id)
1263
1262
@needs_tree_write_lock
1264
1263
def rename_one(self, from_rel, to_rel, after=False):
1265
1264
"""See WorkingTree.rename_one"""
1267
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1266
WorkingTree.rename_one(self, from_rel, to_rel, after)
1269
1268
@needs_tree_write_lock
1270
1269
def apply_inventory_delta(self, changes):
1289
1288
if self._dirty:
1290
1289
raise AssertionError("attempting to write an inventory when the "
1291
1290
"dirstate is dirty will lose pending changes")
1292
had_inventory = self._inventory is not None
1293
# Setting self._inventory = None forces the dirstate to regenerate the
1294
# working inventory. We do this because self.inventory may be inv, or
1295
# may have been modified, and either case would prevent a clean delta
1297
self._inventory = None
1299
delta = inv._make_delta(self.inventory)
1301
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:
1303
1294
self._inventory = inv
1306
@needs_tree_write_lock
1307
def reset_state(self, revision_ids=None):
1308
"""Reset the state of the working tree.
1310
This does a hard-reset to a last-known-good state. This is a way to
1311
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1313
if revision_ids is None:
1314
revision_ids = self.get_parent_ids()
1315
if not revision_ids:
1316
base_tree = self.branch.repository.revision_tree(
1317
_mod_revision.NULL_REVISION)
1320
trees = zip(revision_ids,
1321
self.branch.repository.revision_trees(revision_ids))
1322
base_tree = trees[0][1]
1323
state = self.current_dirstate()
1324
# We don't support ghosts yet
1325
state.set_state_from_scratch(base_tree.inventory, trees, [])
1328
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1334
1304
"""See dirstate.SHA1Provider.sha1()."""
1335
1305
filters = self.tree._content_filter_stack(
1336
1306
self.tree.relpath(osutils.safe_unicode(abspath)))
1337
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1307
return internal_size_sha_file_byname(abspath, filters)[1]
1339
1309
def stat_and_sha1(self, abspath):
1340
1310
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1345
1315
statvalue = os.fstat(file_obj.fileno())
1347
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1317
file_obj = filtered_input_file(file_obj, filters)
1348
1318
sha1 = osutils.size_sha_file(file_obj)[1]
1350
1320
file_obj.close()
1351
1321
return statvalue, sha1
1354
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1355
"""Dirstate working tree that supports content filtering.
1357
The dirstate holds the hash and size of the canonical form of the file,
1358
and most methods must return that.
1361
def _file_content_summary(self, path, stat_result):
1362
# This is to support the somewhat obsolete path_content_summary method
1363
# with content filtering: see
1364
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1366
# If the dirstate cache is up to date and knows the hash and size,
1368
# Otherwise if there are no content filters, return the on-disk size
1369
# and leave the hash blank.
1370
# Otherwise, read and filter the on-disk file and use its size and
1373
# The dirstate doesn't store the size of the canonical form so we
1374
# can't trust it for content-filtered trees. We just return None.
1375
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1376
executable = self._is_executable_from_path_and_stat(path, stat_result)
1377
return ('file', None, executable, dirstate_sha1)
1380
1324
class WorkingTree4(DirStateWorkingTree):
1381
1325
"""This is the Format 4 working tree.
1383
This differs from WorkingTree by:
1327
This differs from WorkingTree3 by:
1384
1328
- Having a consolidated internal dirstate, stored in a
1385
1329
randomly-accessible sorted file on disk.
1386
1330
- Not having a regular inventory attribute. One can be synthesized
1414
1358
return views.PathBasedViews(self)
1417
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1419
missing_parent_conflicts = True
1421
_lock_class = LockDir
1422
_lock_file_name = 'lock'
1424
def _open_control_files(self, a_bzrdir):
1425
transport = a_bzrdir.get_workingtree_transport(None)
1426
return LockableFiles(transport, self._lock_file_name,
1361
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1429
1362
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1430
1363
accelerator_tree=None, hardlink=False):
1431
1364
"""See WorkingTreeFormat.initialize().
1470
1403
wt.lock_tree_write()
1472
1405
self._init_custom_control_files(wt)
1473
if revision_id in (None, _mod_revision.NULL_REVISION):
1406
if revision_id in (None, NULL_REVISION):
1474
1407
if branch.repository.supports_rich_root():
1475
1408
wt._set_root_id(generate_ids.gen_root_id())
1501
1434
if basis_root_id is not None:
1502
1435
wt._set_root_id(basis_root_id)
1504
if wt.supports_content_filtering():
1505
# The original tree may not have the same content filters
1506
# applied so we can't safely build the inventory delta from
1508
delta_from_tree = False
1510
delta_from_tree = True
1511
1437
# delta_from_tree is safe even for DirStateRevisionTrees,
1512
1438
# because wt4.apply_inventory_delta does not mutate the input
1513
1439
# inventory entries.
1514
1440
transform.build_tree(basis, wt, accelerator_tree,
1516
delta_from_tree=delta_from_tree)
1441
hardlink=hardlink, delta_from_tree=True)
1530
1455
:param wt: the WorkingTree object
1533
def open(self, a_bzrdir, _found=False):
1534
"""Return the WorkingTree object for a_bzrdir
1536
_found is a private parameter, do not use it. It is used to indicate
1537
if format probing has already been done.
1540
# we are being called directly and must probe.
1541
raise NotImplementedError
1542
if not isinstance(a_bzrdir.transport, LocalTransport):
1543
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1544
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1547
1458
def _open(self, a_bzrdir, control_files):
1548
1459
"""Open the tree itself.
1643
class DirStateRevisionTree(InventoryTree):
1644
"""A revision tree pulling the inventory from a dirstate.
1646
Note that this is one of the historical (ie revision) trees cached in the
1647
dirstate for easy access, not the workingtree.
1554
class DirStateRevisionTree(Tree):
1555
"""A revision tree pulling the inventory from a dirstate."""
1650
1557
def __init__(self, dirstate, revision_id, repository):
1651
1558
self._dirstate = dirstate
1665
1572
def annotate_iter(self, file_id,
1666
1573
default_revision=_mod_revision.CURRENT_REVISION):
1667
1574
"""See Tree.annotate_iter"""
1668
text_key = (file_id, self.get_file_revision(file_id))
1575
text_key = (file_id, self.inventory[file_id].revision)
1669
1576
annotations = self._repository.texts.annotate(text_key)
1670
1577
return [(key[-1], line) for (key, line) in annotations]
1793
1700
elif kind == 'directory':
1794
1701
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1795
1702
elif kind == 'symlink':
1703
inv_entry.executable = False
1704
inv_entry.text_size = None
1796
1705
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1797
1706
elif kind == 'tree-reference':
1798
1707
inv_entry.reference_revision = fingerprint or None
1822
1731
parent_index = self._get_parent_index()
1823
1732
last_changed_revision = entry[1][parent_index][4]
1825
rev = self._repository.get_revision(last_changed_revision)
1826
except errors.NoSuchRevision:
1827
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1828
return rev.timestamp
1733
return self._repository.get_revision(last_changed_revision).timestamp
1830
1735
def get_file_sha1(self, file_id, path=None, stat_value=None):
1831
1736
entry = self._get_entry(file_id=file_id, path=path)
1835
1740
return parent_details[1]
1839
def get_file_revision(self, file_id):
1840
return self.inventory[file_id].revision
1842
1743
def get_file(self, file_id, path=None):
1843
1744
return StringIO(self.get_file_text(file_id))
1902
1803
entry = self._get_entry(file_id=file_id)[1]
1903
1804
if entry is None:
1904
1805
raise errors.NoSuchId(tree=self, file_id=file_id)
1905
parent_index = self._get_parent_index()
1906
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1806
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1908
1808
def stored_kind(self, file_id):
1909
1809
"""See Tree.stored_kind"""
1926
1826
def is_executable(self, file_id, path=None):
1927
1827
ie = self.inventory[file_id]
1928
1828
if ie.kind != "file":
1930
1830
return ie.executable
1932
def is_locked(self):
1935
def list_files(self, include_root=False, from_dir=None, recursive=True):
1832
def list_files(self, include_root=False):
1936
1833
# We use a standard implementation, because DirStateRevisionTree is
1937
1834
# dealing with one of the parents of the current state
1938
1835
inv = self._get_inventory()
1939
if from_dir is None:
1942
from_dir_id = inv.path2id(from_dir)
1943
if from_dir_id is None:
1944
# Directory not versioned
1946
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1947
if inv.root is not None and not include_root and from_dir is None:
1836
entries = inv.iter_entries()
1837
if self.inventory.root is not None and not include_root:
1949
1839
for path, entry in entries:
1950
1840
yield path, 'V', entry.kind, entry.file_id, entry
1952
1842
def lock_read(self):
1953
"""Lock the tree for a set of operations.
1955
:return: A bzrlib.lock.LogicalLockResult.
1843
"""Lock the tree for a set of operations."""
1957
1844
if not self._locked:
1958
1845
self._repository.lock_read()
1959
1846
if self._dirstate._lock_token is None:
1960
1847
self._dirstate.lock_read()
1961
1848
self._dirstate_locked = True
1962
1849
self._locked += 1
1963
return LogicalLockResult(self.unlock)
1965
1851
def _must_be_locked(self):
1966
1852
if not self._locked:
2045
1931
def make_source_parent_tree(source, target):
2046
1932
"""Change the source tree into a parent of the target."""
2047
1933
revid = source.commit('record tree')
2048
target.branch.fetch(source.branch, revid)
1934
target.branch.repository.fetch(source.branch.repository, revid)
2049
1935
target.set_parent_ids([revid])
2050
1936
return target.basis_tree(), target
2059
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1945
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2061
1946
from bzrlib.tests.test__dirstate_helpers import \
2062
compiled_dirstate_helpers_feature
2063
test_case.requireFeature(compiled_dirstate_helpers_feature)
2064
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1947
CompiledDirstateHelpersFeature
1948
if not CompiledDirstateHelpersFeature.available():
1949
from bzrlib.tests import UnavailableFeature
1950
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1951
from bzrlib._dirstate_helpers_c import ProcessEntryC
2065
1952
result = klass.make_source_parent_tree(source, target)
2066
1953
result[1]._iter_changes = ProcessEntryC
2097
1984
output. An unversioned file is defined as one with (False, False)
2098
1985
for the versioned pair.
1987
# NB: show_status depends on being able to pass in non-versioned files
1988
# and report them as unknown
2100
1989
# TODO: handle extra trees in the dirstate.
2101
1990
if (extra_trees or specific_files == []):
2102
1991
# we can't fast-path these cases (yet)
2105
1994
require_versioned, want_unversioned=want_unversioned)
2106
1995
parent_ids = self.target.get_parent_ids()
2107
1996
if not (self.source._revision_id in parent_ids
2108
or self.source._revision_id == _mod_revision.NULL_REVISION):
1997
or self.source._revision_id == NULL_REVISION):
2109
1998
raise AssertionError(
2110
1999
"revision {%s} is not stored in {%s}, but %s "
2111
2000
"can only be used for trees stored in the dirstate"
2112
2001
% (self.source._revision_id, self.target, self.iter_changes))
2113
2002
target_index = 0
2114
if self.source._revision_id == _mod_revision.NULL_REVISION:
2003
if self.source._revision_id == NULL_REVISION:
2115
2004
source_index = None
2116
2005
indices = (target_index,)
2138
2027
state._read_dirblocks_if_needed()
2139
2028
if require_versioned:
2140
2029
# -- check all supplied paths are versioned in a search tree. --
2030
all_versioned = True
2142
2031
for path in specific_files:
2143
2032
path_entries = state._entries_for_path(path)
2144
2033
if not path_entries:
2145
2034
# this specified path is not present at all: error
2146
not_versioned.append(path)
2035
all_versioned = False
2148
2037
found_versioned = False
2149
2038
# for each id at this path
2150
2039
for entry in path_entries:
2157
2046
if not found_versioned:
2158
2047
# none of the indexes was not 'absent' at all ids for this
2160
not_versioned.append(path)
2161
if len(not_versioned) > 0:
2162
raise errors.PathsNotVersionedError(not_versioned)
2049
all_versioned = False
2051
if not all_versioned:
2052
raise errors.PathsNotVersionedError(specific_files)
2163
2053
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2164
2054
search_specific_files = osutils.minimum_path_selection(specific_files)
2179
2069
(revisiontree.RevisionTree, DirStateRevisionTree)):
2181
2071
# the source revid must be in the target dirstate
2182
if not (source._revision_id == _mod_revision.NULL_REVISION or
2072
if not (source._revision_id == NULL_REVISION or
2183
2073
source._revision_id in target.get_parent_ids()):
2184
2074
# TODO: what about ghosts? it may well need to
2185
2075
# check for them explicitly.