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
1290
1278
if self._dirty:
1291
1279
raise AssertionError("attempting to write an inventory when the "
1292
1280
"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:
1281
had_inventory = self._inventory is not None
1282
# Setting self._inventory = None forces the dirstate to regenerate the
1283
# working inventory. We do this because self.inventory may be inv, or
1284
# may have been modified, and either case would prevent a clean delta
1286
self._inventory = None
1288
delta = inv._make_delta(self.inventory)
1290
self.apply_inventory_delta(delta)
1296
1292
self._inventory = inv
1303
1299
self.tree = tree
1305
1301
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))
1302
"""See dirstate.SHA1Provider.sha1()."""
1303
filters = self.tree._content_filter_stack(
1304
self.tree.relpath(osutils.safe_unicode(abspath)))
1308
1305
return internal_size_sha_file_byname(abspath, filters)[1]
1310
1307
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))
1308
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1309
filters = self.tree._content_filter_stack(
1310
self.tree.relpath(osutils.safe_unicode(abspath)))
1313
1311
file_obj = file(abspath, 'rb', 65000)
1315
1313
statvalue = os.fstat(file_obj.fileno())
1321
1319
return statvalue, sha1
1322
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1323
"""Dirstate working tree that supports content filtering.
1325
The dirstate holds the hash and size of the canonical form of the file,
1326
and most methods must return that.
1329
def _file_content_summary(self, path, stat_result):
1330
# This is to support the somewhat obsolete path_content_summary method
1331
# with content filtering: see
1332
# <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1334
# If the dirstate cache is up to date and knows the hash and size,
1336
# Otherwise if there are no content filters, return the on-disk size
1337
# and leave the hash blank.
1338
# Otherwise, read and filter the on-disk file and use its size and
1341
# The dirstate doesn't store the size of the canonical form so we
1342
# can't trust it for content-filtered trees. We just return None.
1343
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1344
executable = self._is_executable_from_path_and_stat(path, stat_result)
1345
return ('file', None, executable, dirstate_sha1)
1324
1348
class WorkingTree4(DirStateWorkingTree):
1325
1349
"""This is the Format 4 working tree.
1337
class WorkingTree5(DirStateWorkingTree):
1361
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1338
1362
"""This is the Format 5 working tree.
1340
1364
This differs from WorkingTree4 by:
1341
1365
- Supporting content filtering.
1367
This is new in bzr 1.11.
1371
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1372
"""This is the Format 6 working tree.
1374
This differs from WorkingTree5 by:
1342
1375
- Supporting a current view that may mask the set of files in a tree
1343
1376
impacted by most user operations.
1345
This is new in bzr 1.11.
1378
This is new in bzr 1.14.
1348
1381
def _make_views(self):
1352
1385
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1353
1387
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1354
1388
accelerator_tree=None, hardlink=False):
1355
1389
"""See WorkingTreeFormat.initialize().
1394
1428
wt.lock_tree_write()
1396
1430
self._init_custom_control_files(wt)
1397
if revision_id in (None, NULL_REVISION):
1431
if revision_id in (None, _mod_revision.NULL_REVISION):
1398
1432
if branch.repository.supports_rich_root():
1399
1433
wt._set_root_id(generate_ids.gen_root_id())
1412
1446
if basis is None:
1413
1447
basis = branch.repository.revision_tree(revision_id)
1414
if revision_id == NULL_REVISION:
1448
if revision_id == _mod_revision.NULL_REVISION:
1415
1449
parents_list = []
1417
1451
parents_list = [(revision_id, basis)]
1425
1459
if basis_root_id is not None:
1426
1460
wt._set_root_id(basis_root_id)
1462
if wt.supports_content_filtering():
1463
# The original tree may not have the same content filters
1464
# applied so we can't safely build the inventory delta from
1466
delta_from_tree = False
1468
delta_from_tree = True
1428
1469
# delta_from_tree is safe even for DirStateRevisionTrees,
1429
1470
# because wt4.apply_inventory_delta does not mutate the input
1430
1471
# inventory entries.
1431
1472
transform.build_tree(basis, wt, accelerator_tree,
1432
hardlink=hardlink, delta_from_tree=True)
1474
delta_from_tree=delta_from_tree)
1511
1553
"""See WorkingTreeFormat.get_format_description()."""
1512
1554
return "Working tree format 5"
1556
def supports_content_filtering(self):
1560
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1561
"""WorkingTree format supporting views.
1564
upgrade_recommended = False
1566
_tree_class = WorkingTree6
1568
def get_format_string(self):
1569
"""See WorkingTreeFormat.get_format_string()."""
1570
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1572
def get_format_description(self):
1573
"""See WorkingTreeFormat.get_format_description()."""
1574
return "Working tree format 6"
1514
1576
def _init_custom_control_files(self, wt):
1515
1577
"""Subclasses with custom control files should override this method."""
1516
1578
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1525
1587
class DirStateRevisionTree(Tree):
1526
"""A revision tree pulling the inventory from a dirstate."""
1588
"""A revision tree pulling the inventory from a dirstate.
1590
Note that this is one of the historical (ie revision) trees cached in the
1591
dirstate for easy access, not the workingtree.
1528
1594
def __init__(self, dirstate, revision_id, repository):
1529
1595
self._dirstate = dirstate
1702
1768
parent_index = self._get_parent_index()
1703
1769
last_changed_revision = entry[1][parent_index][4]
1704
return self._repository.get_revision(last_changed_revision).timestamp
1771
rev = self._repository.get_revision(last_changed_revision)
1772
except errors.NoSuchRevision:
1773
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1774
return rev.timestamp
1706
1776
def get_file_sha1(self, file_id, path=None, stat_value=None):
1707
1777
entry = self._get_entry(file_id=file_id, path=path)
1745
1815
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]
1818
target = entry[1][parent_index][1]
1819
target = target.decode('utf8')
1753
1822
def get_revision_id(self):
1754
1823
"""Return the revision id for this tree."""
1775
1844
entry = self._get_entry(file_id=file_id)[1]
1776
1845
if entry is None:
1777
1846
raise errors.NoSuchId(tree=self, file_id=file_id)
1778
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1847
parent_index = self._get_parent_index()
1848
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1780
1850
def stored_kind(self, file_id):
1781
1851
"""See Tree.stored_kind"""
1802
1872
return ie.executable
1804
def list_files(self, include_root=False):
1874
def is_locked(self):
1877
def list_files(self, include_root=False, from_dir=None, recursive=True):
1805
1878
# We use a standard implementation, because DirStateRevisionTree is
1806
1879
# dealing with one of the parents of the current state
1807
1880
inv = self._get_inventory()
1808
entries = inv.iter_entries()
1809
if self.inventory.root is not None and not include_root:
1881
if from_dir is None:
1884
from_dir_id = inv.path2id(from_dir)
1885
if from_dir_id is None:
1886
# Directory not versioned
1888
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1889
if inv.root is not None and not include_root and from_dir is None:
1811
1891
for path, entry in entries:
1812
1892
yield path, 'V', entry.kind, entry.file_id, entry
1814
1894
def lock_read(self):
1815
"""Lock the tree for a set of operations."""
1895
"""Lock the tree for a set of operations.
1897
:return: A bzrlib.lock.LogicalLockResult.
1816
1899
if not self._locked:
1817
1900
self._repository.lock_read()
1818
1901
if self._dirstate._lock_token is None:
1819
1902
self._dirstate.lock_read()
1820
1903
self._dirstate_locked = True
1821
1904
self._locked += 1
1905
return LogicalLockResult(self.unlock)
1823
1907
def _must_be_locked(self):
1824
1908
if not self._locked:
1917
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2001
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1918
2003
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
2004
compiled_dirstate_helpers_feature
2005
test_case.requireFeature(compiled_dirstate_helpers_feature)
2006
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1924
2007
result = klass.make_source_parent_tree(source, target)
1925
2008
result[1]._iter_changes = ProcessEntryC
1956
2039
output. An unversioned file is defined as one with (False, False)
1957
2040
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
2042
# TODO: handle extra trees in the dirstate.
1962
2043
if (extra_trees or specific_files == []):
1963
2044
# we can't fast-path these cases (yet)
1966
2047
require_versioned, want_unversioned=want_unversioned)
1967
2048
parent_ids = self.target.get_parent_ids()
1968
2049
if not (self.source._revision_id in parent_ids
1969
or self.source._revision_id == NULL_REVISION):
2050
or self.source._revision_id == _mod_revision.NULL_REVISION):
1970
2051
raise AssertionError(
1971
2052
"revision {%s} is not stored in {%s}, but %s "
1972
2053
"can only be used for trees stored in the dirstate"
1973
2054
% (self.source._revision_id, self.target, self.iter_changes))
1974
2055
target_index = 0
1975
if self.source._revision_id == NULL_REVISION:
2056
if self.source._revision_id == _mod_revision.NULL_REVISION:
1976
2057
source_index = None
1977
2058
indices = (target_index,)
1994
2075
specific_files = set([''])
1995
2076
# -- specific_files is now a utf8 path set --
1996
search_specific_files = set()
1997
2078
# -- get the state object and prepare it.
1998
2079
state = self.target.current_dirstate()
1999
2080
state._read_dirblocks_if_needed()
2000
2081
if require_versioned:
2001
2082
# -- check all supplied paths are versioned in a search tree. --
2002
all_versioned = True
2003
2084
for path in specific_files:
2004
2085
path_entries = state._entries_for_path(path)
2005
2086
if not path_entries:
2006
2087
# this specified path is not present at all: error
2007
all_versioned = False
2088
not_versioned.append(path)
2009
2090
found_versioned = False
2010
2091
# for each id at this path
2011
2092
for entry in path_entries:
2018
2099
if not found_versioned:
2019
2100
# 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)
2102
not_versioned.append(path)
2103
if len(not_versioned) > 0:
2104
raise errors.PathsNotVersionedError(not_versioned)
2025
2105
# -- 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)
2106
search_specific_files = osutils.minimum_path_selection(specific_files)
2032
2108
use_filesystem_for_exec = (sys.platform != 'win32')
2033
2109
iter_changes = self.target._iter_changes(include_unchanged,
2045
2121
(revisiontree.RevisionTree, DirStateRevisionTree)):
2047
2123
# the source revid must be in the target dirstate
2048
if not (source._revision_id == NULL_REVISION or
2124
if not (source._revision_id == _mod_revision.NULL_REVISION or
2049
2125
source._revision_id in target.get_parent_ids()):
2050
2126
# TODO: what about ghosts? it may well need to
2051
2127
# check for them explicitly.
2112
2188
# tree during upgrade.
2113
2189
tree._control_files.lock_write()
2191
self.update_format(tree)
2193
tree._control_files.unlock()
2195
def update_format(self, tree):
2196
"""Change the format marker."""
2197
tree._transport.put_bytes('format',
2198
self.target_format.get_format_string(),
2199
mode=tree.bzrdir._get_file_mode())
2202
class Converter4or5to6(object):
2203
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2206
self.target_format = WorkingTreeFormat6()
2208
def convert(self, tree):
2209
# lock the control files not the tree, so that we don't get tree
2210
# on-unlock behaviours, and so that no-one else diddles with the
2211
# tree during upgrade.
2212
tree._control_files.lock_write()
2115
2214
self.init_custom_control_files(tree)
2116
2215
self.update_format(tree)