13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""WorkingTree4 format and implementation.
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
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
55
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
72
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
73
import bzrlib.mutabletree
56
74
from bzrlib.mutabletree import needs_tree_write_lock
57
75
from bzrlib.osutils import (
64
from bzrlib.trace import mutter
85
from bzrlib.trace import mutter, note
65
86
from bzrlib.transport.local import LocalTransport
66
87
from bzrlib.tree import InterTree
88
from bzrlib.progress import DummyProgress, ProgressPhase
89
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
90
from bzrlib.rio import RioReader, rio_file, Stanza
91
from bzrlib.symbol_versioning import (deprecated_passed,
67
96
from bzrlib.tree import Tree
68
97
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
184
213
WorkingTree3._comparison_data(self, entry, path)
185
214
# it looks like a plain directory, but it's really a reference -- see
187
if (self._repo_supports_tree_reference and kind == 'directory'
188
and entry is not None and entry.kind == 'tree-reference'):
216
if (self._repo_supports_tree_reference and
217
kind == 'directory' and
218
self._directory_is_tree_reference(path)):
189
219
kind = 'tree-reference'
190
220
return kind, executable, stat_value
217
247
return self._dirstate
218
248
local_path = self.bzrdir.get_workingtree_transport(None
219
249
).local_abspath('dirstate')
220
self._dirstate = dirstate.DirState.on_file(local_path,
221
self._sha1_provider())
250
self._dirstate = dirstate.DirState.on_file(local_path)
222
251
return self._dirstate
224
def _sha1_provider(self):
225
"""A function that returns a SHA1Provider suitable for this tree.
227
:return: None if content filtering is not supported by this tree.
228
Otherwise, a SHA1Provider is returned that sha's the canonical
229
form of files, i.e. after read filters are applied.
231
if self.supports_content_filtering():
232
return ContentFilterAwareSHA1Provider(self)
236
253
def filter_unversioned_files(self, paths):
237
254
"""Filter out paths that are versioned.
321
338
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
322
339
elif kind == 'tree-reference':
323
340
if not self._repo_supports_tree_reference:
324
raise errors.UnsupportedOperation(
325
self._generate_inventory,
326
self.branch.repository)
341
raise AssertionError(
343
"doesn't support tree references "
344
"required by entry %r"
327
346
inv_entry.reference_revision = link_or_sha1 or None
328
347
elif kind != 'symlink':
329
348
raise AssertionError("unknown kind %r" % kind)
434
453
return osutils.lexists(pathjoin(
435
454
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
457
def id2path(self, file_id):
444
458
"Convert a file-id to a path."
550
564
def _kind(self, relpath):
551
565
abspath = self.abspath(relpath)
552
566
kind = file_kind(abspath)
553
if (self._repo_supports_tree_reference and kind == 'directory'):
554
entry = self._get_entry(path=relpath)
555
if entry[1] is not None:
556
if entry[1][0][0] == 't':
557
kind = 'tree-reference'
567
if (self._repo_supports_tree_reference and
568
kind == 'directory' and
569
self._directory_is_tree_reference(relpath)):
570
kind = 'tree-reference'
612
620
self.branch.unlock()
616
623
def lock_tree_write(self):
617
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
619
:return: An object with an unlock method which will release the lock
624
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
622
625
self.branch.lock_read()
623
return self._lock_self_write()
626
self._lock_self_write()
625
628
def lock_write(self):
626
"""See MutableTree.lock_write, and WorkingTree.unlock.
628
:return: An object with an unlock method which will release the lock
629
"""See MutableTree.lock_write, and WorkingTree.unlock."""
631
630
self.branch.lock_write()
632
return self._lock_self_write()
631
self._lock_self_write()
634
633
@needs_tree_write_lock
635
634
def move(self, from_paths, to_dir, after=False):
705
704
from_entry = self._get_entry(path=from_rel)
706
705
if from_entry == (None, None):
707
706
raise errors.BzrMoveFailedError(from_rel,to_dir,
708
errors.NotVersionedError(path=from_rel))
707
errors.NotVersionedError(path=str(from_rel)))
710
709
from_id = from_entry[0][2]
711
710
to_rel = pathjoin(to_dir, from_tail)
1213
1212
# just forget the whole block.
1214
1213
entry_index = 0
1215
1214
while entry_index < len(block[1]):
1215
# Mark this file id as having been removed
1216
1216
entry = block[1][entry_index]
1217
if entry[1][0][0] in 'ar':
1218
# don't remove absent or renamed entries
1217
ids_to_unversion.discard(entry[0][2])
1218
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1220
or not state._make_absent(entry)):
1219
1221
entry_index += 1
1221
# Mark this file id as having been removed
1222
ids_to_unversion.discard(entry[0][2])
1223
if not state._make_absent(entry):
1224
# The block has not shrunk.
1226
1222
# go to the next block. (At the moment we dont delete empty
1228
1224
block_index += 1
1280
1276
if self._dirty:
1281
1277
raise AssertionError("attempting to write an inventory when the "
1282
1278
"dirstate is dirty will lose pending changes")
1283
had_inventory = self._inventory is not None
1284
# Setting self._inventory = None forces the dirstate to regenerate the
1285
# working inventory. We do this because self.inventory may be inv, or
1286
# may have been modified, and either case would prevent a clean delta
1288
self._inventory = None
1290
delta = inv._make_delta(self.inventory)
1292
self.apply_inventory_delta(delta)
1279
self.current_dirstate().set_state_from_inventory(inv)
1280
self._make_dirty(reset_inventory=False)
1281
if self._inventory is not None:
1294
1282
self._inventory = inv
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1300
def __init__(self, tree):
1303
def sha1(self, abspath):
1304
"""See dirstate.SHA1Provider.sha1()."""
1305
filters = self.tree._content_filter_stack(
1306
self.tree.relpath(osutils.safe_unicode(abspath)))
1307
return internal_size_sha_file_byname(abspath, filters)[1]
1309
def stat_and_sha1(self, abspath):
1310
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1311
filters = self.tree._content_filter_stack(
1312
self.tree.relpath(osutils.safe_unicode(abspath)))
1313
file_obj = file(abspath, 'rb', 65000)
1315
statvalue = os.fstat(file_obj.fileno())
1317
file_obj = filtered_input_file(file_obj, filters)
1318
sha1 = osutils.size_sha_file(file_obj)[1]
1321
return statvalue, sha1
1324
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1325
"""Dirstate working tree that supports content filtering.
1327
The dirstate holds the hash and size of the canonical form of the file,
1328
and most methods must return that.
1331
def _file_content_summary(self, path, stat_result):
1332
# This is to support the somewhat obsolete path_content_summary method
1333
# with content filtering: see
1334
# <https://bugs.edge.launchpad.net/bzr/+bug/415508>.
1336
# If the dirstate cache is up to date and knows the hash and size,
1338
# Otherwise if there are no content filters, return the on-disk size
1339
# and leave the hash blank.
1340
# Otherwise, read and filter the on-disk file and use its size and
1343
# The dirstate doesn't store the size of the canonical form so we
1344
# can't trust it for content-filtered trees. We just return None.
1345
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1346
executable = self._is_executable_from_path_and_stat(path, stat_result)
1347
return ('file', None, executable, dirstate_sha1)
1350
1286
class WorkingTree4(DirStateWorkingTree):
1351
1287
"""This is the Format 4 working tree.
1363
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1299
class WorkingTree5(DirStateWorkingTree):
1364
1300
"""This is the Format 5 working tree.
1366
1302
This differs from WorkingTree4 by:
1367
1303
- Supporting content filtering.
1369
This is new in bzr 1.11.
1373
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1374
"""This is the Format 6 working tree.
1376
This differs from WorkingTree5 by:
1377
1304
- Supporting a current view that may mask the set of files in a tree
1378
1305
impacted by most user operations.
1380
This is new in bzr 1.14.
1307
This is new in bzr 1.11.
1383
1310
def _make_views(self):
1461
1387
if basis_root_id is not None:
1462
1388
wt._set_root_id(basis_root_id)
1464
if wt.supports_content_filtering():
1465
# The original tree may not have the same content filters
1466
# applied so we can't safely build the inventory delta from
1468
delta_from_tree = False
1470
delta_from_tree = True
1471
1390
# delta_from_tree is safe even for DirStateRevisionTrees,
1472
1391
# because wt4.apply_inventory_delta does not mutate the input
1473
1392
# inventory entries.
1474
1393
transform.build_tree(basis, wt, accelerator_tree,
1476
delta_from_tree=delta_from_tree)
1394
hardlink=hardlink, delta_from_tree=True)
1555
1469
"""See WorkingTreeFormat.get_format_description()."""
1556
1470
return "Working tree format 5"
1558
def supports_content_filtering(self):
1562
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1563
"""WorkingTree format supporting views.
1566
upgrade_recommended = False
1568
_tree_class = WorkingTree6
1570
def get_format_string(self):
1571
"""See WorkingTreeFormat.get_format_string()."""
1572
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1574
def get_format_description(self):
1575
"""See WorkingTreeFormat.get_format_description()."""
1576
return "Working tree format 6"
1578
1472
def _init_custom_control_files(self, wt):
1579
1473
"""Subclasses with custom control files should override this method."""
1580
1474
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1589
1483
class DirStateRevisionTree(Tree):
1590
"""A revision tree pulling the inventory from a dirstate.
1592
Note that this is one of the historical (ie revision) trees cached in the
1593
dirstate for easy access, not the workingtree.
1484
"""A revision tree pulling the inventory from a dirstate."""
1596
1486
def __init__(self, dirstate, revision_id, repository):
1597
1487
self._dirstate = dirstate
1770
1660
parent_index = self._get_parent_index()
1771
1661
last_changed_revision = entry[1][parent_index][4]
1773
rev = self._repository.get_revision(last_changed_revision)
1774
except errors.NoSuchRevision:
1775
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1776
return rev.timestamp
1662
return self._repository.get_revision(last_changed_revision).timestamp
1778
1664
def get_file_sha1(self, file_id, path=None, stat_value=None):
1779
1665
entry = self._get_entry(file_id=file_id, path=path)
1791
1677
return self.inventory[file_id].text_size
1793
1679
def get_file_text(self, file_id, path=None):
1794
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1795
return ''.join(content)
1680
return list(self.iter_files_bytes([(file_id, None)]))[0][1]
1797
1682
def get_reference_revision(self, file_id, path=None):
1798
1683
return self.inventory[file_id].reference_revision
1817
1702
if entry[1][parent_index][0] != 'l':
1820
target = entry[1][parent_index][1]
1821
target = target.decode('utf8')
1705
# At present, none of the tree implementations supports non-ascii
1706
# symlink targets. So we will just assume that the dirstate path is
1708
return entry[1][parent_index][1]
1824
1710
def get_revision_id(self):
1825
1711
"""Return the revision id for this tree."""
1846
1732
entry = self._get_entry(file_id=file_id)[1]
1847
1733
if entry is None:
1848
1734
raise errors.NoSuchId(tree=self, file_id=file_id)
1849
parent_index = self._get_parent_index()
1850
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1735
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1852
1737
def stored_kind(self, file_id):
1853
1738
"""See Tree.stored_kind"""
1874
1759
return ie.executable
1876
def is_locked(self):
1879
def list_files(self, include_root=False, from_dir=None, recursive=True):
1761
def list_files(self, include_root=False):
1880
1762
# We use a standard implementation, because DirStateRevisionTree is
1881
1763
# dealing with one of the parents of the current state
1882
1764
inv = self._get_inventory()
1883
if from_dir is None:
1886
from_dir_id = inv.path2id(from_dir)
1887
if from_dir_id is None:
1888
# Directory not versioned
1890
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1891
if inv.root is not None and not include_root and from_dir is None:
1765
entries = inv.iter_entries()
1766
if self.inventory.root is not None and not include_root:
1893
1768
for path, entry in entries:
1894
1769
yield path, 'V', entry.kind, entry.file_id, entry
1896
1771
def lock_read(self):
1897
"""Lock the tree for a set of operations.
1899
:return: An object with an unlock method which will release the lock
1772
"""Lock the tree for a set of operations."""
1902
1773
if not self._locked:
1903
1774
self._repository.lock_read()
1904
1775
if self._dirstate._lock_token is None:
1905
1776
self._dirstate.lock_read()
1906
1777
self._dirstate_locked = True
1907
1778
self._locked += 1
1910
1780
def _must_be_locked(self):
1911
1781
if not self._locked:
2004
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
1874
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
2006
1875
from bzrlib.tests.test__dirstate_helpers import \
2007
compiled_dirstate_helpers_feature
2008
test_case.requireFeature(compiled_dirstate_helpers_feature)
2009
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
1876
CompiledDirstateHelpersFeature
1877
if not CompiledDirstateHelpersFeature.available():
1878
from bzrlib.tests import UnavailableFeature
1879
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1880
from bzrlib._dirstate_helpers_c import ProcessEntryC
2010
1881
result = klass.make_source_parent_tree(source, target)
2011
1882
result[1]._iter_changes = ProcessEntryC
2050
1923
require_versioned, want_unversioned=want_unversioned)
2051
1924
parent_ids = self.target.get_parent_ids()
2052
1925
if not (self.source._revision_id in parent_ids
2053
or self.source._revision_id == _mod_revision.NULL_REVISION):
1926
or self.source._revision_id == NULL_REVISION):
2054
1927
raise AssertionError(
2055
1928
"revision {%s} is not stored in {%s}, but %s "
2056
1929
"can only be used for trees stored in the dirstate"
2057
1930
% (self.source._revision_id, self.target, self.iter_changes))
2058
1931
target_index = 0
2059
if self.source._revision_id == _mod_revision.NULL_REVISION:
1932
if self.source._revision_id == NULL_REVISION:
2060
1933
source_index = None
2061
1934
indices = (target_index,)
2078
1951
specific_files = set([''])
2079
1952
# -- specific_files is now a utf8 path set --
1953
search_specific_files = set()
2081
1954
# -- get the state object and prepare it.
2082
1955
state = self.target.current_dirstate()
2083
1956
state._read_dirblocks_if_needed()
2084
1957
if require_versioned:
2085
1958
# -- check all supplied paths are versioned in a search tree. --
1959
all_versioned = True
2087
1960
for path in specific_files:
2088
1961
path_entries = state._entries_for_path(path)
2089
1962
if not path_entries:
2090
1963
# this specified path is not present at all: error
2091
not_versioned.append(path)
1964
all_versioned = False
2093
1966
found_versioned = False
2094
1967
# for each id at this path
2095
1968
for entry in path_entries:
2102
1975
if not found_versioned:
2103
1976
# none of the indexes was not 'absent' at all ids for this
2105
not_versioned.append(path)
2106
if len(not_versioned) > 0:
2107
raise errors.PathsNotVersionedError(not_versioned)
1978
all_versioned = False
1980
if not all_versioned:
1981
raise errors.PathsNotVersionedError(specific_files)
2108
1982
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2109
search_specific_files = osutils.minimum_path_selection(specific_files)
1983
for path in specific_files:
1984
other_specific_files = specific_files.difference(set([path]))
1985
if not osutils.is_inside_any(other_specific_files, path):
1986
# this is a top level path, we must check it.
1987
search_specific_files.add(path)
2111
1989
use_filesystem_for_exec = (sys.platform != 'win32')
2112
1990
iter_changes = self.target._iter_changes(include_unchanged,
2124
2002
(revisiontree.RevisionTree, DirStateRevisionTree)):
2126
2004
# the source revid must be in the target dirstate
2127
if not (source._revision_id == _mod_revision.NULL_REVISION or
2005
if not (source._revision_id == NULL_REVISION or
2128
2006
source._revision_id in target.get_parent_ids()):
2129
2007
# TODO: what about ghosts? it may well need to
2130
2008
# check for them explicitly.
2191
2069
# tree during upgrade.
2192
2070
tree._control_files.lock_write()
2194
self.update_format(tree)
2196
tree._control_files.unlock()
2198
def update_format(self, tree):
2199
"""Change the format marker."""
2200
tree._transport.put_bytes('format',
2201
self.target_format.get_format_string(),
2202
mode=tree.bzrdir._get_file_mode())
2205
class Converter4or5to6(object):
2206
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2209
self.target_format = WorkingTreeFormat6()
2211
def convert(self, tree):
2212
# lock the control files not the tree, so that we don't get tree
2213
# on-unlock behaviours, and so that no-one else diddles with the
2214
# tree during upgrade.
2215
tree._control_files.lock_write()
2217
2072
self.init_custom_control_files(tree)
2218
2073
self.update_format(tree)