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(), """
37
conflicts as _mod_conflicts,
41
filters as _mod_filters,
44
revision as _mod_revision,
52
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
from bzrlib.inventory import Inventory, ROOT_ID, entry_factory
54
from bzrlib.lock import LogicalLockResult
55
from bzrlib.lockable_files import LockableFiles
56
from bzrlib.lockdir import LockDir
57
from bzrlib.mutabletree import needs_tree_write_lock
58
from bzrlib.osutils import (
65
from bzrlib.transport.local import LocalTransport
66
from bzrlib.tree import (
70
from bzrlib.workingtree import (
77
class DirStateWorkingTree(InventoryWorkingTree):
79
_DEFAULT_WORTH_SAVING_LIMIT = 10
81
def __init__(self, basedir,
86
"""Construct a WorkingTree for basedir.
88
If the branch is not supplied, it is opened automatically.
89
If the branch is supplied, it must be the branch for this basedir.
90
(branch.base is not cross checked, because for remote branches that
91
would be meaningless).
93
self._format = _format
95
basedir = safe_unicode(basedir)
96
trace.mutter("opening working tree %r", basedir)
98
self.basedir = realpath(basedir)
99
# if branch is at our basedir and is a format 6 or less
100
# assume all other formats have their own control files.
101
self._control_files = _control_files
102
self._transport = self._control_files._transport
105
# during a read or write lock these objects are set, and are
106
# None the rest of the time.
107
self._dirstate = None
108
self._inventory = None
110
self._setup_directory_is_tree_reference()
111
self._detect_case_handling()
112
self._rules_searcher = None
113
self.views = self._make_views()
114
#--- allow tests to select the dirstate iter_changes implementation
115
self._iter_changes = dirstate._process_entry
117
@needs_tree_write_lock
118
def _add(self, files, ids, kinds):
119
"""See MutableTree._add."""
120
state = self.current_dirstate()
121
for f, file_id, kind in zip(files, ids, kinds):
124
# special case tree root handling.
125
if f == '' and self.path2id(f) == ROOT_ID:
126
state.set_path_id('', generate_ids.gen_file_id(f))
129
file_id = generate_ids.gen_file_id(f)
130
# deliberately add the file with no cached stat or sha1
131
# - on the first access it will be gathered, and we can
132
# always change this once tests are all passing.
133
state.add(f, file_id, kind, None, '')
134
self._make_dirty(reset_inventory=True)
136
def _get_check_refs(self):
137
"""Return the references needed to perform a check of this tree."""
138
return [('trees', self.last_revision())]
140
def _make_dirty(self, reset_inventory):
141
"""Make the tree state dirty.
143
:param reset_inventory: True if the cached inventory should be removed
144
(presuming there is one).
147
if reset_inventory and self._inventory is not None:
148
self._inventory = None
150
@needs_tree_write_lock
151
def add_reference(self, sub_tree):
152
# use standard implementation, which calls back to self._add
154
# So we don't store the reference_revision in the working dirstate,
155
# it's just recorded at the moment of commit.
156
self._add_reference(sub_tree)
158
def break_lock(self):
159
"""Break a lock if one is present from another instance.
161
Uses the ui factory to ask for confirmation if the lock may be from
164
This will probe the repository for its lock as well.
166
# if the dirstate is locked by an active process, reject the break lock
169
if self._dirstate is None:
173
state = self._current_dirstate()
174
if state._lock_token is not None:
175
# we already have it locked. sheese, cant break our own lock.
176
raise errors.LockActive(self.basedir)
179
# try for a write lock - need permission to get one anyhow
182
except errors.LockContention:
183
# oslocks fail when a process is still live: fail.
184
# TODO: get the locked lockdir info and give to the user to
185
# assist in debugging.
186
raise errors.LockActive(self.basedir)
191
self._dirstate = None
192
self._control_files.break_lock()
193
self.branch.break_lock()
195
def _comparison_data(self, entry, path):
196
kind, executable, stat_value = \
197
WorkingTree._comparison_data(self, entry, path)
198
# it looks like a plain directory, but it's really a reference -- see
200
if (self._repo_supports_tree_reference and kind == 'directory'
201
and entry is not None and entry.kind == 'tree-reference'):
202
kind = 'tree-reference'
203
return kind, executable, stat_value
206
def commit(self, message=None, revprops=None, *args, **kwargs):
207
# mark the tree as dirty post commit - commit
208
# can change the current versioned list by doing deletes.
209
result = WorkingTree.commit(self, message, revprops, *args, **kwargs)
210
self._make_dirty(reset_inventory=True)
213
def current_dirstate(self):
214
"""Return the current dirstate object.
216
This is not part of the tree interface and only exposed for ease of
219
:raises errors.NotWriteLocked: when not in a lock.
221
self._must_be_locked()
222
return self._current_dirstate()
224
def _current_dirstate(self):
225
"""Internal function that does not check lock status.
227
This is needed for break_lock which also needs the dirstate.
229
if self._dirstate is not None:
230
return self._dirstate
231
local_path = self.bzrdir.get_workingtree_transport(None
232
).local_abspath('dirstate')
233
self._dirstate = dirstate.DirState.on_file(local_path,
234
self._sha1_provider(), self._worth_saving_limit())
235
return self._dirstate
237
def _sha1_provider(self):
238
"""A function that returns a SHA1Provider suitable for this tree.
240
:return: None if content filtering is not supported by this tree.
241
Otherwise, a SHA1Provider is returned that sha's the canonical
242
form of files, i.e. after read filters are applied.
244
if self.supports_content_filtering():
245
return ContentFilterAwareSHA1Provider(self)
249
def _worth_saving_limit(self):
250
"""How many hash changes are ok before we must save the dirstate.
252
:return: an integer. -1 means never save.
254
config = self.branch.get_config()
255
val = config.get_user_option('bzr.workingtree.worth_saving_limit')
257
val = self._DEFAULT_WORTH_SAVING_LIMIT
261
except ValueError, e:
262
trace.warning('Invalid config value for'
263
' "bzr.workingtree.worth_saving_limit"'
264
' value %r is not an integer.'
266
val = self._DEFAULT_WORTH_SAVING_LIMIT
269
def filter_unversioned_files(self, paths):
270
"""Filter out paths that are versioned.
272
:return: set of paths.
274
# TODO: make a generic multi-bisect routine roughly that should list
275
# the paths, then process one half at a time recursively, and feed the
276
# results of each bisect in further still
277
paths = sorted(paths)
279
state = self.current_dirstate()
280
# TODO we want a paths_to_dirblocks helper I think
282
dirname, basename = os.path.split(path.encode('utf8'))
283
_, _, _, path_is_versioned = state._get_block_entry_index(
284
dirname, basename, 0)
285
if not path_is_versioned:
290
"""Write all cached data to disk."""
291
if self._control_files._lock_mode != 'w':
292
raise errors.NotWriteLocked(self)
293
self.current_dirstate().save()
294
self._inventory = None
297
@needs_tree_write_lock
298
def _gather_kinds(self, files, kinds):
299
"""See MutableTree._gather_kinds."""
300
for pos, f in enumerate(files):
301
if kinds[pos] is None:
302
kinds[pos] = self._kind(f)
304
def _generate_inventory(self):
305
"""Create and set self.inventory from the dirstate object.
307
This is relatively expensive: we have to walk the entire dirstate.
308
Ideally we would not, and can deprecate this function.
310
#: uncomment to trap on inventory requests.
311
# import pdb;pdb.set_trace()
312
state = self.current_dirstate()
313
state._read_dirblocks_if_needed()
314
root_key, current_entry = self._get_entry(path='')
315
current_id = root_key[2]
316
if not (current_entry[0][0] == 'd'): # directory
317
raise AssertionError(current_entry)
318
inv = Inventory(root_id=current_id)
319
# Turn some things into local variables
320
minikind_to_kind = dirstate.DirState._minikind_to_kind
321
factory = entry_factory
322
utf8_decode = cache_utf8._utf8_decode
324
# we could do this straight out of the dirstate; it might be fast
325
# and should be profiled - RBC 20070216
326
parent_ies = {'' : inv.root}
327
for block in state._dirblocks[1:]: # skip the root
330
parent_ie = parent_ies[dirname]
332
# all the paths in this block are not versioned in this tree
334
for key, entry in block[1]:
335
minikind, link_or_sha1, size, executable, stat = entry[0]
336
if minikind in ('a', 'r'): # absent, relocated
337
# a parent tree only entry
340
name_unicode = utf8_decode(name)[0]
342
kind = minikind_to_kind[minikind]
343
inv_entry = factory[kind](file_id, name_unicode,
346
# This is only needed on win32, where this is the only way
347
# we know the executable bit.
348
inv_entry.executable = executable
349
# not strictly needed: working tree
350
#inv_entry.text_size = size
351
#inv_entry.text_sha1 = sha1
352
elif kind == 'directory':
353
# add this entry to the parent map.
354
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
355
elif kind == 'tree-reference':
356
if not self._repo_supports_tree_reference:
357
raise errors.UnsupportedOperation(
358
self._generate_inventory,
359
self.branch.repository)
360
inv_entry.reference_revision = link_or_sha1 or None
361
elif kind != 'symlink':
362
raise AssertionError("unknown kind %r" % kind)
363
# These checks cost us around 40ms on a 55k entry tree
364
if file_id in inv_byid:
365
raise AssertionError('file_id %s already in'
366
' inventory as %s' % (file_id, inv_byid[file_id]))
367
if name_unicode in parent_ie.children:
368
raise AssertionError('name %r already in parent'
370
inv_byid[file_id] = inv_entry
371
parent_ie.children[name_unicode] = inv_entry
372
self._inventory = inv
374
def _get_entry(self, file_id=None, path=None):
375
"""Get the dirstate row for file_id or path.
377
If either file_id or path is supplied, it is used as the key to lookup.
378
If both are supplied, the fastest lookup is used, and an error is
379
raised if they do not both point at the same row.
381
:param file_id: An optional unicode file_id to be looked up.
382
:param path: An optional unicode path to be looked up.
383
:return: The dirstate row tuple for path/file_id, or (None, None)
385
if file_id is None and path is None:
386
raise errors.BzrError('must supply file_id or path')
387
state = self.current_dirstate()
389
path = path.encode('utf8')
390
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
392
def get_file_sha1(self, file_id, path=None, stat_value=None):
393
# check file id is valid unconditionally.
394
entry = self._get_entry(file_id=file_id, path=path)
396
raise errors.NoSuchId(self, file_id)
398
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
400
file_abspath = self.abspath(path)
401
state = self.current_dirstate()
402
if stat_value is None:
404
stat_value = osutils.lstat(file_abspath)
406
if e.errno == errno.ENOENT:
410
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
411
stat_value=stat_value)
412
if entry[1][0][0] == 'f':
413
if link_or_sha1 is None:
414
file_obj, statvalue = self.get_file_with_stat(file_id, path)
416
sha1 = osutils.sha_file(file_obj)
419
self._observed_sha1(file_id, path, (sha1, statvalue))
425
def _get_inventory(self):
426
"""Get the inventory for the tree. This is only valid within a lock."""
427
if 'evil' in debug.debug_flags:
428
trace.mutter_callsite(2,
429
"accessing .inventory forces a size of tree translation.")
430
if self._inventory is not None:
431
return self._inventory
432
self._must_be_locked()
433
self._generate_inventory()
434
return self._inventory
436
inventory = property(_get_inventory,
437
doc="Inventory of this Tree")
440
def get_parent_ids(self):
441
"""See Tree.get_parent_ids.
443
This implementation requests the ids list from the dirstate file.
445
return self.current_dirstate().get_parent_ids()
447
def get_reference_revision(self, file_id, path=None):
448
# referenced tree's revision is whatever's currently there
449
return self.get_nested_tree(file_id, path).last_revision()
451
def get_nested_tree(self, file_id, path=None):
453
path = self.id2path(file_id)
454
# else: check file_id is at path?
455
return WorkingTree.open(self.abspath(path))
458
def get_root_id(self):
459
"""Return the id of this trees root"""
460
return self._get_entry(path='')[0][2]
462
def has_id(self, file_id):
463
state = self.current_dirstate()
464
row, parents = self._get_entry(file_id=file_id)
467
return osutils.lexists(pathjoin(
468
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
470
def has_or_had_id(self, file_id):
471
state = self.current_dirstate()
472
row, parents = self._get_entry(file_id=file_id)
473
return row is not None
476
def id2path(self, file_id):
477
"Convert a file-id to a path."
478
state = self.current_dirstate()
479
entry = self._get_entry(file_id=file_id)
480
if entry == (None, None):
481
raise errors.NoSuchId(tree=self, file_id=file_id)
482
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
483
return path_utf8.decode('utf8')
485
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
486
entry = self._get_entry(path=path)
487
if entry == (None, None):
488
return False # Missing entries are not executable
489
return entry[1][0][3] # Executable?
491
if not osutils.supports_executable():
492
def is_executable(self, file_id, path=None):
493
"""Test if a file is executable or not.
495
Note: The caller is expected to take a read-lock before calling this.
497
entry = self._get_entry(file_id=file_id, path=path)
498
if entry == (None, None):
500
return entry[1][0][3]
502
_is_executable_from_path_and_stat = \
503
_is_executable_from_path_and_stat_from_basis
505
def is_executable(self, file_id, path=None):
506
"""Test if a file is executable or not.
508
Note: The caller is expected to take a read-lock before calling this.
510
self._must_be_locked()
512
path = self.id2path(file_id)
513
mode = osutils.lstat(self.abspath(path)).st_mode
514
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
516
def all_file_ids(self):
517
"""See Tree.iter_all_file_ids"""
518
self._must_be_locked()
520
for key, tree_details in self.current_dirstate()._iter_entries():
521
if tree_details[0][0] in ('a', 'r'): # relocated
528
"""Iterate through file_ids for this tree.
530
file_ids are in a WorkingTree if they are in the working inventory
531
and the working file exists.
534
for key, tree_details in self.current_dirstate()._iter_entries():
535
if tree_details[0][0] in ('a', 'r'): # absent, relocated
536
# not relevant to the working tree
538
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
539
if osutils.lexists(path):
540
result.append(key[2])
543
def iter_references(self):
544
if not self._repo_supports_tree_reference:
545
# When the repo doesn't support references, we will have nothing to
548
for key, tree_details in self.current_dirstate()._iter_entries():
549
if tree_details[0][0] in ('a', 'r'): # absent, relocated
550
# not relevant to the working tree
553
# the root is not a reference.
555
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
557
if self._kind(relpath) == 'tree-reference':
558
yield relpath, key[2]
559
except errors.NoSuchFile:
560
# path is missing on disk.
563
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
564
"""See MutableTree._observed_sha1."""
565
state = self.current_dirstate()
566
entry = self._get_entry(file_id=file_id, path=path)
567
state._observed_sha1(entry, sha1, statvalue)
569
def kind(self, file_id):
570
"""Return the kind of a file.
572
This is always the actual kind that's on disk, regardless of what it
575
Note: The caller is expected to take a read-lock before calling this.
577
relpath = self.id2path(file_id)
579
raise AssertionError(
580
"path for id {%s} is None!" % file_id)
581
return self._kind(relpath)
583
def _kind(self, relpath):
584
abspath = self.abspath(relpath)
585
kind = file_kind(abspath)
586
if (self._repo_supports_tree_reference and kind == 'directory'):
587
entry = self._get_entry(path=relpath)
588
if entry[1] is not None:
589
if entry[1][0][0] == 't':
590
kind = 'tree-reference'
594
def _last_revision(self):
595
"""See Mutable.last_revision."""
596
parent_ids = self.current_dirstate().get_parent_ids()
600
return _mod_revision.NULL_REVISION
603
"""See Branch.lock_read, and WorkingTree.unlock.
605
:return: A bzrlib.lock.LogicalLockResult.
607
self.branch.lock_read()
609
self._control_files.lock_read()
611
state = self.current_dirstate()
612
if not state._lock_token:
614
# set our support for tree references from the repository in
616
self._repo_supports_tree_reference = getattr(
617
self.branch.repository._format, "supports_tree_reference",
620
self._control_files.unlock()
625
return LogicalLockResult(self.unlock)
627
def _lock_self_write(self):
628
"""This should be called after the branch is locked."""
630
self._control_files.lock_write()
632
state = self.current_dirstate()
633
if not state._lock_token:
635
# set our support for tree references from the repository in
637
self._repo_supports_tree_reference = getattr(
638
self.branch.repository._format, "supports_tree_reference",
641
self._control_files.unlock()
646
return LogicalLockResult(self.unlock)
648
def lock_tree_write(self):
649
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
651
:return: A bzrlib.lock.LogicalLockResult.
653
self.branch.lock_read()
654
return self._lock_self_write()
656
def lock_write(self):
657
"""See MutableTree.lock_write, and WorkingTree.unlock.
659
:return: A bzrlib.lock.LogicalLockResult.
661
self.branch.lock_write()
662
return self._lock_self_write()
664
@needs_tree_write_lock
665
def move(self, from_paths, to_dir, after=False):
666
"""See WorkingTree.move()."""
670
state = self.current_dirstate()
671
if isinstance(from_paths, basestring):
673
to_dir_utf8 = to_dir.encode('utf8')
674
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
675
id_index = state._get_id_index()
676
# check destination directory
677
# get the details for it
678
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
679
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
680
if not entry_present:
681
raise errors.BzrMoveFailedError('', to_dir,
682
errors.NotVersionedError(to_dir))
683
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
684
# get a handle on the block itself.
685
to_block_index = state._ensure_block(
686
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
687
to_block = state._dirblocks[to_block_index]
688
to_abs = self.abspath(to_dir)
689
if not isdir(to_abs):
690
raise errors.BzrMoveFailedError('',to_dir,
691
errors.NotADirectory(to_abs))
693
if to_entry[1][0][0] != 'd':
694
raise errors.BzrMoveFailedError('',to_dir,
695
errors.NotADirectory(to_abs))
697
if self._inventory is not None:
698
update_inventory = True
700
to_dir_id = to_entry[0][2]
701
to_dir_ie = inv[to_dir_id]
703
update_inventory = False
706
def move_one(old_entry, from_path_utf8, minikind, executable,
707
fingerprint, packed_stat, size,
708
to_block, to_key, to_path_utf8):
709
state._make_absent(old_entry)
710
from_key = old_entry[0]
712
lambda:state.update_minimal(from_key,
714
executable=executable,
715
fingerprint=fingerprint,
716
packed_stat=packed_stat,
718
path_utf8=from_path_utf8))
719
state.update_minimal(to_key,
721
executable=executable,
722
fingerprint=fingerprint,
723
packed_stat=packed_stat,
725
path_utf8=to_path_utf8)
726
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
727
new_entry = to_block[1][added_entry_index]
728
rollbacks.append(lambda:state._make_absent(new_entry))
730
for from_rel in from_paths:
731
# from_rel is 'pathinroot/foo/bar'
732
from_rel_utf8 = from_rel.encode('utf8')
733
from_dirname, from_tail = osutils.split(from_rel)
734
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
735
from_entry = self._get_entry(path=from_rel)
736
if from_entry == (None, None):
737
raise errors.BzrMoveFailedError(from_rel,to_dir,
738
errors.NotVersionedError(path=from_rel))
740
from_id = from_entry[0][2]
741
to_rel = pathjoin(to_dir, from_tail)
742
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
743
item_to_entry = self._get_entry(path=to_rel)
744
if item_to_entry != (None, None):
745
raise errors.BzrMoveFailedError(from_rel, to_rel,
746
"Target is already versioned.")
748
if from_rel == to_rel:
749
raise errors.BzrMoveFailedError(from_rel, to_rel,
750
"Source and target are identical.")
752
from_missing = not self.has_filename(from_rel)
753
to_missing = not self.has_filename(to_rel)
760
raise errors.BzrMoveFailedError(from_rel, to_rel,
761
errors.NoSuchFile(path=to_rel,
762
extra="New file has not been created yet"))
764
# neither path exists
765
raise errors.BzrRenameFailedError(from_rel, to_rel,
766
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
768
if from_missing: # implicitly just update our path mapping
771
raise errors.RenameFailedFilesExist(from_rel, to_rel)
774
def rollback_rename():
775
"""A single rename has failed, roll it back."""
776
# roll back everything, even if we encounter trouble doing one
779
# TODO: at least log the other exceptions rather than just
780
# losing them mbp 20070307
782
for rollback in reversed(rollbacks):
786
exc_info = sys.exc_info()
788
raise exc_info[0], exc_info[1], exc_info[2]
790
# perform the disk move first - its the most likely failure point.
792
from_rel_abs = self.abspath(from_rel)
793
to_rel_abs = self.abspath(to_rel)
795
osutils.rename(from_rel_abs, to_rel_abs)
797
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
798
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
800
# perform the rename in the inventory next if needed: its easy
804
from_entry = inv[from_id]
805
current_parent = from_entry.parent_id
806
inv.rename(from_id, to_dir_id, from_tail)
808
lambda: inv.rename(from_id, current_parent, from_tail))
809
# finally do the rename in the dirstate, which is a little
810
# tricky to rollback, but least likely to need it.
811
old_block_index, old_entry_index, dir_present, file_present = \
812
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
813
old_block = state._dirblocks[old_block_index][1]
814
old_entry = old_block[old_entry_index]
815
from_key, old_entry_details = old_entry
816
cur_details = old_entry_details[0]
818
to_key = ((to_block[0],) + from_key[1:3])
819
minikind = cur_details[0]
820
move_one(old_entry, from_path_utf8=from_rel_utf8,
822
executable=cur_details[3],
823
fingerprint=cur_details[1],
824
packed_stat=cur_details[4],
828
to_path_utf8=to_rel_utf8)
831
def update_dirblock(from_dir, to_key, to_dir_utf8):
832
"""Recursively update all entries in this dirblock."""
834
raise AssertionError("renaming root not supported")
835
from_key = (from_dir, '')
836
from_block_idx, present = \
837
state._find_block_index_from_key(from_key)
839
# This is the old record, if it isn't present, then
840
# there is theoretically nothing to update.
841
# (Unless it isn't present because of lazy loading,
842
# but we don't do that yet)
844
from_block = state._dirblocks[from_block_idx]
845
to_block_index, to_entry_index, _, _ = \
846
state._get_block_entry_index(to_key[0], to_key[1], 0)
847
to_block_index = state._ensure_block(
848
to_block_index, to_entry_index, to_dir_utf8)
849
to_block = state._dirblocks[to_block_index]
851
# Grab a copy since move_one may update the list.
852
for entry in from_block[1][:]:
853
if not (entry[0][0] == from_dir):
854
raise AssertionError()
855
cur_details = entry[1][0]
856
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
857
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
858
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
859
minikind = cur_details[0]
861
# Deleted children of a renamed directory
862
# Do not need to be updated.
863
# Children that have been renamed out of this
864
# directory should also not be updated
866
move_one(entry, from_path_utf8=from_path_utf8,
868
executable=cur_details[3],
869
fingerprint=cur_details[1],
870
packed_stat=cur_details[4],
874
to_path_utf8=to_path_utf8)
876
# We need to move all the children of this
878
update_dirblock(from_path_utf8, to_key,
880
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
884
result.append((from_rel, to_rel))
885
state._mark_modified()
886
self._make_dirty(reset_inventory=False)
890
def _must_be_locked(self):
891
if not self._control_files._lock_count:
892
raise errors.ObjectNotLocked(self)
895
"""Initialize the state in this tree to be a new tree."""
899
def path2id(self, path):
900
"""Return the id for path in this tree."""
901
path = path.strip('/')
902
entry = self._get_entry(path=path)
903
if entry == (None, None):
907
def paths2ids(self, paths, trees=[], require_versioned=True):
908
"""See Tree.paths2ids().
910
This specialisation fast-paths the case where all the trees are in the
915
parents = self.get_parent_ids()
917
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
919
return super(DirStateWorkingTree, self).paths2ids(paths,
920
trees, require_versioned)
921
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
922
# -- make all paths utf8 --
925
paths_utf8.add(path.encode('utf8'))
927
# -- paths is now a utf8 path set --
928
# -- get the state object and prepare it.
929
state = self.current_dirstate()
930
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
931
and '' not in paths):
932
paths2ids = self._paths2ids_using_bisect
934
paths2ids = self._paths2ids_in_memory
935
return paths2ids(paths, search_indexes,
936
require_versioned=require_versioned)
938
def _paths2ids_in_memory(self, paths, search_indexes,
939
require_versioned=True):
940
state = self.current_dirstate()
941
state._read_dirblocks_if_needed()
942
def _entries_for_path(path):
943
"""Return a list with all the entries that match path for all ids.
945
dirname, basename = os.path.split(path)
946
key = (dirname, basename, '')
947
block_index, present = state._find_block_index_from_key(key)
949
# the block which should contain path is absent.
952
block = state._dirblocks[block_index][1]
953
entry_index, _ = state._find_entry_index(key, block)
954
# we may need to look at multiple entries at this path: walk while the paths match.
955
while (entry_index < len(block) and
956
block[entry_index][0][0:2] == key[0:2]):
957
result.append(block[entry_index])
960
if require_versioned:
961
# -- check all supplied paths are versioned in a search tree. --
964
path_entries = _entries_for_path(path)
966
# this specified path is not present at all: error
967
all_versioned = False
969
found_versioned = False
970
# for each id at this path
971
for entry in path_entries:
973
for index in search_indexes:
974
if entry[1][index][0] != 'a': # absent
975
found_versioned = True
976
# all good: found a versioned cell
978
if not found_versioned:
979
# none of the indexes was not 'absent' at all ids for this
981
all_versioned = False
983
if not all_versioned:
984
raise errors.PathsNotVersionedError(paths)
985
# -- remove redundancy in supplied paths to prevent over-scanning --
986
search_paths = osutils.minimum_path_selection(paths)
988
# for all search_indexs in each path at or under each element of
989
# search_paths, if the detail is relocated: add the id, and add the
990
# relocated path as one to search if its not searched already. If the
991
# detail is not relocated, add the id.
992
searched_paths = set()
994
def _process_entry(entry):
995
"""Look at search_indexes within entry.
997
If a specific tree's details are relocated, add the relocation
998
target to search_paths if not searched already. If it is absent, do
999
nothing. Otherwise add the id to found_ids.
1001
for index in search_indexes:
1002
if entry[1][index][0] == 'r': # relocated
1003
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
1004
search_paths.add(entry[1][index][1])
1005
elif entry[1][index][0] != 'a': # absent
1006
found_ids.add(entry[0][2])
1008
current_root = search_paths.pop()
1009
searched_paths.add(current_root)
1010
# process the entries for this containing directory: the rest will be
1011
# found by their parents recursively.
1012
root_entries = _entries_for_path(current_root)
1013
if not root_entries:
1014
# this specified path is not present at all, skip it.
1016
for entry in root_entries:
1017
_process_entry(entry)
1018
initial_key = (current_root, '', '')
1019
block_index, _ = state._find_block_index_from_key(initial_key)
1020
while (block_index < len(state._dirblocks) and
1021
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1022
for entry in state._dirblocks[block_index][1]:
1023
_process_entry(entry)
1027
def _paths2ids_using_bisect(self, paths, search_indexes,
1028
require_versioned=True):
1029
state = self.current_dirstate()
1032
split_paths = sorted(osutils.split(p) for p in paths)
1033
found = state._bisect_recursive(split_paths)
1035
if require_versioned:
1036
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1037
for dir_name in split_paths:
1038
if dir_name not in found_dir_names:
1039
raise errors.PathsNotVersionedError(paths)
1041
for dir_name_id, trees_info in found.iteritems():
1042
for index in search_indexes:
1043
if trees_info[index][0] not in ('r', 'a'):
1044
found_ids.add(dir_name_id[2])
1047
def read_working_inventory(self):
1048
"""Read the working inventory.
1050
This is a meaningless operation for dirstate, but we obey it anyhow.
1052
return self.inventory
1055
def revision_tree(self, revision_id):
1056
"""See Tree.revision_tree.
1058
WorkingTree4 supplies revision_trees for any basis tree.
1060
dirstate = self.current_dirstate()
1061
parent_ids = dirstate.get_parent_ids()
1062
if revision_id not in parent_ids:
1063
raise errors.NoSuchRevisionInTree(self, revision_id)
1064
if revision_id in dirstate.get_ghosts():
1065
raise errors.NoSuchRevisionInTree(self, revision_id)
1066
return DirStateRevisionTree(dirstate, revision_id,
1067
self.branch.repository)
1069
@needs_tree_write_lock
1070
def set_last_revision(self, new_revision):
1071
"""Change the last revision in the working tree."""
1072
parents = self.get_parent_ids()
1073
if new_revision in (_mod_revision.NULL_REVISION, None):
1074
if len(parents) >= 2:
1075
raise AssertionError(
1076
"setting the last parent to none with a pending merge is "
1078
self.set_parent_ids([])
1080
self.set_parent_ids([new_revision] + parents[1:],
1081
allow_leftmost_as_ghost=True)
1083
@needs_tree_write_lock
1084
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1085
"""Set the parent ids to revision_ids.
1087
See also set_parent_trees. This api will try to retrieve the tree data
1088
for each element of revision_ids from the trees repository. If you have
1089
tree data already available, it is more efficient to use
1090
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1091
an easier API to use.
1093
:param revision_ids: The revision_ids to set as the parent ids of this
1094
working tree. Any of these may be ghosts.
1097
for revision_id in revision_ids:
1099
revtree = self.branch.repository.revision_tree(revision_id)
1100
# TODO: jam 20070213 KnitVersionedFile raises
1101
# RevisionNotPresent rather than NoSuchRevision if a
1102
# given revision_id is not present. Should Repository be
1103
# catching it and re-raising NoSuchRevision?
1104
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1106
trees.append((revision_id, revtree))
1107
self.set_parent_trees(trees,
1108
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1110
@needs_tree_write_lock
1111
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1112
"""Set the parents of the working tree.
1114
:param parents_list: A list of (revision_id, tree) tuples.
1115
If tree is None, then that element is treated as an unreachable
1116
parent tree - i.e. a ghost.
1118
dirstate = self.current_dirstate()
1119
if len(parents_list) > 0:
1120
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1121
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1125
parent_ids = [rev_id for rev_id, tree in parents_list]
1126
graph = self.branch.repository.get_graph()
1127
heads = graph.heads(parent_ids)
1128
accepted_revisions = set()
1130
# convert absent trees to the null tree, which we convert back to
1131
# missing on access.
1132
for rev_id, tree in parents_list:
1133
if len(accepted_revisions) > 0:
1134
# we always accept the first tree
1135
if rev_id in accepted_revisions or rev_id not in heads:
1136
# We have already included either this tree, or its
1137
# descendent, so we skip it.
1139
_mod_revision.check_not_reserved_id(rev_id)
1140
if tree is not None:
1141
real_trees.append((rev_id, tree))
1143
real_trees.append((rev_id,
1144
self.branch.repository.revision_tree(
1145
_mod_revision.NULL_REVISION)))
1146
ghosts.append(rev_id)
1147
accepted_revisions.add(rev_id)
1148
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1149
self._make_dirty(reset_inventory=False)
1151
def _set_root_id(self, file_id):
1152
"""See WorkingTree.set_root_id."""
1153
state = self.current_dirstate()
1154
state.set_path_id('', file_id)
1155
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1156
self._make_dirty(reset_inventory=True)
1158
def _sha_from_stat(self, path, stat_result):
1159
"""Get a sha digest from the tree's stat cache.
1161
The default implementation assumes no stat cache is present.
1163
:param path: The path.
1164
:param stat_result: The stat result being looked up.
1166
return self.current_dirstate().sha1_from_stat(path, stat_result)
1169
def supports_tree_reference(self):
1170
return self._repo_supports_tree_reference
1173
"""Unlock in format 4 trees needs to write the entire dirstate."""
1174
# do non-implementation specific cleanup
1177
if self._control_files._lock_count == 1:
1178
# eventually we should do signature checking during read locks for
1180
if self._control_files._lock_mode == 'w':
1183
if self._dirstate is not None:
1184
# This is a no-op if there are no modifications.
1185
self._dirstate.save()
1186
self._dirstate.unlock()
1187
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1188
# point. Instead, it could check if the header has been
1189
# modified when it is locked, and if not, it can hang on to
1190
# the data it has in memory.
1191
self._dirstate = None
1192
self._inventory = None
1193
# reverse order of locking.
1195
return self._control_files.unlock()
1197
self.branch.unlock()
1199
@needs_tree_write_lock
1200
def unversion(self, file_ids):
1201
"""Remove the file ids in file_ids from the current versioned set.
1203
When a file_id is unversioned, all of its children are automatically
1206
:param file_ids: The file ids to stop versioning.
1207
:raises: NoSuchId if any fileid is not currently versioned.
1211
state = self.current_dirstate()
1212
state._read_dirblocks_if_needed()
1213
ids_to_unversion = set(file_ids)
1214
paths_to_unversion = set()
1216
# check if the root is to be unversioned, if so, assert for now.
1217
# walk the state marking unversioned things as absent.
1218
# if there are any un-unversioned ids at the end, raise
1219
for key, details in state._dirblocks[0][1]:
1220
if (details[0][0] not in ('a', 'r') and # absent or relocated
1221
key[2] in ids_to_unversion):
1222
# I haven't written the code to unversion / yet - it should be
1224
raise errors.BzrError('Unversioning the / is not currently supported')
1226
while block_index < len(state._dirblocks):
1227
# process one directory at a time.
1228
block = state._dirblocks[block_index]
1229
# first check: is the path one to remove - it or its children
1230
delete_block = False
1231
for path in paths_to_unversion:
1232
if (block[0].startswith(path) and
1233
(len(block[0]) == len(path) or
1234
block[0][len(path)] == '/')):
1235
# this entire block should be deleted - its the block for a
1236
# path to unversion; or the child of one
1239
# TODO: trim paths_to_unversion as we pass by paths
1241
# this block is to be deleted: process it.
1242
# TODO: we can special case the no-parents case and
1243
# just forget the whole block.
1245
while entry_index < len(block[1]):
1246
entry = block[1][entry_index]
1247
if entry[1][0][0] in 'ar':
1248
# don't remove absent or renamed entries
1251
# Mark this file id as having been removed
1252
ids_to_unversion.discard(entry[0][2])
1253
if not state._make_absent(entry):
1254
# The block has not shrunk.
1256
# go to the next block. (At the moment we dont delete empty
1261
while entry_index < len(block[1]):
1262
entry = block[1][entry_index]
1263
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1264
# ^ some parent row.
1265
entry[0][2] not in ids_to_unversion):
1266
# ^ not an id to unversion
1269
if entry[1][0][0] == 'd':
1270
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1271
if not state._make_absent(entry):
1273
# we have unversioned this id
1274
ids_to_unversion.remove(entry[0][2])
1276
if ids_to_unversion:
1277
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1278
self._make_dirty(reset_inventory=False)
1279
# have to change the legacy inventory too.
1280
if self._inventory is not None:
1281
for file_id in file_ids:
1282
if self._inventory.has_id(file_id):
1283
self._inventory.remove_recursive_id(file_id)
1285
@needs_tree_write_lock
1286
def rename_one(self, from_rel, to_rel, after=False):
1287
"""See WorkingTree.rename_one"""
1289
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1291
@needs_tree_write_lock
1292
def apply_inventory_delta(self, changes):
1293
"""See MutableTree.apply_inventory_delta"""
1294
state = self.current_dirstate()
1295
state.update_by_delta(changes)
1296
self._make_dirty(reset_inventory=True)
1298
def update_basis_by_delta(self, new_revid, delta):
1299
"""See MutableTree.update_basis_by_delta."""
1300
if self.last_revision() == new_revid:
1301
raise AssertionError()
1302
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1305
def _validate(self):
1306
self._dirstate._validate()
1308
@needs_tree_write_lock
1309
def _write_inventory(self, inv):
1310
"""Write inventory as the current inventory."""
1312
raise AssertionError("attempting to write an inventory when the "
1313
"dirstate is dirty will lose pending changes")
1314
had_inventory = self._inventory is not None
1315
# Setting self._inventory = None forces the dirstate to regenerate the
1316
# working inventory. We do this because self.inventory may be inv, or
1317
# may have been modified, and either case would prevent a clean delta
1319
self._inventory = None
1321
delta = inv._make_delta(self.inventory)
1323
self.apply_inventory_delta(delta)
1325
self._inventory = inv
1328
@needs_tree_write_lock
1329
def reset_state(self, revision_ids=None):
1330
"""Reset the state of the working tree.
1332
This does a hard-reset to a last-known-good state. This is a way to
1333
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1335
if revision_ids is None:
1336
revision_ids = self.get_parent_ids()
1337
if not revision_ids:
1338
base_tree = self.branch.repository.revision_tree(
1339
_mod_revision.NULL_REVISION)
1342
trees = zip(revision_ids,
1343
self.branch.repository.revision_trees(revision_ids))
1344
base_tree = trees[0][1]
1345
state = self.current_dirstate()
1346
# We don't support ghosts yet
1347
state.set_state_from_scratch(base_tree.inventory, trees, [])
1350
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1352
def __init__(self, tree):
1355
def sha1(self, abspath):
1356
"""See dirstate.SHA1Provider.sha1()."""
1357
filters = self.tree._content_filter_stack(
1358
self.tree.relpath(osutils.safe_unicode(abspath)))
1359
return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
1361
def stat_and_sha1(self, abspath):
1362
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1363
filters = self.tree._content_filter_stack(
1364
self.tree.relpath(osutils.safe_unicode(abspath)))
1365
file_obj = file(abspath, 'rb', 65000)
1367
statvalue = os.fstat(file_obj.fileno())
1369
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
1370
sha1 = osutils.size_sha_file(file_obj)[1]
1373
return statvalue, sha1
1376
class ContentFilteringDirStateWorkingTree(DirStateWorkingTree):
1377
"""Dirstate working tree that supports content filtering.
1379
The dirstate holds the hash and size of the canonical form of the file,
1380
and most methods must return that.
1383
def _file_content_summary(self, path, stat_result):
1384
# This is to support the somewhat obsolete path_content_summary method
1385
# with content filtering: see
1386
# <https://bugs.launchpad.net/bzr/+bug/415508>.
1388
# If the dirstate cache is up to date and knows the hash and size,
1390
# Otherwise if there are no content filters, return the on-disk size
1391
# and leave the hash blank.
1392
# Otherwise, read and filter the on-disk file and use its size and
1395
# The dirstate doesn't store the size of the canonical form so we
1396
# can't trust it for content-filtered trees. We just return None.
1397
dirstate_sha1 = self._dirstate.sha1_from_stat(path, stat_result)
1398
executable = self._is_executable_from_path_and_stat(path, stat_result)
1399
return ('file', None, executable, dirstate_sha1)
1402
class WorkingTree4(DirStateWorkingTree):
1403
"""This is the Format 4 working tree.
1405
This differs from WorkingTree by:
1406
- Having a consolidated internal dirstate, stored in a
1407
randomly-accessible sorted file on disk.
1408
- Not having a regular inventory attribute. One can be synthesized
1409
on demand but this is expensive and should be avoided.
1411
This is new in bzr 0.15.
1415
class WorkingTree5(ContentFilteringDirStateWorkingTree):
1416
"""This is the Format 5 working tree.
1418
This differs from WorkingTree4 by:
1419
- Supporting content filtering.
1421
This is new in bzr 1.11.
1425
class WorkingTree6(ContentFilteringDirStateWorkingTree):
1426
"""This is the Format 6 working tree.
1428
This differs from WorkingTree5 by:
1429
- Supporting a current view that may mask the set of files in a tree
1430
impacted by most user operations.
1432
This is new in bzr 1.14.
1435
def _make_views(self):
1436
return views.PathBasedViews(self)
1439
class DirStateWorkingTreeFormat(WorkingTreeFormat):
1441
missing_parent_conflicts = True
1443
_lock_class = LockDir
1444
_lock_file_name = 'lock'
1446
def _open_control_files(self, a_bzrdir):
1447
transport = a_bzrdir.get_workingtree_transport(None)
1448
return LockableFiles(transport, self._lock_file_name,
1451
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1452
accelerator_tree=None, hardlink=False):
1453
"""See WorkingTreeFormat.initialize().
1455
:param revision_id: allows creating a working tree at a different
1456
revision than the branch is at.
1457
:param accelerator_tree: A tree which can be used for retrieving file
1458
contents more quickly than the revision tree, i.e. a workingtree.
1459
The revision tree will be used for cases where accelerator_tree's
1460
content is different.
1461
:param hardlink: If true, hard-link files from accelerator_tree,
1464
These trees get an initial random root id, if their repository supports
1465
rich root data, TREE_ROOT otherwise.
1467
if not isinstance(a_bzrdir.transport, LocalTransport):
1468
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1469
transport = a_bzrdir.get_workingtree_transport(self)
1470
control_files = self._open_control_files(a_bzrdir)
1471
control_files.create_lock()
1472
control_files.lock_write()
1473
transport.put_bytes('format', self.get_format_string(),
1474
mode=a_bzrdir._get_file_mode())
1475
if from_branch is not None:
1476
branch = from_branch
1478
branch = a_bzrdir.open_branch()
1479
if revision_id is None:
1480
revision_id = branch.last_revision()
1481
local_path = transport.local_abspath('dirstate')
1482
# write out new dirstate (must exist when we create the tree)
1483
state = dirstate.DirState.initialize(local_path)
1486
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1490
_control_files=control_files)
1492
wt.lock_tree_write()
1494
self._init_custom_control_files(wt)
1495
if revision_id in (None, _mod_revision.NULL_REVISION):
1496
if branch.repository.supports_rich_root():
1497
wt._set_root_id(generate_ids.gen_root_id())
1499
wt._set_root_id(ROOT_ID)
1502
# frequently, we will get here due to branching. The accelerator
1503
# tree will be the tree from the branch, so the desired basis
1504
# tree will often be a parent of the accelerator tree.
1505
if accelerator_tree is not None:
1507
basis = accelerator_tree.revision_tree(revision_id)
1508
except errors.NoSuchRevision:
1511
basis = branch.repository.revision_tree(revision_id)
1512
if revision_id == _mod_revision.NULL_REVISION:
1515
parents_list = [(revision_id, basis)]
1518
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1520
# if the basis has a root id we have to use that; otherwise we
1521
# use a new random one
1522
basis_root_id = basis.get_root_id()
1523
if basis_root_id is not None:
1524
wt._set_root_id(basis_root_id)
1526
if wt.supports_content_filtering():
1527
# The original tree may not have the same content filters
1528
# applied so we can't safely build the inventory delta from
1530
delta_from_tree = False
1532
delta_from_tree = True
1533
# delta_from_tree is safe even for DirStateRevisionTrees,
1534
# because wt4.apply_inventory_delta does not mutate the input
1535
# inventory entries.
1536
transform.build_tree(basis, wt, accelerator_tree,
1538
delta_from_tree=delta_from_tree)
1542
control_files.unlock()
1546
def _init_custom_control_files(self, wt):
1547
"""Subclasses with custom control files should override this method.
1549
The working tree and control files are locked for writing when this
1552
:param wt: the WorkingTree object
1555
def open(self, a_bzrdir, _found=False):
1556
"""Return the WorkingTree object for a_bzrdir
1558
_found is a private parameter, do not use it. It is used to indicate
1559
if format probing has already been done.
1562
# we are being called directly and must probe.
1563
raise NotImplementedError
1564
if not isinstance(a_bzrdir.transport, LocalTransport):
1565
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1566
wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1569
def _open(self, a_bzrdir, control_files):
1570
"""Open the tree itself.
1572
:param a_bzrdir: the dir for the tree.
1573
:param control_files: the control files for the tree.
1575
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1576
branch=a_bzrdir.open_branch(),
1579
_control_files=control_files)
1581
def __get_matchingbzrdir(self):
1582
return self._get_matchingbzrdir()
1584
def _get_matchingbzrdir(self):
1585
"""Overrideable method to get a bzrdir for testing."""
1586
# please test against something that will let us do tree references
1587
return bzrdir.format_registry.make_bzrdir(
1588
'dirstate-with-subtree')
1590
_matchingbzrdir = property(__get_matchingbzrdir)
1593
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1594
"""The first consolidated dirstate working tree format.
1597
- exists within a metadir controlling .bzr
1598
- includes an explicit version marker for the workingtree control
1599
files, separate from the BzrDir format
1600
- modifies the hash cache format
1601
- is new in bzr 0.15
1602
- uses a LockDir to guard access to it.
1605
upgrade_recommended = False
1607
_tree_class = WorkingTree4
1609
def get_format_string(self):
1610
"""See WorkingTreeFormat.get_format_string()."""
1611
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1613
def get_format_description(self):
1614
"""See WorkingTreeFormat.get_format_description()."""
1615
return "Working tree format 4"
1618
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1619
"""WorkingTree format supporting content filtering.
1622
upgrade_recommended = False
1624
_tree_class = WorkingTree5
1626
def get_format_string(self):
1627
"""See WorkingTreeFormat.get_format_string()."""
1628
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1630
def get_format_description(self):
1631
"""See WorkingTreeFormat.get_format_description()."""
1632
return "Working tree format 5"
1634
def supports_content_filtering(self):
1638
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1639
"""WorkingTree format supporting views.
1642
upgrade_recommended = False
1644
_tree_class = WorkingTree6
1646
def get_format_string(self):
1647
"""See WorkingTreeFormat.get_format_string()."""
1648
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1650
def get_format_description(self):
1651
"""See WorkingTreeFormat.get_format_description()."""
1652
return "Working tree format 6"
1654
def _init_custom_control_files(self, wt):
1655
"""Subclasses with custom control files should override this method."""
1656
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1658
def supports_content_filtering(self):
1661
def supports_views(self):
1665
class DirStateRevisionTree(InventoryTree):
1666
"""A revision tree pulling the inventory from a dirstate.
1668
Note that this is one of the historical (ie revision) trees cached in the
1669
dirstate for easy access, not the workingtree.
1672
def __init__(self, dirstate, revision_id, repository):
1673
self._dirstate = dirstate
1674
self._revision_id = revision_id
1675
self._repository = repository
1676
self._inventory = None
1678
self._dirstate_locked = False
1679
self._repo_supports_tree_reference = getattr(
1680
repository._format, "supports_tree_reference",
1684
return "<%s of %s in %s>" % \
1685
(self.__class__.__name__, self._revision_id, self._dirstate)
1687
def annotate_iter(self, file_id,
1688
default_revision=_mod_revision.CURRENT_REVISION):
1689
"""See Tree.annotate_iter"""
1690
text_key = (file_id, self.get_file_revision(file_id))
1691
annotations = self._repository.texts.annotate(text_key)
1692
return [(key[-1], line) for (key, line) in annotations]
1694
def _get_ancestors(self, default_revision):
1695
return set(self._repository.get_ancestry(self._revision_id,
1697
def _comparison_data(self, entry, path):
1698
"""See Tree._comparison_data."""
1700
return None, False, None
1701
# trust the entry as RevisionTree does, but this may not be
1702
# sensible: the entry might not have come from us?
1703
return entry.kind, entry.executable, None
1705
def _file_size(self, entry, stat_value):
1706
return entry.text_size
1708
def filter_unversioned_files(self, paths):
1709
"""Filter out paths that are not versioned.
1711
:return: set of paths.
1713
pred = self.has_filename
1714
return set((p for p in paths if not pred(p)))
1716
def get_root_id(self):
1717
return self.path2id('')
1719
def id2path(self, file_id):
1720
"Convert a file-id to a path."
1721
entry = self._get_entry(file_id=file_id)
1722
if entry == (None, None):
1723
raise errors.NoSuchId(tree=self, file_id=file_id)
1724
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1725
return path_utf8.decode('utf8')
1727
def iter_references(self):
1728
if not self._repo_supports_tree_reference:
1729
# When the repo doesn't support references, we will have nothing to
1732
# Otherwise, fall back to the default implementation
1733
return super(DirStateRevisionTree, self).iter_references()
1735
def _get_parent_index(self):
1736
"""Return the index in the dirstate referenced by this tree."""
1737
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1739
def _get_entry(self, file_id=None, path=None):
1740
"""Get the dirstate row for file_id or path.
1742
If either file_id or path is supplied, it is used as the key to lookup.
1743
If both are supplied, the fastest lookup is used, and an error is
1744
raised if they do not both point at the same row.
1746
:param file_id: An optional unicode file_id to be looked up.
1747
:param path: An optional unicode path to be looked up.
1748
:return: The dirstate row tuple for path/file_id, or (None, None)
1750
if file_id is None and path is None:
1751
raise errors.BzrError('must supply file_id or path')
1752
if path is not None:
1753
path = path.encode('utf8')
1754
parent_index = self._get_parent_index()
1755
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1757
def _generate_inventory(self):
1758
"""Create and set self.inventory from the dirstate object.
1760
(So this is only called the first time the inventory is requested for
1761
this tree; it then remains in memory until it's out of date.)
1763
This is relatively expensive: we have to walk the entire dirstate.
1765
if not self._locked:
1766
raise AssertionError(
1767
'cannot generate inventory of an unlocked '
1768
'dirstate revision tree')
1769
# separate call for profiling - makes it clear where the costs are.
1770
self._dirstate._read_dirblocks_if_needed()
1771
if self._revision_id not in self._dirstate.get_parent_ids():
1772
raise AssertionError(
1773
'parent %s has disappeared from %s' % (
1774
self._revision_id, self._dirstate.get_parent_ids()))
1775
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1776
# This is identical now to the WorkingTree _generate_inventory except
1777
# for the tree index use.
1778
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1779
current_id = root_key[2]
1780
if current_entry[parent_index][0] != 'd':
1781
raise AssertionError()
1782
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1783
inv.root.revision = current_entry[parent_index][4]
1784
# Turn some things into local variables
1785
minikind_to_kind = dirstate.DirState._minikind_to_kind
1786
factory = entry_factory
1787
utf8_decode = cache_utf8._utf8_decode
1788
inv_byid = inv._byid
1789
# we could do this straight out of the dirstate; it might be fast
1790
# and should be profiled - RBC 20070216
1791
parent_ies = {'' : inv.root}
1792
for block in self._dirstate._dirblocks[1:]: #skip root
1795
parent_ie = parent_ies[dirname]
1797
# all the paths in this block are not versioned in this tree
1799
for key, entry in block[1]:
1800
minikind, fingerprint, size, executable, revid = entry[parent_index]
1801
if minikind in ('a', 'r'): # absent, relocated
1805
name_unicode = utf8_decode(name)[0]
1807
kind = minikind_to_kind[minikind]
1808
inv_entry = factory[kind](file_id, name_unicode,
1810
inv_entry.revision = revid
1812
inv_entry.executable = executable
1813
inv_entry.text_size = size
1814
inv_entry.text_sha1 = fingerprint
1815
elif kind == 'directory':
1816
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1817
elif kind == 'symlink':
1818
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1819
elif kind == 'tree-reference':
1820
inv_entry.reference_revision = fingerprint or None
1822
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1824
# These checks cost us around 40ms on a 55k entry tree
1825
if file_id in inv_byid:
1826
raise AssertionError('file_id %s already in'
1827
' inventory as %s' % (file_id, inv_byid[file_id]))
1828
if name_unicode in parent_ie.children:
1829
raise AssertionError('name %r already in parent'
1831
inv_byid[file_id] = inv_entry
1832
parent_ie.children[name_unicode] = inv_entry
1833
self._inventory = inv
1835
def get_file_mtime(self, file_id, path=None):
1836
"""Return the modification time for this record.
1838
We return the timestamp of the last-changed revision.
1840
# Make sure the file exists
1841
entry = self._get_entry(file_id, path=path)
1842
if entry == (None, None): # do we raise?
1844
parent_index = self._get_parent_index()
1845
last_changed_revision = entry[1][parent_index][4]
1847
rev = self._repository.get_revision(last_changed_revision)
1848
except errors.NoSuchRevision:
1849
raise errors.FileTimestampUnavailable(self.id2path(file_id))
1850
return rev.timestamp
1852
def get_file_sha1(self, file_id, path=None, stat_value=None):
1853
entry = self._get_entry(file_id=file_id, path=path)
1854
parent_index = self._get_parent_index()
1855
parent_details = entry[1][parent_index]
1856
if parent_details[0] == 'f':
1857
return parent_details[1]
1861
def get_file_revision(self, file_id):
1862
return self.inventory[file_id].revision
1864
def get_file(self, file_id, path=None):
1865
return StringIO(self.get_file_text(file_id))
1867
def get_file_size(self, file_id):
1868
"""See Tree.get_file_size"""
1869
return self.inventory[file_id].text_size
1871
def get_file_text(self, file_id, path=None):
1872
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1873
return ''.join(content)
1875
def get_reference_revision(self, file_id, path=None):
1876
return self.inventory[file_id].reference_revision
1878
def iter_files_bytes(self, desired_files):
1879
"""See Tree.iter_files_bytes.
1881
This version is implemented on top of Repository.iter_files_bytes"""
1882
parent_index = self._get_parent_index()
1883
repo_desired_files = []
1884
for file_id, identifier in desired_files:
1885
entry = self._get_entry(file_id)
1886
if entry == (None, None):
1887
raise errors.NoSuchId(self, file_id)
1888
repo_desired_files.append((file_id, entry[1][parent_index][4],
1890
return self._repository.iter_files_bytes(repo_desired_files)
1892
def get_symlink_target(self, file_id, path=None):
1893
entry = self._get_entry(file_id=file_id)
1894
parent_index = self._get_parent_index()
1895
if entry[1][parent_index][0] != 'l':
1898
target = entry[1][parent_index][1]
1899
target = target.decode('utf8')
1902
def get_revision_id(self):
1903
"""Return the revision id for this tree."""
1904
return self._revision_id
1906
def _get_inventory(self):
1907
if self._inventory is not None:
1908
return self._inventory
1909
self._must_be_locked()
1910
self._generate_inventory()
1911
return self._inventory
1913
inventory = property(_get_inventory,
1914
doc="Inventory of this Tree")
1916
def get_parent_ids(self):
1917
"""The parents of a tree in the dirstate are not cached."""
1918
return self._repository.get_revision(self._revision_id).parent_ids
1920
def has_filename(self, filename):
1921
return bool(self.path2id(filename))
1923
def kind(self, file_id):
1924
entry = self._get_entry(file_id=file_id)[1]
1926
raise errors.NoSuchId(tree=self, file_id=file_id)
1927
parent_index = self._get_parent_index()
1928
return dirstate.DirState._minikind_to_kind[entry[parent_index][0]]
1930
def stored_kind(self, file_id):
1931
"""See Tree.stored_kind"""
1932
return self.kind(file_id)
1934
def path_content_summary(self, path):
1935
"""See Tree.path_content_summary."""
1936
id = self.inventory.path2id(path)
1938
return ('missing', None, None, None)
1939
entry = self._inventory[id]
1942
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1943
elif kind == 'symlink':
1944
return (kind, None, None, entry.symlink_target)
1946
return (kind, None, None, None)
1948
def is_executable(self, file_id, path=None):
1949
ie = self.inventory[file_id]
1950
if ie.kind != "file":
1952
return ie.executable
1954
def is_locked(self):
1957
def list_files(self, include_root=False, from_dir=None, recursive=True):
1958
# We use a standard implementation, because DirStateRevisionTree is
1959
# dealing with one of the parents of the current state
1960
inv = self._get_inventory()
1961
if from_dir is None:
1964
from_dir_id = inv.path2id(from_dir)
1965
if from_dir_id is None:
1966
# Directory not versioned
1968
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1969
if inv.root is not None and not include_root and from_dir is None:
1971
for path, entry in entries:
1972
yield path, 'V', entry.kind, entry.file_id, entry
1974
def lock_read(self):
1975
"""Lock the tree for a set of operations.
1977
:return: A bzrlib.lock.LogicalLockResult.
1979
if not self._locked:
1980
self._repository.lock_read()
1981
if self._dirstate._lock_token is None:
1982
self._dirstate.lock_read()
1983
self._dirstate_locked = True
1985
return LogicalLockResult(self.unlock)
1987
def _must_be_locked(self):
1988
if not self._locked:
1989
raise errors.ObjectNotLocked(self)
1992
def path2id(self, path):
1993
"""Return the id for path in this tree."""
1994
# lookup by path: faster than splitting and walking the ivnentory.
1995
entry = self._get_entry(path=path)
1996
if entry == (None, None):
2001
"""Unlock, freeing any cache memory used during the lock."""
2002
# outside of a lock, the inventory is suspect: release it.
2004
if not self._locked:
2005
self._inventory = None
2007
if self._dirstate_locked:
2008
self._dirstate.unlock()
2009
self._dirstate_locked = False
2010
self._repository.unlock()
2013
def supports_tree_reference(self):
2014
return self._repo_supports_tree_reference
2016
def walkdirs(self, prefix=""):
2017
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
2018
# implementation based on an inventory.
2019
# This should be cleaned up to use the much faster Dirstate code
2020
# So for now, we just build up the parent inventory, and extract
2021
# it the same way RevisionTree does.
2022
_directory = 'directory'
2023
inv = self._get_inventory()
2024
top_id = inv.path2id(prefix)
2028
pending = [(prefix, top_id)]
2031
relpath, file_id = pending.pop()
2032
# 0 - relpath, 1- file-id
2034
relroot = relpath + '/'
2037
# FIXME: stash the node in pending
2038
entry = inv[file_id]
2039
for name, child in entry.sorted_children():
2040
toppath = relroot + name
2041
dirblock.append((toppath, name, child.kind, None,
2042
child.file_id, child.kind
2044
yield (relpath, entry.file_id), dirblock
2045
# push the user specified dirs from dirblock
2046
for dir in reversed(dirblock):
2047
if dir[2] == _directory:
2048
pending.append((dir[0], dir[4]))
2051
class InterDirStateTree(InterTree):
2052
"""Fast path optimiser for changes_from with dirstate trees.
2054
This is used only when both trees are in the dirstate working file, and
2055
the source is any parent within the dirstate, and the destination is
2056
the current working tree of the same dirstate.
2058
# this could be generalized to allow comparisons between any trees in the
2059
# dirstate, and possibly between trees stored in different dirstates.
2061
def __init__(self, source, target):
2062
super(InterDirStateTree, self).__init__(source, target)
2063
if not InterDirStateTree.is_compatible(source, target):
2064
raise Exception, "invalid source %r and target %r" % (source, target)
2067
def make_source_parent_tree(source, target):
2068
"""Change the source tree into a parent of the target."""
2069
revid = source.commit('record tree')
2070
target.branch.fetch(source.branch, revid)
2071
target.set_parent_ids([revid])
2072
return target.basis_tree(), target
2075
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
2076
result = klass.make_source_parent_tree(source, target)
2077
result[1]._iter_changes = dirstate.ProcessEntryPython
2081
def make_source_parent_tree_compiled_dirstate(klass, test_case, source,
2083
from bzrlib.tests.test__dirstate_helpers import \
2084
compiled_dirstate_helpers_feature
2085
test_case.requireFeature(compiled_dirstate_helpers_feature)
2086
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
2087
result = klass.make_source_parent_tree(source, target)
2088
result[1]._iter_changes = ProcessEntryC
2091
_matching_from_tree_format = WorkingTreeFormat4()
2092
_matching_to_tree_format = WorkingTreeFormat4()
2095
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
2096
# This method shouldn't be called, because we have python and C
2097
# specific flavours.
2098
raise NotImplementedError
2100
def iter_changes(self, include_unchanged=False,
2101
specific_files=None, pb=None, extra_trees=[],
2102
require_versioned=True, want_unversioned=False):
2103
"""Return the changes from source to target.
2105
:return: An iterator that yields tuples. See InterTree.iter_changes
2107
:param specific_files: An optional list of file paths to restrict the
2108
comparison to. When mapping filenames to ids, all matches in all
2109
trees (including optional extra_trees) are used, and all children of
2110
matched directories are included.
2111
:param include_unchanged: An optional boolean requesting the inclusion of
2112
unchanged entries in the result.
2113
:param extra_trees: An optional list of additional trees to use when
2114
mapping the contents of specific_files (paths) to file_ids.
2115
:param require_versioned: If True, all files in specific_files must be
2116
versioned in one of source, target, extra_trees or
2117
PathsNotVersionedError is raised.
2118
:param want_unversioned: Should unversioned files be returned in the
2119
output. An unversioned file is defined as one with (False, False)
2120
for the versioned pair.
2122
# TODO: handle extra trees in the dirstate.
2123
if (extra_trees or specific_files == []):
2124
# we can't fast-path these cases (yet)
2125
return super(InterDirStateTree, self).iter_changes(
2126
include_unchanged, specific_files, pb, extra_trees,
2127
require_versioned, want_unversioned=want_unversioned)
2128
parent_ids = self.target.get_parent_ids()
2129
if not (self.source._revision_id in parent_ids
2130
or self.source._revision_id == _mod_revision.NULL_REVISION):
2131
raise AssertionError(
2132
"revision {%s} is not stored in {%s}, but %s "
2133
"can only be used for trees stored in the dirstate"
2134
% (self.source._revision_id, self.target, self.iter_changes))
2136
if self.source._revision_id == _mod_revision.NULL_REVISION:
2138
indices = (target_index,)
2140
if not (self.source._revision_id in parent_ids):
2141
raise AssertionError(
2142
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2143
self.source._revision_id, parent_ids))
2144
source_index = 1 + parent_ids.index(self.source._revision_id)
2145
indices = (source_index, target_index)
2146
# -- make all specific_files utf8 --
2148
specific_files_utf8 = set()
2149
for path in specific_files:
2150
# Note, if there are many specific files, using cache_utf8
2151
# would be good here.
2152
specific_files_utf8.add(path.encode('utf8'))
2153
specific_files = specific_files_utf8
2155
specific_files = set([''])
2156
# -- specific_files is now a utf8 path set --
2158
# -- get the state object and prepare it.
2159
state = self.target.current_dirstate()
2160
state._read_dirblocks_if_needed()
2161
if require_versioned:
2162
# -- check all supplied paths are versioned in a search tree. --
2164
for path in specific_files:
2165
path_entries = state._entries_for_path(path)
2166
if not path_entries:
2167
# this specified path is not present at all: error
2168
not_versioned.append(path)
2170
found_versioned = False
2171
# for each id at this path
2172
for entry in path_entries:
2174
for index in indices:
2175
if entry[1][index][0] != 'a': # absent
2176
found_versioned = True
2177
# all good: found a versioned cell
2179
if not found_versioned:
2180
# none of the indexes was not 'absent' at all ids for this
2182
not_versioned.append(path)
2183
if len(not_versioned) > 0:
2184
raise errors.PathsNotVersionedError(not_versioned)
2185
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2186
search_specific_files = osutils.minimum_path_selection(specific_files)
2188
use_filesystem_for_exec = (sys.platform != 'win32')
2189
iter_changes = self.target._iter_changes(include_unchanged,
2190
use_filesystem_for_exec, search_specific_files, state,
2191
source_index, target_index, want_unversioned, self.target)
2192
return iter_changes.iter_changes()
2195
def is_compatible(source, target):
2196
# the target must be a dirstate working tree
2197
if not isinstance(target, DirStateWorkingTree):
2199
# the source must be a revtree or dirstate rev tree.
2200
if not isinstance(source,
2201
(revisiontree.RevisionTree, DirStateRevisionTree)):
2203
# the source revid must be in the target dirstate
2204
if not (source._revision_id == _mod_revision.NULL_REVISION or
2205
source._revision_id in target.get_parent_ids()):
2206
# TODO: what about ghosts? it may well need to
2207
# check for them explicitly.
2211
InterTree.register_optimiser(InterDirStateTree)
2214
class Converter3to4(object):
2215
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2218
self.target_format = WorkingTreeFormat4()
2220
def convert(self, tree):
2221
# lock the control files not the tree, so that we dont get tree
2222
# on-unlock behaviours, and so that noone else diddles with the
2223
# tree during upgrade.
2224
tree._control_files.lock_write()
2226
tree.read_working_inventory()
2227
self.create_dirstate_data(tree)
2228
self.update_format(tree)
2229
self.remove_xml_files(tree)
2231
tree._control_files.unlock()
2233
def create_dirstate_data(self, tree):
2234
"""Create the dirstate based data for tree."""
2235
local_path = tree.bzrdir.get_workingtree_transport(None
2236
).local_abspath('dirstate')
2237
state = dirstate.DirState.from_tree(tree, local_path)
2241
def remove_xml_files(self, tree):
2242
"""Remove the oldformat 3 data."""
2243
transport = tree.bzrdir.get_workingtree_transport(None)
2244
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2245
'pending-merges', 'stat-cache']:
2247
transport.delete(path)
2248
except errors.NoSuchFile:
2249
# some files are optional - just deal.
2252
def update_format(self, tree):
2253
"""Change the format marker."""
2254
tree._transport.put_bytes('format',
2255
self.target_format.get_format_string(),
2256
mode=tree.bzrdir._get_file_mode())
2259
class Converter4to5(object):
2260
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2263
self.target_format = WorkingTreeFormat5()
2265
def convert(self, tree):
2266
# lock the control files not the tree, so that we don't get tree
2267
# on-unlock behaviours, and so that no-one else diddles with the
2268
# tree during upgrade.
2269
tree._control_files.lock_write()
2271
self.update_format(tree)
2273
tree._control_files.unlock()
2275
def update_format(self, tree):
2276
"""Change the format marker."""
2277
tree._transport.put_bytes('format',
2278
self.target_format.get_format_string(),
2279
mode=tree.bzrdir._get_file_mode())
2282
class Converter4or5to6(object):
2283
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2286
self.target_format = WorkingTreeFormat6()
2288
def convert(self, tree):
2289
# lock the control files not the tree, so that we don't get tree
2290
# on-unlock behaviours, and so that no-one else diddles with the
2291
# tree during upgrade.
2292
tree._control_files.lock_write()
2294
self.init_custom_control_files(tree)
2295
self.update_format(tree)
2297
tree._control_files.unlock()
2299
def init_custom_control_files(self, tree):
2300
"""Initialize custom control files."""
2301
tree._transport.put_bytes('views', '',
2302
mode=tree.bzrdir._get_file_mode())
2304
def update_format(self, tree):
2305
"""Change the format marker."""
2306
tree._transport.put_bytes('format',
2307
self.target_format.get_format_string(),
2308
mode=tree.bzrdir._get_file_mode())