13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""WorkingTree4 format and implementation.
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
35
42
from bzrlib import (
45
conflicts as _mod_conflicts,
43
55
revision as _mod_revision,
49
65
import bzrlib.branch
66
from bzrlib.transport import get_transport
70
from bzrlib import symbol_versioning
53
71
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
72
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
73
import bzrlib.mutabletree
56
74
from bzrlib.mutabletree import needs_tree_write_lock
57
75
from bzrlib.osutils import (
64
from bzrlib.trace import mutter
85
from bzrlib.trace import mutter, note
65
86
from bzrlib.transport.local import LocalTransport
66
87
from bzrlib.tree import InterTree
88
from bzrlib.progress import DummyProgress, ProgressPhase
89
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
90
from bzrlib.rio import RioReader, rio_file, Stanza
91
from bzrlib.symbol_versioning import (deprecated_passed,
67
96
from bzrlib.tree import Tree
68
97
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
228
257
Otherwise, a SHA1Provider is returned that sha's the canonical
229
258
form of files, i.e. after read filters are applied.
231
if self.supports_content_filtering():
232
return ContentFilterAwareSHA1Provider(self)
260
#if self.supports_content_filtering():
261
# return ContentFilterAWareSHA1Provider(self)
236
264
def filter_unversioned_files(self, paths):
237
265
"""Filter out paths that are versioned.
321
349
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
322
350
elif kind == 'tree-reference':
323
351
if not self._repo_supports_tree_reference:
324
raise errors.UnsupportedOperation(
325
self._generate_inventory,
326
self.branch.repository)
352
raise AssertionError(
354
"doesn't support tree references "
355
"required by entry %r"
327
357
inv_entry.reference_revision = link_or_sha1 or None
328
358
elif kind != 'symlink':
329
359
raise AssertionError("unknown kind %r" % kind)
434
464
return osutils.lexists(pathjoin(
435
465
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
437
def has_or_had_id(self, file_id):
438
state = self.current_dirstate()
439
row, parents = self._get_entry(file_id=file_id)
440
return row is not None
443
468
def id2path(self, file_id):
444
469
"Convert a file-id to a path."
691
716
from_entry = self._get_entry(path=from_rel)
692
717
if from_entry == (None, None):
693
718
raise errors.BzrMoveFailedError(from_rel,to_dir,
694
errors.NotVersionedError(path=from_rel))
719
errors.NotVersionedError(path=str(from_rel)))
696
721
from_id = from_entry[0][2]
697
722
to_rel = pathjoin(to_dir, from_tail)
1199
1224
# just forget the whole block.
1200
1225
entry_index = 0
1201
1226
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1202
1228
entry = block[1][entry_index]
1203
if entry[1][0][0] in 'ar':
1204
# 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)):
1205
1233
entry_index += 1
1207
# Mark this file id as having been removed
1208
ids_to_unversion.discard(entry[0][2])
1209
if not state._make_absent(entry):
1210
# The block has not shrunk.
1212
1234
# go to the next block. (At the moment we dont delete empty
1214
1236
block_index += 1
1266
1288
if self._dirty:
1267
1289
raise AssertionError("attempting to write an inventory when the "
1268
1290
"dirstate is dirty will lose pending changes")
1269
had_inventory = self._inventory is not None
1270
# Setting self._inventory = None forces the dirstate to regenerate the
1271
# working inventory. We do this because self.inventory may be inv, or
1272
# may have been modified, and either case would prevent a clean delta
1274
self._inventory = None
1276
delta = inv._make_delta(self.inventory)
1278
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:
1280
1294
self._inventory = inv
1284
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1286
def __init__(self, tree):
1289
def sha1(self, abspath):
1290
"""See dirstate.SHA1Provider.sha1()."""
1291
filters = self.tree._content_filter_stack(
1292
self.tree.relpath(osutils.safe_unicode(abspath)))
1293
return internal_size_sha_file_byname(abspath, filters)[1]
1295
def stat_and_sha1(self, abspath):
1296
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1297
filters = self.tree._content_filter_stack(
1298
self.tree.relpath(osutils.safe_unicode(abspath)))
1299
file_obj = file(abspath, 'rb', 65000)
1301
statvalue = os.fstat(file_obj.fileno())
1303
file_obj = filtered_input_file(file_obj, filters)
1304
sha1 = osutils.size_sha_file(file_obj)[1]
1307
return statvalue, sha1
1310
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1311
"""Dirstate working tree that supports content filtering.
1313
The dirstate holds the hash and size of the canonical form of the file,
1314
and most methods must return that.
1317
def _file_content_summary(self, path, stat_result):
1318
# This is to support the somewhat obsolete path_content_summary method
1319
# with content filtering: see
1320
# <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1322
# If the dirstate cache is up to date and knows the hash and size,
1324
# Otherwise if there are no content filters, return the on-disk size
1325
# and leave the hash blank.
1326
# Otherwise, read and filter the on-disk file and use its size and
1329
# The dirstate doesn't store the size of the canonical form so we
1330
# can't trust it for content-filtered trees. We just return None.
1331
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1332
executable = self._is_executable_from_path_and_stat(path, stat_result)
1333
return ('file', None, executable, dirstate_sha1)
1336
1298
class WorkingTree4(DirStateWorkingTree):
1337
1299
"""This is the Format 4 working tree.
1349
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1311
class WorkingTree5(DirStateWorkingTree):
1350
1312
"""This is the Format 5 working tree.
1352
1314
This differs from WorkingTree4 by:
1353
1315
- Supporting content filtering.
1355
This is new in bzr 1.11.
1359
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1360
"""This is the Format 6 working tree.
1362
This differs from WorkingTree5 by:
1363
1316
- Supporting a current view that may mask the set of files in a tree
1364
1317
impacted by most user operations.
1366
This is new in bzr 1.14.
1319
This is new in bzr 1.11.
1369
1322
def _make_views(self):
1447
1399
if basis_root_id is not None:
1448
1400
wt._set_root_id(basis_root_id)
1450
if wt.supports_content_filtering():
1451
# The original tree may not have the same content filters
1452
# applied so we can't safely build the inventory delta from
1454
delta_from_tree = False
1456
delta_from_tree = True
1457
1402
# delta_from_tree is safe even for DirStateRevisionTrees,
1458
1403
# because wt4.apply_inventory_delta does not mutate the input
1459
1404
# inventory entries.
1460
1405
transform.build_tree(basis, wt, accelerator_tree,
1462
delta_from_tree=delta_from_tree)
1406
hardlink=hardlink, delta_from_tree=True)
1541
1481
"""See WorkingTreeFormat.get_format_description()."""
1542
1482
return "Working tree format 5"
1544
def supports_content_filtering(self):
1548
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1549
"""WorkingTree format supporting views.
1552
upgrade_recommended = False
1554
_tree_class = WorkingTree6
1556
def get_format_string(self):
1557
"""See WorkingTreeFormat.get_format_string()."""
1558
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1560
def get_format_description(self):
1561
"""See WorkingTreeFormat.get_format_description()."""
1562
return "Working tree format 6"
1564
1484
def _init_custom_control_files(self, wt):
1565
1485
"""Subclasses with custom control files should override this method."""
1566
1486
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1575
1495
class DirStateRevisionTree(Tree):
1576
"""A revision tree pulling the inventory from a dirstate.
1578
Note that this is one of the historical (ie revision) trees cached in the
1579
dirstate for easy access, not the workingtree.
1496
"""A revision tree pulling the inventory from a dirstate."""
1582
1498
def __init__(self, dirstate, revision_id, repository):
1583
1499
self._dirstate = dirstate
1756
1672
parent_index = self._get_parent_index()
1757
1673
last_changed_revision = entry[1][parent_index][4]
1759
rev = self._repository.get_revision(last_changed_revision)
1760
except errors.NoSuchRevision:
1761
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1762
return rev.timestamp
1674
return self._repository.get_revision(last_changed_revision).timestamp
1764
1676
def get_file_sha1(self, file_id, path=None, stat_value=None):
1765
1677
entry = self._get_entry(file_id=file_id, path=path)
1777
1689
return self.inventory[file_id].text_size
1779
1691
def get_file_text(self, file_id, path=None):
1780
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1781
return ''.join(content)
1692
return list(self.iter_files_bytes([(file_id, None)]))[0][1]
1783
1694
def get_reference_revision(self, file_id, path=None):
1784
1695
return self.inventory[file_id].reference_revision
1803
1714
if entry[1][parent_index][0] != 'l':
1806
target = entry[1][parent_index][1]
1807
target = target.decode('utf8')
1717
# At present, none of the tree implementations supports non-ascii
1718
# symlink targets. So we will just assume that the dirstate path is
1720
return entry[1][parent_index][1]
1810
1722
def get_revision_id(self):
1811
1723
"""Return the revision id for this tree."""
1832
1744
entry = self._get_entry(file_id=file_id)[1]
1833
1745
if entry is None:
1834
1746
raise errors.NoSuchId(tree=self, file_id=file_id)
1835
parent_index = self._get_parent_index()
1836
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1747
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1838
1749
def stored_kind(self, file_id):
1839
1750
"""See Tree.stored_kind"""
1860
1771
return ie.executable
1862
def list_files(self, include_root=False, from_dir=None, recursive=True):
1773
def list_files(self, include_root=False):
1863
1774
# We use a standard implementation, because DirStateRevisionTree is
1864
1775
# dealing with one of the parents of the current state
1865
1776
inv = self._get_inventory()
1866
if from_dir is None:
1869
from_dir_id = inv.path2id(from_dir)
1870
if from_dir_id is None:
1871
# Directory not versioned
1873
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1874
if inv.root is not None and not include_root and from_dir is None:
1777
entries = inv.iter_entries()
1778
if self.inventory.root is not None and not include_root:
1876
1780
for path, entry in entries:
1877
1781
yield path, 'V', entry.kind, entry.file_id, entry
1982
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1886
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1984
1887
from bzrlib.tests.test__dirstate_helpers import \
1985
compiled_dirstate_helpers_feature
1986
test_case.requireFeature(compiled_dirstate_helpers_feature)
1987
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1888
CompiledDirstateHelpersFeature
1889
if not CompiledDirstateHelpersFeature.available():
1890
from bzrlib.tests import UnavailableFeature
1891
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1892
from bzrlib._dirstate_helpers_c import ProcessEntryC
1988
1893
result = klass.make_source_parent_tree(source, target)
1989
1894
result[1]._iter_changes = ProcessEntryC
2028
1935
require_versioned, want_unversioned=want_unversioned)
2029
1936
parent_ids = self.target.get_parent_ids()
2030
1937
if not (self.source._revision_id in parent_ids
2031
or self.source._revision_id == _mod_revision.NULL_REVISION):
1938
or self.source._revision_id == NULL_REVISION):
2032
1939
raise AssertionError(
2033
1940
"revision {%s} is not stored in {%s}, but %s "
2034
1941
"can only be used for trees stored in the dirstate"
2035
1942
% (self.source._revision_id, self.target, self.iter_changes))
2036
1943
target_index = 0
2037
if self.source._revision_id == _mod_revision.NULL_REVISION:
1944
if self.source._revision_id == NULL_REVISION:
2038
1945
source_index = None
2039
1946
indices = (target_index,)
2056
1963
specific_files = set([''])
2057
1964
# -- specific_files is now a utf8 path set --
1965
search_specific_files = set()
2059
1966
# -- get the state object and prepare it.
2060
1967
state = self.target.current_dirstate()
2061
1968
state._read_dirblocks_if_needed()
2062
1969
if require_versioned:
2063
1970
# -- check all supplied paths are versioned in a search tree. --
1971
all_versioned = True
2065
1972
for path in specific_files:
2066
1973
path_entries = state._entries_for_path(path)
2067
1974
if not path_entries:
2068
1975
# this specified path is not present at all: error
2069
not_versioned.append(path)
1976
all_versioned = False
2071
1978
found_versioned = False
2072
1979
# for each id at this path
2073
1980
for entry in path_entries:
2080
1987
if not found_versioned:
2081
1988
# none of the indexes was not 'absent' at all ids for this
2083
not_versioned.append(path)
2084
if len(not_versioned) > 0:
2085
raise errors.PathsNotVersionedError(not_versioned)
1990
all_versioned = False
1992
if not all_versioned:
1993
raise errors.PathsNotVersionedError(specific_files)
2086
1994
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2087
search_specific_files = osutils.minimum_path_selection(specific_files)
1995
for path in specific_files:
1996
other_specific_files = specific_files.difference(set([path]))
1997
if not osutils.is_inside_any(other_specific_files, path):
1998
# this is a top level path, we must check it.
1999
search_specific_files.add(path)
2089
2001
use_filesystem_for_exec = (sys.platform != 'win32')
2090
2002
iter_changes = self.target._iter_changes(include_unchanged,
2102
2014
(revisiontree.RevisionTree, DirStateRevisionTree)):
2104
2016
# the source revid must be in the target dirstate
2105
if not (source._revision_id == _mod_revision.NULL_REVISION or
2017
if not (source._revision_id == NULL_REVISION or
2106
2018
source._revision_id in target.get_parent_ids()):
2107
2019
# TODO: what about ghosts? it may well need to
2108
2020
# check for them explicitly.
2169
2081
# tree during upgrade.
2170
2082
tree._control_files.lock_write()
2172
self.update_format(tree)
2174
tree._control_files.unlock()
2176
def update_format(self, tree):
2177
"""Change the format marker."""
2178
tree._transport.put_bytes('format',
2179
self.target_format.get_format_string(),
2180
mode=tree.bzrdir._get_file_mode())
2183
class Converter4or5to6(object):
2184
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2187
self.target_format = WorkingTreeFormat6()
2189
def convert(self, tree):
2190
# lock the control files not the tree, so that we don't get tree
2191
# on-unlock behaviours, and so that no-one else diddles with the
2192
# tree during upgrade.
2193
tree._control_files.lock_write()
2195
2084
self.init_custom_control_files(tree)
2196
2085
self.update_format(tree)