1
# Copyright (C) 2007-2010 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008 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
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
72
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
56
from bzrlib.lock import LogicalLockResult
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 (
65
from bzrlib.trace import mutter
86
from bzrlib.trace import mutter, note
66
87
from bzrlib.transport.local import LocalTransport
67
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,
68
97
from bzrlib.tree import Tree
69
98
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
435
464
return osutils.lexists(pathjoin(
436
465
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
444
468
def id2path(self, file_id):
445
469
"Convert a file-id to a path."
568
592
return _mod_revision.NULL_REVISION
570
594
def lock_read(self):
571
"""See Branch.lock_read, and WorkingTree.unlock.
573
:return: A bzrlib.lock.LogicalLockResult.
595
"""See Branch.lock_read, and WorkingTree.unlock."""
575
596
self.branch.lock_read()
577
598
self._control_files.lock_read()
612
632
self.branch.unlock()
614
return LogicalLockResult(self.unlock)
616
635
def lock_tree_write(self):
617
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
619
:return: A bzrlib.lock.LogicalLockResult.
636
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
621
637
self.branch.lock_read()
622
return self._lock_self_write()
638
self._lock_self_write()
624
640
def lock_write(self):
625
"""See MutableTree.lock_write, and WorkingTree.unlock.
627
:return: A bzrlib.lock.LogicalLockResult.
641
"""See MutableTree.lock_write, and WorkingTree.unlock."""
629
642
self.branch.lock_write()
630
return self._lock_self_write()
643
self._lock_self_write()
632
645
@needs_tree_write_lock
633
646
def move(self, from_paths, to_dir, after=False):
703
716
from_entry = self._get_entry(path=from_rel)
704
717
if from_entry == (None, None):
705
718
raise errors.BzrMoveFailedError(from_rel,to_dir,
706
errors.NotVersionedError(path=from_rel))
719
errors.NotVersionedError(path=str(from_rel)))
708
721
from_id = from_entry[0][2]
709
722
to_rel = pathjoin(to_dir, from_tail)
1038
1051
def set_last_revision(self, new_revision):
1039
1052
"""Change the last revision in the working tree."""
1040
1053
parents = self.get_parent_ids()
1041
if new_revision in (_mod_revision.NULL_REVISION, None):
1054
if new_revision in (NULL_REVISION, None):
1042
1055
if len(parents) >= 2:
1043
1056
raise AssertionError(
1044
1057
"setting the last parent to none with a pending merge is "
1211
1224
# just forget the whole block.
1212
1225
entry_index = 0
1213
1226
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1214
1228
entry = block[1][entry_index]
1215
if entry[1][0][0] in 'ar':
1216
# 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)):
1217
1233
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.
1224
1234
# go to the next block. (At the moment we dont delete empty
1226
1236
block_index += 1
1247
1257
# have to change the legacy inventory too.
1248
1258
if self._inventory is not None:
1249
1259
for file_id in file_ids:
1250
if self._inventory.has_id(file_id):
1251
self._inventory.remove_recursive_id(file_id)
1260
self._inventory.remove_recursive_id(file_id)
1253
1262
@needs_tree_write_lock
1254
1263
def rename_one(self, from_rel, to_rel, after=False):
1279
1288
if self._dirty:
1280
1289
raise AssertionError("attempting to write an inventory when the "
1281
1290
"dirstate is dirty will lose pending changes")
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)
1291
self.current_dirstate().set_state_from_inventory(inv)
1292
self._make_dirty(reset_inventory=False)
1293
if self._inventory is not None:
1293
1294
self._inventory = inv
1300
1301
self.tree = tree
1302
1303
def sha1(self, abspath):
1303
"""See dirstate.SHA1Provider.sha1()."""
1304
filters = self.tree._content_filter_stack(
1305
self.tree.relpath(osutils.safe_unicode(abspath)))
1304
"""Return the sha1 of a file given its absolute path."""
1305
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1306
1306
return internal_size_sha_file_byname(abspath, filters)[1]
1308
1308
def stat_and_sha1(self, abspath):
1309
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1310
filters = self.tree._content_filter_stack(
1311
self.tree.relpath(osutils.safe_unicode(abspath)))
1309
"""Return the stat and sha1 of a file given its absolute path."""
1310
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1312
1311
file_obj = file(abspath, 'rb', 65000)
1314
1313
statvalue = os.fstat(file_obj.fileno())
1320
1319
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)
1349
1322
class WorkingTree4(DirStateWorkingTree):
1350
1323
"""This is the Format 4 working tree.
1386
1359
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1388
1360
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1389
1361
accelerator_tree=None, hardlink=False):
1390
1362
"""See WorkingTreeFormat.initialize().
1429
1401
wt.lock_tree_write()
1431
1403
self._init_custom_control_files(wt)
1432
if revision_id in (None, _mod_revision.NULL_REVISION):
1404
if revision_id in (None, NULL_REVISION):
1433
1405
if branch.repository.supports_rich_root():
1434
1406
wt._set_root_id(generate_ids.gen_root_id())
1447
1419
if basis is None:
1448
1420
basis = branch.repository.revision_tree(revision_id)
1449
if revision_id == _mod_revision.NULL_REVISION:
1421
if revision_id == NULL_REVISION:
1450
1422
parents_list = []
1452
1424
parents_list = [(revision_id, basis)]
1460
1432
if basis_root_id is not None:
1461
1433
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
1470
1435
# delta_from_tree is safe even for DirStateRevisionTrees,
1471
1436
# because wt4.apply_inventory_delta does not mutate the input
1472
1437
# inventory entries.
1473
1438
transform.build_tree(basis, wt, accelerator_tree,
1475
delta_from_tree=delta_from_tree)
1439
hardlink=hardlink, delta_from_tree=True)
1588
1552
class DirStateRevisionTree(Tree):
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.
1553
"""A revision tree pulling the inventory from a dirstate."""
1595
1555
def __init__(self, dirstate, revision_id, repository):
1596
1556
self._dirstate = dirstate
1769
1729
parent_index = self._get_parent_index()
1770
1730
last_changed_revision = entry[1][parent_index][4]
1772
rev = self._repository.get_revision(last_changed_revision)
1773
except errors.NoSuchRevision:
1774
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1775
return rev.timestamp
1731
return self._repository.get_revision(last_changed_revision).timestamp
1777
1733
def get_file_sha1(self, file_id, path=None, stat_value=None):
1778
1734
entry = self._get_entry(file_id=file_id, path=path)
1845
1801
entry = self._get_entry(file_id=file_id)[1]
1846
1802
if entry is None:
1847
1803
raise errors.NoSuchId(tree=self, file_id=file_id)
1848
parent_index = self._get_parent_index()
1849
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1804
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1851
1806
def stored_kind(self, file_id):
1852
1807
"""See Tree.stored_kind"""
1873
1828
return ie.executable
1875
def is_locked(self):
1878
def list_files(self, include_root=False, from_dir=None, recursive=True):
1830
def list_files(self, include_root=False):
1879
1831
# We use a standard implementation, because DirStateRevisionTree is
1880
1832
# dealing with one of the parents of the current state
1881
1833
inv = self._get_inventory()
1882
if from_dir is None:
1885
from_dir_id = inv.path2id(from_dir)
1886
if from_dir_id is None:
1887
# Directory not versioned
1889
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1890
if inv.root is not None and not include_root and from_dir is None:
1834
entries = inv.iter_entries()
1835
if self.inventory.root is not None and not include_root:
1892
1837
for path, entry in entries:
1893
1838
yield path, 'V', entry.kind, entry.file_id, entry
1895
1840
def lock_read(self):
1896
"""Lock the tree for a set of operations.
1898
:return: A bzrlib.lock.LogicalLockResult.
1841
"""Lock the tree for a set of operations."""
1900
1842
if not self._locked:
1901
1843
self._repository.lock_read()
1902
1844
if self._dirstate._lock_token is None:
1903
1845
self._dirstate.lock_read()
1904
1846
self._dirstate_locked = True
1905
1847
self._locked += 1
1906
return LogicalLockResult(self.unlock)
1908
1849
def _must_be_locked(self):
1909
1850
if not self._locked:
2002
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1943
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2004
1944
from bzrlib.tests.test__dirstate_helpers import \
2005
compiled_dirstate_helpers_feature
2006
test_case.requireFeature(compiled_dirstate_helpers_feature)
2007
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1945
CompiledDirstateHelpersFeature
1946
if not CompiledDirstateHelpersFeature.available():
1947
from bzrlib.tests import UnavailableFeature
1948
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1949
from bzrlib._dirstate_helpers_c import ProcessEntryC
2008
1950
result = klass.make_source_parent_tree(source, target)
2009
1951
result[1]._iter_changes = ProcessEntryC
2040
1982
output. An unversioned file is defined as one with (False, False)
2041
1983
for the versioned pair.
1985
# NB: show_status depends on being able to pass in non-versioned files
1986
# and report them as unknown
2043
1987
# TODO: handle extra trees in the dirstate.
2044
1988
if (extra_trees or specific_files == []):
2045
1989
# we can't fast-path these cases (yet)
2048
1992
require_versioned, want_unversioned=want_unversioned)
2049
1993
parent_ids = self.target.get_parent_ids()
2050
1994
if not (self.source._revision_id in parent_ids
2051
or self.source._revision_id == _mod_revision.NULL_REVISION):
1995
or self.source._revision_id == NULL_REVISION):
2052
1996
raise AssertionError(
2053
1997
"revision {%s} is not stored in {%s}, but %s "
2054
1998
"can only be used for trees stored in the dirstate"
2055
1999
% (self.source._revision_id, self.target, self.iter_changes))
2056
2000
target_index = 0
2057
if self.source._revision_id == _mod_revision.NULL_REVISION:
2001
if self.source._revision_id == NULL_REVISION:
2058
2002
source_index = None
2059
2003
indices = (target_index,)
2076
2020
specific_files = set([''])
2077
2021
# -- specific_files is now a utf8 path set --
2022
search_specific_files = set()
2079
2023
# -- get the state object and prepare it.
2080
2024
state = self.target.current_dirstate()
2081
2025
state._read_dirblocks_if_needed()
2082
2026
if require_versioned:
2083
2027
# -- check all supplied paths are versioned in a search tree. --
2028
all_versioned = True
2085
2029
for path in specific_files:
2086
2030
path_entries = state._entries_for_path(path)
2087
2031
if not path_entries:
2088
2032
# this specified path is not present at all: error
2089
not_versioned.append(path)
2033
all_versioned = False
2091
2035
found_versioned = False
2092
2036
# for each id at this path
2093
2037
for entry in path_entries:
2100
2044
if not found_versioned:
2101
2045
# none of the indexes was not 'absent' at all ids for this
2103
not_versioned.append(path)
2104
if len(not_versioned) > 0:
2105
raise errors.PathsNotVersionedError(not_versioned)
2047
all_versioned = False
2049
if not all_versioned:
2050
raise errors.PathsNotVersionedError(specific_files)
2106
2051
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2107
search_specific_files = osutils.minimum_path_selection(specific_files)
2052
for path in specific_files:
2053
other_specific_files = specific_files.difference(set([path]))
2054
if not osutils.is_inside_any(other_specific_files, path):
2055
# this is a top level path, we must check it.
2056
search_specific_files.add(path)
2109
2058
use_filesystem_for_exec = (sys.platform != 'win32')
2110
2059
iter_changes = self.target._iter_changes(include_unchanged,
2122
2071
(revisiontree.RevisionTree, DirStateRevisionTree)):
2124
2073
# the source revid must be in the target dirstate
2125
if not (source._revision_id == _mod_revision.NULL_REVISION or
2074
if not (source._revision_id == NULL_REVISION or
2126
2075
source._revision_id in target.get_parent_ids()):
2127
2076
# TODO: what about ghosts? it may well need to
2128
2077
# check for them explicitly.