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 cStringIO import StringIO
29
from bzrlib.lazy_import import lazy_import
30
lazy_import(globals(), """
38
conflicts as _mod_conflicts,
42
filters as _mod_filters,
45
revision as _mod_revision,
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
54
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
55
from bzrlib.lock import LogicalLockResult
56
from bzrlib.lockable_files import LockableFiles
57
from bzrlib.lockdir import LockDir
58
from bzrlib.mutabletree import needs_tree_write_lock
59
from bzrlib.osutils import (
66
from bzrlib.transport.local import LocalTransport
67
from bzrlib.tree import (
71
from bzrlib.workingtree import (
78
class DirStateWorkingTree(InventoryWorkingTree):
80
def __init__(self, basedir,
85
"""Construct a WorkingTree for basedir.
87
If the branch is not supplied, it is opened automatically.
88
If the branch is supplied, it must be the branch for this basedir.
89
(branch.base is not cross checked, because for remote branches that
90
would be meaningless).
92
self._format = _format
94
basedir = safe_unicode(basedir)
95
trace.mutter("opening working tree %r", basedir)
97
self.basedir = realpath(basedir)
98
# if branch is at our basedir and is a format 6 or less
99
# assume all other formats have their own control files.
100
self._control_files = _control_files
101
self._transport = self._control_files._transport
104
# during a read or write lock these objects are set, and are
105
# None the rest of the time.
106
self._dirstate = None
107
self._inventory = None
109
self._setup_directory_is_tree_reference()
110
self._detect_case_handling()
111
self._rules_searcher = None
112
self.views = self._make_views()
113
#--- allow tests to select the dirstate iter_changes implementation
114
self._iter_changes = dirstate._process_entry
116
@needs_tree_write_lock
117
def _add(self, files, ids, kinds):
118
"""See MutableTree._add."""
119
state = self.current_dirstate()
120
for f, file_id, kind in zip(files, ids, kinds):
123
# special case tree root handling.
124
if f == '' and self.path2id(f) == ROOT_ID:
125
state.set_path_id('', generate_ids.gen_file_id(f))
128
file_id = generate_ids.gen_file_id(f)
129
# deliberately add the file with no cached stat or sha1
130
# - on the first access it will be gathered, and we can
131
# always change this once tests are all passing.
132
state.add(f, file_id, kind, None, '')
133
self._make_dirty(reset_inventory=True)
135
def _get_check_refs(self):
136
"""Return the references needed to perform a check of this tree."""
137
return [('trees', self.last_revision())]
139
def _make_dirty(self, reset_inventory):
140
"""Make the tree state dirty.
142
:param reset_inventory: True if the cached inventory should be removed
143
(presuming there is one).
146
if reset_inventory and self._inventory is not None:
147
self._inventory = None
149
@needs_tree_write_lock
150
def add_reference(self, sub_tree):
151
# use standard implementation, which calls back to self._add
153
# So we don't store the reference_revision in the working dirstate,
154
# it's just recorded at the moment of commit.
155
self._add_reference(sub_tree)
157
def break_lock(self):
158
"""Break a lock if one is present from another instance.
160
Uses the ui factory to ask for confirmation if the lock may be from
163
This will probe the repository for its lock as well.
165
# if the dirstate is locked by an active process, reject the break lock
168
if self._dirstate is None:
172
state = self._current_dirstate()
173
if state._lock_token is not None:
174
# we already have it locked. sheese, cant break our own lock.
175
raise errors.LockActive(self.basedir)
178
# try for a write lock - need permission to get one anyhow
181
except errors.LockContention:
182
# oslocks fail when a process is still live: fail.
183
# TODO: get the locked lockdir info and give to the user to
184
# assist in debugging.
185
raise errors.LockActive(self.basedir)
190
self._dirstate = None
191
self._control_files.break_lock()
192
self.branch.break_lock()
194
def _comparison_data(self, entry, path):
195
kind, executable, stat_value = \
196
WorkingTree._comparison_data(self, entry, path)
197
# it looks like a plain directory, but it's really a reference -- see
199
if (self._repo_supports_tree_reference and kind == 'directory'
200
and entry is not None and entry.kind == 'tree-reference'):
201
kind = 'tree-reference'
202
return kind, executable, stat_value
205
def commit(self, message=None, revprops=None, *args, **kwargs):
206
# mark the tree as dirty post commit - commit
207
# can change the current versioned list by doing deletes.
208
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
209
self._make_dirty(reset_inventory=True)
212
def current_dirstate(self):
213
"""Return the current dirstate object.
215
This is not part of the tree interface and only exposed for ease of
218
:raises errors.NotWriteLocked: when not in a lock.
220
self._must_be_locked()
221
return self._current_dirstate()
223
def _current_dirstate(self):
224
"""Internal function that does not check lock status.
226
This is needed for break_lock which also needs the dirstate.
228
if self._dirstate is not None:
229
return self._dirstate
230
local_path = self.bzrdir.get_workingtree_transport(None
231
).local_abspath('dirstate')
232
self._dirstate = dirstate.DirState.on_file(local_path,
233
self._sha1_provider(), self._worth_saving_limit())
234
return self._dirstate
236
def _sha1_provider(self):
237
"""A function that returns a SHA1Provider suitable for this tree.
239
:return: None if content filtering is not supported by this tree.
240
Otherwise, a SHA1Provider is returned that sha's the canonical
241
form of files, i.e. after read filters are applied.
243
if self.supports_content_filtering():
244
return ContentFilterAwareSHA1Provider(self)
248
def _worth_saving_limit(self):
249
"""How many hash changes are ok before we must save the dirstate.
251
:return: an integer. -1 means never save.
253
# FIXME: We want a WorkingTreeStack here -- vila 20110812
254
conf = config.BranchStack(self.branch)
255
return conf.get('bzr.workingtree.worth_saving_limit')
257
def filter_unversioned_files(self, paths):
258
"""Filter out paths that are versioned.
260
:return: set of paths.
262
# TODO: make a generic multi-bisect routine roughly that should list
263
# the paths, then process one half at a time recursively, and feed the
264
# results of each bisect in further still
265
paths = sorted(paths)
267
state = self.current_dirstate()
268
# TODO we want a paths_to_dirblocks helper I think
270
dirname, basename = os.path.split(path.encode('utf8'))
271
_, _, _, path_is_versioned = state._get_block_entry_index(
272
dirname, basename, 0)
273
if not path_is_versioned:
278
"""Write all cached data to disk."""
279
if self._control_files._lock_mode != 'w':
280
raise errors.NotWriteLocked(self)
281
self.current_dirstate().save()
282
self._inventory = None
285
@needs_tree_write_lock
286
def _gather_kinds(self, files, kinds):
287
"""See MutableTree._gather_kinds."""
288
for pos, f in enumerate(files):
289
if kinds[pos] is None:
290
kinds[pos] = self._kind(f)
292
def _generate_inventory(self):
293
"""Create and set self.inventory from the dirstate object.
295
This is relatively expensive: we have to walk the entire dirstate.
296
Ideally we would not, and can deprecate this function.
298
#: uncomment to trap on inventory requests.
299
# import pdb;pdb.set_trace()
300
state = self.current_dirstate()
301
state._read_dirblocks_if_needed()
302
root_key, current_entry = self._get_entry(path='')
303
current_id = root_key[2]
304
if not (current_entry[0][0] == 'd'): # directory
305
raise AssertionError(current_entry)
306
inv = Inventory(root_id=current_id)
307
# Turn some things into local variables
308
minikind_to_kind = dirstate.DirState._minikind_to_kind
309
factory = entry_factory
310
utf8_decode = cache_utf8._utf8_decode
312
# we could do this straight out of the dirstate; it might be fast
313
# and should be profiled - RBC 20070216
314
parent_ies = {'' : inv.root}
315
for block in state._dirblocks[1:]: # skip the root
318
parent_ie = parent_ies[dirname]
320
# all the paths in this block are not versioned in this tree
322
for key, entry in block[1]:
323
minikind, link_or_sha1, size, executable, stat = entry[0]
324
if minikind in ('a', 'r'): # absent, relocated
325
# a parent tree only entry
328
name_unicode = utf8_decode(name)[0]
330
kind = minikind_to_kind[minikind]
331
inv_entry = factory[kind](file_id, name_unicode,
334
# This is only needed on win32, where this is the only way
335
# we know the executable bit.
336
inv_entry.executable = executable
337
# not strictly needed: working tree
338
#inv_entry.text_size = size
339
#inv_entry.text_sha1 = sha1
340
elif kind == 'directory':
341
# add this entry to the parent map.
342
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
343
elif kind == 'tree-reference':
344
if not self._repo_supports_tree_reference:
345
raise errors.UnsupportedOperation(
346
self._generate_inventory,
347
self.branch.repository)
348
inv_entry.reference_revision = link_or_sha1 or None
349
elif kind != 'symlink':
350
raise AssertionError("unknown kind %r" % kind)
351
# These checks cost us around 40ms on a 55k entry tree
352
if file_id in inv_byid:
353
raise AssertionError('file_id %s already in'
354
' inventory as %s' % (file_id, inv_byid[file_id]))
355
if name_unicode in parent_ie.children:
356
raise AssertionError('name %r already in parent'
358
inv_byid[file_id] = inv_entry
359
parent_ie.children[name_unicode] = inv_entry
360
self._inventory = inv
362
def _get_entry(self, file_id=None, path=None):
363
"""Get the dirstate row for file_id or path.
365
If either file_id or path is supplied, it is used as the key to lookup.
366
If both are supplied, the fastest lookup is used, and an error is
367
raised if they do not both point at the same row.
369
:param file_id: An optional unicode file_id to be looked up.
370
:param path: An optional unicode path to be looked up.
371
:return: The dirstate row tuple for path/file_id, or (None, None)
373
if file_id is None and path is None:
374
raise errors.BzrError('must supply file_id or path')
375
state = self.current_dirstate()
377
path = path.encode('utf8')
378
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
380
def get_file_sha1(self, file_id, path=None, stat_value=None):
381
# check file id is valid unconditionally.
382
entry = self._get_entry(file_id=file_id, path=path)
384
raise errors.NoSuchId(self, file_id)
386
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
388
file_abspath = self.abspath(path)
389
state = self.current_dirstate()
390
if stat_value is None:
392
stat_value = osutils.lstat(file_abspath)
394
if e.errno == errno.ENOENT:
398
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
399
stat_value=stat_value)
400
if entry[1][0][0] == 'f':
401
if link_or_sha1 is None:
402
file_obj, statvalue = self.get_file_with_stat(file_id, path)
404
sha1 = osutils.sha_file(file_obj)
407
self._observed_sha1(file_id, path, (sha1, statvalue))
413
def _get_inventory(self):
414
"""Get the inventory for the tree. This is only valid within a lock."""
415
if 'evil' in debug.debug_flags:
416
trace.mutter_callsite(2,
417
"accessing .inventory forces a size of tree translation.")
418
if self._inventory is not None:
419
return self._inventory
420
self._must_be_locked()
421
self._generate_inventory()
422
return self._inventory
424
inventory = property(_get_inventory,
425
doc="Inventory of this Tree")
428
def get_parent_ids(self):
429
"""See Tree.get_parent_ids.
431
This implementation requests the ids list from the dirstate file.
433
return self.current_dirstate().get_parent_ids()
435
def get_reference_revision(self, file_id, path=None):
436
# referenced tree's revision is whatever's currently there
437
return self.get_nested_tree(file_id, path).last_revision()
439
def get_nested_tree(self, file_id, path=None):
441
path = self.id2path(file_id)
442
# else: check file_id is at path?
443
return WorkingTree.open(self.abspath(path))
446
def get_root_id(self):
447
"""Return the id of this trees root"""
448
return self._get_entry(path='')[0][2]
450
def has_id(self, file_id):
451
state = self.current_dirstate()
452
row, parents = self._get_entry(file_id=file_id)
455
return osutils.lexists(pathjoin(
456
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
458
def has_or_had_id(self, file_id):
459
state = self.current_dirstate()
460
row, parents = self._get_entry(file_id=file_id)
461
return row is not None
464
def id2path(self, file_id):
465
"Convert a file-id to a path."
466
state = self.current_dirstate()
467
entry = self._get_entry(file_id=file_id)
468
if entry == (None, None):
469
raise errors.NoSuchId(tree=self, file_id=file_id)
470
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
471
return path_utf8.decode('utf8')
473
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
474
entry = self._get_entry(path=path)
475
if entry == (None, None):
476
return False # Missing entries are not executable
477
return entry[1][0][3] # Executable?
479
if not osutils.supports_executable():
480
def is_executable(self, file_id, path=None):
481
"""Test if a file is executable or not.
483
Note: The caller is expected to take a read-lock before calling this.
485
entry = self._get_entry(file_id=file_id, path=path)
486
if entry == (None, None):
488
return entry[1][0][3]
490
_is_executable_from_path_and_stat = \
491
_is_executable_from_path_and_stat_from_basis
493
def is_executable(self, file_id, path=None):
494
"""Test if a file is executable or not.
496
Note: The caller is expected to take a read-lock before calling this.
498
self._must_be_locked()
500
path = self.id2path(file_id)
501
mode = osutils.lstat(self.abspath(path)).st_mode
502
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
504
def all_file_ids(self):
505
"""See Tree.iter_all_file_ids"""
506
self._must_be_locked()
508
for key, tree_details in self.current_dirstate()._iter_entries():
509
if tree_details[0][0] in ('a', 'r'): # relocated
516
"""Iterate through file_ids for this tree.
518
file_ids are in a WorkingTree if they are in the working inventory
519
and the working file exists.
522
for key, tree_details in self.current_dirstate()._iter_entries():
523
if tree_details[0][0] in ('a', 'r'): # absent, relocated
524
# not relevant to the working tree
526
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
527
if osutils.lexists(path):
528
result.append(key[2])
531
def iter_references(self):
532
if not self._repo_supports_tree_reference:
533
# When the repo doesn't support references, we will have nothing to
536
for key, tree_details in self.current_dirstate()._iter_entries():
537
if tree_details[0][0] in ('a', 'r'): # absent, relocated
538
# not relevant to the working tree
541
# the root is not a reference.
543
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
545
if self._kind(relpath) == 'tree-reference':
546
yield relpath, key[2]
547
except errors.NoSuchFile:
548
# path is missing on disk.
551
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
552
"""See MutableTree._observed_sha1."""
553
state = self.current_dirstate()
554
entry = self._get_entry(file_id=file_id, path=path)
555
state._observed_sha1(entry, sha1, statvalue)
557
def kind(self, file_id):
558
"""Return the kind of a file.
560
This is always the actual kind that's on disk, regardless of what it
563
Note: The caller is expected to take a read-lock before calling this.
565
relpath = self.id2path(file_id)
567
raise AssertionError(
568
"path for id {%s} is None!" % file_id)
569
return self._kind(relpath)
571
def _kind(self, relpath):
572
abspath = self.abspath(relpath)
573
kind = file_kind(abspath)
574
if (self._repo_supports_tree_reference and kind == 'directory'):
575
entry = self._get_entry(path=relpath)
576
if entry[1] is not None:
577
if entry[1][0][0] == 't':
578
kind = 'tree-reference'
582
def _last_revision(self):
583
"""See Mutable.last_revision."""
584
parent_ids = self.current_dirstate().get_parent_ids()
588
return _mod_revision.NULL_REVISION
591
"""See Branch.lock_read, and WorkingTree.unlock.
593
:return: A bzrlib.lock.LogicalLockResult.
595
self.branch.lock_read()
597
self._control_files.lock_read()
599
state = self.current_dirstate()
600
if not state._lock_token:
602
# set our support for tree references from the repository in
604
self._repo_supports_tree_reference = getattr(
605
self.branch.repository._format, "supports_tree_reference",
608
self._control_files.unlock()
613
return LogicalLockResult(self.unlock)
615
def _lock_self_write(self):
616
"""This should be called after the branch is locked."""
618
self._control_files.lock_write()
620
state = self.current_dirstate()
621
if not state._lock_token:
623
# set our support for tree references from the repository in
625
self._repo_supports_tree_reference = getattr(
626
self.branch.repository._format, "supports_tree_reference",
629
self._control_files.unlock()
634
return LogicalLockResult(self.unlock)
636
def lock_tree_write(self):
637
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
639
:return: A bzrlib.lock.LogicalLockResult.
641
self.branch.lock_read()
642
return self._lock_self_write()
644
def lock_write(self):
645
"""See MutableTree.lock_write, and WorkingTree.unlock.
647
:return: A bzrlib.lock.LogicalLockResult.
649
self.branch.lock_write()
650
return self._lock_self_write()
652
@needs_tree_write_lock
653
def move(self, from_paths, to_dir, after=False):
654
"""See WorkingTree.move()."""
658
state = self.current_dirstate()
659
if isinstance(from_paths, basestring):
661
to_dir_utf8 = to_dir.encode('utf8')
662
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
663
id_index = state._get_id_index()
664
# check destination directory
665
# get the details for it
666
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
667
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
668
if not entry_present:
669
raise errors.BzrMoveFailedError('', to_dir,
670
errors.NotVersionedError(to_dir))
671
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
672
# get a handle on the block itself.
673
to_block_index = state._ensure_block(
674
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
675
to_block = state._dirblocks[to_block_index]
676
to_abs = self.abspath(to_dir)
677
if not isdir(to_abs):
678
raise errors.BzrMoveFailedError('',to_dir,
679
errors.NotADirectory(to_abs))
681
if to_entry[1][0][0] != 'd':
682
raise errors.BzrMoveFailedError('',to_dir,
683
errors.NotADirectory(to_abs))
685
if self._inventory is not None:
686
update_inventory = True
688
to_dir_id = to_entry[0][2]
689
to_dir_ie = inv[to_dir_id]
691
update_inventory = False
694
def move_one(old_entry, from_path_utf8, minikind, executable,
695
fingerprint, packed_stat, size,
696
to_block, to_key, to_path_utf8):
697
state._make_absent(old_entry)
698
from_key = old_entry[0]
700
lambda:state.update_minimal(from_key,
702
executable=executable,
703
fingerprint=fingerprint,
704
packed_stat=packed_stat,
706
path_utf8=from_path_utf8))
707
state.update_minimal(to_key,
709
executable=executable,
710
fingerprint=fingerprint,
711
packed_stat=packed_stat,
713
path_utf8=to_path_utf8)
714
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
715
new_entry = to_block[1][added_entry_index]
716
rollbacks.append(lambda:state._make_absent(new_entry))
718
for from_rel in from_paths:
719
# from_rel is 'pathinroot/foo/bar'
720
from_rel_utf8 = from_rel.encode('utf8')
721
from_dirname, from_tail = osutils.split(from_rel)
722
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
723
from_entry = self._get_entry(path=from_rel)
724
if from_entry == (None, None):
725
raise errors.BzrMoveFailedError(from_rel,to_dir,
726
errors.NotVersionedError(path=from_rel))
728
from_id = from_entry[0][2]
729
to_rel = pathjoin(to_dir, from_tail)
730
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
731
item_to_entry = self._get_entry(path=to_rel)
732
if item_to_entry != (None, None):
733
raise errors.BzrMoveFailedError(from_rel, to_rel,
734
"Target is already versioned.")
736
if from_rel == to_rel:
737
raise errors.BzrMoveFailedError(from_rel, to_rel,
738
"Source and target are identical.")
740
from_missing = not self.has_filename(from_rel)
741
to_missing = not self.has_filename(to_rel)
748
raise errors.BzrMoveFailedError(from_rel, to_rel,
749
errors.NoSuchFile(path=to_rel,
750
extra="New file has not been created yet"))
752
# neither path exists
753
raise errors.BzrRenameFailedError(from_rel, to_rel,
754
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
756
if from_missing: # implicitly just update our path mapping
759
raise errors.RenameFailedFilesExist(from_rel, to_rel)
762
def rollback_rename():
763
"""A single rename has failed, roll it back."""
764
# roll back everything, even if we encounter trouble doing one
767
# TODO: at least log the other exceptions rather than just
768
# losing them mbp 20070307
770
for rollback in reversed(rollbacks):
774
exc_info = sys.exc_info()
776
raise exc_info[0], exc_info[1], exc_info[2]
778
# perform the disk move first - its the most likely failure point.
780
from_rel_abs = self.abspath(from_rel)
781
to_rel_abs = self.abspath(to_rel)
783
osutils.rename(from_rel_abs, to_rel_abs)
785
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
786
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
788
# perform the rename in the inventory next if needed: its easy
792
from_entry = inv[from_id]
793
current_parent = from_entry.parent_id
794
inv.rename(from_id, to_dir_id, from_tail)
796
lambda: inv.rename(from_id, current_parent, from_tail))
797
# finally do the rename in the dirstate, which is a little
798
# tricky to rollback, but least likely to need it.
799
old_block_index, old_entry_index, dir_present, file_present = \
800
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
801
old_block = state._dirblocks[old_block_index][1]
802
old_entry = old_block[old_entry_index]
803
from_key, old_entry_details = old_entry
804
cur_details = old_entry_details[0]
806
to_key = ((to_block[0],) + from_key[1:3])
807
minikind = cur_details[0]
808
move_one(old_entry, from_path_utf8=from_rel_utf8,
810
executable=cur_details[3],
811
fingerprint=cur_details[1],
812
packed_stat=cur_details[4],
816
to_path_utf8=to_rel_utf8)
819
def update_dirblock(from_dir, to_key, to_dir_utf8):
820
"""Recursively update all entries in this dirblock."""
822
raise AssertionError("renaming root not supported")
823
from_key = (from_dir, '')
824
from_block_idx, present = \
825
state._find_block_index_from_key(from_key)
827
# This is the old record, if it isn't present, then
828
# there is theoretically nothing to update.
829
# (Unless it isn't present because of lazy loading,
830
# but we don't do that yet)
832
from_block = state._dirblocks[from_block_idx]
833
to_block_index, to_entry_index, _, _ = \
834
state._get_block_entry_index(to_key[0], to_key[1], 0)
835
to_block_index = state._ensure_block(
836
to_block_index, to_entry_index, to_dir_utf8)
837
to_block = state._dirblocks[to_block_index]
839
# Grab a copy since move_one may update the list.
840
for entry in from_block[1][:]:
841
if not (entry[0][0] == from_dir):
842
raise AssertionError()
843
cur_details = entry[1][0]
844
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
845
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
846
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
847
minikind = cur_details[0]
849
# Deleted children of a renamed directory
850
# Do not need to be updated.
851
# Children that have been renamed out of this
852
# directory should also not be updated
854
move_one(entry, from_path_utf8=from_path_utf8,
856
executable=cur_details[3],
857
fingerprint=cur_details[1],
858
packed_stat=cur_details[4],
862
to_path_utf8=to_path_utf8)
864
# We need to move all the children of this
866
update_dirblock(from_path_utf8, to_key,
868
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
872
result.append((from_rel, to_rel))
873
state._mark_modified()
874
self._make_dirty(reset_inventory=False)
878
def _must_be_locked(self):
879
if not self._control_files._lock_count:
880
raise errors.ObjectNotLocked(self)
883
"""Initialize the state in this tree to be a new tree."""
887
def path2id(self, path):
888
"""Return the id for path in this tree."""
889
path = path.strip('/')
890
entry = self._get_entry(path=path)
891
if entry == (None, None):
895
def paths2ids(self, paths, trees=[], require_versioned=True):
896
"""See Tree.paths2ids().
898
This specialisation fast-paths the case where all the trees are in the
903
parents = self.get_parent_ids()
905
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
907
return super(DirStateWorkingTree, self).paths2ids(paths,
908
trees, require_versioned)
909
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
910
# -- make all paths utf8 --
913
paths_utf8.add(path.encode('utf8'))
915
# -- paths is now a utf8 path set --
916
# -- get the state object and prepare it.
917
state = self.current_dirstate()
918
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
919
and '' not in paths):
920
paths2ids = self._paths2ids_using_bisect
922
paths2ids = self._paths2ids_in_memory
923
return paths2ids(paths, search_indexes,
924
require_versioned=require_versioned)
926
def _paths2ids_in_memory(self, paths, search_indexes,
927
require_versioned=True):
928
state = self.current_dirstate()
929
state._read_dirblocks_if_needed()
930
def _entries_for_path(path):
931
"""Return a list with all the entries that match path for all ids.
933
dirname, basename = os.path.split(path)
934
key = (dirname, basename, '')
935
block_index, present = state._find_block_index_from_key(key)
937
# the block which should contain path is absent.
940
block = state._dirblocks[block_index][1]
941
entry_index, _ = state._find_entry_index(key, block)
942
# we may need to look at multiple entries at this path: walk while the paths match.
943
while (entry_index < len(block) and
944
block[entry_index][0][0:2] == key[0:2]):
945
result.append(block[entry_index])
948
if require_versioned:
949
# -- check all supplied paths are versioned in a search tree. --
952
path_entries = _entries_for_path(path)
954
# this specified path is not present at all: error
955
all_versioned = False
957
found_versioned = False
958
# for each id at this path
959
for entry in path_entries:
961
for index in search_indexes:
962
if entry[1][index][0] != 'a': # absent
963
found_versioned = True
964
# all good: found a versioned cell
966
if not found_versioned:
967
# none of the indexes was not 'absent' at all ids for this
969
all_versioned = False
971
if not all_versioned:
972
raise errors.PathsNotVersionedError(paths)
973
# -- remove redundancy in supplied paths to prevent over-scanning --
974
search_paths = osutils.minimum_path_selection(paths)
976
# for all search_indexs in each path at or under each element of
977
# search_paths, if the detail is relocated: add the id, and add the
978
# relocated path as one to search if its not searched already. If the
979
# detail is not relocated, add the id.
980
searched_paths = set()
982
def _process_entry(entry):
983
"""Look at search_indexes within entry.
985
If a specific tree's details are relocated, add the relocation
986
target to search_paths if not searched already. If it is absent, do
987
nothing. Otherwise add the id to found_ids.
989
for index in search_indexes:
990
if entry[1][index][0] == 'r': # relocated
991
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
992
search_paths.add(entry[1][index][1])
993
elif entry[1][index][0] != 'a': # absent
994
found_ids.add(entry[0][2])
996
current_root = search_paths.pop()
997
searched_paths.add(current_root)
998
# process the entries for this containing directory: the rest will be
999
# found by their parents recursively.
1000
root_entries = _entries_for_path(current_root)
1001
if not root_entries:
1002
# this specified path is not present at all, skip it.
1004
for entry in root_entries:
1005
_process_entry(entry)
1006
initial_key = (current_root, '', '')
1007
block_index, _ = state._find_block_index_from_key(initial_key)
1008
while (block_index < len(state._dirblocks) and
1009
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1010
for entry in state._dirblocks[block_index][1]:
1011
_process_entry(entry)
1015
def _paths2ids_using_bisect(self, paths, search_indexes,
1016
require_versioned=True):
1017
state = self.current_dirstate()
1020
split_paths = sorted(osutils.split(p) for p in paths)
1021
found = state._bisect_recursive(split_paths)
1023
if require_versioned:
1024
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1025
for dir_name in split_paths:
1026
if dir_name not in found_dir_names:
1027
raise errors.PathsNotVersionedError(paths)
1029
for dir_name_id, trees_info in found.iteritems():
1030
for index in search_indexes:
1031
if trees_info[index][0] not in ('r', 'a'):
1032
found_ids.add(dir_name_id[2])
1035
def read_working_inventory(self):
1036
"""Read the working inventory.
1038
This is a meaningless operation for dirstate, but we obey it anyhow.
1040
return self.inventory
1043
def revision_tree(self, revision_id):
1044
"""See Tree.revision_tree.
1046
WorkingTree4 supplies revision_trees for any basis tree.
1048
dirstate = self.current_dirstate()
1049
parent_ids = dirstate.get_parent_ids()
1050
if revision_id not in parent_ids:
1051
raise errors.NoSuchRevisionInTree(self, revision_id)
1052
if revision_id in dirstate.get_ghosts():
1053
raise errors.NoSuchRevisionInTree(self, revision_id)
1054
return DirStateRevisionTree(dirstate, revision_id,
1055
self.branch.repository)
1057
@needs_tree_write_lock
1058
def set_last_revision(self, new_revision):
1059
"""Change the last revision in the working tree."""
1060
parents = self.get_parent_ids()
1061
if new_revision in (_mod_revision.NULL_REVISION, None):
1062
if len(parents) >= 2:
1063
raise AssertionError(
1064
"setting the last parent to none with a pending merge is "
1066
self.set_parent_ids([])
1068
self.set_parent_ids([new_revision] + parents[1:],
1069
allow_leftmost_as_ghost=True)
1071
@needs_tree_write_lock
1072
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1073
"""Set the parent ids to revision_ids.
1075
See also set_parent_trees. This api will try to retrieve the tree data
1076
for each element of revision_ids from the trees repository. If you have
1077
tree data already available, it is more efficient to use
1078
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1079
an easier API to use.
1081
:param revision_ids: The revision_ids to set as the parent ids of this
1082
working tree. Any of these may be ghosts.
1085
for revision_id in revision_ids:
1087
revtree = self.branch.repository.revision_tree(revision_id)
1088
# TODO: jam 20070213 KnitVersionedFile raises
1089
# RevisionNotPresent rather than NoSuchRevision if a
1090
# given revision_id is not present. Should Repository be
1091
# catching it and re-raising NoSuchRevision?
1092
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1094
trees.append((revision_id, revtree))
1095
self.set_parent_trees(trees,
1096
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1098
@needs_tree_write_lock
1099
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1100
"""Set the parents of the working tree.
1102
:param parents_list: A list of (revision_id, tree) tuples.
1103
If tree is None, then that element is treated as an unreachable
1104
parent tree - i.e. a ghost.
1106
dirstate = self.current_dirstate()
1107
if len(parents_list) > 0:
1108
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1109
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1113
parent_ids = [rev_id for rev_id, tree in parents_list]
1114
graph = self.branch.repository.get_graph()
1115
heads = graph.heads(parent_ids)
1116
accepted_revisions = set()
1118
# convert absent trees to the null tree, which we convert back to
1119
# missing on access.
1120
for rev_id, tree in parents_list:
1121
if len(accepted_revisions) > 0:
1122
# we always accept the first tree
1123
if rev_id in accepted_revisions or rev_id not in heads:
1124
# We have already included either this tree, or its
1125
# descendent, so we skip it.
1127
_mod_revision.check_not_reserved_id(rev_id)
1128
if tree is not None:
1129
real_trees.append((rev_id, tree))
1131
real_trees.append((rev_id,
1132
self.branch.repository.revision_tree(
1133
_mod_revision.NULL_REVISION)))
1134
ghosts.append(rev_id)
1135
accepted_revisions.add(rev_id)
1137
if (len(real_trees) == 1
1139
and self.branch.repository._format.fast_deltas
1140
and isinstance(real_trees[0][1],
1141
revisiontree.InventoryRevisionTree)
1142
and self.get_parent_ids()):
1143
rev_id, rev_tree = real_trees[0]
1144
basis_id = self.get_parent_ids()[0]
1145
# There are times when basis_tree won't be in
1146
# self.branch.repository, (switch, for example)
1148
basis_tree = self.branch.repository.revision_tree(basis_id)
1149
except errors.NoSuchRevision:
1150
# Fall back to the set_parent_trees(), since we can't use
1151
# _make_delta if we can't get the RevisionTree
1154
delta = rev_tree.inventory._make_delta(basis_tree.inventory)
1155
dirstate.update_basis_by_delta(delta, rev_id)
1158
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1159
self._make_dirty(reset_inventory=False)
1161
def _set_root_id(self, file_id):
1162
"""See WorkingTree.set_root_id."""
1163
state = self.current_dirstate()
1164
state.set_path_id('', file_id)
1165
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1166
self._make_dirty(reset_inventory=True)
1168
def _sha_from_stat(self, path, stat_result):
1169
"""Get a sha digest from the tree's stat cache.
1171
The default implementation assumes no stat cache is present.
1173
:param path: The path.
1174
:param stat_result: The stat result being looked up.
1176
return self.current_dirstate().sha1_from_stat(path, stat_result)
1179
def supports_tree_reference(self):
1180
return self._repo_supports_tree_reference
1183
"""Unlock in format 4 trees needs to write the entire dirstate."""
1184
if self._control_files._lock_count == 1:
1185
# do non-implementation specific cleanup
1188
# eventually we should do signature checking during read locks for
1190
if self._control_files._lock_mode == 'w':
1193
if self._dirstate is not None:
1194
# This is a no-op if there are no modifications.
1195
self._dirstate.save()
1196
self._dirstate.unlock()
1197
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1198
# point. Instead, it could check if the header has been
1199
# modified when it is locked, and if not, it can hang on to
1200
# the data it has in memory.
1201
self._dirstate = None
1202
self._inventory = None
1203
# reverse order of locking.
1205
return self._control_files.unlock()
1207
self.branch.unlock()
1209
@needs_tree_write_lock
1210
def unversion(self, file_ids):
1211
"""Remove the file ids in file_ids from the current versioned set.
1213
When a file_id is unversioned, all of its children are automatically
1216
:param file_ids: The file ids to stop versioning.
1217
:raises: NoSuchId if any fileid is not currently versioned.
1221
state = self.current_dirstate()
1222
state._read_dirblocks_if_needed()
1223
ids_to_unversion = set(file_ids)
1224
paths_to_unversion = set()
1226
# check if the root is to be unversioned, if so, assert for now.
1227
# walk the state marking unversioned things as absent.
1228
# if there are any un-unversioned ids at the end, raise
1229
for key, details in state._dirblocks[0][1]:
1230
if (details[0][0] not in ('a', 'r') and # absent or relocated
1231
key[2] in ids_to_unversion):
1232
# I haven't written the code to unversion / yet - it should be
1234
raise errors.BzrError('Unversioning the / is not currently supported')
1236
while block_index < len(state._dirblocks):
1237
# process one directory at a time.
1238
block = state._dirblocks[block_index]
1239
# first check: is the path one to remove - it or its children
1240
delete_block = False
1241
for path in paths_to_unversion:
1242
if (block[0].startswith(path) and
1243
(len(block[0]) == len(path) or
1244
block[0][len(path)] == '/')):
1245
# this entire block should be deleted - its the block for a
1246
# path to unversion; or the child of one
1249
# TODO: trim paths_to_unversion as we pass by paths
1251
# this block is to be deleted: process it.
1252
# TODO: we can special case the no-parents case and
1253
# just forget the whole block.
1255
while entry_index < len(block[1]):
1256
entry = block[1][entry_index]
1257
if entry[1][0][0] in 'ar':
1258
# don't remove absent or renamed entries
1261
# Mark this file id as having been removed
1262
ids_to_unversion.discard(entry[0][2])
1263
if not state._make_absent(entry):
1264
# The block has not shrunk.
1266
# go to the next block. (At the moment we dont delete empty
1271
while entry_index < len(block[1]):
1272
entry = block[1][entry_index]
1273
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1274
# ^ some parent row.
1275
entry[0][2] not in ids_to_unversion):
1276
# ^ not an id to unversion
1279
if entry[1][0][0] == 'd':
1280
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1281
if not state._make_absent(entry):
1283
# we have unversioned this id
1284
ids_to_unversion.remove(entry[0][2])
1286
if ids_to_unversion:
1287
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1288
self._make_dirty(reset_inventory=False)
1289
# have to change the legacy inventory too.
1290
if self._inventory is not None:
1291
for file_id in file_ids:
1292
if self._inventory.has_id(file_id):
1293
self._inventory.remove_recursive_id(file_id)
1295
@needs_tree_write_lock
1296
def rename_one(self, from_rel, to_rel, after=False):
1297
"""See WorkingTree.rename_one"""
1299
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1301
@needs_tree_write_lock
1302
def apply_inventory_delta(self, changes):
1303
"""See MutableTree.apply_inventory_delta"""
1304
state = self.current_dirstate()
1305
state.update_by_delta(changes)
1306
self._make_dirty(reset_inventory=True)
1308
def update_basis_by_delta(self, new_revid, delta):
1309
"""See MutableTree.update_basis_by_delta."""
1310
if self.last_revision() == new_revid:
1311
raise AssertionError()
1312
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1315
def _validate(self):
1316
self._dirstate._validate()
1318
@needs_tree_write_lock
1319
def _write_inventory(self, inv):
1320
"""Write inventory as the current inventory."""
1322
raise AssertionError("attempting to write an inventory when the "
1323
"dirstate is dirty will lose pending changes")
1324
had_inventory = self._inventory is not None
1325
# Setting self._inventory = None forces the dirstate to regenerate the
1326
# working inventory. We do this because self.inventory may be inv, or
1327
# may have been modified, and either case would prevent a clean delta
1329
self._inventory = None
1331
delta = inv._make_delta(self.inventory)
1333
self.apply_inventory_delta(delta)
1335
self._inventory = inv
1338
@needs_tree_write_lock
1339
def reset_state(self, revision_ids=None):
1340
"""Reset the state of the working tree.
1342
This does a hard-reset to a last-known-good state. This is a way to
1343
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1345
if revision_ids is None:
1346
revision_ids = self.get_parent_ids()
1347
if not revision_ids:
1348
base_tree = self.branch.repository.revision_tree(
1349
_mod_revision.NULL_REVISION)
1352
trees = zip(revision_ids,
1353
self.branch.repository.revision_trees(revision_ids))
1354
base_tree = trees[0][1]
1355
state = self.current_dirstate()
1356
# We don't support ghosts yet
1357
state.set_state_from_scratch(base_tree.inventory, trees, [])
1360
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1362
def __init__(self, tree):
1365
def sha1(self, abspath):
1366
"""See dirstate.SHA1Provider.sha1()."""
1367
filters = self.tree._content_filter_stack(
1368
self.tree.relpath(osutils.safe_unicode(abspath)))
1369
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1371
def stat_and_sha1(self, abspath):
1372
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1373
filters = self.tree._content_filter_stack(
1374
self.tree.relpath(osutils.safe_unicode(abspath)))
1375
file_obj = file(abspath, 'rb', 65000)
1377
statvalue = os.fstat(file_obj.fileno())
1379
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1380
sha1 = osutils.size_sha_file(file_obj)[1]
1383
return statvalue, sha1
1386
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1387
"""Dirstate working tree that supports content filtering.
1389
The dirstate holds the hash and size of the canonical form of the file,
1390
and most methods must return that.
1393
def _file_content_summary(self, path, stat_result):
1394
# This is to support the somewhat obsolete path_content_summary method
1395
# with content filtering: see
1396
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1398
# If the dirstate cache is up to date and knows the hash and size,
1400
# Otherwise if there are no content filters, return the on-disk size
1401
# and leave the hash blank.
1402
# Otherwise, read and filter the on-disk file and use its size and
1405
# The dirstate doesn't store the size of the canonical form so we
1406
# can't trust it for content-filtered trees. We just return None.
1407
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1408
executable = self._is_executable_from_path_and_stat(path, stat_result)
1409
return ('file', None, executable, dirstate_sha1)
1412
class WorkingTree4(DirStateWorkingTree):
1413
"""This is the Format 4 working tree.
1415
This differs from WorkingTree by:
1416
- Having a consolidated internal dirstate, stored in a
1417
randomly-accessible sorted file on disk.
1418
- Not having a regular inventory attribute. One can be synthesized
1419
on demand but this is expensive and should be avoided.
1421
This is new in bzr 0.15.
1425
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1426
"""This is the Format 5 working tree.
1428
This differs from WorkingTree4 by:
1429
- Supporting content filtering.
1431
This is new in bzr 1.11.
1435
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1436
"""This is the Format 6 working tree.
1438
This differs from WorkingTree5 by:
1439
- Supporting a current view that may mask the set of files in a tree
1440
impacted by most user operations.
1442
This is new in bzr 1.14.
1445
def _make_views(self):
1446
return views.PathBasedViews(self)
1449
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1451
missing_parent_conflicts = True
1453
supports_versioned_directories = True
1455
_lock_class = LockDir
1456
_lock_file_name = 'lock'
1458
def _open_control_files(self, a_bzrdir):
1459
transport = a_bzrdir.get_workingtree_transport(None)
1460
return LockableFiles(transport, self._lock_file_name,
1463
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1464
accelerator_tree=None, hardlink=False):
1465
"""See WorkingTreeFormat.initialize().
1467
:param revision_id: allows creating a working tree at a different
1468
revision than the branch is at.
1469
:param accelerator_tree: A tree which can be used for retrieving file
1470
contents more quickly than the revision tree, i.e. a workingtree.
1471
The revision tree will be used for cases where accelerator_tree's
1472
content is different.
1473
:param hardlink: If true, hard-link files from accelerator_tree,
1476
These trees get an initial random root id, if their repository supports
1477
rich root data, TREE_ROOT otherwise.
1479
if not isinstance(a_bzrdir.transport, LocalTransport):
1480
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1481
transport = a_bzrdir.get_workingtree_transport(self)
1482
control_files = self._open_control_files(a_bzrdir)
1483
control_files.create_lock()
1484
control_files.lock_write()
1485
transport.put_bytes('format', self.get_format_string(),
1486
mode=a_bzrdir._get_file_mode())
1487
if from_branch is not None:
1488
branch = from_branch
1490
branch = a_bzrdir.open_branch()
1491
if revision_id is None:
1492
revision_id = branch.last_revision()
1493
local_path = transport.local_abspath('dirstate')
1494
# write out new dirstate (must exist when we create the tree)
1495
state = dirstate.DirState.initialize(local_path)
1498
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1502
_control_files=control_files)
1504
wt.lock_tree_write()
1506
self._init_custom_control_files(wt)
1507
if revision_id in (None, _mod_revision.NULL_REVISION):
1508
if branch.repository.supports_rich_root():
1509
wt._set_root_id(generate_ids.gen_root_id())
1511
wt._set_root_id(ROOT_ID)
1514
# frequently, we will get here due to branching. The accelerator
1515
# tree will be the tree from the branch, so the desired basis
1516
# tree will often be a parent of the accelerator tree.
1517
if accelerator_tree is not None:
1519
basis = accelerator_tree.revision_tree(revision_id)
1520
except errors.NoSuchRevision:
1523
basis = branch.repository.revision_tree(revision_id)
1524
if revision_id == _mod_revision.NULL_REVISION:
1527
parents_list = [(revision_id, basis)]
1530
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1532
# if the basis has a root id we have to use that; otherwise we
1533
# use a new random one
1534
basis_root_id = basis.get_root_id()
1535
if basis_root_id is not None:
1536
wt._set_root_id(basis_root_id)
1538
if wt.supports_content_filtering():
1539
# The original tree may not have the same content filters
1540
# applied so we can't safely build the inventory delta from
1542
delta_from_tree = False
1544
delta_from_tree = True
1545
# delta_from_tree is safe even for DirStateRevisionTrees,
1546
# because wt4.apply_inventory_delta does not mutate the input
1547
# inventory entries.
1548
transform.build_tree(basis, wt, accelerator_tree,
1550
delta_from_tree=delta_from_tree)
1554
control_files.unlock()
1558
def _init_custom_control_files(self, wt):
1559
"""Subclasses with custom control files should override this method.
1561
The working tree and control files are locked for writing when this
1564
:param wt: the WorkingTree object
1567
def open(self, a_bzrdir, _found=False):
1568
"""Return the WorkingTree object for a_bzrdir
1570
_found is a private parameter, do not use it. It is used to indicate
1571
if format probing has already been done.
1574
# we are being called directly and must probe.
1575
raise NotImplementedError
1576
if not isinstance(a_bzrdir.transport, LocalTransport):
1577
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1578
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1581
def _open(self, a_bzrdir, control_files):
1582
"""Open the tree itself.
1584
:param a_bzrdir: the dir for the tree.
1585
:param control_files: the control files for the tree.
1587
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1588
branch=a_bzrdir.open_branch(),
1591
_control_files=control_files)
1593
def __get_matchingbzrdir(self):
1594
return self._get_matchingbzrdir()
1596
def _get_matchingbzrdir(self):
1597
"""Overrideable method to get a bzrdir for testing."""
1598
# please test against something that will let us do tree references
1599
return bzrdir.format_registry.make_bzrdir(
1600
'dirstate-with-subtree')
1602
_matchingbzrdir = property(__get_matchingbzrdir)
1605
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1606
"""The first consolidated dirstate working tree format.
1609
- exists within a metadir controlling .bzr
1610
- includes an explicit version marker for the workingtree control
1611
files, separate from the BzrDir format
1612
- modifies the hash cache format
1613
- is new in bzr 0.15
1614
- uses a LockDir to guard access to it.
1617
upgrade_recommended = False
1619
_tree_class = WorkingTree4
1621
def get_format_string(self):
1622
"""See WorkingTreeFormat.get_format_string()."""
1623
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1625
def get_format_description(self):
1626
"""See WorkingTreeFormat.get_format_description()."""
1627
return "Working tree format 4"
1630
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1631
"""WorkingTree format supporting content filtering.
1634
upgrade_recommended = False
1636
_tree_class = WorkingTree5
1638
def get_format_string(self):
1639
"""See WorkingTreeFormat.get_format_string()."""
1640
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1642
def get_format_description(self):
1643
"""See WorkingTreeFormat.get_format_description()."""
1644
return "Working tree format 5"
1646
def supports_content_filtering(self):
1650
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1651
"""WorkingTree format supporting views.
1654
upgrade_recommended = False
1656
_tree_class = WorkingTree6
1658
def get_format_string(self):
1659
"""See WorkingTreeFormat.get_format_string()."""
1660
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1662
def get_format_description(self):
1663
"""See WorkingTreeFormat.get_format_description()."""
1664
return "Working tree format 6"
1666
def _init_custom_control_files(self, wt):
1667
"""Subclasses with custom control files should override this method."""
1668
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1670
def supports_content_filtering(self):
1673
def supports_views(self):
1677
class DirStateRevisionTree(InventoryTree):
1678
"""A revision tree pulling the inventory from a dirstate.
1680
Note that this is one of the historical (ie revision) trees cached in the
1681
dirstate for easy access, not the workingtree.
1684
def __init__(self, dirstate, revision_id, repository):
1685
self._dirstate = dirstate
1686
self._revision_id = revision_id
1687
self._repository = repository
1688
self._inventory = None
1690
self._dirstate_locked = False
1691
self._repo_supports_tree_reference = getattr(
1692
repository._format, "supports_tree_reference",
1696
return "<%s of %s in %s>" % \
1697
(self.__class__.__name__, self._revision_id, self._dirstate)
1699
def annotate_iter(self, file_id,
1700
default_revision=_mod_revision.CURRENT_REVISION):
1701
"""See Tree.annotate_iter"""
1702
text_key = (file_id, self.get_file_revision(file_id))
1703
annotations = self._repository.texts.annotate(text_key)
1704
return [(key[-1], line) for (key, line) in annotations]
1706
def _comparison_data(self, entry, path):
1707
"""See Tree._comparison_data."""
1709
return None, False, None
1710
# trust the entry as RevisionTree does, but this may not be
1711
# sensible: the entry might not have come from us?
1712
return entry.kind, entry.executable, None
1714
def _file_size(self, entry, stat_value):
1715
return entry.text_size
1717
def filter_unversioned_files(self, paths):
1718
"""Filter out paths that are not versioned.
1720
:return: set of paths.
1722
pred = self.has_filename
1723
return set((p for p in paths if not pred(p)))
1725
def get_root_id(self):
1726
return self.path2id('')
1728
def id2path(self, file_id):
1729
"Convert a file-id to a path."
1730
entry = self._get_entry(file_id=file_id)
1731
if entry == (None, None):
1732
raise errors.NoSuchId(tree=self, file_id=file_id)
1733
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1734
return path_utf8.decode('utf8')
1736
def iter_references(self):
1737
if not self._repo_supports_tree_reference:
1738
# When the repo doesn't support references, we will have nothing to
1741
# Otherwise, fall back to the default implementation
1742
return super(DirStateRevisionTree, self).iter_references()
1744
def _get_parent_index(self):
1745
"""Return the index in the dirstate referenced by this tree."""
1746
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1748
def _get_entry(self, file_id=None, path=None):
1749
"""Get the dirstate row for file_id or path.
1751
If either file_id or path is supplied, it is used as the key to lookup.
1752
If both are supplied, the fastest lookup is used, and an error is
1753
raised if they do not both point at the same row.
1755
:param file_id: An optional unicode file_id to be looked up.
1756
:param path: An optional unicode path to be looked up.
1757
:return: The dirstate row tuple for path/file_id, or (None, None)
1759
if file_id is None and path is None:
1760
raise errors.BzrError('must supply file_id or path')
1761
if path is not None:
1762
path = path.encode('utf8')
1763
parent_index = self._get_parent_index()
1764
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1766
def _generate_inventory(self):
1767
"""Create and set self.inventory from the dirstate object.
1769
(So this is only called the first time the inventory is requested for
1770
this tree; it then remains in memory until it's out of date.)
1772
This is relatively expensive: we have to walk the entire dirstate.
1774
if not self._locked:
1775
raise AssertionError(
1776
'cannot generate inventory of an unlocked '
1777
'dirstate revision tree')
1778
# separate call for profiling - makes it clear where the costs are.
1779
self._dirstate._read_dirblocks_if_needed()
1780
if self._revision_id not in self._dirstate.get_parent_ids():
1781
raise AssertionError(
1782
'parent %s has disappeared from %s' % (
1783
self._revision_id, self._dirstate.get_parent_ids()))
1784
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1785
# This is identical now to the WorkingTree _generate_inventory except
1786
# for the tree index use.
1787
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1788
current_id = root_key[2]
1789
if current_entry[parent_index][0] != 'd':
1790
raise AssertionError()
1791
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1792
inv.root.revision = current_entry[parent_index][4]
1793
# Turn some things into local variables
1794
minikind_to_kind = dirstate.DirState._minikind_to_kind
1795
factory = entry_factory
1796
utf8_decode = cache_utf8._utf8_decode
1797
inv_byid = inv._byid
1798
# we could do this straight out of the dirstate; it might be fast
1799
# and should be profiled - RBC 20070216
1800
parent_ies = {'' : inv.root}
1801
for block in self._dirstate._dirblocks[1:]: #skip root
1804
parent_ie = parent_ies[dirname]
1806
# all the paths in this block are not versioned in this tree
1808
for key, entry in block[1]:
1809
minikind, fingerprint, size, executable, revid = entry[parent_index]
1810
if minikind in ('a', 'r'): # absent, relocated
1814
name_unicode = utf8_decode(name)[0]
1816
kind = minikind_to_kind[minikind]
1817
inv_entry = factory[kind](file_id, name_unicode,
1819
inv_entry.revision = revid
1821
inv_entry.executable = executable
1822
inv_entry.text_size = size
1823
inv_entry.text_sha1 = fingerprint
1824
elif kind == 'directory':
1825
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1826
elif kind == 'symlink':
1827
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1828
elif kind == 'tree-reference':
1829
inv_entry.reference_revision = fingerprint or None
1831
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1833
# These checks cost us around 40ms on a 55k entry tree
1834
if file_id in inv_byid:
1835
raise AssertionError('file_id %s already in'
1836
' inventory as %s' % (file_id, inv_byid[file_id]))
1837
if name_unicode in parent_ie.children:
1838
raise AssertionError('name %r already in parent'
1840
inv_byid[file_id] = inv_entry
1841
parent_ie.children[name_unicode] = inv_entry
1842
self._inventory = inv
1844
def get_file_mtime(self, file_id, path=None):
1845
"""Return the modification time for this record.
1847
We return the timestamp of the last-changed revision.
1849
# Make sure the file exists
1850
entry = self._get_entry(file_id, path=path)
1851
if entry == (None, None): # do we raise?
1853
parent_index = self._get_parent_index()
1854
last_changed_revision = entry[1][parent_index][4]
1856
rev = self._repository.get_revision(last_changed_revision)
1857
except errors.NoSuchRevision:
1858
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1859
return rev.timestamp
1861
def get_file_sha1(self, file_id, path=None, stat_value=None):
1862
entry = self._get_entry(file_id=file_id, path=path)
1863
parent_index = self._get_parent_index()
1864
parent_details = entry[1][parent_index]
1865
if parent_details[0] == 'f':
1866
return parent_details[1]
1870
def get_file_revision(self, file_id):
1871
return self.inventory[file_id].revision
1873
def get_file(self, file_id, path=None):
1874
return StringIO(self.get_file_text(file_id))
1876
def get_file_size(self, file_id):
1877
"""See Tree.get_file_size"""
1878
return self.inventory[file_id].text_size
1880
def get_file_text(self, file_id, path=None):
1881
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1882
return ''.join(content)
1884
def get_reference_revision(self, file_id, path=None):
1885
return self.inventory[file_id].reference_revision
1887
def iter_files_bytes(self, desired_files):
1888
"""See Tree.iter_files_bytes.
1890
This version is implemented on top of Repository.iter_files_bytes"""
1891
parent_index = self._get_parent_index()
1892
repo_desired_files = []
1893
for file_id, identifier in desired_files:
1894
entry = self._get_entry(file_id)
1895
if entry == (None, None):
1896
raise errors.NoSuchId(self, file_id)
1897
repo_desired_files.append((file_id, entry[1][parent_index][4],
1899
return self._repository.iter_files_bytes(repo_desired_files)
1901
def get_symlink_target(self, file_id, path=None):
1902
entry = self._get_entry(file_id=file_id)
1903
parent_index = self._get_parent_index()
1904
if entry[1][parent_index][0] != 'l':
1907
target = entry[1][parent_index][1]
1908
target = target.decode('utf8')
1911
def get_revision_id(self):
1912
"""Return the revision id for this tree."""
1913
return self._revision_id
1915
def _get_inventory(self):
1916
if self._inventory is not None:
1917
return self._inventory
1918
self._must_be_locked()
1919
self._generate_inventory()
1920
return self._inventory
1922
inventory = property(_get_inventory,
1923
doc="Inventory of this Tree")
1925
def get_parent_ids(self):
1926
"""The parents of a tree in the dirstate are not cached."""
1927
return self._repository.get_revision(self._revision_id).parent_ids
1929
def has_filename(self, filename):
1930
return bool(self.path2id(filename))
1932
def kind(self, file_id):
1933
entry = self._get_entry(file_id=file_id)[1]
1935
raise errors.NoSuchId(tree=self, file_id=file_id)
1936
parent_index = self._get_parent_index()
1937
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1939
def stored_kind(self, file_id):
1940
"""See Tree.stored_kind"""
1941
return self.kind(file_id)
1943
def path_content_summary(self, path):
1944
"""See Tree.path_content_summary."""
1945
id = self.inventory.path2id(path)
1947
return ('missing', None, None, None)
1948
entry = self._inventory[id]
1951
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1952
elif kind == 'symlink':
1953
return (kind, None, None, entry.symlink_target)
1955
return (kind, None, None, None)
1957
def is_executable(self, file_id, path=None):
1958
ie = self.inventory[file_id]
1959
if ie.kind != "file":
1961
return ie.executable
1963
def is_locked(self):
1966
def list_files(self, include_root=False, from_dir=None, recursive=True):
1967
# We use a standard implementation, because DirStateRevisionTree is
1968
# dealing with one of the parents of the current state
1969
inv = self._get_inventory()
1970
if from_dir is None:
1973
from_dir_id = inv.path2id(from_dir)
1974
if from_dir_id is None:
1975
# Directory not versioned
1977
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1978
if inv.root is not None and not include_root and from_dir is None:
1980
for path, entry in entries:
1981
yield path, 'V', entry.kind, entry.file_id, entry
1983
def lock_read(self):
1984
"""Lock the tree for a set of operations.
1986
:return: A bzrlib.lock.LogicalLockResult.
1988
if not self._locked:
1989
self._repository.lock_read()
1990
if self._dirstate._lock_token is None:
1991
self._dirstate.lock_read()
1992
self._dirstate_locked = True
1994
return LogicalLockResult(self.unlock)
1996
def _must_be_locked(self):
1997
if not self._locked:
1998
raise errors.ObjectNotLocked(self)
2001
def path2id(self, path):
2002
"""Return the id for path in this tree."""
2003
# lookup by path: faster than splitting and walking the ivnentory.
2004
entry = self._get_entry(path=path)
2005
if entry == (None, None):
2010
"""Unlock, freeing any cache memory used during the lock."""
2011
# outside of a lock, the inventory is suspect: release it.
2013
if not self._locked:
2014
self._inventory = None
2016
if self._dirstate_locked:
2017
self._dirstate.unlock()
2018
self._dirstate_locked = False
2019
self._repository.unlock()
2022
def supports_tree_reference(self):
2023
return self._repo_supports_tree_reference
2025
def walkdirs(self, prefix=""):
2026
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
2027
# implementation based on an inventory.
2028
# This should be cleaned up to use the much faster Dirstate code
2029
# So for now, we just build up the parent inventory, and extract
2030
# it the same way RevisionTree does.
2031
_directory = 'directory'
2032
inv = self._get_inventory()
2033
top_id = inv.path2id(prefix)
2037
pending = [(prefix, top_id)]
2040
relpath, file_id = pending.pop()
2041
# 0 - relpath, 1- file-id
2043
relroot = relpath + '/'
2046
# FIXME: stash the node in pending
2047
entry = inv[file_id]
2048
for name, child in entry.sorted_children():
2049
toppath = relroot + name
2050
dirblock.append((toppath, name, child.kind, None,
2051
child.file_id, child.kind
2053
yield (relpath, entry.file_id), dirblock
2054
# push the user specified dirs from dirblock
2055
for dir in reversed(dirblock):
2056
if dir[2] == _directory:
2057
pending.append((dir[0], dir[4]))
2060
class InterDirStateTree(InterTree):
2061
"""Fast path optimiser for changes_from with dirstate trees.
2063
This is used only when both trees are in the dirstate working file, and
2064
the source is any parent within the dirstate, and the destination is
2065
the current working tree of the same dirstate.
2067
# this could be generalized to allow comparisons between any trees in the
2068
# dirstate, and possibly between trees stored in different dirstates.
2070
def __init__(self, source, target):
2071
super(InterDirStateTree, self).__init__(source, target)
2072
if not InterDirStateTree.is_compatible(source, target):
2073
raise Exception, "invalid source %r and target %r" % (source, target)
2076
def make_source_parent_tree(source, target):
2077
"""Change the source tree into a parent of the target."""
2078
revid = source.commit('record tree')
2079
target.branch.fetch(source.branch, revid)
2080
target.set_parent_ids([revid])
2081
return target.basis_tree(), target
2084
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
2085
result = klass.make_source_parent_tree(source, target)
2086
result[1]._iter_changes = dirstate.ProcessEntryPython
2090
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2092
from bzrlib.tests.test__dirstate_helpers import \
2093
compiled_dirstate_helpers_feature
2094
test_case.requireFeature(compiled_dirstate_helpers_feature)
2095
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2096
result = klass.make_source_parent_tree(source, target)
2097
result[1]._iter_changes = ProcessEntryC
2100
_matching_from_tree_format = WorkingTreeFormat4()
2101
_matching_to_tree_format = WorkingTreeFormat4()
2104
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
2105
# This method shouldn't be called, because we have python and C
2106
# specific flavours.
2107
raise NotImplementedError
2109
def iter_changes(self, include_unchanged=False,
2110
specific_files=None, pb=None, extra_trees=[],
2111
require_versioned=True, want_unversioned=False):
2112
"""Return the changes from source to target.
2114
:return: An iterator that yields tuples. See InterTree.iter_changes
2116
:param specific_files: An optional list of file paths to restrict the
2117
comparison to. When mapping filenames to ids, all matches in all
2118
trees (including optional extra_trees) are used, and all children of
2119
matched directories are included.
2120
:param include_unchanged: An optional boolean requesting the inclusion of
2121
unchanged entries in the result.
2122
:param extra_trees: An optional list of additional trees to use when
2123
mapping the contents of specific_files (paths) to file_ids.
2124
:param require_versioned: If True, all files in specific_files must be
2125
versioned in one of source, target, extra_trees or
2126
PathsNotVersionedError is raised.
2127
:param want_unversioned: Should unversioned files be returned in the
2128
output. An unversioned file is defined as one with (False, False)
2129
for the versioned pair.
2131
# TODO: handle extra trees in the dirstate.
2132
if (extra_trees or specific_files == []):
2133
# we can't fast-path these cases (yet)
2134
return super(InterDirStateTree, self).iter_changes(
2135
include_unchanged, specific_files, pb, extra_trees,
2136
require_versioned, want_unversioned=want_unversioned)
2137
parent_ids = self.target.get_parent_ids()
2138
if not (self.source._revision_id in parent_ids
2139
or self.source._revision_id == _mod_revision.NULL_REVISION):
2140
raise AssertionError(
2141
"revision {%s} is not stored in {%s}, but %s "
2142
"can only be used for trees stored in the dirstate"
2143
% (self.source._revision_id, self.target, self.iter_changes))
2145
if self.source._revision_id == _mod_revision.NULL_REVISION:
2147
indices = (target_index,)
2149
if not (self.source._revision_id in parent_ids):
2150
raise AssertionError(
2151
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2152
self.source._revision_id, parent_ids))
2153
source_index = 1 + parent_ids.index(self.source._revision_id)
2154
indices = (source_index, target_index)
2155
# -- make all specific_files utf8 --
2157
specific_files_utf8 = set()
2158
for path in specific_files:
2159
# Note, if there are many specific files, using cache_utf8
2160
# would be good here.
2161
specific_files_utf8.add(path.encode('utf8'))
2162
specific_files = specific_files_utf8
2164
specific_files = set([''])
2165
# -- specific_files is now a utf8 path set --
2167
# -- get the state object and prepare it.
2168
state = self.target.current_dirstate()
2169
state._read_dirblocks_if_needed()
2170
if require_versioned:
2171
# -- check all supplied paths are versioned in a search tree. --
2173
for path in specific_files:
2174
path_entries = state._entries_for_path(path)
2175
if not path_entries:
2176
# this specified path is not present at all: error
2177
not_versioned.append(path)
2179
found_versioned = False
2180
# for each id at this path
2181
for entry in path_entries:
2183
for index in indices:
2184
if entry[1][index][0] != 'a': # absent
2185
found_versioned = True
2186
# all good: found a versioned cell
2188
if not found_versioned:
2189
# none of the indexes was not 'absent' at all ids for this
2191
not_versioned.append(path)
2192
if len(not_versioned) > 0:
2193
raise errors.PathsNotVersionedError(not_versioned)
2194
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2195
search_specific_files = osutils.minimum_path_selection(specific_files)
2197
use_filesystem_for_exec = (sys.platform != 'win32')
2198
iter_changes = self.target._iter_changes(include_unchanged,
2199
use_filesystem_for_exec, search_specific_files, state,
2200
source_index, target_index, want_unversioned, self.target)
2201
return iter_changes.iter_changes()
2204
def is_compatible(source, target):
2205
# the target must be a dirstate working tree
2206
if not isinstance(target, DirStateWorkingTree):
2208
# the source must be a revtree or dirstate rev tree.
2209
if not isinstance(source,
2210
(revisiontree.RevisionTree, DirStateRevisionTree)):
2212
# the source revid must be in the target dirstate
2213
if not (source._revision_id == _mod_revision.NULL_REVISION or
2214
source._revision_id in target.get_parent_ids()):
2215
# TODO: what about ghosts? it may well need to
2216
# check for them explicitly.
2220
InterTree.register_optimiser(InterDirStateTree)
2223
class Converter3to4(object):
2224
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2227
self.target_format = WorkingTreeFormat4()
2229
def convert(self, tree):
2230
# lock the control files not the tree, so that we dont get tree
2231
# on-unlock behaviours, and so that noone else diddles with the
2232
# tree during upgrade.
2233
tree._control_files.lock_write()
2235
tree.read_working_inventory()
2236
self.create_dirstate_data(tree)
2237
self.update_format(tree)
2238
self.remove_xml_files(tree)
2240
tree._control_files.unlock()
2242
def create_dirstate_data(self, tree):
2243
"""Create the dirstate based data for tree."""
2244
local_path = tree.bzrdir.get_workingtree_transport(None
2245
).local_abspath('dirstate')
2246
state = dirstate.DirState.from_tree(tree, local_path)
2250
def remove_xml_files(self, tree):
2251
"""Remove the oldformat 3 data."""
2252
transport = tree.bzrdir.get_workingtree_transport(None)
2253
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2254
'pending-merges', 'stat-cache']:
2256
transport.delete(path)
2257
except errors.NoSuchFile:
2258
# some files are optional - just deal.
2261
def update_format(self, tree):
2262
"""Change the format marker."""
2263
tree._transport.put_bytes('format',
2264
self.target_format.get_format_string(),
2265
mode=tree.bzrdir._get_file_mode())
2268
class Converter4to5(object):
2269
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2272
self.target_format = WorkingTreeFormat5()
2274
def convert(self, tree):
2275
# lock the control files not the tree, so that we don't get tree
2276
# on-unlock behaviours, and so that no-one else diddles with the
2277
# tree during upgrade.
2278
tree._control_files.lock_write()
2280
self.update_format(tree)
2282
tree._control_files.unlock()
2284
def update_format(self, tree):
2285
"""Change the format marker."""
2286
tree._transport.put_bytes('format',
2287
self.target_format.get_format_string(),
2288
mode=tree.bzrdir._get_file_mode())
2291
class Converter4or5to6(object):
2292
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2295
self.target_format = WorkingTreeFormat6()
2297
def convert(self, tree):
2298
# lock the control files not the tree, so that we don't get tree
2299
# on-unlock behaviours, and so that no-one else diddles with the
2300
# tree during upgrade.
2301
tree._control_files.lock_write()
2303
self.init_custom_control_files(tree)
2304
self.update_format(tree)
2306
tree._control_files.unlock()
2308
def init_custom_control_files(self, tree):
2309
"""Initialize custom control files."""
2310
tree._transport.put_bytes('views', '',
2311
mode=tree.bzrdir._get_file_mode())
2313
def update_format(self, tree):
2314
"""Change the format marker."""
2315
tree._transport.put_bytes('format',
2316
self.target_format.get_format_string(),
2317
mode=tree.bzrdir._get_file_mode())