1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
74
56
import bzrlib.mutabletree
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
464
435
return osutils.lexists(pathjoin(
465
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
468
444
def id2path(self, file_id):
469
445
"Convert a file-id to a path."
716
692
from_entry = self._get_entry(path=from_rel)
717
693
if from_entry == (None, None):
718
694
raise errors.BzrMoveFailedError(from_rel,to_dir,
719
errors.NotVersionedError(path=str(from_rel)))
695
errors.NotVersionedError(path=from_rel))
721
697
from_id = from_entry[0][2]
722
698
to_rel = pathjoin(to_dir, from_tail)
1051
1027
def set_last_revision(self, new_revision):
1052
1028
"""Change the last revision in the working tree."""
1053
1029
parents = self.get_parent_ids()
1054
if new_revision in (NULL_REVISION, None):
1030
if new_revision in (_mod_revision.NULL_REVISION, None):
1055
1031
if len(parents) >= 2:
1056
1032
raise AssertionError(
1057
1033
"setting the last parent to none with a pending merge is "
1224
1200
# just forget the whole block.
1225
1201
entry_index = 0
1226
1202
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1228
1203
entry = block[1][entry_index]
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)):
1204
if entry[1][0][0] in 'ar':
1205
# don't remove absent or renamed entries
1233
1206
entry_index += 1
1208
# Mark this file id as having been removed
1209
ids_to_unversion.discard(entry[0][2])
1210
if not state._make_absent(entry):
1211
# The block has not shrunk.
1234
1213
# go to the next block. (At the moment we dont delete empty
1236
1215
block_index += 1
1288
1267
if self._dirty:
1289
1268
raise AssertionError("attempting to write an inventory when the "
1290
1269
"dirstate is dirty will lose pending changes")
1291
self.current_dirstate().set_state_from_inventory(inv)
1292
self._make_dirty(reset_inventory=False)
1293
if self._inventory is not None:
1270
had_inventory = self._inventory is not None
1271
# Setting self._inventory = None forces the dirstate to regenerate the
1272
# working inventory. We do this because self.inventory may be inv, or
1273
# may have been modified, and either case would prevent a clean delta
1275
self._inventory = None
1277
delta = inv._make_delta(self.inventory)
1279
self.apply_inventory_delta(delta)
1294
1281
self._inventory = inv
1321
1308
return statvalue, sha1
1311
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1312
"""Dirstate working tree that supports content filtering.
1314
The dirstate holds the hash and size of the canonical form of the file,
1315
and most methods must return that.
1318
def _file_content_summary(self, path, stat_result):
1319
# This is to support the somewhat obsolete path_content_summary method
1320
# with content filtering: see
1321
# <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1323
# If the dirstate cache is up to date and knows the hash and size,
1325
# Otherwise if there are no content filters, return the on-disk size
1326
# and leave the hash blank.
1327
# Otherwise, read and filter the on-disk file and use its size and
1330
# The dirstate doesn't store the size of the canonical form so we
1331
# can't trust it for content-filtered trees. We just return None.
1332
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1333
executable = self._is_executable_from_path_and_stat(path, stat_result)
1334
return ('file', None, executable, dirstate_sha1)
1324
1337
class WorkingTree4(DirStateWorkingTree):
1325
1338
"""This is the Format 4 working tree.
1403
1416
wt.lock_tree_write()
1405
1418
self._init_custom_control_files(wt)
1406
if revision_id in (None, NULL_REVISION):
1419
if revision_id in (None, _mod_revision.NULL_REVISION):
1407
1420
if branch.repository.supports_rich_root():
1408
1421
wt._set_root_id(generate_ids.gen_root_id())
1421
1434
if basis is None:
1422
1435
basis = branch.repository.revision_tree(revision_id)
1423
if revision_id == NULL_REVISION:
1436
if revision_id == _mod_revision.NULL_REVISION:
1424
1437
parents_list = []
1426
1439
parents_list = [(revision_id, basis)]
1434
1447
if basis_root_id is not None:
1435
1448
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
1437
1457
# delta_from_tree is safe even for DirStateRevisionTrees,
1438
1458
# because wt4.apply_inventory_delta does not mutate the input
1439
1459
# inventory entries.
1440
1460
transform.build_tree(basis, wt, accelerator_tree,
1441
hardlink=hardlink, delta_from_tree=True)
1462
delta_from_tree=delta_from_tree)
1554
1575
class DirStateRevisionTree(Tree):
1555
"""A revision tree pulling the inventory from a dirstate."""
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.
1557
1582
def __init__(self, dirstate, revision_id, repository):
1558
1583
self._dirstate = dirstate
1830
1855
return ie.executable
1832
def list_files(self, include_root=False):
1857
def list_files(self, include_root=False, from_dir=None, recursive=True):
1833
1858
# We use a standard implementation, because DirStateRevisionTree is
1834
1859
# dealing with one of the parents of the current state
1835
1860
inv = self._get_inventory()
1836
entries = inv.iter_entries()
1837
if self.inventory.root is not None and not include_root:
1861
if from_dir is None:
1864
from_dir_id = inv.path2id(from_dir)
1865
if from_dir_id is None:
1866
# Directory not versioned
1868
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1869
if inv.root is not None and not include_root and from_dir is None:
1839
1871
for path, entry in entries:
1840
1872
yield path, 'V', entry.kind, entry.file_id, entry
1945
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1977
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1946
1979
from bzrlib.tests.test__dirstate_helpers import \
1947
CompiledDirstateHelpersFeature
1948
if not CompiledDirstateHelpersFeature.available():
1949
from bzrlib.tests import UnavailableFeature
1950
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1951
from bzrlib._dirstate_helpers_c import ProcessEntryC
1980
compiled_dirstate_helpers_feature
1981
test_case.requireFeature(compiled_dirstate_helpers_feature)
1982
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1952
1983
result = klass.make_source_parent_tree(source, target)
1953
1984
result[1]._iter_changes = ProcessEntryC
1984
2015
output. An unversioned file is defined as one with (False, False)
1985
2016
for the versioned pair.
1987
# NB: show_status depends on being able to pass in non-versioned files
1988
# and report them as unknown
1989
2018
# TODO: handle extra trees in the dirstate.
1990
2019
if (extra_trees or specific_files == []):
1991
2020
# we can't fast-path these cases (yet)
1994
2023
require_versioned, want_unversioned=want_unversioned)
1995
2024
parent_ids = self.target.get_parent_ids()
1996
2025
if not (self.source._revision_id in parent_ids
1997
or self.source._revision_id == NULL_REVISION):
2026
or self.source._revision_id == _mod_revision.NULL_REVISION):
1998
2027
raise AssertionError(
1999
2028
"revision {%s} is not stored in {%s}, but %s "
2000
2029
"can only be used for trees stored in the dirstate"
2001
2030
% (self.source._revision_id, self.target, self.iter_changes))
2002
2031
target_index = 0
2003
if self.source._revision_id == NULL_REVISION:
2032
if self.source._revision_id == _mod_revision.NULL_REVISION:
2004
2033
source_index = None
2005
2034
indices = (target_index,)
2022
2051
specific_files = set([''])
2023
2052
# -- specific_files is now a utf8 path set --
2024
search_specific_files = set()
2025
2054
# -- get the state object and prepare it.
2026
2055
state = self.target.current_dirstate()
2027
2056
state._read_dirblocks_if_needed()
2028
2057
if require_versioned:
2029
2058
# -- check all supplied paths are versioned in a search tree. --
2030
all_versioned = True
2031
2060
for path in specific_files:
2032
2061
path_entries = state._entries_for_path(path)
2033
2062
if not path_entries:
2034
2063
# this specified path is not present at all: error
2035
all_versioned = False
2064
not_versioned.append(path)
2037
2066
found_versioned = False
2038
2067
# for each id at this path
2039
2068
for entry in path_entries:
2046
2075
if not found_versioned:
2047
2076
# none of the indexes was not 'absent' at all ids for this
2049
all_versioned = False
2051
if not all_versioned:
2052
raise errors.PathsNotVersionedError(specific_files)
2078
not_versioned.append(path)
2079
if len(not_versioned) > 0:
2080
raise errors.PathsNotVersionedError(not_versioned)
2053
2081
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2054
for path in specific_files:
2055
other_specific_files = specific_files.difference(set([path]))
2056
if not osutils.is_inside_any(other_specific_files, path):
2057
# this is a top level path, we must check it.
2058
search_specific_files.add(path)
2082
search_specific_files = osutils.minimum_path_selection(specific_files)
2060
2084
use_filesystem_for_exec = (sys.platform != 'win32')
2061
2085
iter_changes = self.target._iter_changes(include_unchanged,
2073
2097
(revisiontree.RevisionTree, DirStateRevisionTree)):
2075
2099
# the source revid must be in the target dirstate
2076
if not (source._revision_id == NULL_REVISION or
2100
if not (source._revision_id == _mod_revision.NULL_REVISION or
2077
2101
source._revision_id in target.get_parent_ids()):
2078
2102
# TODO: what about ghosts? it may well need to
2079
2103
# check for them explicitly.