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
73
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
74
import bzrlib.mutabletree
56
75
from bzrlib.mutabletree import needs_tree_write_lock
57
76
from bzrlib.osutils import (
64
from bzrlib.trace import mutter
86
from bzrlib.trace import mutter, note
65
87
from bzrlib.transport.local import LocalTransport
66
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,
67
97
from bzrlib.tree import Tree
68
98
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
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)
1026
1051
def set_last_revision(self, new_revision):
1027
1052
"""Change the last revision in the working tree."""
1028
1053
parents = self.get_parent_ids()
1029
if new_revision in (_mod_revision.NULL_REVISION, None):
1054
if new_revision in (NULL_REVISION, None):
1030
1055
if len(parents) >= 2:
1031
1056
raise AssertionError(
1032
1057
"setting the last parent to none with a pending merge is "
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
1287
1301
self.tree = tree
1289
1303
def sha1(self, abspath):
1290
"""See dirstate.SHA1Provider.sha1()."""
1291
filters = self.tree._content_filter_stack(
1292
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))
1293
1306
return internal_size_sha_file_byname(abspath, filters)[1]
1295
1308
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)))
1309
"""Return the stat and sha1 of a file given its absolute path."""
1310
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1299
1311
file_obj = file(abspath, 'rb', 65000)
1301
1313
statvalue = os.fstat(file_obj.fileno())
1307
1319
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
1322
class WorkingTree4(DirStateWorkingTree):
1337
1323
"""This is the Format 4 working tree.
1373
1359
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1375
1360
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1376
1361
accelerator_tree=None, hardlink=False):
1377
1362
"""See WorkingTreeFormat.initialize().
1416
1401
wt.lock_tree_write()
1418
1403
self._init_custom_control_files(wt)
1419
if revision_id in (None, _mod_revision.NULL_REVISION):
1404
if revision_id in (None, NULL_REVISION):
1420
1405
if branch.repository.supports_rich_root():
1421
1406
wt._set_root_id(generate_ids.gen_root_id())
1434
1419
if basis is None:
1435
1420
basis = branch.repository.revision_tree(revision_id)
1436
if revision_id == _mod_revision.NULL_REVISION:
1421
if revision_id == NULL_REVISION:
1437
1422
parents_list = []
1439
1424
parents_list = [(revision_id, basis)]
1447
1432
if basis_root_id is not None:
1448
1433
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
1435
# delta_from_tree is safe even for DirStateRevisionTrees,
1458
1436
# because wt4.apply_inventory_delta does not mutate the input
1459
1437
# inventory entries.
1460
1438
transform.build_tree(basis, wt, accelerator_tree,
1462
delta_from_tree=delta_from_tree)
1439
hardlink=hardlink, delta_from_tree=True)
1489
1466
_control_files=control_files)
1491
1468
def __get_matchingbzrdir(self):
1492
return self._get_matchingbzrdir()
1494
def _get_matchingbzrdir(self):
1495
"""Overrideable method to get a bzrdir for testing."""
1496
1469
# please test against something that will let us do tree references
1497
1470
return bzrdir.format_registry.make_bzrdir(
1498
1471
'dirstate-with-subtree')
1575
1548
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.
1549
"""A revision tree pulling the inventory from a dirstate."""
1582
1551
def __init__(self, dirstate, revision_id, repository):
1583
1552
self._dirstate = dirstate
1756
1725
parent_index = self._get_parent_index()
1757
1726
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
1727
return self._repository.get_revision(last_changed_revision).timestamp
1764
1729
def get_file_sha1(self, file_id, path=None, stat_value=None):
1765
1730
entry = self._get_entry(file_id=file_id, path=path)
1832
1797
entry = self._get_entry(file_id=file_id)[1]
1833
1798
if entry is None:
1834
1799
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]]
1800
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1838
1802
def stored_kind(self, file_id):
1839
1803
"""See Tree.stored_kind"""
1860
1824
return ie.executable
1862
def list_files(self, include_root=False, from_dir=None, recursive=True):
1826
def list_files(self, include_root=False):
1863
1827
# We use a standard implementation, because DirStateRevisionTree is
1864
1828
# dealing with one of the parents of the current state
1865
1829
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:
1830
entries = inv.iter_entries()
1831
if self.inventory.root is not None and not include_root:
1876
1833
for path, entry in entries:
1877
1834
yield path, 'V', entry.kind, entry.file_id, entry
1982
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1939
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1984
1940
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
1941
CompiledDirstateHelpersFeature
1942
if not CompiledDirstateHelpersFeature.available():
1943
from bzrlib.tests import UnavailableFeature
1944
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1945
from bzrlib._dirstate_helpers_c import ProcessEntryC
1988
1946
result = klass.make_source_parent_tree(source, target)
1989
1947
result[1]._iter_changes = ProcessEntryC
2020
1978
output. An unversioned file is defined as one with (False, False)
2021
1979
for the versioned pair.
1981
# NB: show_status depends on being able to pass in non-versioned files
1982
# and report them as unknown
2023
1983
# TODO: handle extra trees in the dirstate.
2024
1984
if (extra_trees or specific_files == []):
2025
1985
# we can't fast-path these cases (yet)
2028
1988
require_versioned, want_unversioned=want_unversioned)
2029
1989
parent_ids = self.target.get_parent_ids()
2030
1990
if not (self.source._revision_id in parent_ids
2031
or self.source._revision_id == _mod_revision.NULL_REVISION):
1991
or self.source._revision_id == NULL_REVISION):
2032
1992
raise AssertionError(
2033
1993
"revision {%s} is not stored in {%s}, but %s "
2034
1994
"can only be used for trees stored in the dirstate"
2035
1995
% (self.source._revision_id, self.target, self.iter_changes))
2036
1996
target_index = 0
2037
if self.source._revision_id == _mod_revision.NULL_REVISION:
1997
if self.source._revision_id == NULL_REVISION:
2038
1998
source_index = None
2039
1999
indices = (target_index,)
2056
2016
specific_files = set([''])
2057
2017
# -- specific_files is now a utf8 path set --
2018
search_specific_files = set()
2059
2019
# -- get the state object and prepare it.
2060
2020
state = self.target.current_dirstate()
2061
2021
state._read_dirblocks_if_needed()
2062
2022
if require_versioned:
2063
2023
# -- check all supplied paths are versioned in a search tree. --
2024
all_versioned = True
2065
2025
for path in specific_files:
2066
2026
path_entries = state._entries_for_path(path)
2067
2027
if not path_entries:
2068
2028
# this specified path is not present at all: error
2069
not_versioned.append(path)
2029
all_versioned = False
2071
2031
found_versioned = False
2072
2032
# for each id at this path
2073
2033
for entry in path_entries:
2080
2040
if not found_versioned:
2081
2041
# 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)
2043
all_versioned = False
2045
if not all_versioned:
2046
raise errors.PathsNotVersionedError(specific_files)
2086
2047
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2087
search_specific_files = osutils.minimum_path_selection(specific_files)
2048
for path in specific_files:
2049
other_specific_files = specific_files.difference(set([path]))
2050
if not osutils.is_inside_any(other_specific_files, path):
2051
# this is a top level path, we must check it.
2052
search_specific_files.add(path)
2089
2054
use_filesystem_for_exec = (sys.platform != 'win32')
2090
2055
iter_changes = self.target._iter_changes(include_unchanged,
2102
2067
(revisiontree.RevisionTree, DirStateRevisionTree)):
2104
2069
# the source revid must be in the target dirstate
2105
if not (source._revision_id == _mod_revision.NULL_REVISION or
2070
if not (source._revision_id == NULL_REVISION or
2106
2071
source._revision_id in target.get_parent_ids()):
2107
2072
# TODO: what about ghosts? it may well need to
2108
2073
# check for them explicitly.