1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# Copyright (C) 2007-2010 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
42
35
from bzrlib import (
45
conflicts as _mod_conflicts,
55
43
revision as _mod_revision,
65
49
import bzrlib.branch
66
from bzrlib.transport import get_transport
70
from bzrlib import symbol_versioning
71
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
72
54
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
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
from bzrlib.lock import LogicalLockResult
75
57
from bzrlib.mutabletree import needs_tree_write_lock
76
58
from bzrlib.osutils import (
86
from bzrlib.trace import mutter, note
65
from bzrlib.trace import mutter
87
66
from bzrlib.transport.local import LocalTransport
88
67
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
68
from bzrlib.tree import Tree
98
69
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
351
322
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
352
323
elif kind == 'tree-reference':
353
324
if not self._repo_supports_tree_reference:
354
raise AssertionError(
356
"doesn't support tree references "
357
"required by entry %r"
325
raise errors.UnsupportedOperation(
326
self._generate_inventory,
327
self.branch.repository)
359
328
inv_entry.reference_revision = link_or_sha1 or None
360
329
elif kind != 'symlink':
361
330
raise AssertionError("unknown kind %r" % kind)
466
435
return osutils.lexists(pathjoin(
467
436
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
438
def has_or_had_id(self, file_id):
439
state = self.current_dirstate()
440
row, parents = self._get_entry(file_id=file_id)
441
return row is not None
470
444
def id2path(self, file_id):
471
445
"Convert a file-id to a path."
594
568
return _mod_revision.NULL_REVISION
596
570
def lock_read(self):
597
"""See Branch.lock_read, and WorkingTree.unlock."""
571
"""See Branch.lock_read, and WorkingTree.unlock.
573
:return: A bzrlib.lock.LogicalLockResult.
598
575
self.branch.lock_read()
600
577
self._control_files.lock_read()
634
612
self.branch.unlock()
614
return LogicalLockResult(self.unlock)
637
616
def lock_tree_write(self):
638
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
617
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
619
:return: A bzrlib.lock.LogicalLockResult.
639
621
self.branch.lock_read()
640
self._lock_self_write()
622
return self._lock_self_write()
642
624
def lock_write(self):
643
"""See MutableTree.lock_write, and WorkingTree.unlock."""
625
"""See MutableTree.lock_write, and WorkingTree.unlock.
627
:return: A bzrlib.lock.LogicalLockResult.
644
629
self.branch.lock_write()
645
self._lock_self_write()
630
return self._lock_self_write()
647
632
@needs_tree_write_lock
648
633
def move(self, from_paths, to_dir, after=False):
718
703
from_entry = self._get_entry(path=from_rel)
719
704
if from_entry == (None, None):
720
705
raise errors.BzrMoveFailedError(from_rel,to_dir,
721
errors.NotVersionedError(path=str(from_rel)))
706
errors.NotVersionedError(path=from_rel))
723
708
from_id = from_entry[0][2]
724
709
to_rel = pathjoin(to_dir, from_tail)
1053
1038
def set_last_revision(self, new_revision):
1054
1039
"""Change the last revision in the working tree."""
1055
1040
parents = self.get_parent_ids()
1056
if new_revision in (NULL_REVISION, None):
1041
if new_revision in (_mod_revision.NULL_REVISION, None):
1057
1042
if len(parents) >= 2:
1058
1043
raise AssertionError(
1059
1044
"setting the last parent to none with a pending merge is "
1226
1211
# just forget the whole block.
1227
1212
entry_index = 0
1228
1213
while entry_index < len(block[1]):
1229
# Mark this file id as having been removed
1230
1214
entry = block[1][entry_index]
1231
ids_to_unversion.discard(entry[0][2])
1232
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1234
or not state._make_absent(entry)):
1215
if entry[1][0][0] in 'ar':
1216
# don't remove absent or renamed entries
1235
1217
entry_index += 1
1219
# Mark this file id as having been removed
1220
ids_to_unversion.discard(entry[0][2])
1221
if not state._make_absent(entry):
1222
# The block has not shrunk.
1236
1224
# go to the next block. (At the moment we dont delete empty
1238
1226
block_index += 1
1259
1247
# have to change the legacy inventory too.
1260
1248
if self._inventory is not None:
1261
1249
for file_id in file_ids:
1262
self._inventory.remove_recursive_id(file_id)
1250
if self._inventory.has_id(file_id):
1251
self._inventory.remove_recursive_id(file_id)
1264
1253
@needs_tree_write_lock
1265
1254
def rename_one(self, from_rel, to_rel, after=False):
1290
1279
if self._dirty:
1291
1280
raise AssertionError("attempting to write an inventory when the "
1292
1281
"dirstate is dirty will lose pending changes")
1293
self.current_dirstate().set_state_from_inventory(inv)
1294
self._make_dirty(reset_inventory=False)
1295
if self._inventory is not None:
1282
had_inventory = self._inventory is not None
1283
# Setting self._inventory = None forces the dirstate to regenerate the
1284
# working inventory. We do this because self.inventory may be inv, or
1285
# may have been modified, and either case would prevent a clean delta
1287
self._inventory = None
1289
delta = inv._make_delta(self.inventory)
1291
self.apply_inventory_delta(delta)
1296
1293
self._inventory = inv
1303
1300
self.tree = tree
1305
1302
def sha1(self, abspath):
1306
"""Return the sha1 of a file given its absolute path."""
1307
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1303
"""See dirstate.SHA1Provider.sha1()."""
1304
filters = self.tree._content_filter_stack(
1305
self.tree.relpath(osutils.safe_unicode(abspath)))
1308
1306
return internal_size_sha_file_byname(abspath, filters)[1]
1310
1308
def stat_and_sha1(self, abspath):
1311
"""Return the stat and sha1 of a file given its absolute path."""
1312
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1309
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1310
filters = self.tree._content_filter_stack(
1311
self.tree.relpath(osutils.safe_unicode(abspath)))
1313
1312
file_obj = file(abspath, 'rb', 65000)
1315
1314
statvalue = os.fstat(file_obj.fileno())
1321
1320
return statvalue, sha1
1323
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1324
"""Dirstate working tree that supports content filtering.
1326
The dirstate holds the hash and size of the canonical form of the file,
1327
and most methods must return that.
1330
def _file_content_summary(self, path, stat_result):
1331
# This is to support the somewhat obsolete path_content_summary method
1332
# with content filtering: see
1333
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1335
# If the dirstate cache is up to date and knows the hash and size,
1337
# Otherwise if there are no content filters, return the on-disk size
1338
# and leave the hash blank.
1339
# Otherwise, read and filter the on-disk file and use its size and
1342
# The dirstate doesn't store the size of the canonical form so we
1343
# can't trust it for content-filtered trees. We just return None.
1344
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1345
executable = self._is_executable_from_path_and_stat(path, stat_result)
1346
return ('file', None, executable, dirstate_sha1)
1324
1349
class WorkingTree4(DirStateWorkingTree):
1325
1350
"""This is the Format 4 working tree.
1337
class WorkingTree5(DirStateWorkingTree):
1362
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1338
1363
"""This is the Format 5 working tree.
1340
1365
This differs from WorkingTree4 by:
1341
1366
- Supporting content filtering.
1368
This is new in bzr 1.11.
1372
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1373
"""This is the Format 6 working tree.
1375
This differs from WorkingTree5 by:
1342
1376
- Supporting a current view that may mask the set of files in a tree
1343
1377
impacted by most user operations.
1345
This is new in bzr 1.11.
1379
This is new in bzr 1.14.
1348
1382
def _make_views(self):
1352
1386
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1353
1388
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1354
1389
accelerator_tree=None, hardlink=False):
1355
1390
"""See WorkingTreeFormat.initialize().
1394
1429
wt.lock_tree_write()
1396
1431
self._init_custom_control_files(wt)
1397
if revision_id in (None, NULL_REVISION):
1432
if revision_id in (None, _mod_revision.NULL_REVISION):
1398
1433
if branch.repository.supports_rich_root():
1399
1434
wt._set_root_id(generate_ids.gen_root_id())
1412
1447
if basis is None:
1413
1448
basis = branch.repository.revision_tree(revision_id)
1414
if revision_id == NULL_REVISION:
1449
if revision_id == _mod_revision.NULL_REVISION:
1415
1450
parents_list = []
1417
1452
parents_list = [(revision_id, basis)]
1425
1460
if basis_root_id is not None:
1426
1461
wt._set_root_id(basis_root_id)
1463
if wt.supports_content_filtering():
1464
# The original tree may not have the same content filters
1465
# applied so we can't safely build the inventory delta from
1467
delta_from_tree = False
1469
delta_from_tree = True
1428
1470
# delta_from_tree is safe even for DirStateRevisionTrees,
1429
1471
# because wt4.apply_inventory_delta does not mutate the input
1430
1472
# inventory entries.
1431
1473
transform.build_tree(basis, wt, accelerator_tree,
1432
hardlink=hardlink, delta_from_tree=True)
1475
delta_from_tree=delta_from_tree)
1511
1554
"""See WorkingTreeFormat.get_format_description()."""
1512
1555
return "Working tree format 5"
1557
def supports_content_filtering(self):
1561
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1562
"""WorkingTree format supporting views.
1565
upgrade_recommended = False
1567
_tree_class = WorkingTree6
1569
def get_format_string(self):
1570
"""See WorkingTreeFormat.get_format_string()."""
1571
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1573
def get_format_description(self):
1574
"""See WorkingTreeFormat.get_format_description()."""
1575
return "Working tree format 6"
1514
1577
def _init_custom_control_files(self, wt):
1515
1578
"""Subclasses with custom control files should override this method."""
1516
1579
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1525
1588
class DirStateRevisionTree(Tree):
1526
"""A revision tree pulling the inventory from a dirstate."""
1589
"""A revision tree pulling the inventory from a dirstate.
1591
Note that this is one of the historical (ie revision) trees cached in the
1592
dirstate for easy access, not the workingtree.
1528
1595
def __init__(self, dirstate, revision_id, repository):
1529
1596
self._dirstate = dirstate
1671
1738
elif kind == 'directory':
1672
1739
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1673
1740
elif kind == 'symlink':
1674
inv_entry.executable = False
1675
inv_entry.text_size = None
1676
1741
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1677
1742
elif kind == 'tree-reference':
1678
1743
inv_entry.reference_revision = fingerprint or None
1702
1767
parent_index = self._get_parent_index()
1703
1768
last_changed_revision = entry[1][parent_index][4]
1704
return self._repository.get_revision(last_changed_revision).timestamp
1770
rev = self._repository.get_revision(last_changed_revision)
1771
except errors.NoSuchRevision:
1772
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1773
return rev.timestamp
1706
1775
def get_file_sha1(self, file_id, path=None, stat_value=None):
1707
1776
entry = self._get_entry(file_id=file_id, path=path)
1745
1814
if entry[1][parent_index][0] != 'l':
1748
# At present, none of the tree implementations supports non-ascii
1749
# symlink targets. So we will just assume that the dirstate path is
1751
return entry[1][parent_index][1]
1817
target = entry[1][parent_index][1]
1818
target = target.decode('utf8')
1753
1821
def get_revision_id(self):
1754
1822
"""Return the revision id for this tree."""
1775
1843
entry = self._get_entry(file_id=file_id)[1]
1776
1844
if entry is None:
1777
1845
raise errors.NoSuchId(tree=self, file_id=file_id)
1778
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1846
parent_index = self._get_parent_index()
1847
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1780
1849
def stored_kind(self, file_id):
1781
1850
"""See Tree.stored_kind"""
1802
1871
return ie.executable
1804
def list_files(self, include_root=False):
1873
def is_locked(self):
1876
def list_files(self, include_root=False, from_dir=None, recursive=True):
1805
1877
# We use a standard implementation, because DirStateRevisionTree is
1806
1878
# dealing with one of the parents of the current state
1807
1879
inv = self._get_inventory()
1808
entries = inv.iter_entries()
1809
if self.inventory.root is not None and not include_root:
1880
if from_dir is None:
1883
from_dir_id = inv.path2id(from_dir)
1884
if from_dir_id is None:
1885
# Directory not versioned
1887
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1888
if inv.root is not None and not include_root and from_dir is None:
1811
1890
for path, entry in entries:
1812
1891
yield path, 'V', entry.kind, entry.file_id, entry
1814
1893
def lock_read(self):
1815
"""Lock the tree for a set of operations."""
1894
"""Lock the tree for a set of operations.
1896
:return: A bzrlib.lock.LogicalLockResult.
1816
1898
if not self._locked:
1817
1899
self._repository.lock_read()
1818
1900
if self._dirstate._lock_token is None:
1819
1901
self._dirstate.lock_read()
1820
1902
self._dirstate_locked = True
1821
1903
self._locked += 1
1904
return LogicalLockResult(self.unlock)
1823
1906
def _must_be_locked(self):
1824
1907
if not self._locked:
1917
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2000
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1918
2002
from bzrlib.tests.test__dirstate_helpers import \
1919
CompiledDirstateHelpersFeature
1920
if not CompiledDirstateHelpersFeature.available():
1921
from bzrlib.tests import UnavailableFeature
1922
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1923
from bzrlib._dirstate_helpers_c import ProcessEntryC
2003
compiled_dirstate_helpers_feature
2004
test_case.requireFeature(compiled_dirstate_helpers_feature)
2005
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1924
2006
result = klass.make_source_parent_tree(source, target)
1925
2007
result[1]._iter_changes = ProcessEntryC
1956
2038
output. An unversioned file is defined as one with (False, False)
1957
2039
for the versioned pair.
1959
# NB: show_status depends on being able to pass in non-versioned files
1960
# and report them as unknown
1961
2041
# TODO: handle extra trees in the dirstate.
1962
2042
if (extra_trees or specific_files == []):
1963
2043
# we can't fast-path these cases (yet)
1966
2046
require_versioned, want_unversioned=want_unversioned)
1967
2047
parent_ids = self.target.get_parent_ids()
1968
2048
if not (self.source._revision_id in parent_ids
1969
or self.source._revision_id == NULL_REVISION):
2049
or self.source._revision_id == _mod_revision.NULL_REVISION):
1970
2050
raise AssertionError(
1971
2051
"revision {%s} is not stored in {%s}, but %s "
1972
2052
"can only be used for trees stored in the dirstate"
1973
2053
% (self.source._revision_id, self.target, self.iter_changes))
1974
2054
target_index = 0
1975
if self.source._revision_id == NULL_REVISION:
2055
if self.source._revision_id == _mod_revision.NULL_REVISION:
1976
2056
source_index = None
1977
2057
indices = (target_index,)
1994
2074
specific_files = set([''])
1995
2075
# -- specific_files is now a utf8 path set --
1996
search_specific_files = set()
1997
2077
# -- get the state object and prepare it.
1998
2078
state = self.target.current_dirstate()
1999
2079
state._read_dirblocks_if_needed()
2000
2080
if require_versioned:
2001
2081
# -- check all supplied paths are versioned in a search tree. --
2002
all_versioned = True
2003
2083
for path in specific_files:
2004
2084
path_entries = state._entries_for_path(path)
2005
2085
if not path_entries:
2006
2086
# this specified path is not present at all: error
2007
all_versioned = False
2087
not_versioned.append(path)
2009
2089
found_versioned = False
2010
2090
# for each id at this path
2011
2091
for entry in path_entries:
2018
2098
if not found_versioned:
2019
2099
# none of the indexes was not 'absent' at all ids for this
2021
all_versioned = False
2023
if not all_versioned:
2024
raise errors.PathsNotVersionedError(specific_files)
2101
not_versioned.append(path)
2102
if len(not_versioned) > 0:
2103
raise errors.PathsNotVersionedError(not_versioned)
2025
2104
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2026
for path in specific_files:
2027
other_specific_files = specific_files.difference(set([path]))
2028
if not osutils.is_inside_any(other_specific_files, path):
2029
# this is a top level path, we must check it.
2030
search_specific_files.add(path)
2105
search_specific_files = osutils.minimum_path_selection(specific_files)
2032
2107
use_filesystem_for_exec = (sys.platform != 'win32')
2033
2108
iter_changes = self.target._iter_changes(include_unchanged,
2045
2120
(revisiontree.RevisionTree, DirStateRevisionTree)):
2047
2122
# the source revid must be in the target dirstate
2048
if not (source._revision_id == NULL_REVISION or
2123
if not (source._revision_id == _mod_revision.NULL_REVISION or
2049
2124
source._revision_id in target.get_parent_ids()):
2050
2125
# TODO: what about ghosts? it may well need to
2051
2126
# check for them explicitly.
2112
2187
# tree during upgrade.
2113
2188
tree._control_files.lock_write()
2190
self.update_format(tree)
2192
tree._control_files.unlock()
2194
def update_format(self, tree):
2195
"""Change the format marker."""
2196
tree._transport.put_bytes('format',
2197
self.target_format.get_format_string(),
2198
mode=tree.bzrdir._get_file_mode())
2201
class Converter4or5to6(object):
2202
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2205
self.target_format = WorkingTreeFormat6()
2207
def convert(self, tree):
2208
# lock the control files not the tree, so that we don't get tree
2209
# on-unlock behaviours, and so that no-one else diddles with the
2210
# tree during upgrade.
2211
tree._control_files.lock_write()
2115
2213
self.init_custom_control_files(tree)
2116
2214
self.update_format(tree)