1
# Copyright (C) 2007-2011 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
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
17
"""WorkingTree4 format and implementation.
19
WorkingTree4 provides the dirstate based working tree logic.
21
To get a WorkingTree, call bzrdir.open_workingtree() or
22
WorkingTree.open(dir).
25
from __future__ import absolute_import
27
from cStringIO import StringIO
31
from bzrlib.lazy_import import lazy_import
32
lazy_import(globals(), """
40
conflicts as _mod_conflicts,
44
filters as _mod_filters,
47
revision as _mod_revision,
55
from bzrlib.decorators import needs_read_lock, needs_write_lock
56
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
57
from bzrlib.lock import LogicalLockResult
58
from bzrlib.lockable_files import LockableFiles
59
from bzrlib.lockdir import LockDir
60
from bzrlib.mutabletree import needs_tree_write_lock
61
from bzrlib.osutils import (
68
from bzrlib.transport.local import LocalTransport
69
from bzrlib.tree import (
73
from bzrlib.workingtree import (
76
WorkingTreeFormatMetaDir,
80
class DirStateWorkingTree(InventoryWorkingTree):
82
def __init__(self, basedir,
87
"""Construct a WorkingTree for basedir.
89
If the branch is not supplied, it is opened automatically.
90
If the branch is supplied, it must be the branch for this basedir.
91
(branch.base is not cross checked, because for remote branches that
92
would be meaningless).
94
self._format = _format
96
basedir = safe_unicode(basedir)
97
trace.mutter("opening working tree %r", basedir)
99
self.basedir = realpath(basedir)
100
# if branch is at our basedir and is a format 6 or less
101
# assume all other formats have their own control files.
102
self._control_files = _control_files
103
self._transport = self._control_files._transport
106
# during a read or write lock these objects are set, and are
107
# None the rest of the time.
108
self._dirstate = None
109
self._inventory = None
111
self._setup_directory_is_tree_reference()
112
self._detect_case_handling()
113
self._rules_searcher = None
114
self.views = self._make_views()
115
#--- allow tests to select the dirstate iter_changes implementation
116
self._iter_changes = dirstate._process_entry
118
@needs_tree_write_lock
119
def _add(self, files, ids, kinds):
120
"""See MutableTree._add."""
121
state = self.current_dirstate()
122
for f, file_id, kind in zip(files, ids, kinds):
125
# special case tree root handling.
126
if f == '' and self.path2id(f) == ROOT_ID:
127
state.set_path_id('', generate_ids.gen_file_id(f))
130
file_id = generate_ids.gen_file_id(f)
131
# deliberately add the file with no cached stat or sha1
132
# - on the first access it will be gathered, and we can
133
# always change this once tests are all passing.
134
state.add(f, file_id, kind, None, '')
135
self._make_dirty(reset_inventory=True)
137
def _get_check_refs(self):
138
"""Return the references needed to perform a check of this tree."""
139
return [('trees', self.last_revision())]
141
def _make_dirty(self, reset_inventory):
142
"""Make the tree state dirty.
144
:param reset_inventory: True if the cached inventory should be removed
145
(presuming there is one).
148
if reset_inventory and self._inventory is not None:
149
self._inventory = None
151
@needs_tree_write_lock
152
def add_reference(self, sub_tree):
153
# use standard implementation, which calls back to self._add
155
# So we don't store the reference_revision in the working dirstate,
156
# it's just recorded at the moment of commit.
157
self._add_reference(sub_tree)
159
def break_lock(self):
160
"""Break a lock if one is present from another instance.
162
Uses the ui factory to ask for confirmation if the lock may be from
165
This will probe the repository for its lock as well.
167
# if the dirstate is locked by an active process, reject the break lock
170
if self._dirstate is None:
174
state = self._current_dirstate()
175
if state._lock_token is not None:
176
# we already have it locked. sheese, cant break our own lock.
177
raise errors.LockActive(self.basedir)
180
# try for a write lock - need permission to get one anyhow
183
except errors.LockContention:
184
# oslocks fail when a process is still live: fail.
185
# TODO: get the locked lockdir info and give to the user to
186
# assist in debugging.
187
raise errors.LockActive(self.basedir)
192
self._dirstate = None
193
self._control_files.break_lock()
194
self.branch.break_lock()
196
def _comparison_data(self, entry, path):
197
kind, executable, stat_value = \
198
WorkingTree._comparison_data(self, entry, path)
199
# it looks like a plain directory, but it's really a reference -- see
201
if (self._repo_supports_tree_reference and kind == 'directory'
202
and entry is not None and entry.kind == 'tree-reference'):
203
kind = 'tree-reference'
204
return kind, executable, stat_value
207
def commit(self, message=None, revprops=None, *args, **kwargs):
208
# mark the tree as dirty post commit - commit
209
# can change the current versioned list by doing deletes.
210
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
211
self._make_dirty(reset_inventory=True)
214
def current_dirstate(self):
215
"""Return the current dirstate object.
217
This is not part of the tree interface and only exposed for ease of
220
:raises errors.NotWriteLocked: when not in a lock.
222
self._must_be_locked()
223
return self._current_dirstate()
225
def _current_dirstate(self):
226
"""Internal function that does not check lock status.
228
This is needed for break_lock which also needs the dirstate.
230
if self._dirstate is not None:
231
return self._dirstate
232
local_path = self.bzrdir.get_workingtree_transport(None
233
).local_abspath('dirstate')
234
self._dirstate = dirstate.DirState.on_file(local_path,
235
self._sha1_provider(), self._worth_saving_limit())
236
return self._dirstate
238
def _sha1_provider(self):
239
"""A function that returns a SHA1Provider suitable for this tree.
241
:return: None if content filtering is not supported by this tree.
242
Otherwise, a SHA1Provider is returned that sha's the canonical
243
form of files, i.e. after read filters are applied.
245
if self.supports_content_filtering():
246
return ContentFilterAwareSHA1Provider(self)
250
def _worth_saving_limit(self):
251
"""How many hash changes are ok before we must save the dirstate.
253
:return: an integer. -1 means never save.
255
# FIXME: We want a WorkingTreeStack here -- vila 20110812
256
conf = config.BranchStack(self.branch)
257
return conf.get('bzr.workingtree.worth_saving_limit')
259
def filter_unversioned_files(self, paths):
260
"""Filter out paths that are versioned.
262
:return: set of paths.
264
# TODO: make a generic multi-bisect routine roughly that should list
265
# the paths, then process one half at a time recursively, and feed the
266
# results of each bisect in further still
267
paths = sorted(paths)
269
state = self.current_dirstate()
270
# TODO we want a paths_to_dirblocks helper I think
272
dirname, basename = os.path.split(path.encode('utf8'))
273
_, _, _, path_is_versioned = state._get_block_entry_index(
274
dirname, basename, 0)
275
if not path_is_versioned:
280
"""Write all cached data to disk."""
281
if self._control_files._lock_mode != 'w':
282
raise errors.NotWriteLocked(self)
283
self.current_dirstate().save()
284
self._inventory = None
287
@needs_tree_write_lock
288
def _gather_kinds(self, files, kinds):
289
"""See MutableTree._gather_kinds."""
290
for pos, f in enumerate(files):
291
if kinds[pos] is None:
292
kinds[pos] = self._kind(f)
294
def _generate_inventory(self):
295
"""Create and set self.inventory from the dirstate object.
297
This is relatively expensive: we have to walk the entire dirstate.
298
Ideally we would not, and can deprecate this function.
300
#: uncomment to trap on inventory requests.
301
# import pdb;pdb.set_trace()
302
state = self.current_dirstate()
303
state._read_dirblocks_if_needed()
304
root_key, current_entry = self._get_entry(path='')
305
current_id = root_key[2]
306
if not (current_entry[0][0] == 'd'): # directory
307
raise AssertionError(current_entry)
308
inv = Inventory(root_id=current_id)
309
# Turn some things into local variables
310
minikind_to_kind = dirstate.DirState._minikind_to_kind
311
factory = entry_factory
312
utf8_decode = cache_utf8._utf8_decode
314
# we could do this straight out of the dirstate; it might be fast
315
# and should be profiled - RBC 20070216
316
parent_ies = {'' : inv.root}
317
for block in state._dirblocks[1:]: # skip the root
320
parent_ie = parent_ies[dirname]
322
# all the paths in this block are not versioned in this tree
324
for key, entry in block[1]:
325
minikind, link_or_sha1, size, executable, stat = entry[0]
326
if minikind in ('a', 'r'): # absent, relocated
327
# a parent tree only entry
330
name_unicode = utf8_decode(name)[0]
332
kind = minikind_to_kind[minikind]
333
inv_entry = factory[kind](file_id, name_unicode,
336
# This is only needed on win32, where this is the only way
337
# we know the executable bit.
338
inv_entry.executable = executable
339
# not strictly needed: working tree
340
#inv_entry.text_size = size
341
#inv_entry.text_sha1 = sha1
342
elif kind == 'directory':
343
# add this entry to the parent map.
344
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
345
elif kind == 'tree-reference':
346
if not self._repo_supports_tree_reference:
347
raise errors.UnsupportedOperation(
348
self._generate_inventory,
349
self.branch.repository)
350
inv_entry.reference_revision = link_or_sha1 or None
351
elif kind != 'symlink':
352
raise AssertionError("unknown kind %r" % kind)
353
# These checks cost us around 40ms on a 55k entry tree
354
if file_id in inv_byid:
355
raise AssertionError('file_id %s already in'
356
' inventory as %s' % (file_id, inv_byid[file_id]))
357
if name_unicode in parent_ie.children:
358
raise AssertionError('name %r already in parent'
360
inv_byid[file_id] = inv_entry
361
parent_ie.children[name_unicode] = inv_entry
362
self._inventory = inv
364
def _get_entry(self, file_id=None, path=None):
365
"""Get the dirstate row for file_id or path.
367
If either file_id or path is supplied, it is used as the key to lookup.
368
If both are supplied, the fastest lookup is used, and an error is
369
raised if they do not both point at the same row.
371
:param file_id: An optional unicode file_id to be looked up.
372
:param path: An optional unicode path to be looked up.
373
:return: The dirstate row tuple for path/file_id, or (None, None)
375
if file_id is None and path is None:
376
raise errors.BzrError('must supply file_id or path')
377
state = self.current_dirstate()
379
path = path.encode('utf8')
380
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
382
def get_file_sha1(self, file_id, path=None, stat_value=None):
383
# check file id is valid unconditionally.
384
entry = self._get_entry(file_id=file_id, path=path)
386
raise errors.NoSuchId(self, file_id)
388
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
390
file_abspath = self.abspath(path)
391
state = self.current_dirstate()
392
if stat_value is None:
394
stat_value = osutils.lstat(file_abspath)
396
if e.errno == errno.ENOENT:
400
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
401
stat_value=stat_value)
402
if entry[1][0][0] == 'f':
403
if link_or_sha1 is None:
404
file_obj, statvalue = self.get_file_with_stat(file_id, path)
406
sha1 = osutils.sha_file(file_obj)
409
self._observed_sha1(file_id, path, (sha1, statvalue))
415
def _get_inventory(self):
416
"""Get the inventory for the tree. This is only valid within a lock."""
417
if 'evil' in debug.debug_flags:
418
trace.mutter_callsite(2,
419
"accessing .inventory forces a size of tree translation.")
420
if self._inventory is not None:
421
return self._inventory
422
self._must_be_locked()
423
self._generate_inventory()
424
return self._inventory
426
inventory = property(_get_inventory,
427
doc="Inventory of this Tree")
430
def get_parent_ids(self):
431
"""See Tree.get_parent_ids.
433
This implementation requests the ids list from the dirstate file.
435
return self.current_dirstate().get_parent_ids()
437
def get_reference_revision(self, file_id, path=None):
438
# referenced tree's revision is whatever's currently there
439
return self.get_nested_tree(file_id, path).last_revision()
441
def get_nested_tree(self, file_id, path=None):
443
path = self.id2path(file_id)
444
# else: check file_id is at path?
445
return WorkingTree.open(self.abspath(path))
448
def get_root_id(self):
449
"""Return the id of this trees root"""
450
return self._get_entry(path='')[0][2]
452
def has_id(self, file_id):
453
state = self.current_dirstate()
454
row, parents = self._get_entry(file_id=file_id)
457
return osutils.lexists(pathjoin(
458
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
460
def has_or_had_id(self, file_id):
461
state = self.current_dirstate()
462
row, parents = self._get_entry(file_id=file_id)
463
return row is not None
466
def id2path(self, file_id):
467
"Convert a file-id to a path."
468
state = self.current_dirstate()
469
entry = self._get_entry(file_id=file_id)
470
if entry == (None, None):
471
raise errors.NoSuchId(tree=self, file_id=file_id)
472
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
473
return path_utf8.decode('utf8')
475
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
476
entry = self._get_entry(path=path)
477
if entry == (None, None):
478
return False # Missing entries are not executable
479
return entry[1][0][3] # Executable?
481
def is_executable(self, file_id, path=None):
482
"""Test if a file is executable or not.
484
Note: The caller is expected to take a read-lock before calling this.
486
if not self._supports_executable():
487
entry = self._get_entry(file_id=file_id, path=path)
488
if entry == (None, None):
490
return entry[1][0][3]
492
self._must_be_locked()
494
path = self.id2path(file_id)
495
mode = osutils.lstat(self.abspath(path)).st_mode
496
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
498
def all_file_ids(self):
499
"""See Tree.iter_all_file_ids"""
500
self._must_be_locked()
502
for key, tree_details in self.current_dirstate()._iter_entries():
503
if tree_details[0][0] in ('a', 'r'): # relocated
510
"""Iterate through file_ids for this tree.
512
file_ids are in a WorkingTree if they are in the working inventory
513
and the working file exists.
516
for key, tree_details in self.current_dirstate()._iter_entries():
517
if tree_details[0][0] in ('a', 'r'): # absent, relocated
518
# not relevant to the working tree
520
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
521
if osutils.lexists(path):
522
result.append(key[2])
525
def iter_references(self):
526
if not self._repo_supports_tree_reference:
527
# When the repo doesn't support references, we will have nothing to
530
for key, tree_details in self.current_dirstate()._iter_entries():
531
if tree_details[0][0] in ('a', 'r'): # absent, relocated
532
# not relevant to the working tree
535
# the root is not a reference.
537
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
539
if self._kind(relpath) == 'tree-reference':
540
yield relpath, key[2]
541
except errors.NoSuchFile:
542
# path is missing on disk.
545
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
546
"""See MutableTree._observed_sha1."""
547
state = self.current_dirstate()
548
entry = self._get_entry(file_id=file_id, path=path)
549
state._observed_sha1(entry, sha1, statvalue)
551
def kind(self, file_id):
552
"""Return the kind of a file.
554
This is always the actual kind that's on disk, regardless of what it
557
Note: The caller is expected to take a read-lock before calling this.
559
relpath = self.id2path(file_id)
561
raise AssertionError(
562
"path for id {%s} is None!" % file_id)
563
return self._kind(relpath)
565
def _kind(self, relpath):
566
abspath = self.abspath(relpath)
567
kind = file_kind(abspath)
568
if (self._repo_supports_tree_reference and kind == 'directory'):
569
entry = self._get_entry(path=relpath)
570
if entry[1] is not None:
571
if entry[1][0][0] == 't':
572
kind = 'tree-reference'
576
def _last_revision(self):
577
"""See Mutable.last_revision."""
578
parent_ids = self.current_dirstate().get_parent_ids()
582
return _mod_revision.NULL_REVISION
585
"""See Branch.lock_read, and WorkingTree.unlock.
587
:return: A bzrlib.lock.LogicalLockResult.
589
self.branch.lock_read()
591
self._control_files.lock_read()
593
state = self.current_dirstate()
594
if not state._lock_token:
596
# set our support for tree references from the repository in
598
self._repo_supports_tree_reference = getattr(
599
self.branch.repository._format, "supports_tree_reference",
602
self._control_files.unlock()
607
return LogicalLockResult(self.unlock)
609
def _lock_self_write(self):
610
"""This should be called after the branch is locked."""
612
self._control_files.lock_write()
614
state = self.current_dirstate()
615
if not state._lock_token:
617
# set our support for tree references from the repository in
619
self._repo_supports_tree_reference = getattr(
620
self.branch.repository._format, "supports_tree_reference",
623
self._control_files.unlock()
628
return LogicalLockResult(self.unlock)
630
def lock_tree_write(self):
631
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
633
:return: A bzrlib.lock.LogicalLockResult.
635
self.branch.lock_read()
636
return self._lock_self_write()
638
def lock_write(self):
639
"""See MutableTree.lock_write, and WorkingTree.unlock.
641
:return: A bzrlib.lock.LogicalLockResult.
643
self.branch.lock_write()
644
return self._lock_self_write()
646
@needs_tree_write_lock
647
def move(self, from_paths, to_dir, after=False):
648
"""See WorkingTree.move()."""
652
state = self.current_dirstate()
653
if isinstance(from_paths, basestring):
655
to_dir_utf8 = to_dir.encode('utf8')
656
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
657
id_index = state._get_id_index()
658
# check destination directory
659
# get the details for it
660
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
661
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
662
if not entry_present:
663
raise errors.BzrMoveFailedError('', to_dir,
664
errors.NotVersionedError(to_dir))
665
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
666
# get a handle on the block itself.
667
to_block_index = state._ensure_block(
668
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
669
to_block = state._dirblocks[to_block_index]
670
to_abs = self.abspath(to_dir)
671
if not isdir(to_abs):
672
raise errors.BzrMoveFailedError('',to_dir,
673
errors.NotADirectory(to_abs))
675
if to_entry[1][0][0] != 'd':
676
raise errors.BzrMoveFailedError('',to_dir,
677
errors.NotADirectory(to_abs))
679
if self._inventory is not None:
680
update_inventory = True
682
to_dir_id = to_entry[0][2]
683
to_dir_ie = inv[to_dir_id]
685
update_inventory = False
688
def move_one(old_entry, from_path_utf8, minikind, executable,
689
fingerprint, packed_stat, size,
690
to_block, to_key, to_path_utf8):
691
state._make_absent(old_entry)
692
from_key = old_entry[0]
694
lambda:state.update_minimal(from_key,
696
executable=executable,
697
fingerprint=fingerprint,
698
packed_stat=packed_stat,
700
path_utf8=from_path_utf8))
701
state.update_minimal(to_key,
703
executable=executable,
704
fingerprint=fingerprint,
705
packed_stat=packed_stat,
707
path_utf8=to_path_utf8)
708
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
709
new_entry = to_block[1][added_entry_index]
710
rollbacks.append(lambda:state._make_absent(new_entry))
712
for from_rel in from_paths:
713
# from_rel is 'pathinroot/foo/bar'
714
from_rel_utf8 = from_rel.encode('utf8')
715
from_dirname, from_tail = osutils.split(from_rel)
716
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
717
from_entry = self._get_entry(path=from_rel)
718
if from_entry == (None, None):
719
raise errors.BzrMoveFailedError(from_rel,to_dir,
720
errors.NotVersionedError(path=from_rel))
722
from_id = from_entry[0][2]
723
to_rel = pathjoin(to_dir, from_tail)
724
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
725
item_to_entry = self._get_entry(path=to_rel)
726
if item_to_entry != (None, None):
727
raise errors.BzrMoveFailedError(from_rel, to_rel,
728
"Target is already versioned.")
730
if from_rel == to_rel:
731
raise errors.BzrMoveFailedError(from_rel, to_rel,
732
"Source and target are identical.")
734
from_missing = not self.has_filename(from_rel)
735
to_missing = not self.has_filename(to_rel)
742
raise errors.BzrMoveFailedError(from_rel, to_rel,
743
errors.NoSuchFile(path=to_rel,
744
extra="New file has not been created yet"))
746
# neither path exists
747
raise errors.BzrRenameFailedError(from_rel, to_rel,
748
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
750
if from_missing: # implicitly just update our path mapping
753
raise errors.RenameFailedFilesExist(from_rel, to_rel)
756
def rollback_rename():
757
"""A single rename has failed, roll it back."""
758
# roll back everything, even if we encounter trouble doing one
761
# TODO: at least log the other exceptions rather than just
762
# losing them mbp 20070307
764
for rollback in reversed(rollbacks):
768
exc_info = sys.exc_info()
770
raise exc_info[0], exc_info[1], exc_info[2]
772
# perform the disk move first - its the most likely failure point.
774
from_rel_abs = self.abspath(from_rel)
775
to_rel_abs = self.abspath(to_rel)
777
osutils.rename(from_rel_abs, to_rel_abs)
779
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
780
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
782
# perform the rename in the inventory next if needed: its easy
786
from_entry = inv[from_id]
787
current_parent = from_entry.parent_id
788
inv.rename(from_id, to_dir_id, from_tail)
790
lambda: inv.rename(from_id, current_parent, from_tail))
791
# finally do the rename in the dirstate, which is a little
792
# tricky to rollback, but least likely to need it.
793
old_block_index, old_entry_index, dir_present, file_present = \
794
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
795
old_block = state._dirblocks[old_block_index][1]
796
old_entry = old_block[old_entry_index]
797
from_key, old_entry_details = old_entry
798
cur_details = old_entry_details[0]
800
to_key = ((to_block[0],) + from_key[1:3])
801
minikind = cur_details[0]
802
move_one(old_entry, from_path_utf8=from_rel_utf8,
804
executable=cur_details[3],
805
fingerprint=cur_details[1],
806
packed_stat=cur_details[4],
810
to_path_utf8=to_rel_utf8)
813
def update_dirblock(from_dir, to_key, to_dir_utf8):
814
"""Recursively update all entries in this dirblock."""
816
raise AssertionError("renaming root not supported")
817
from_key = (from_dir, '')
818
from_block_idx, present = \
819
state._find_block_index_from_key(from_key)
821
# This is the old record, if it isn't present, then
822
# there is theoretically nothing to update.
823
# (Unless it isn't present because of lazy loading,
824
# but we don't do that yet)
826
from_block = state._dirblocks[from_block_idx]
827
to_block_index, to_entry_index, _, _ = \
828
state._get_block_entry_index(to_key[0], to_key[1], 0)
829
to_block_index = state._ensure_block(
830
to_block_index, to_entry_index, to_dir_utf8)
831
to_block = state._dirblocks[to_block_index]
833
# Grab a copy since move_one may update the list.
834
for entry in from_block[1][:]:
835
if not (entry[0][0] == from_dir):
836
raise AssertionError()
837
cur_details = entry[1][0]
838
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
839
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
840
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
841
minikind = cur_details[0]
843
# Deleted children of a renamed directory
844
# Do not need to be updated.
845
# Children that have been renamed out of this
846
# directory should also not be updated
848
move_one(entry, from_path_utf8=from_path_utf8,
850
executable=cur_details[3],
851
fingerprint=cur_details[1],
852
packed_stat=cur_details[4],
856
to_path_utf8=to_path_utf8)
858
# We need to move all the children of this
860
update_dirblock(from_path_utf8, to_key,
862
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
866
result.append((from_rel, to_rel))
867
state._mark_modified()
868
self._make_dirty(reset_inventory=False)
872
def _must_be_locked(self):
873
if not self._control_files._lock_count:
874
raise errors.ObjectNotLocked(self)
877
"""Initialize the state in this tree to be a new tree."""
881
def path2id(self, path):
882
"""Return the id for path in this tree."""
883
path = path.strip('/')
884
entry = self._get_entry(path=path)
885
if entry == (None, None):
889
def paths2ids(self, paths, trees=[], require_versioned=True):
890
"""See Tree.paths2ids().
892
This specialisation fast-paths the case where all the trees are in the
897
parents = self.get_parent_ids()
899
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
901
return super(DirStateWorkingTree, self).paths2ids(paths,
902
trees, require_versioned)
903
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
904
# -- make all paths utf8 --
907
paths_utf8.add(path.encode('utf8'))
909
# -- paths is now a utf8 path set --
910
# -- get the state object and prepare it.
911
state = self.current_dirstate()
912
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
913
and '' not in paths):
914
paths2ids = self._paths2ids_using_bisect
916
paths2ids = self._paths2ids_in_memory
917
return paths2ids(paths, search_indexes,
918
require_versioned=require_versioned)
920
def _paths2ids_in_memory(self, paths, search_indexes,
921
require_versioned=True):
922
state = self.current_dirstate()
923
state._read_dirblocks_if_needed()
924
def _entries_for_path(path):
925
"""Return a list with all the entries that match path for all ids.
927
dirname, basename = os.path.split(path)
928
key = (dirname, basename, '')
929
block_index, present = state._find_block_index_from_key(key)
931
# the block which should contain path is absent.
934
block = state._dirblocks[block_index][1]
935
entry_index, _ = state._find_entry_index(key, block)
936
# we may need to look at multiple entries at this path: walk while the paths match.
937
while (entry_index < len(block) and
938
block[entry_index][0][0:2] == key[0:2]):
939
result.append(block[entry_index])
942
if require_versioned:
943
# -- check all supplied paths are versioned in a search tree. --
946
path_entries = _entries_for_path(path)
948
# this specified path is not present at all: error
949
all_versioned = False
951
found_versioned = False
952
# for each id at this path
953
for entry in path_entries:
955
for index in search_indexes:
956
if entry[1][index][0] != 'a': # absent
957
found_versioned = True
958
# all good: found a versioned cell
960
if not found_versioned:
961
# none of the indexes was not 'absent' at all ids for this
963
all_versioned = False
965
if not all_versioned:
966
raise errors.PathsNotVersionedError(
967
[p.decode('utf-8') for p in paths])
968
# -- remove redundancy in supplied paths to prevent over-scanning --
969
search_paths = osutils.minimum_path_selection(paths)
971
# for all search_indexs in each path at or under each element of
972
# search_paths, if the detail is relocated: add the id, and add the
973
# relocated path as one to search if its not searched already. If the
974
# detail is not relocated, add the id.
975
searched_paths = set()
977
def _process_entry(entry):
978
"""Look at search_indexes within entry.
980
If a specific tree's details are relocated, add the relocation
981
target to search_paths if not searched already. If it is absent, do
982
nothing. Otherwise add the id to found_ids.
984
for index in search_indexes:
985
if entry[1][index][0] == 'r': # relocated
986
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
987
search_paths.add(entry[1][index][1])
988
elif entry[1][index][0] != 'a': # absent
989
found_ids.add(entry[0][2])
991
current_root = search_paths.pop()
992
searched_paths.add(current_root)
993
# process the entries for this containing directory: the rest will be
994
# found by their parents recursively.
995
root_entries = _entries_for_path(current_root)
997
# this specified path is not present at all, skip it.
999
for entry in root_entries:
1000
_process_entry(entry)
1001
initial_key = (current_root, '', '')
1002
block_index, _ = state._find_block_index_from_key(initial_key)
1003
while (block_index < len(state._dirblocks) and
1004
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1005
for entry in state._dirblocks[block_index][1]:
1006
_process_entry(entry)
1010
def _paths2ids_using_bisect(self, paths, search_indexes,
1011
require_versioned=True):
1012
state = self.current_dirstate()
1015
split_paths = sorted(osutils.split(p) for p in paths)
1016
found = state._bisect_recursive(split_paths)
1018
if require_versioned:
1019
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1020
for dir_name in split_paths:
1021
if dir_name not in found_dir_names:
1022
raise errors.PathsNotVersionedError(
1023
[p.decode('utf-8') for p in paths])
1025
for dir_name_id, trees_info in found.iteritems():
1026
for index in search_indexes:
1027
if trees_info[index][0] not in ('r', 'a'):
1028
found_ids.add(dir_name_id[2])
1031
def read_working_inventory(self):
1032
"""Read the working inventory.
1034
This is a meaningless operation for dirstate, but we obey it anyhow.
1036
return self.inventory
1039
def revision_tree(self, revision_id):
1040
"""See Tree.revision_tree.
1042
WorkingTree4 supplies revision_trees for any basis tree.
1044
dirstate = self.current_dirstate()
1045
parent_ids = dirstate.get_parent_ids()
1046
if revision_id not in parent_ids:
1047
raise errors.NoSuchRevisionInTree(self, revision_id)
1048
if revision_id in dirstate.get_ghosts():
1049
raise errors.NoSuchRevisionInTree(self, revision_id)
1050
return DirStateRevisionTree(dirstate, revision_id,
1051
self.branch.repository)
1053
@needs_tree_write_lock
1054
def set_last_revision(self, new_revision):
1055
"""Change the last revision in the working tree."""
1056
parents = self.get_parent_ids()
1057
if new_revision in (_mod_revision.NULL_REVISION, None):
1058
if len(parents) >= 2:
1059
raise AssertionError(
1060
"setting the last parent to none with a pending merge is "
1062
self.set_parent_ids([])
1064
self.set_parent_ids([new_revision] + parents[1:],
1065
allow_leftmost_as_ghost=True)
1067
@needs_tree_write_lock
1068
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1069
"""Set the parent ids to revision_ids.
1071
See also set_parent_trees. This api will try to retrieve the tree data
1072
for each element of revision_ids from the trees repository. If you have
1073
tree data already available, it is more efficient to use
1074
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1075
an easier API to use.
1077
:param revision_ids: The revision_ids to set as the parent ids of this
1078
working tree. Any of these may be ghosts.
1081
for revision_id in revision_ids:
1083
revtree = self.branch.repository.revision_tree(revision_id)
1084
# TODO: jam 20070213 KnitVersionedFile raises
1085
# RevisionNotPresent rather than NoSuchRevision if a
1086
# given revision_id is not present. Should Repository be
1087
# catching it and re-raising NoSuchRevision?
1088
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1090
trees.append((revision_id, revtree))
1091
self.set_parent_trees(trees,
1092
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1094
@needs_tree_write_lock
1095
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1096
"""Set the parents of the working tree.
1098
:param parents_list: A list of (revision_id, tree) tuples.
1099
If tree is None, then that element is treated as an unreachable
1100
parent tree - i.e. a ghost.
1102
dirstate = self.current_dirstate()
1103
if len(parents_list) > 0:
1104
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1105
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1109
parent_ids = [rev_id for rev_id, tree in parents_list]
1110
graph = self.branch.repository.get_graph()
1111
heads = graph.heads(parent_ids)
1112
accepted_revisions = set()
1114
# convert absent trees to the null tree, which we convert back to
1115
# missing on access.
1116
for rev_id, tree in parents_list:
1117
if len(accepted_revisions) > 0:
1118
# we always accept the first tree
1119
if rev_id in accepted_revisions or rev_id not in heads:
1120
# We have already included either this tree, or its
1121
# descendent, so we skip it.
1123
_mod_revision.check_not_reserved_id(rev_id)
1124
if tree is not None:
1125
real_trees.append((rev_id, tree))
1127
real_trees.append((rev_id,
1128
self.branch.repository.revision_tree(
1129
_mod_revision.NULL_REVISION)))
1130
ghosts.append(rev_id)
1131
accepted_revisions.add(rev_id)
1133
if (len(real_trees) == 1
1135
and self.branch.repository._format.fast_deltas
1136
and isinstance(real_trees[0][1],
1137
revisiontree.InventoryRevisionTree)
1138
and self.get_parent_ids()):
1139
rev_id, rev_tree = real_trees[0]
1140
basis_id = self.get_parent_ids()[0]
1141
# There are times when basis_tree won't be in
1142
# self.branch.repository, (switch, for example)
1144
basis_tree = self.branch.repository.revision_tree(basis_id)
1145
except errors.NoSuchRevision:
1146
# Fall back to the set_parent_trees(), since we can't use
1147
# _make_delta if we can't get the RevisionTree
1150
delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1151
dirstate.update_basis_by_delta(delta, rev_id)
1154
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1155
self._make_dirty(reset_inventory=False)
1157
def _set_root_id(self, file_id):
1158
"""See WorkingTree.set_root_id."""
1159
state = self.current_dirstate()
1160
state.set_path_id('', file_id)
1161
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1162
self._make_dirty(reset_inventory=True)
1164
def _sha_from_stat(self, path, stat_result):
1165
"""Get a sha digest from the tree's stat cache.
1167
The default implementation assumes no stat cache is present.
1169
:param path: The path.
1170
:param stat_result: The stat result being looked up.
1172
return self.current_dirstate().sha1_from_stat(path, stat_result)
1175
def supports_tree_reference(self):
1176
return self._repo_supports_tree_reference
1179
"""Unlock in format 4 trees needs to write the entire dirstate."""
1180
if self._control_files._lock_count == 1:
1181
# do non-implementation specific cleanup
1184
# eventually we should do signature checking during read locks for
1186
if self._control_files._lock_mode == 'w':
1189
if self._dirstate is not None:
1190
# This is a no-op if there are no modifications.
1191
self._dirstate.save()
1192
self._dirstate.unlock()
1193
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1194
# point. Instead, it could check if the header has been
1195
# modified when it is locked, and if not, it can hang on to
1196
# the data it has in memory.
1197
self._dirstate = None
1198
self._inventory = None
1199
# reverse order of locking.
1201
return self._control_files.unlock()
1203
self.branch.unlock()
1205
@needs_tree_write_lock
1206
def unversion(self, file_ids):
1207
"""Remove the file ids in file_ids from the current versioned set.
1209
When a file_id is unversioned, all of its children are automatically
1212
:param file_ids: The file ids to stop versioning.
1213
:raises: NoSuchId if any fileid is not currently versioned.
1217
state = self.current_dirstate()
1218
state._read_dirblocks_if_needed()
1219
ids_to_unversion = set(file_ids)
1220
paths_to_unversion = set()
1222
# check if the root is to be unversioned, if so, assert for now.
1223
# walk the state marking unversioned things as absent.
1224
# if there are any un-unversioned ids at the end, raise
1225
for key, details in state._dirblocks[0][1]:
1226
if (details[0][0] not in ('a', 'r') and # absent or relocated
1227
key[2] in ids_to_unversion):
1228
# I haven't written the code to unversion / yet - it should be
1230
raise errors.BzrError('Unversioning the / is not currently supported')
1232
while block_index < len(state._dirblocks):
1233
# process one directory at a time.
1234
block = state._dirblocks[block_index]
1235
# first check: is the path one to remove - it or its children
1236
delete_block = False
1237
for path in paths_to_unversion:
1238
if (block[0].startswith(path) and
1239
(len(block[0]) == len(path) or
1240
block[0][len(path)] == '/')):
1241
# this entire block should be deleted - its the block for a
1242
# path to unversion; or the child of one
1245
# TODO: trim paths_to_unversion as we pass by paths
1247
# this block is to be deleted: process it.
1248
# TODO: we can special case the no-parents case and
1249
# just forget the whole block.
1251
while entry_index < len(block[1]):
1252
entry = block[1][entry_index]
1253
if entry[1][0][0] in 'ar':
1254
# don't remove absent or renamed entries
1257
# Mark this file id as having been removed
1258
ids_to_unversion.discard(entry[0][2])
1259
if not state._make_absent(entry):
1260
# The block has not shrunk.
1262
# go to the next block. (At the moment we dont delete empty
1267
while entry_index < len(block[1]):
1268
entry = block[1][entry_index]
1269
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1270
# ^ some parent row.
1271
entry[0][2] not in ids_to_unversion):
1272
# ^ not an id to unversion
1275
if entry[1][0][0] == 'd':
1276
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1277
if not state._make_absent(entry):
1279
# we have unversioned this id
1280
ids_to_unversion.remove(entry[0][2])
1282
if ids_to_unversion:
1283
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1284
self._make_dirty(reset_inventory=False)
1285
# have to change the legacy inventory too.
1286
if self._inventory is not None:
1287
for file_id in file_ids:
1288
if self._inventory.has_id(file_id):
1289
self._inventory.remove_recursive_id(file_id)
1291
@needs_tree_write_lock
1292
def rename_one(self, from_rel, to_rel, after=False):
1293
"""See WorkingTree.rename_one"""
1295
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1297
@needs_tree_write_lock
1298
def apply_inventory_delta(self, changes):
1299
"""See MutableTree.apply_inventory_delta"""
1300
state = self.current_dirstate()
1301
state.update_by_delta(changes)
1302
self._make_dirty(reset_inventory=True)
1304
def update_basis_by_delta(self, new_revid, delta):
1305
"""See MutableTree.update_basis_by_delta."""
1306
if self.last_revision() == new_revid:
1307
raise AssertionError()
1308
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1311
def _validate(self):
1312
self._dirstate._validate()
1314
@needs_tree_write_lock
1315
def _write_inventory(self, inv):
1316
"""Write inventory as the current inventory."""
1318
raise AssertionError("attempting to write an inventory when the "
1319
"dirstate is dirty will lose pending changes")
1320
had_inventory = self._inventory is not None
1321
# Setting self._inventory = None forces the dirstate to regenerate the
1322
# working inventory. We do this because self.inventory may be inv, or
1323
# may have been modified, and either case would prevent a clean delta
1325
self._inventory = None
1327
delta = inv._make_delta(self.inventory)
1329
self.apply_inventory_delta(delta)
1331
self._inventory = inv
1334
@needs_tree_write_lock
1335
def reset_state(self, revision_ids=None):
1336
"""Reset the state of the working tree.
1338
This does a hard-reset to a last-known-good state. This is a way to
1339
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1341
if revision_ids is None:
1342
revision_ids = self.get_parent_ids()
1343
if not revision_ids:
1344
base_tree = self.branch.repository.revision_tree(
1345
_mod_revision.NULL_REVISION)
1348
trees = zip(revision_ids,
1349
self.branch.repository.revision_trees(revision_ids))
1350
base_tree = trees[0][1]
1351
state = self.current_dirstate()
1352
# We don't support ghosts yet
1353
state.set_state_from_scratch(base_tree.inventory, trees, [])
1356
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1358
def __init__(self, tree):
1361
def sha1(self, abspath):
1362
"""See dirstate.SHA1Provider.sha1()."""
1363
filters = self.tree._content_filter_stack(
1364
self.tree.relpath(osutils.safe_unicode(abspath)))
1365
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1367
def stat_and_sha1(self, abspath):
1368
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1369
filters = self.tree._content_filter_stack(
1370
self.tree.relpath(osutils.safe_unicode(abspath)))
1371
file_obj = file(abspath, 'rb', 65000)
1373
statvalue = os.fstat(file_obj.fileno())
1375
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1376
sha1 = osutils.size_sha_file(file_obj)[1]
1379
return statvalue, sha1
1382
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1383
"""Dirstate working tree that supports content filtering.
1385
The dirstate holds the hash and size of the canonical form of the file,
1386
and most methods must return that.
1389
def _file_content_summary(self, path, stat_result):
1390
# This is to support the somewhat obsolete path_content_summary method
1391
# with content filtering: see
1392
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1394
# If the dirstate cache is up to date and knows the hash and size,
1396
# Otherwise if there are no content filters, return the on-disk size
1397
# and leave the hash blank.
1398
# Otherwise, read and filter the on-disk file and use its size and
1401
# The dirstate doesn't store the size of the canonical form so we
1402
# can't trust it for content-filtered trees. We just return None.
1403
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1404
executable = self._is_executable_from_path_and_stat(path, stat_result)
1405
return ('file', None, executable, dirstate_sha1)
1408
class WorkingTree4(DirStateWorkingTree):
1409
"""This is the Format 4 working tree.
1411
This differs from WorkingTree by:
1412
- Having a consolidated internal dirstate, stored in a
1413
randomly-accessible sorted file on disk.
1414
- Not having a regular inventory attribute. One can be synthesized
1415
on demand but this is expensive and should be avoided.
1417
This is new in bzr 0.15.
1421
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1422
"""This is the Format 5 working tree.
1424
This differs from WorkingTree4 by:
1425
- Supporting content filtering.
1427
This is new in bzr 1.11.
1431
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1432
"""This is the Format 6 working tree.
1434
This differs from WorkingTree5 by:
1435
- Supporting a current view that may mask the set of files in a tree
1436
impacted by most user operations.
1438
This is new in bzr 1.14.
1441
def _make_views(self):
1442
return views.PathBasedViews(self)
1445
class DirStateWorkingTreeFormat(WorkingTreeFormatMetaDir):
1447
missing_parent_conflicts = True
1449
supports_versioned_directories = True
1451
_lock_class = LockDir
1452
_lock_file_name = 'lock'
1454
def _open_control_files(self, a_bzrdir):
1455
transport = a_bzrdir.get_workingtree_transport(None)
1456
return LockableFiles(transport, self._lock_file_name,
1459
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1460
accelerator_tree=None, hardlink=False):
1461
"""See WorkingTreeFormat.initialize().
1463
:param revision_id: allows creating a working tree at a different
1464
revision than the branch is at.
1465
:param accelerator_tree: A tree which can be used for retrieving file
1466
contents more quickly than the revision tree, i.e. a workingtree.
1467
The revision tree will be used for cases where accelerator_tree's
1468
content is different.
1469
:param hardlink: If true, hard-link files from accelerator_tree,
1472
These trees get an initial random root id, if their repository supports
1473
rich root data, TREE_ROOT otherwise.
1475
if not isinstance(a_bzrdir.transport, LocalTransport):
1476
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1477
transport = a_bzrdir.get_workingtree_transport(self)
1478
control_files = self._open_control_files(a_bzrdir)
1479
control_files.create_lock()
1480
control_files.lock_write()
1481
transport.put_bytes('format', self.as_string(),
1482
mode=a_bzrdir._get_file_mode())
1483
if from_branch is not None:
1484
branch = from_branch
1486
branch = a_bzrdir.open_branch()
1487
if revision_id is None:
1488
revision_id = branch.last_revision()
1489
local_path = transport.local_abspath('dirstate')
1490
# write out new dirstate (must exist when we create the tree)
1491
state = dirstate.DirState.initialize(local_path)
1494
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1498
_control_files=control_files)
1500
wt.lock_tree_write()
1502
self._init_custom_control_files(wt)
1503
if revision_id in (None, _mod_revision.NULL_REVISION):
1504
if branch.repository.supports_rich_root():
1505
wt._set_root_id(generate_ids.gen_root_id())
1507
wt._set_root_id(ROOT_ID)
1510
# frequently, we will get here due to branching. The accelerator
1511
# tree will be the tree from the branch, so the desired basis
1512
# tree will often be a parent of the accelerator tree.
1513
if accelerator_tree is not None:
1515
basis = accelerator_tree.revision_tree(revision_id)
1516
except errors.NoSuchRevision:
1519
basis = branch.repository.revision_tree(revision_id)
1520
if revision_id == _mod_revision.NULL_REVISION:
1523
parents_list = [(revision_id, basis)]
1526
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1528
# if the basis has a root id we have to use that; otherwise we
1529
# use a new random one
1530
basis_root_id = basis.get_root_id()
1531
if basis_root_id is not None:
1532
wt._set_root_id(basis_root_id)
1534
if wt.supports_content_filtering():
1535
# The original tree may not have the same content filters
1536
# applied so we can't safely build the inventory delta from
1538
delta_from_tree = False
1540
delta_from_tree = True
1541
# delta_from_tree is safe even for DirStateRevisionTrees,
1542
# because wt4.apply_inventory_delta does not mutate the input
1543
# inventory entries.
1544
transform.build_tree(basis, wt, accelerator_tree,
1546
delta_from_tree=delta_from_tree)
1550
control_files.unlock()
1554
def _init_custom_control_files(self, wt):
1555
"""Subclasses with custom control files should override this method.
1557
The working tree and control files are locked for writing when this
1560
:param wt: the WorkingTree object
1563
def open(self, a_bzrdir, _found=False):
1564
"""Return the WorkingTree object for a_bzrdir
1566
_found is a private parameter, do not use it. It is used to indicate
1567
if format probing has already been done.
1570
# we are being called directly and must probe.
1571
raise NotImplementedError
1572
if not isinstance(a_bzrdir.transport, LocalTransport):
1573
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1574
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1577
def _open(self, a_bzrdir, control_files):
1578
"""Open the tree itself.
1580
:param a_bzrdir: the dir for the tree.
1581
:param control_files: the control files for the tree.
1583
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1584
branch=a_bzrdir.open_branch(),
1587
_control_files=control_files)
1589
def __get_matchingbzrdir(self):
1590
return self._get_matchingbzrdir()
1592
def _get_matchingbzrdir(self):
1593
"""Overrideable method to get a bzrdir for testing."""
1594
# please test against something that will let us do tree references
1595
return bzrdir.format_registry.make_bzrdir(
1596
'dirstate-with-subtree')
1598
_matchingbzrdir = property(__get_matchingbzrdir)
1601
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1602
"""The first consolidated dirstate working tree format.
1605
- exists within a metadir controlling .bzr
1606
- includes an explicit version marker for the workingtree control
1607
files, separate from the ControlDir format
1608
- modifies the hash cache format
1609
- is new in bzr 0.15
1610
- uses a LockDir to guard access to it.
1613
upgrade_recommended = False
1615
_tree_class = WorkingTree4
1618
def get_format_string(cls):
1619
"""See WorkingTreeFormat.get_format_string()."""
1620
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1622
def get_format_description(self):
1623
"""See WorkingTreeFormat.get_format_description()."""
1624
return "Working tree format 4"
1627
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1628
"""WorkingTree format supporting content filtering.
1631
upgrade_recommended = False
1633
_tree_class = WorkingTree5
1636
def get_format_string(cls):
1637
"""See WorkingTreeFormat.get_format_string()."""
1638
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1640
def get_format_description(self):
1641
"""See WorkingTreeFormat.get_format_description()."""
1642
return "Working tree format 5"
1644
def supports_content_filtering(self):
1648
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1649
"""WorkingTree format supporting views.
1652
upgrade_recommended = False
1654
_tree_class = WorkingTree6
1657
def get_format_string(cls):
1658
"""See WorkingTreeFormat.get_format_string()."""
1659
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1661
def get_format_description(self):
1662
"""See WorkingTreeFormat.get_format_description()."""
1663
return "Working tree format 6"
1665
def _init_custom_control_files(self, wt):
1666
"""Subclasses with custom control files should override this method."""
1667
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1669
def supports_content_filtering(self):
1672
def supports_views(self):
1676
class DirStateRevisionTree(InventoryTree):
1677
"""A revision tree pulling the inventory from a dirstate.
1679
Note that this is one of the historical (ie revision) trees cached in the
1680
dirstate for easy access, not the workingtree.
1683
def __init__(self, dirstate, revision_id, repository):
1684
self._dirstate = dirstate
1685
self._revision_id = revision_id
1686
self._repository = repository
1687
self._inventory = None
1689
self._dirstate_locked = False
1690
self._repo_supports_tree_reference = getattr(
1691
repository._format, "supports_tree_reference",
1695
return "<%s of %s in %s>" % \
1696
(self.__class__.__name__, self._revision_id, self._dirstate)
1698
def annotate_iter(self, file_id,
1699
default_revision=_mod_revision.CURRENT_REVISION):
1700
"""See Tree.annotate_iter"""
1701
text_key = (file_id, self.get_file_revision(file_id))
1702
annotations = self._repository.texts.annotate(text_key)
1703
return [(key[-1], line) for (key, line) in annotations]
1705
def _comparison_data(self, entry, path):
1706
"""See Tree._comparison_data."""
1708
return None, False, None
1709
# trust the entry as RevisionTree does, but this may not be
1710
# sensible: the entry might not have come from us?
1711
return entry.kind, entry.executable, None
1713
def _file_size(self, entry, stat_value):
1714
return entry.text_size
1716
def filter_unversioned_files(self, paths):
1717
"""Filter out paths that are not versioned.
1719
:return: set of paths.
1721
pred = self.has_filename
1722
return set((p for p in paths if not pred(p)))
1724
def get_root_id(self):
1725
return self.path2id('')
1727
def id2path(self, file_id):
1728
"Convert a file-id to a path."
1729
entry = self._get_entry(file_id=file_id)
1730
if entry == (None, None):
1731
raise errors.NoSuchId(tree=self, file_id=file_id)
1732
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1733
return path_utf8.decode('utf8')
1735
def iter_references(self):
1736
if not self._repo_supports_tree_reference:
1737
# When the repo doesn't support references, we will have nothing to
1740
# Otherwise, fall back to the default implementation
1741
return super(DirStateRevisionTree, self).iter_references()
1743
def _get_parent_index(self):
1744
"""Return the index in the dirstate referenced by this tree."""
1745
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1747
def _get_entry(self, file_id=None, path=None):
1748
"""Get the dirstate row for file_id or path.
1750
If either file_id or path is supplied, it is used as the key to lookup.
1751
If both are supplied, the fastest lookup is used, and an error is
1752
raised if they do not both point at the same row.
1754
:param file_id: An optional unicode file_id to be looked up.
1755
:param path: An optional unicode path to be looked up.
1756
:return: The dirstate row tuple for path/file_id, or (None, None)
1758
if file_id is None and path is None:
1759
raise errors.BzrError('must supply file_id or path')
1760
if path is not None:
1761
path = path.encode('utf8')
1762
parent_index = self._get_parent_index()
1763
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1765
def _generate_inventory(self):
1766
"""Create and set self.inventory from the dirstate object.
1768
(So this is only called the first time the inventory is requested for
1769
this tree; it then remains in memory until it's out of date.)
1771
This is relatively expensive: we have to walk the entire dirstate.
1773
if not self._locked:
1774
raise AssertionError(
1775
'cannot generate inventory of an unlocked '
1776
'dirstate revision tree')
1777
# separate call for profiling - makes it clear where the costs are.
1778
self._dirstate._read_dirblocks_if_needed()
1779
if self._revision_id not in self._dirstate.get_parent_ids():
1780
raise AssertionError(
1781
'parent %s has disappeared from %s' % (
1782
self._revision_id, self._dirstate.get_parent_ids()))
1783
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1784
# This is identical now to the WorkingTree _generate_inventory except
1785
# for the tree index use.
1786
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1787
current_id = root_key[2]
1788
if current_entry[parent_index][0] != 'd':
1789
raise AssertionError()
1790
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1791
inv.root.revision = current_entry[parent_index][4]
1792
# Turn some things into local variables
1793
minikind_to_kind = dirstate.DirState._minikind_to_kind
1794
factory = entry_factory
1795
utf8_decode = cache_utf8._utf8_decode
1796
inv_byid = inv._byid
1797
# we could do this straight out of the dirstate; it might be fast
1798
# and should be profiled - RBC 20070216
1799
parent_ies = {'' : inv.root}
1800
for block in self._dirstate._dirblocks[1:]: #skip root
1803
parent_ie = parent_ies[dirname]
1805
# all the paths in this block are not versioned in this tree
1807
for key, entry in block[1]:
1808
minikind, fingerprint, size, executable, revid = entry[parent_index]
1809
if minikind in ('a', 'r'): # absent, relocated
1813
name_unicode = utf8_decode(name)[0]
1815
kind = minikind_to_kind[minikind]
1816
inv_entry = factory[kind](file_id, name_unicode,
1818
inv_entry.revision = revid
1820
inv_entry.executable = executable
1821
inv_entry.text_size = size
1822
inv_entry.text_sha1 = fingerprint
1823
elif kind == 'directory':
1824
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1825
elif kind == 'symlink':
1826
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1827
elif kind == 'tree-reference':
1828
inv_entry.reference_revision = fingerprint or None
1830
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1832
# These checks cost us around 40ms on a 55k entry tree
1833
if file_id in inv_byid:
1834
raise AssertionError('file_id %s already in'
1835
' inventory as %s' % (file_id, inv_byid[file_id]))
1836
if name_unicode in parent_ie.children:
1837
raise AssertionError('name %r already in parent'
1839
inv_byid[file_id] = inv_entry
1840
parent_ie.children[name_unicode] = inv_entry
1841
self._inventory = inv
1843
def get_file_mtime(self, file_id, path=None):
1844
"""Return the modification time for this record.
1846
We return the timestamp of the last-changed revision.
1848
# Make sure the file exists
1849
entry = self._get_entry(file_id, path=path)
1850
if entry == (None, None): # do we raise?
1851
raise errors.NoSuchId(self, file_id)
1852
parent_index = self._get_parent_index()
1853
last_changed_revision = entry[1][parent_index][4]
1855
rev = self._repository.get_revision(last_changed_revision)
1856
except errors.NoSuchRevision:
1857
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1858
return rev.timestamp
1860
def get_file_sha1(self, file_id, path=None, stat_value=None):
1861
entry = self._get_entry(file_id=file_id, path=path)
1862
parent_index = self._get_parent_index()
1863
parent_details = entry[1][parent_index]
1864
if parent_details[0] == 'f':
1865
return parent_details[1]
1869
def get_file_revision(self, file_id):
1870
return self.inventory[file_id].revision
1872
def get_file(self, file_id, path=None):
1873
return StringIO(self.get_file_text(file_id))
1875
def get_file_size(self, file_id):
1876
"""See Tree.get_file_size"""
1877
return self.inventory[file_id].text_size
1879
def get_file_text(self, file_id, path=None):
1880
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1881
return ''.join(content)
1883
def get_reference_revision(self, file_id, path=None):
1884
return self.inventory[file_id].reference_revision
1886
def iter_files_bytes(self, desired_files):
1887
"""See Tree.iter_files_bytes.
1889
This version is implemented on top of Repository.iter_files_bytes"""
1890
parent_index = self._get_parent_index()
1891
repo_desired_files = []
1892
for file_id, identifier in desired_files:
1893
entry = self._get_entry(file_id)
1894
if entry == (None, None):
1895
raise errors.NoSuchId(self, file_id)
1896
repo_desired_files.append((file_id, entry[1][parent_index][4],
1898
return self._repository.iter_files_bytes(repo_desired_files)
1900
def get_symlink_target(self, file_id, path=None):
1901
entry = self._get_entry(file_id=file_id)
1902
parent_index = self._get_parent_index()
1903
if entry[1][parent_index][0] != 'l':
1906
target = entry[1][parent_index][1]
1907
target = target.decode('utf8')
1910
def get_revision_id(self):
1911
"""Return the revision id for this tree."""
1912
return self._revision_id
1914
def _get_inventory(self):
1915
if self._inventory is not None:
1916
return self._inventory
1917
self._must_be_locked()
1918
self._generate_inventory()
1919
return self._inventory
1921
inventory = property(_get_inventory,
1922
doc="Inventory of this Tree")
1924
def get_parent_ids(self):
1925
"""The parents of a tree in the dirstate are not cached."""
1926
return self._repository.get_revision(self._revision_id).parent_ids
1928
def has_filename(self, filename):
1929
return bool(self.path2id(filename))
1931
def kind(self, file_id):
1932
entry = self._get_entry(file_id=file_id)[1]
1934
raise errors.NoSuchId(tree=self, file_id=file_id)
1935
parent_index = self._get_parent_index()
1936
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1938
def stored_kind(self, file_id):
1939
"""See Tree.stored_kind"""
1940
return self.kind(file_id)
1942
def path_content_summary(self, path):
1943
"""See Tree.path_content_summary."""
1944
id = self.inventory.path2id(path)
1946
return ('missing', None, None, None)
1947
entry = self._inventory[id]
1950
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1951
elif kind == 'symlink':
1952
return (kind, None, None, entry.symlink_target)
1954
return (kind, None, None, None)
1956
def is_executable(self, file_id, path=None):
1957
ie = self.inventory[file_id]
1958
if ie.kind != "file":
1960
return ie.executable
1962
def is_locked(self):
1965
def list_files(self, include_root=False, from_dir=None, recursive=True):
1966
# We use a standard implementation, because DirStateRevisionTree is
1967
# dealing with one of the parents of the current state
1968
inv = self._get_inventory()
1969
if from_dir is None:
1972
from_dir_id = inv.path2id(from_dir)
1973
if from_dir_id is None:
1974
# Directory not versioned
1976
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1977
if inv.root is not None and not include_root and from_dir is None:
1979
for path, entry in entries:
1980
yield path, 'V', entry.kind, entry.file_id, entry
1982
def lock_read(self):
1983
"""Lock the tree for a set of operations.
1985
:return: A bzrlib.lock.LogicalLockResult.
1987
if not self._locked:
1988
self._repository.lock_read()
1989
if self._dirstate._lock_token is None:
1990
self._dirstate.lock_read()
1991
self._dirstate_locked = True
1993
return LogicalLockResult(self.unlock)
1995
def _must_be_locked(self):
1996
if not self._locked:
1997
raise errors.ObjectNotLocked(self)
2000
def path2id(self, path):
2001
"""Return the id for path in this tree."""
2002
# lookup by path: faster than splitting and walking the ivnentory.
2003
entry = self._get_entry(path=path)
2004
if entry == (None, None):
2009
"""Unlock, freeing any cache memory used during the lock."""
2010
# outside of a lock, the inventory is suspect: release it.
2012
if not self._locked:
2013
self._inventory = None
2015
if self._dirstate_locked:
2016
self._dirstate.unlock()
2017
self._dirstate_locked = False
2018
self._repository.unlock()
2021
def supports_tree_reference(self):
2022
return self._repo_supports_tree_reference
2024
def walkdirs(self, prefix=""):
2025
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
2026
# implementation based on an inventory.
2027
# This should be cleaned up to use the much faster Dirstate code
2028
# So for now, we just build up the parent inventory, and extract
2029
# it the same way RevisionTree does.
2030
_directory = 'directory'
2031
inv = self._get_inventory()
2032
top_id = inv.path2id(prefix)
2036
pending = [(prefix, top_id)]
2039
relpath, file_id = pending.pop()
2040
# 0 - relpath, 1- file-id
2042
relroot = relpath + '/'
2045
# FIXME: stash the node in pending
2046
entry = inv[file_id]
2047
for name, child in entry.sorted_children():
2048
toppath = relroot + name
2049
dirblock.append((toppath, name, child.kind, None,
2050
child.file_id, child.kind
2052
yield (relpath, entry.file_id), dirblock
2053
# push the user specified dirs from dirblock
2054
for dir in reversed(dirblock):
2055
if dir[2] == _directory:
2056
pending.append((dir[0], dir[4]))
2059
class InterDirStateTree(InterTree):
2060
"""Fast path optimiser for changes_from with dirstate trees.
2062
This is used only when both trees are in the dirstate working file, and
2063
the source is any parent within the dirstate, and the destination is
2064
the current working tree of the same dirstate.
2066
# this could be generalized to allow comparisons between any trees in the
2067
# dirstate, and possibly between trees stored in different dirstates.
2069
def __init__(self, source, target):
2070
super(InterDirStateTree, self).__init__(source, target)
2071
if not InterDirStateTree.is_compatible(source, target):
2072
raise Exception, "invalid source %r and target %r" % (source, target)
2075
def make_source_parent_tree(source, target):
2076
"""Change the source tree into a parent of the target."""
2077
revid = source.commit('record tree')
2078
target.branch.fetch(source.branch, revid)
2079
target.set_parent_ids([revid])
2080
return target.basis_tree(), target
2083
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
2084
result = klass.make_source_parent_tree(source, target)
2085
result[1]._iter_changes = dirstate.ProcessEntryPython
2089
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2091
from bzrlib.tests.test__dirstate_helpers import \
2092
compiled_dirstate_helpers_feature
2093
test_case.requireFeature(compiled_dirstate_helpers_feature)
2094
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2095
result = klass.make_source_parent_tree(source, target)
2096
result[1]._iter_changes = ProcessEntryC
2099
_matching_from_tree_format = WorkingTreeFormat4()
2100
_matching_to_tree_format = WorkingTreeFormat4()
2103
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
2104
# This method shouldn't be called, because we have python and C
2105
# specific flavours.
2106
raise NotImplementedError
2108
def iter_changes(self, include_unchanged=False,
2109
specific_files=None, pb=None, extra_trees=[],
2110
require_versioned=True, want_unversioned=False):
2111
"""Return the changes from source to target.
2113
:return: An iterator that yields tuples. See InterTree.iter_changes
2115
:param specific_files: An optional list of file paths to restrict the
2116
comparison to. When mapping filenames to ids, all matches in all
2117
trees (including optional extra_trees) are used, and all children of
2118
matched directories are included.
2119
:param include_unchanged: An optional boolean requesting the inclusion of
2120
unchanged entries in the result.
2121
:param extra_trees: An optional list of additional trees to use when
2122
mapping the contents of specific_files (paths) to file_ids.
2123
:param require_versioned: If True, all files in specific_files must be
2124
versioned in one of source, target, extra_trees or
2125
PathsNotVersionedError is raised.
2126
:param want_unversioned: Should unversioned files be returned in the
2127
output. An unversioned file is defined as one with (False, False)
2128
for the versioned pair.
2130
# TODO: handle extra trees in the dirstate.
2131
if (extra_trees or specific_files == []):
2132
# we can't fast-path these cases (yet)
2133
return super(InterDirStateTree, self).iter_changes(
2134
include_unchanged, specific_files, pb, extra_trees,
2135
require_versioned, want_unversioned=want_unversioned)
2136
parent_ids = self.target.get_parent_ids()
2137
if not (self.source._revision_id in parent_ids
2138
or self.source._revision_id == _mod_revision.NULL_REVISION):
2139
raise AssertionError(
2140
"revision {%s} is not stored in {%s}, but %s "
2141
"can only be used for trees stored in the dirstate"
2142
% (self.source._revision_id, self.target, self.iter_changes))
2144
if self.source._revision_id == _mod_revision.NULL_REVISION:
2146
indices = (target_index,)
2148
if not (self.source._revision_id in parent_ids):
2149
raise AssertionError(
2150
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2151
self.source._revision_id, parent_ids))
2152
source_index = 1 + parent_ids.index(self.source._revision_id)
2153
indices = (source_index, target_index)
2154
# -- make all specific_files utf8 --
2156
specific_files_utf8 = set()
2157
for path in specific_files:
2158
# Note, if there are many specific files, using cache_utf8
2159
# would be good here.
2160
specific_files_utf8.add(path.encode('utf8'))
2161
specific_files = specific_files_utf8
2163
specific_files = set([''])
2164
# -- specific_files is now a utf8 path set --
2166
# -- get the state object and prepare it.
2167
state = self.target.current_dirstate()
2168
state._read_dirblocks_if_needed()
2169
if require_versioned:
2170
# -- check all supplied paths are versioned in a search tree. --
2172
for path in specific_files:
2173
path_entries = state._entries_for_path(path)
2174
if not path_entries:
2175
# this specified path is not present at all: error
2176
not_versioned.append(path.decode('utf-8'))
2178
found_versioned = False
2179
# for each id at this path
2180
for entry in path_entries:
2182
for index in indices:
2183
if entry[1][index][0] != 'a': # absent
2184
found_versioned = True
2185
# all good: found a versioned cell
2187
if not found_versioned:
2188
# none of the indexes was not 'absent' at all ids for this
2190
not_versioned.append(path.decode('utf-8'))
2191
if len(not_versioned) > 0:
2192
raise errors.PathsNotVersionedError(not_versioned)
2193
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2194
search_specific_files = osutils.minimum_path_selection(specific_files)
2196
use_filesystem_for_exec = (sys.platform != 'win32')
2197
iter_changes = self.target._iter_changes(include_unchanged,
2198
use_filesystem_for_exec, search_specific_files, state,
2199
source_index, target_index, want_unversioned, self.target)
2200
return iter_changes.iter_changes()
2203
def is_compatible(source, target):
2204
# the target must be a dirstate working tree
2205
if not isinstance(target, DirStateWorkingTree):
2207
# the source must be a revtree or dirstate rev tree.
2208
if not isinstance(source,
2209
(revisiontree.RevisionTree, DirStateRevisionTree)):
2211
# the source revid must be in the target dirstate
2212
if not (source._revision_id == _mod_revision.NULL_REVISION or
2213
source._revision_id in target.get_parent_ids()):
2214
# TODO: what about ghosts? it may well need to
2215
# check for them explicitly.
2219
InterTree.register_optimiser(InterDirStateTree)
2222
class Converter3to4(object):
2223
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2226
self.target_format = WorkingTreeFormat4()
2228
def convert(self, tree):
2229
# lock the control files not the tree, so that we dont get tree
2230
# on-unlock behaviours, and so that noone else diddles with the
2231
# tree during upgrade.
2232
tree._control_files.lock_write()
2234
tree.read_working_inventory()
2235
self.create_dirstate_data(tree)
2236
self.update_format(tree)
2237
self.remove_xml_files(tree)
2239
tree._control_files.unlock()
2241
def create_dirstate_data(self, tree):
2242
"""Create the dirstate based data for tree."""
2243
local_path = tree.bzrdir.get_workingtree_transport(None
2244
).local_abspath('dirstate')
2245
state = dirstate.DirState.from_tree(tree, local_path)
2249
def remove_xml_files(self, tree):
2250
"""Remove the oldformat 3 data."""
2251
transport = tree.bzrdir.get_workingtree_transport(None)
2252
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2253
'pending-merges', 'stat-cache']:
2255
transport.delete(path)
2256
except errors.NoSuchFile:
2257
# some files are optional - just deal.
2260
def update_format(self, tree):
2261
"""Change the format marker."""
2262
tree._transport.put_bytes('format',
2263
self.target_format.as_string(),
2264
mode=tree.bzrdir._get_file_mode())
2267
class Converter4to5(object):
2268
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2271
self.target_format = WorkingTreeFormat5()
2273
def convert(self, tree):
2274
# lock the control files not the tree, so that we don't get tree
2275
# on-unlock behaviours, and so that no-one else diddles with the
2276
# tree during upgrade.
2277
tree._control_files.lock_write()
2279
self.update_format(tree)
2281
tree._control_files.unlock()
2283
def update_format(self, tree):
2284
"""Change the format marker."""
2285
tree._transport.put_bytes('format',
2286
self.target_format.as_string(),
2287
mode=tree.bzrdir._get_file_mode())
2290
class Converter4or5to6(object):
2291
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2294
self.target_format = WorkingTreeFormat6()
2296
def convert(self, tree):
2297
# lock the control files not the tree, so that we don't get tree
2298
# on-unlock behaviours, and so that no-one else diddles with the
2299
# tree during upgrade.
2300
tree._control_files.lock_write()
2302
self.init_custom_control_files(tree)
2303
self.update_format(tree)
2305
tree._control_files.unlock()
2307
def init_custom_control_files(self, tree):
2308
"""Initialize custom control files."""
2309
tree._transport.put_bytes('views', '',
2310
mode=tree.bzrdir._get_file_mode())
2312
def update_format(self, tree):
2313
"""Change the format marker."""
2314
tree._transport.put_bytes('format',
2315
self.target_format.as_string(),
2316
mode=tree.bzrdir._get_file_mode())