1
# Copyright (C) 2005, 2006, 2007, 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(), """
31
from bisect import bisect_left
33
from copy import deepcopy
45
conflicts as _mod_conflicts,
55
revision as _mod_revision,
65
from bzrlib.transport import get_transport
69
from bzrlib import symbol_versioning
70
from bzrlib.decorators import needs_read_lock, needs_write_lock
71
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
72
import bzrlib.mutabletree
73
from bzrlib.mutabletree import needs_tree_write_lock
74
from bzrlib.osutils import (
84
from bzrlib.trace import mutter, note
85
from bzrlib.transport.local import LocalTransport
86
from bzrlib.tree import InterTree
87
from bzrlib.progress import DummyProgress, ProgressPhase
88
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
89
from bzrlib.rio import RioReader, rio_file, Stanza
90
from bzrlib.symbol_versioning import (deprecated_passed,
95
from bzrlib.tree import Tree
96
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
99
class DirStateWorkingTree(WorkingTree3):
100
def __init__(self, basedir,
105
"""Construct a WorkingTree for basedir.
107
If the branch is not supplied, it is opened automatically.
108
If the branch is supplied, it must be the branch for this basedir.
109
(branch.base is not cross checked, because for remote branches that
110
would be meaningless).
112
self._format = _format
113
self.bzrdir = _bzrdir
114
basedir = safe_unicode(basedir)
115
mutter("opening working tree %r", basedir)
116
self._branch = branch
117
self.basedir = realpath(basedir)
118
# if branch is at our basedir and is a format 6 or less
119
# assume all other formats have their own control files.
120
self._control_files = _control_files
121
self._transport = self._control_files._transport
124
# during a read or write lock these objects are set, and are
125
# None the rest of the time.
126
self._dirstate = None
127
self._inventory = None
129
self._setup_directory_is_tree_reference()
130
self._detect_case_handling()
131
self._rules_searcher = None
132
#--- allow tests to select the dirstate iter_changes implementation
133
self._iter_changes = dirstate._process_entry
135
@needs_tree_write_lock
136
def _add(self, files, ids, kinds):
137
"""See MutableTree._add."""
138
state = self.current_dirstate()
139
for f, file_id, kind in zip(files, ids, kinds):
142
# special case tree root handling.
143
if f == '' and self.path2id(f) == ROOT_ID:
144
state.set_path_id('', generate_ids.gen_file_id(f))
147
file_id = generate_ids.gen_file_id(f)
148
# deliberately add the file with no cached stat or sha1
149
# - on the first access it will be gathered, and we can
150
# always change this once tests are all passing.
151
state.add(f, file_id, kind, None, '')
152
self._make_dirty(reset_inventory=True)
154
def _make_dirty(self, reset_inventory):
155
"""Make the tree state dirty.
157
:param reset_inventory: True if the cached inventory should be removed
158
(presuming there is one).
161
if reset_inventory and self._inventory is not None:
162
self._inventory = None
164
@needs_tree_write_lock
165
def add_reference(self, sub_tree):
166
# use standard implementation, which calls back to self._add
168
# So we don't store the reference_revision in the working dirstate,
169
# it's just recorded at the moment of commit.
170
self._add_reference(sub_tree)
172
def break_lock(self):
173
"""Break a lock if one is present from another instance.
175
Uses the ui factory to ask for confirmation if the lock may be from
178
This will probe the repository for its lock as well.
180
# if the dirstate is locked by an active process, reject the break lock
183
if self._dirstate is None:
187
state = self._current_dirstate()
188
if state._lock_token is not None:
189
# we already have it locked. sheese, cant break our own lock.
190
raise errors.LockActive(self.basedir)
193
# try for a write lock - need permission to get one anyhow
196
except errors.LockContention:
197
# oslocks fail when a process is still live: fail.
198
# TODO: get the locked lockdir info and give to the user to
199
# assist in debugging.
200
raise errors.LockActive(self.basedir)
205
self._dirstate = None
206
self._control_files.break_lock()
207
self.branch.break_lock()
209
def _comparison_data(self, entry, path):
210
kind, executable, stat_value = \
211
WorkingTree3._comparison_data(self, entry, path)
212
# it looks like a plain directory, but it's really a reference -- see
214
if (self._repo_supports_tree_reference and
215
kind == 'directory' and
216
self._directory_is_tree_reference(path)):
217
kind = 'tree-reference'
218
return kind, executable, stat_value
221
def commit(self, message=None, revprops=None, *args, **kwargs):
222
# mark the tree as dirty post commit - commit
223
# can change the current versioned list by doing deletes.
224
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
225
self._make_dirty(reset_inventory=True)
228
def current_dirstate(self):
229
"""Return the current dirstate object.
231
This is not part of the tree interface and only exposed for ease of
234
:raises errors.NotWriteLocked: when not in a lock.
236
self._must_be_locked()
237
return self._current_dirstate()
239
def _current_dirstate(self):
240
"""Internal function that does not check lock status.
242
This is needed for break_lock which also needs the dirstate.
244
if self._dirstate is not None:
245
return self._dirstate
246
local_path = self.bzrdir.get_workingtree_transport(None
247
).local_abspath('dirstate')
248
self._dirstate = dirstate.DirState.on_file(local_path)
249
return self._dirstate
251
def filter_unversioned_files(self, paths):
252
"""Filter out paths that are versioned.
254
:return: set of paths.
256
# TODO: make a generic multi-bisect routine roughly that should list
257
# the paths, then process one half at a time recursively, and feed the
258
# results of each bisect in further still
259
paths = sorted(paths)
261
state = self.current_dirstate()
262
# TODO we want a paths_to_dirblocks helper I think
264
dirname, basename = os.path.split(path.encode('utf8'))
265
_, _, _, path_is_versioned = state._get_block_entry_index(
266
dirname, basename, 0)
267
if not path_is_versioned:
272
"""Write all cached data to disk."""
273
if self._control_files._lock_mode != 'w':
274
raise errors.NotWriteLocked(self)
275
self.current_dirstate().save()
276
self._inventory = None
279
@needs_tree_write_lock
280
def _gather_kinds(self, files, kinds):
281
"""See MutableTree._gather_kinds."""
282
for pos, f in enumerate(files):
283
if kinds[pos] is None:
284
kinds[pos] = self._kind(f)
286
def _generate_inventory(self):
287
"""Create and set self.inventory from the dirstate object.
289
This is relatively expensive: we have to walk the entire dirstate.
290
Ideally we would not, and can deprecate this function.
292
#: uncomment to trap on inventory requests.
293
# import pdb;pdb.set_trace()
294
state = self.current_dirstate()
295
state._read_dirblocks_if_needed()
296
root_key, current_entry = self._get_entry(path='')
297
current_id = root_key[2]
298
if not (current_entry[0][0] == 'd'): # directory
299
raise AssertionError(current_entry)
300
inv = Inventory(root_id=current_id)
301
# Turn some things into local variables
302
minikind_to_kind = dirstate.DirState._minikind_to_kind
303
factory = entry_factory
304
utf8_decode = cache_utf8._utf8_decode
306
# we could do this straight out of the dirstate; it might be fast
307
# and should be profiled - RBC 20070216
308
parent_ies = {'' : inv.root}
309
for block in state._dirblocks[1:]: # skip the root
312
parent_ie = parent_ies[dirname]
314
# all the paths in this block are not versioned in this tree
316
for key, entry in block[1]:
317
minikind, link_or_sha1, size, executable, stat = entry[0]
318
if minikind in ('a', 'r'): # absent, relocated
319
# a parent tree only entry
322
name_unicode = utf8_decode(name)[0]
324
kind = minikind_to_kind[minikind]
325
inv_entry = factory[kind](file_id, name_unicode,
328
# This is only needed on win32, where this is the only way
329
# we know the executable bit.
330
inv_entry.executable = executable
331
# not strictly needed: working tree
332
#inv_entry.text_size = size
333
#inv_entry.text_sha1 = sha1
334
elif kind == 'directory':
335
# add this entry to the parent map.
336
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
337
elif kind == 'tree-reference':
338
if not self._repo_supports_tree_reference:
339
raise AssertionError(
341
"doesn't support tree references "
342
"required by entry %r"
344
inv_entry.reference_revision = link_or_sha1 or None
345
elif kind != 'symlink':
346
raise AssertionError("unknown kind %r" % kind)
347
# These checks cost us around 40ms on a 55k entry tree
348
if file_id in inv_byid:
349
raise AssertionError('file_id %s already in'
350
' inventory as %s' % (file_id, inv_byid[file_id]))
351
if name_unicode in parent_ie.children:
352
raise AssertionError('name %r already in parent'
354
inv_byid[file_id] = inv_entry
355
parent_ie.children[name_unicode] = inv_entry
356
self._inventory = inv
358
def _get_entry(self, file_id=None, path=None):
359
"""Get the dirstate row for file_id or path.
361
If either file_id or path is supplied, it is used as the key to lookup.
362
If both are supplied, the fastest lookup is used, and an error is
363
raised if they do not both point at the same row.
365
:param file_id: An optional unicode file_id to be looked up.
366
:param path: An optional unicode path to be looked up.
367
:return: The dirstate row tuple for path/file_id, or (None, None)
369
if file_id is None and path is None:
370
raise errors.BzrError('must supply file_id or path')
371
state = self.current_dirstate()
373
path = path.encode('utf8')
374
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
376
def get_file_sha1(self, file_id, path=None, stat_value=None):
377
# check file id is valid unconditionally.
378
entry = self._get_entry(file_id=file_id, path=path)
380
raise errors.NoSuchId(self, file_id)
382
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
384
file_abspath = self.abspath(path)
385
state = self.current_dirstate()
386
if stat_value is None:
388
stat_value = os.lstat(file_abspath)
390
if e.errno == errno.ENOENT:
394
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
395
stat_value=stat_value)
396
if entry[1][0][0] == 'f':
397
if link_or_sha1 is None:
398
file_obj, statvalue = self.get_file_with_stat(file_id, path)
400
sha1 = osutils.sha_file(file_obj)
403
self._observed_sha1(file_id, path, (sha1, statvalue))
409
def _get_inventory(self):
410
"""Get the inventory for the tree. This is only valid within a lock."""
411
if 'evil' in debug.debug_flags:
412
trace.mutter_callsite(2,
413
"accessing .inventory forces a size of tree translation.")
414
if self._inventory is not None:
415
return self._inventory
416
self._must_be_locked()
417
self._generate_inventory()
418
return self._inventory
420
inventory = property(_get_inventory,
421
doc="Inventory of this Tree")
424
def get_parent_ids(self):
425
"""See Tree.get_parent_ids.
427
This implementation requests the ids list from the dirstate file.
429
return self.current_dirstate().get_parent_ids()
431
def get_reference_revision(self, file_id, path=None):
432
# referenced tree's revision is whatever's currently there
433
return self.get_nested_tree(file_id, path).last_revision()
435
def get_nested_tree(self, file_id, path=None):
437
path = self.id2path(file_id)
438
# else: check file_id is at path?
439
return WorkingTree.open(self.abspath(path))
442
def get_root_id(self):
443
"""Return the id of this trees root"""
444
return self._get_entry(path='')[0][2]
446
def has_id(self, file_id):
447
state = self.current_dirstate()
448
row, parents = self._get_entry(file_id=file_id)
451
return osutils.lexists(pathjoin(
452
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
455
def id2path(self, file_id):
456
"Convert a file-id to a path."
457
state = self.current_dirstate()
458
entry = self._get_entry(file_id=file_id)
459
if entry == (None, None):
460
raise errors.NoSuchId(tree=self, file_id=file_id)
461
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
462
return path_utf8.decode('utf8')
464
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
465
entry = self._get_entry(path=path)
466
if entry == (None, None):
467
return False # Missing entries are not executable
468
return entry[1][0][3] # Executable?
470
if not osutils.supports_executable():
471
def is_executable(self, file_id, path=None):
472
"""Test if a file is executable or not.
474
Note: The caller is expected to take a read-lock before calling this.
476
entry = self._get_entry(file_id=file_id, path=path)
477
if entry == (None, None):
479
return entry[1][0][3]
481
_is_executable_from_path_and_stat = \
482
_is_executable_from_path_and_stat_from_basis
484
def is_executable(self, file_id, path=None):
485
"""Test if a file is executable or not.
487
Note: The caller is expected to take a read-lock before calling this.
489
self._must_be_locked()
491
path = self.id2path(file_id)
492
mode = os.lstat(self.abspath(path)).st_mode
493
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
495
def all_file_ids(self):
496
"""See Tree.iter_all_file_ids"""
497
self._must_be_locked()
499
for key, tree_details in self.current_dirstate()._iter_entries():
500
if tree_details[0][0] in ('a', 'r'): # relocated
507
"""Iterate through file_ids for this tree.
509
file_ids are in a WorkingTree if they are in the working inventory
510
and the working file exists.
513
for key, tree_details in self.current_dirstate()._iter_entries():
514
if tree_details[0][0] in ('a', 'r'): # absent, relocated
515
# not relevant to the working tree
517
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
518
if osutils.lexists(path):
519
result.append(key[2])
522
def iter_references(self):
523
if not self._repo_supports_tree_reference:
524
# When the repo doesn't support references, we will have nothing to
527
for key, tree_details in self.current_dirstate()._iter_entries():
528
if tree_details[0][0] in ('a', 'r'): # absent, relocated
529
# not relevant to the working tree
532
# the root is not a reference.
534
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
536
if self._kind(relpath) == 'tree-reference':
537
yield relpath, key[2]
538
except errors.NoSuchFile:
539
# path is missing on disk.
542
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
543
"""See MutableTree._observed_sha1."""
544
state = self.current_dirstate()
545
entry = self._get_entry(file_id=file_id, path=path)
546
state._observed_sha1(entry, sha1, statvalue)
548
def kind(self, file_id):
549
"""Return the kind of a file.
551
This is always the actual kind that's on disk, regardless of what it
554
Note: The caller is expected to take a read-lock before calling this.
556
relpath = self.id2path(file_id)
558
raise AssertionError(
559
"path for id {%s} is None!" % file_id)
560
return self._kind(relpath)
562
def _kind(self, relpath):
563
abspath = self.abspath(relpath)
564
kind = file_kind(abspath)
565
if (self._repo_supports_tree_reference and
566
kind == 'directory' and
567
self._directory_is_tree_reference(relpath)):
568
kind = 'tree-reference'
572
def _last_revision(self):
573
"""See Mutable.last_revision."""
574
parent_ids = self.current_dirstate().get_parent_ids()
578
return _mod_revision.NULL_REVISION
581
"""See Branch.lock_read, and WorkingTree.unlock."""
582
self.branch.lock_read()
584
self._control_files.lock_read()
586
state = self.current_dirstate()
587
if not state._lock_token:
589
# set our support for tree references from the repository in
591
self._repo_supports_tree_reference = getattr(
592
self.branch.repository._format, "supports_tree_reference",
595
self._control_files.unlock()
601
def _lock_self_write(self):
602
"""This should be called after the branch is locked."""
604
self._control_files.lock_write()
606
state = self.current_dirstate()
607
if not state._lock_token:
609
# set our support for tree references from the repository in
611
self._repo_supports_tree_reference = getattr(
612
self.branch.repository._format, "supports_tree_reference",
615
self._control_files.unlock()
621
def lock_tree_write(self):
622
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
623
self.branch.lock_read()
624
self._lock_self_write()
626
def lock_write(self):
627
"""See MutableTree.lock_write, and WorkingTree.unlock."""
628
self.branch.lock_write()
629
self._lock_self_write()
631
@needs_tree_write_lock
632
def move(self, from_paths, to_dir, after=False):
633
"""See WorkingTree.move()."""
637
state = self.current_dirstate()
638
if isinstance(from_paths, basestring):
640
to_dir_utf8 = to_dir.encode('utf8')
641
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
642
id_index = state._get_id_index()
643
# check destination directory
644
# get the details for it
645
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
646
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
647
if not entry_present:
648
raise errors.BzrMoveFailedError('', to_dir,
649
errors.NotVersionedError(to_dir))
650
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
651
# get a handle on the block itself.
652
to_block_index = state._ensure_block(
653
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
654
to_block = state._dirblocks[to_block_index]
655
to_abs = self.abspath(to_dir)
656
if not isdir(to_abs):
657
raise errors.BzrMoveFailedError('',to_dir,
658
errors.NotADirectory(to_abs))
660
if to_entry[1][0][0] != 'd':
661
raise errors.BzrMoveFailedError('',to_dir,
662
errors.NotADirectory(to_abs))
664
if self._inventory is not None:
665
update_inventory = True
667
to_dir_id = to_entry[0][2]
668
to_dir_ie = inv[to_dir_id]
670
update_inventory = False
673
def move_one(old_entry, from_path_utf8, minikind, executable,
674
fingerprint, packed_stat, size,
675
to_block, to_key, to_path_utf8):
676
state._make_absent(old_entry)
677
from_key = old_entry[0]
679
lambda:state.update_minimal(from_key,
681
executable=executable,
682
fingerprint=fingerprint,
683
packed_stat=packed_stat,
685
path_utf8=from_path_utf8))
686
state.update_minimal(to_key,
688
executable=executable,
689
fingerprint=fingerprint,
690
packed_stat=packed_stat,
692
path_utf8=to_path_utf8)
693
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
694
new_entry = to_block[1][added_entry_index]
695
rollbacks.append(lambda:state._make_absent(new_entry))
697
for from_rel in from_paths:
698
# from_rel is 'pathinroot/foo/bar'
699
from_rel_utf8 = from_rel.encode('utf8')
700
from_dirname, from_tail = osutils.split(from_rel)
701
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
702
from_entry = self._get_entry(path=from_rel)
703
if from_entry == (None, None):
704
raise errors.BzrMoveFailedError(from_rel,to_dir,
705
errors.NotVersionedError(path=str(from_rel)))
707
from_id = from_entry[0][2]
708
to_rel = pathjoin(to_dir, from_tail)
709
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
710
item_to_entry = self._get_entry(path=to_rel)
711
if item_to_entry != (None, None):
712
raise errors.BzrMoveFailedError(from_rel, to_rel,
713
"Target is already versioned.")
715
if from_rel == to_rel:
716
raise errors.BzrMoveFailedError(from_rel, to_rel,
717
"Source and target are identical.")
719
from_missing = not self.has_filename(from_rel)
720
to_missing = not self.has_filename(to_rel)
727
raise errors.BzrMoveFailedError(from_rel, to_rel,
728
errors.NoSuchFile(path=to_rel,
729
extra="New file has not been created yet"))
731
# neither path exists
732
raise errors.BzrRenameFailedError(from_rel, to_rel,
733
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
735
if from_missing: # implicitly just update our path mapping
738
raise errors.RenameFailedFilesExist(from_rel, to_rel)
741
def rollback_rename():
742
"""A single rename has failed, roll it back."""
743
# roll back everything, even if we encounter trouble doing one
746
# TODO: at least log the other exceptions rather than just
747
# losing them mbp 20070307
749
for rollback in reversed(rollbacks):
753
exc_info = sys.exc_info()
755
raise exc_info[0], exc_info[1], exc_info[2]
757
# perform the disk move first - its the most likely failure point.
759
from_rel_abs = self.abspath(from_rel)
760
to_rel_abs = self.abspath(to_rel)
762
osutils.rename(from_rel_abs, to_rel_abs)
764
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
765
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
767
# perform the rename in the inventory next if needed: its easy
771
from_entry = inv[from_id]
772
current_parent = from_entry.parent_id
773
inv.rename(from_id, to_dir_id, from_tail)
775
lambda: inv.rename(from_id, current_parent, from_tail))
776
# finally do the rename in the dirstate, which is a little
777
# tricky to rollback, but least likely to need it.
778
old_block_index, old_entry_index, dir_present, file_present = \
779
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
780
old_block = state._dirblocks[old_block_index][1]
781
old_entry = old_block[old_entry_index]
782
from_key, old_entry_details = old_entry
783
cur_details = old_entry_details[0]
785
to_key = ((to_block[0],) + from_key[1:3])
786
minikind = cur_details[0]
787
move_one(old_entry, from_path_utf8=from_rel_utf8,
789
executable=cur_details[3],
790
fingerprint=cur_details[1],
791
packed_stat=cur_details[4],
795
to_path_utf8=to_rel_utf8)
798
def update_dirblock(from_dir, to_key, to_dir_utf8):
799
"""Recursively update all entries in this dirblock."""
801
raise AssertionError("renaming root not supported")
802
from_key = (from_dir, '')
803
from_block_idx, present = \
804
state._find_block_index_from_key(from_key)
806
# This is the old record, if it isn't present, then
807
# there is theoretically nothing to update.
808
# (Unless it isn't present because of lazy loading,
809
# but we don't do that yet)
811
from_block = state._dirblocks[from_block_idx]
812
to_block_index, to_entry_index, _, _ = \
813
state._get_block_entry_index(to_key[0], to_key[1], 0)
814
to_block_index = state._ensure_block(
815
to_block_index, to_entry_index, to_dir_utf8)
816
to_block = state._dirblocks[to_block_index]
818
# Grab a copy since move_one may update the list.
819
for entry in from_block[1][:]:
820
if not (entry[0][0] == from_dir):
821
raise AssertionError()
822
cur_details = entry[1][0]
823
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
824
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
825
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
826
minikind = cur_details[0]
828
# Deleted children of a renamed directory
829
# Do not need to be updated.
830
# Children that have been renamed out of this
831
# directory should also not be updated
833
move_one(entry, from_path_utf8=from_path_utf8,
835
executable=cur_details[3],
836
fingerprint=cur_details[1],
837
packed_stat=cur_details[4],
841
to_path_utf8=to_path_utf8)
843
# We need to move all the children of this
845
update_dirblock(from_path_utf8, to_key,
847
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
851
result.append((from_rel, to_rel))
852
state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
853
self._make_dirty(reset_inventory=False)
857
def _must_be_locked(self):
858
if not self._control_files._lock_count:
859
raise errors.ObjectNotLocked(self)
862
"""Initialize the state in this tree to be a new tree."""
866
def path2id(self, path):
867
"""Return the id for path in this tree."""
868
path = path.strip('/')
869
entry = self._get_entry(path=path)
870
if entry == (None, None):
874
def paths2ids(self, paths, trees=[], require_versioned=True):
875
"""See Tree.paths2ids().
877
This specialisation fast-paths the case where all the trees are in the
882
parents = self.get_parent_ids()
884
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
886
return super(DirStateWorkingTree, self).paths2ids(paths,
887
trees, require_versioned)
888
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
889
# -- make all paths utf8 --
892
paths_utf8.add(path.encode('utf8'))
894
# -- paths is now a utf8 path set --
895
# -- get the state object and prepare it.
896
state = self.current_dirstate()
897
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
898
and '' not in paths):
899
paths2ids = self._paths2ids_using_bisect
901
paths2ids = self._paths2ids_in_memory
902
return paths2ids(paths, search_indexes,
903
require_versioned=require_versioned)
905
def _paths2ids_in_memory(self, paths, search_indexes,
906
require_versioned=True):
907
state = self.current_dirstate()
908
state._read_dirblocks_if_needed()
909
def _entries_for_path(path):
910
"""Return a list with all the entries that match path for all ids.
912
dirname, basename = os.path.split(path)
913
key = (dirname, basename, '')
914
block_index, present = state._find_block_index_from_key(key)
916
# the block which should contain path is absent.
919
block = state._dirblocks[block_index][1]
920
entry_index, _ = state._find_entry_index(key, block)
921
# we may need to look at multiple entries at this path: walk while the paths match.
922
while (entry_index < len(block) and
923
block[entry_index][0][0:2] == key[0:2]):
924
result.append(block[entry_index])
927
if require_versioned:
928
# -- check all supplied paths are versioned in a search tree. --
931
path_entries = _entries_for_path(path)
933
# this specified path is not present at all: error
934
all_versioned = False
936
found_versioned = False
937
# for each id at this path
938
for entry in path_entries:
940
for index in search_indexes:
941
if entry[1][index][0] != 'a': # absent
942
found_versioned = True
943
# all good: found a versioned cell
945
if not found_versioned:
946
# none of the indexes was not 'absent' at all ids for this
948
all_versioned = False
950
if not all_versioned:
951
raise errors.PathsNotVersionedError(paths)
952
# -- remove redundancy in supplied paths to prevent over-scanning --
953
search_paths = osutils.minimum_path_selection(paths)
955
# for all search_indexs in each path at or under each element of
956
# search_paths, if the detail is relocated: add the id, and add the
957
# relocated path as one to search if its not searched already. If the
958
# detail is not relocated, add the id.
959
searched_paths = set()
961
def _process_entry(entry):
962
"""Look at search_indexes within entry.
964
If a specific tree's details are relocated, add the relocation
965
target to search_paths if not searched already. If it is absent, do
966
nothing. Otherwise add the id to found_ids.
968
for index in search_indexes:
969
if entry[1][index][0] == 'r': # relocated
970
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
971
search_paths.add(entry[1][index][1])
972
elif entry[1][index][0] != 'a': # absent
973
found_ids.add(entry[0][2])
975
current_root = search_paths.pop()
976
searched_paths.add(current_root)
977
# process the entries for this containing directory: the rest will be
978
# found by their parents recursively.
979
root_entries = _entries_for_path(current_root)
981
# this specified path is not present at all, skip it.
983
for entry in root_entries:
984
_process_entry(entry)
985
initial_key = (current_root, '', '')
986
block_index, _ = state._find_block_index_from_key(initial_key)
987
while (block_index < len(state._dirblocks) and
988
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
989
for entry in state._dirblocks[block_index][1]:
990
_process_entry(entry)
994
def _paths2ids_using_bisect(self, paths, search_indexes,
995
require_versioned=True):
996
state = self.current_dirstate()
999
split_paths = sorted(osutils.split(p) for p in paths)
1000
found = state._bisect_recursive(split_paths)
1002
if require_versioned:
1003
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1004
for dir_name in split_paths:
1005
if dir_name not in found_dir_names:
1006
raise errors.PathsNotVersionedError(paths)
1008
for dir_name_id, trees_info in found.iteritems():
1009
for index in search_indexes:
1010
if trees_info[index][0] not in ('r', 'a'):
1011
found_ids.add(dir_name_id[2])
1014
def read_working_inventory(self):
1015
"""Read the working inventory.
1017
This is a meaningless operation for dirstate, but we obey it anyhow.
1019
return self.inventory
1022
def revision_tree(self, revision_id):
1023
"""See Tree.revision_tree.
1025
WorkingTree4 supplies revision_trees for any basis tree.
1027
dirstate = self.current_dirstate()
1028
parent_ids = dirstate.get_parent_ids()
1029
if revision_id not in parent_ids:
1030
raise errors.NoSuchRevisionInTree(self, revision_id)
1031
if revision_id in dirstate.get_ghosts():
1032
raise errors.NoSuchRevisionInTree(self, revision_id)
1033
return DirStateRevisionTree(dirstate, revision_id,
1034
self.branch.repository)
1036
@needs_tree_write_lock
1037
def set_last_revision(self, new_revision):
1038
"""Change the last revision in the working tree."""
1039
parents = self.get_parent_ids()
1040
if new_revision in (NULL_REVISION, None):
1041
if len(parents) >= 2:
1042
raise AssertionError(
1043
"setting the last parent to none with a pending merge is "
1045
self.set_parent_ids([])
1047
self.set_parent_ids([new_revision] + parents[1:],
1048
allow_leftmost_as_ghost=True)
1050
@needs_tree_write_lock
1051
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1052
"""Set the parent ids to revision_ids.
1054
See also set_parent_trees. This api will try to retrieve the tree data
1055
for each element of revision_ids from the trees repository. If you have
1056
tree data already available, it is more efficient to use
1057
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1058
an easier API to use.
1060
:param revision_ids: The revision_ids to set as the parent ids of this
1061
working tree. Any of these may be ghosts.
1064
for revision_id in revision_ids:
1066
revtree = self.branch.repository.revision_tree(revision_id)
1067
# TODO: jam 20070213 KnitVersionedFile raises
1068
# RevisionNotPresent rather than NoSuchRevision if a
1069
# given revision_id is not present. Should Repository be
1070
# catching it and re-raising NoSuchRevision?
1071
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1073
trees.append((revision_id, revtree))
1074
self.set_parent_trees(trees,
1075
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1077
@needs_tree_write_lock
1078
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1079
"""Set the parents of the working tree.
1081
:param parents_list: A list of (revision_id, tree) tuples.
1082
If tree is None, then that element is treated as an unreachable
1083
parent tree - i.e. a ghost.
1085
dirstate = self.current_dirstate()
1086
if len(parents_list) > 0:
1087
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1088
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1092
parent_ids = [rev_id for rev_id, tree in parents_list]
1093
graph = self.branch.repository.get_graph()
1094
heads = graph.heads(parent_ids)
1095
accepted_revisions = set()
1097
# convert absent trees to the null tree, which we convert back to
1098
# missing on access.
1099
for rev_id, tree in parents_list:
1100
if len(accepted_revisions) > 0:
1101
# we always accept the first tree
1102
if rev_id in accepted_revisions or rev_id not in heads:
1103
# We have already included either this tree, or its
1104
# descendent, so we skip it.
1106
_mod_revision.check_not_reserved_id(rev_id)
1107
if tree is not None:
1108
real_trees.append((rev_id, tree))
1110
real_trees.append((rev_id,
1111
self.branch.repository.revision_tree(
1112
_mod_revision.NULL_REVISION)))
1113
ghosts.append(rev_id)
1114
accepted_revisions.add(rev_id)
1115
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1116
self._make_dirty(reset_inventory=False)
1118
def _set_root_id(self, file_id):
1119
"""See WorkingTree.set_root_id."""
1120
state = self.current_dirstate()
1121
state.set_path_id('', file_id)
1122
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1123
self._make_dirty(reset_inventory=True)
1125
def _sha_from_stat(self, path, stat_result):
1126
"""Get a sha digest from the tree's stat cache.
1128
The default implementation assumes no stat cache is present.
1130
:param path: The path.
1131
:param stat_result: The stat result being looked up.
1133
return self.current_dirstate().sha1_from_stat(path, stat_result)
1136
def supports_tree_reference(self):
1137
return self._repo_supports_tree_reference
1140
"""Unlock in format 4 trees needs to write the entire dirstate."""
1141
# do non-implementation specific cleanup
1144
if self._control_files._lock_count == 1:
1145
# eventually we should do signature checking during read locks for
1147
if self._control_files._lock_mode == 'w':
1150
if self._dirstate is not None:
1151
# This is a no-op if there are no modifications.
1152
self._dirstate.save()
1153
self._dirstate.unlock()
1154
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1155
# point. Instead, it could check if the header has been
1156
# modified when it is locked, and if not, it can hang on to
1157
# the data it has in memory.
1158
self._dirstate = None
1159
self._inventory = None
1160
# reverse order of locking.
1162
return self._control_files.unlock()
1164
self.branch.unlock()
1166
@needs_tree_write_lock
1167
def unversion(self, file_ids):
1168
"""Remove the file ids in file_ids from the current versioned set.
1170
When a file_id is unversioned, all of its children are automatically
1173
:param file_ids: The file ids to stop versioning.
1174
:raises: NoSuchId if any fileid is not currently versioned.
1178
state = self.current_dirstate()
1179
state._read_dirblocks_if_needed()
1180
ids_to_unversion = set(file_ids)
1181
paths_to_unversion = set()
1183
# check if the root is to be unversioned, if so, assert for now.
1184
# walk the state marking unversioned things as absent.
1185
# if there are any un-unversioned ids at the end, raise
1186
for key, details in state._dirblocks[0][1]:
1187
if (details[0][0] not in ('a', 'r') and # absent or relocated
1188
key[2] in ids_to_unversion):
1189
# I haven't written the code to unversion / yet - it should be
1191
raise errors.BzrError('Unversioning the / is not currently supported')
1193
while block_index < len(state._dirblocks):
1194
# process one directory at a time.
1195
block = state._dirblocks[block_index]
1196
# first check: is the path one to remove - it or its children
1197
delete_block = False
1198
for path in paths_to_unversion:
1199
if (block[0].startswith(path) and
1200
(len(block[0]) == len(path) or
1201
block[0][len(path)] == '/')):
1202
# this entire block should be deleted - its the block for a
1203
# path to unversion; or the child of one
1206
# TODO: trim paths_to_unversion as we pass by paths
1208
# this block is to be deleted: process it.
1209
# TODO: we can special case the no-parents case and
1210
# just forget the whole block.
1212
while entry_index < len(block[1]):
1213
# Mark this file id as having been removed
1214
entry = block[1][entry_index]
1215
ids_to_unversion.discard(entry[0][2])
1216
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1218
or not state._make_absent(entry)):
1220
# go to the next block. (At the moment we dont delete empty
1225
while entry_index < len(block[1]):
1226
entry = block[1][entry_index]
1227
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1228
# ^ some parent row.
1229
entry[0][2] not in ids_to_unversion):
1230
# ^ not an id to unversion
1233
if entry[1][0][0] == 'd':
1234
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1235
if not state._make_absent(entry):
1237
# we have unversioned this id
1238
ids_to_unversion.remove(entry[0][2])
1240
if ids_to_unversion:
1241
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1242
self._make_dirty(reset_inventory=False)
1243
# have to change the legacy inventory too.
1244
if self._inventory is not None:
1245
for file_id in file_ids:
1246
self._inventory.remove_recursive_id(file_id)
1248
@needs_tree_write_lock
1249
def rename_one(self, from_rel, to_rel, after=False):
1250
"""See WorkingTree.rename_one"""
1252
WorkingTree.rename_one(self, from_rel, to_rel, after)
1254
@needs_tree_write_lock
1255
def apply_inventory_delta(self, changes):
1256
"""See MutableTree.apply_inventory_delta"""
1257
state = self.current_dirstate()
1258
state.update_by_delta(changes)
1259
self._make_dirty(reset_inventory=True)
1261
def update_basis_by_delta(self, new_revid, delta):
1262
"""See MutableTree.update_basis_by_delta."""
1263
if self.last_revision() == new_revid:
1264
raise AssertionError()
1265
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1268
def _validate(self):
1269
self._dirstate._validate()
1271
@needs_tree_write_lock
1272
def _write_inventory(self, inv):
1273
"""Write inventory as the current inventory."""
1275
raise AssertionError("attempting to write an inventory when the "
1276
"dirstate is dirty will lose pending changes")
1277
self.current_dirstate().set_state_from_inventory(inv)
1278
self._make_dirty(reset_inventory=False)
1279
if self._inventory is not None:
1280
self._inventory = inv
1284
class WorkingTree4(DirStateWorkingTree):
1285
"""This is the Format 4 working tree.
1287
This differs from WorkingTree3 by:
1288
- Having a consolidated internal dirstate, stored in a
1289
randomly-accessible sorted file on disk.
1290
- Not having a regular inventory attribute. One can be synthesized
1291
on demand but this is expensive and should be avoided.
1293
This is new in bzr 0.15.
1297
class WorkingTree5(DirStateWorkingTree):
1298
"""This is the Format 5 working tree.
1300
This differs from WorkingTree4 by:
1301
- Supporting content filtering.
1302
- Supporting a current view that may mask the set of files in a tree
1303
impacted by most user operations.
1305
This is new in bzr 1.11.
1309
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1310
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1311
accelerator_tree=None, hardlink=False):
1312
"""See WorkingTreeFormat.initialize().
1314
:param revision_id: allows creating a working tree at a different
1315
revision than the branch is at.
1316
:param accelerator_tree: A tree which can be used for retrieving file
1317
contents more quickly than the revision tree, i.e. a workingtree.
1318
The revision tree will be used for cases where accelerator_tree's
1319
content is different.
1320
:param hardlink: If true, hard-link files from accelerator_tree,
1323
These trees get an initial random root id, if their repository supports
1324
rich root data, TREE_ROOT otherwise.
1326
if not isinstance(a_bzrdir.transport, LocalTransport):
1327
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1328
transport = a_bzrdir.get_workingtree_transport(self)
1329
control_files = self._open_control_files(a_bzrdir)
1330
control_files.create_lock()
1331
control_files.lock_write()
1332
transport.put_bytes('format', self.get_format_string(),
1333
mode=a_bzrdir._get_file_mode())
1334
if from_branch is not None:
1335
branch = from_branch
1337
branch = a_bzrdir.open_branch()
1338
if revision_id is None:
1339
revision_id = branch.last_revision()
1340
local_path = transport.local_abspath('dirstate')
1341
# write out new dirstate (must exist when we create the tree)
1342
state = dirstate.DirState.initialize(local_path)
1345
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1349
_control_files=control_files)
1351
wt.lock_tree_write()
1353
self._init_custom_control_files(wt)
1354
if revision_id in (None, NULL_REVISION):
1355
if branch.repository.supports_rich_root():
1356
wt._set_root_id(generate_ids.gen_root_id())
1358
wt._set_root_id(ROOT_ID)
1361
# frequently, we will get here due to branching. The accelerator
1362
# tree will be the tree from the branch, so the desired basis
1363
# tree will often be a parent of the accelerator tree.
1364
if accelerator_tree is not None:
1366
basis = accelerator_tree.revision_tree(revision_id)
1367
except errors.NoSuchRevision:
1370
basis = branch.repository.revision_tree(revision_id)
1371
if revision_id == NULL_REVISION:
1374
parents_list = [(revision_id, basis)]
1377
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1379
# if the basis has a root id we have to use that; otherwise we
1380
# use a new random one
1381
basis_root_id = basis.get_root_id()
1382
if basis_root_id is not None:
1383
wt._set_root_id(basis_root_id)
1385
# delta_from_tree is safe even for DirStateRevisionTrees,
1386
# because wt4.apply_inventory_delta does not mutate the input
1387
# inventory entries.
1388
transform.build_tree(basis, wt, accelerator_tree,
1389
hardlink=hardlink, delta_from_tree=True)
1393
control_files.unlock()
1397
def _init_custom_control_files(self, wt):
1398
"""Subclasses with custom control files should override this method.
1400
The working tree and control files are locked for writing when this
1403
:param wt: the WorkingTree object
1406
def _open(self, a_bzrdir, control_files):
1407
"""Open the tree itself.
1409
:param a_bzrdir: the dir for the tree.
1410
:param control_files: the control files for the tree.
1412
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1413
branch=a_bzrdir.open_branch(),
1416
_control_files=control_files)
1418
def __get_matchingbzrdir(self):
1419
# please test against something that will let us do tree references
1420
return bzrdir.format_registry.make_bzrdir(
1421
'dirstate-with-subtree')
1423
_matchingbzrdir = property(__get_matchingbzrdir)
1426
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1427
"""The first consolidated dirstate working tree format.
1430
- exists within a metadir controlling .bzr
1431
- includes an explicit version marker for the workingtree control
1432
files, separate from the BzrDir format
1433
- modifies the hash cache format
1434
- is new in bzr 0.15
1435
- uses a LockDir to guard access to it.
1438
upgrade_recommended = False
1440
_tree_class = WorkingTree4
1442
def get_format_string(self):
1443
"""See WorkingTreeFormat.get_format_string()."""
1444
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1446
def get_format_description(self):
1447
"""See WorkingTreeFormat.get_format_description()."""
1448
return "Working tree format 4"
1451
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1452
"""WorkingTree format supporting views.
1455
upgrade_recommended = False
1457
_tree_class = WorkingTree5
1459
def get_format_string(self):
1460
"""See WorkingTreeFormat.get_format_string()."""
1461
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1463
def get_format_description(self):
1464
"""See WorkingTreeFormat.get_format_description()."""
1465
return "Working tree format 5"
1467
def _init_custom_control_files(self, wt):
1468
"""Subclasses with custom control files should override this method."""
1469
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1471
def supports_content_filtering(self):
1474
def supports_views(self):
1478
class DirStateRevisionTree(Tree):
1479
"""A revision tree pulling the inventory from a dirstate."""
1481
def __init__(self, dirstate, revision_id, repository):
1482
self._dirstate = dirstate
1483
self._revision_id = revision_id
1484
self._repository = repository
1485
self._inventory = None
1487
self._dirstate_locked = False
1488
self._repo_supports_tree_reference = getattr(
1489
repository._format, "supports_tree_reference",
1493
return "<%s of %s in %s>" % \
1494
(self.__class__.__name__, self._revision_id, self._dirstate)
1496
def annotate_iter(self, file_id,
1497
default_revision=_mod_revision.CURRENT_REVISION):
1498
"""See Tree.annotate_iter"""
1499
text_key = (file_id, self.inventory[file_id].revision)
1500
annotations = self._repository.texts.annotate(text_key)
1501
return [(key[-1], line) for (key, line) in annotations]
1503
def _get_ancestors(self, default_revision):
1504
return set(self._repository.get_ancestry(self._revision_id,
1506
def _comparison_data(self, entry, path):
1507
"""See Tree._comparison_data."""
1509
return None, False, None
1510
# trust the entry as RevisionTree does, but this may not be
1511
# sensible: the entry might not have come from us?
1512
return entry.kind, entry.executable, None
1514
def _file_size(self, entry, stat_value):
1515
return entry.text_size
1517
def filter_unversioned_files(self, paths):
1518
"""Filter out paths that are not versioned.
1520
:return: set of paths.
1522
pred = self.has_filename
1523
return set((p for p in paths if not pred(p)))
1525
def get_root_id(self):
1526
return self.path2id('')
1528
def id2path(self, file_id):
1529
"Convert a file-id to a path."
1530
entry = self._get_entry(file_id=file_id)
1531
if entry == (None, None):
1532
raise errors.NoSuchId(tree=self, file_id=file_id)
1533
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1534
return path_utf8.decode('utf8')
1536
def iter_references(self):
1537
if not self._repo_supports_tree_reference:
1538
# When the repo doesn't support references, we will have nothing to
1541
# Otherwise, fall back to the default implementation
1542
return super(DirStateRevisionTree, self).iter_references()
1544
def _get_parent_index(self):
1545
"""Return the index in the dirstate referenced by this tree."""
1546
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1548
def _get_entry(self, file_id=None, path=None):
1549
"""Get the dirstate row for file_id or path.
1551
If either file_id or path is supplied, it is used as the key to lookup.
1552
If both are supplied, the fastest lookup is used, and an error is
1553
raised if they do not both point at the same row.
1555
:param file_id: An optional unicode file_id to be looked up.
1556
:param path: An optional unicode path to be looked up.
1557
:return: The dirstate row tuple for path/file_id, or (None, None)
1559
if file_id is None and path is None:
1560
raise errors.BzrError('must supply file_id or path')
1561
if path is not None:
1562
path = path.encode('utf8')
1563
parent_index = self._get_parent_index()
1564
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1566
def _generate_inventory(self):
1567
"""Create and set self.inventory from the dirstate object.
1569
(So this is only called the first time the inventory is requested for
1570
this tree; it then remains in memory until it's out of date.)
1572
This is relatively expensive: we have to walk the entire dirstate.
1574
if not self._locked:
1575
raise AssertionError(
1576
'cannot generate inventory of an unlocked '
1577
'dirstate revision tree')
1578
# separate call for profiling - makes it clear where the costs are.
1579
self._dirstate._read_dirblocks_if_needed()
1580
if self._revision_id not in self._dirstate.get_parent_ids():
1581
raise AssertionError(
1582
'parent %s has disappeared from %s' % (
1583
self._revision_id, self._dirstate.get_parent_ids()))
1584
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1585
# This is identical now to the WorkingTree _generate_inventory except
1586
# for the tree index use.
1587
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1588
current_id = root_key[2]
1589
if current_entry[parent_index][0] != 'd':
1590
raise AssertionError()
1591
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1592
inv.root.revision = current_entry[parent_index][4]
1593
# Turn some things into local variables
1594
minikind_to_kind = dirstate.DirState._minikind_to_kind
1595
factory = entry_factory
1596
utf8_decode = cache_utf8._utf8_decode
1597
inv_byid = inv._byid
1598
# we could do this straight out of the dirstate; it might be fast
1599
# and should be profiled - RBC 20070216
1600
parent_ies = {'' : inv.root}
1601
for block in self._dirstate._dirblocks[1:]: #skip root
1604
parent_ie = parent_ies[dirname]
1606
# all the paths in this block are not versioned in this tree
1608
for key, entry in block[1]:
1609
minikind, fingerprint, size, executable, revid = entry[parent_index]
1610
if minikind in ('a', 'r'): # absent, relocated
1614
name_unicode = utf8_decode(name)[0]
1616
kind = minikind_to_kind[minikind]
1617
inv_entry = factory[kind](file_id, name_unicode,
1619
inv_entry.revision = revid
1621
inv_entry.executable = executable
1622
inv_entry.text_size = size
1623
inv_entry.text_sha1 = fingerprint
1624
elif kind == 'directory':
1625
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1626
elif kind == 'symlink':
1627
inv_entry.executable = False
1628
inv_entry.text_size = None
1629
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1630
elif kind == 'tree-reference':
1631
inv_entry.reference_revision = fingerprint or None
1633
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1635
# These checks cost us around 40ms on a 55k entry tree
1636
if file_id in inv_byid:
1637
raise AssertionError('file_id %s already in'
1638
' inventory as %s' % (file_id, inv_byid[file_id]))
1639
if name_unicode in parent_ie.children:
1640
raise AssertionError('name %r already in parent'
1642
inv_byid[file_id] = inv_entry
1643
parent_ie.children[name_unicode] = inv_entry
1644
self._inventory = inv
1646
def get_file_mtime(self, file_id, path=None):
1647
"""Return the modification time for this record.
1649
We return the timestamp of the last-changed revision.
1651
# Make sure the file exists
1652
entry = self._get_entry(file_id, path=path)
1653
if entry == (None, None): # do we raise?
1655
parent_index = self._get_parent_index()
1656
last_changed_revision = entry[1][parent_index][4]
1657
return self._repository.get_revision(last_changed_revision).timestamp
1659
def get_file_sha1(self, file_id, path=None, stat_value=None):
1660
entry = self._get_entry(file_id=file_id, path=path)
1661
parent_index = self._get_parent_index()
1662
parent_details = entry[1][parent_index]
1663
if parent_details[0] == 'f':
1664
return parent_details[1]
1667
def get_file(self, file_id, path=None):
1668
return StringIO(self.get_file_text(file_id))
1670
def get_file_size(self, file_id):
1671
"""See Tree.get_file_size"""
1672
return self.inventory[file_id].text_size
1674
def get_file_text(self, file_id, path=None):
1675
return list(self.iter_files_bytes([(file_id, None)]))[0][1]
1677
def get_reference_revision(self, file_id, path=None):
1678
return self.inventory[file_id].reference_revision
1680
def iter_files_bytes(self, desired_files):
1681
"""See Tree.iter_files_bytes.
1683
This version is implemented on top of Repository.iter_files_bytes"""
1684
parent_index = self._get_parent_index()
1685
repo_desired_files = []
1686
for file_id, identifier in desired_files:
1687
entry = self._get_entry(file_id)
1688
if entry == (None, None):
1689
raise errors.NoSuchId(self, file_id)
1690
repo_desired_files.append((file_id, entry[1][parent_index][4],
1692
return self._repository.iter_files_bytes(repo_desired_files)
1694
def get_symlink_target(self, file_id):
1695
entry = self._get_entry(file_id=file_id)
1696
parent_index = self._get_parent_index()
1697
if entry[1][parent_index][0] != 'l':
1700
# At present, none of the tree implementations supports non-ascii
1701
# symlink targets. So we will just assume that the dirstate path is
1703
return entry[1][parent_index][1]
1705
def get_revision_id(self):
1706
"""Return the revision id for this tree."""
1707
return self._revision_id
1709
def _get_inventory(self):
1710
if self._inventory is not None:
1711
return self._inventory
1712
self._must_be_locked()
1713
self._generate_inventory()
1714
return self._inventory
1716
inventory = property(_get_inventory,
1717
doc="Inventory of this Tree")
1719
def get_parent_ids(self):
1720
"""The parents of a tree in the dirstate are not cached."""
1721
return self._repository.get_revision(self._revision_id).parent_ids
1723
def has_filename(self, filename):
1724
return bool(self.path2id(filename))
1726
def kind(self, file_id):
1727
entry = self._get_entry(file_id=file_id)[1]
1729
raise errors.NoSuchId(tree=self, file_id=file_id)
1730
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1732
def stored_kind(self, file_id):
1733
"""See Tree.stored_kind"""
1734
return self.kind(file_id)
1736
def path_content_summary(self, path):
1737
"""See Tree.path_content_summary."""
1738
id = self.inventory.path2id(path)
1740
return ('missing', None, None, None)
1741
entry = self._inventory[id]
1744
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1745
elif kind == 'symlink':
1746
return (kind, None, None, entry.symlink_target)
1748
return (kind, None, None, None)
1750
def is_executable(self, file_id, path=None):
1751
ie = self.inventory[file_id]
1752
if ie.kind != "file":
1754
return ie.executable
1756
def list_files(self, include_root=False):
1757
# We use a standard implementation, because DirStateRevisionTree is
1758
# dealing with one of the parents of the current state
1759
inv = self._get_inventory()
1760
entries = inv.iter_entries()
1761
if self.inventory.root is not None and not include_root:
1763
for path, entry in entries:
1764
yield path, 'V', entry.kind, entry.file_id, entry
1766
def lock_read(self):
1767
"""Lock the tree for a set of operations."""
1768
if not self._locked:
1769
self._repository.lock_read()
1770
if self._dirstate._lock_token is None:
1771
self._dirstate.lock_read()
1772
self._dirstate_locked = True
1775
def _must_be_locked(self):
1776
if not self._locked:
1777
raise errors.ObjectNotLocked(self)
1780
def path2id(self, path):
1781
"""Return the id for path in this tree."""
1782
# lookup by path: faster than splitting and walking the ivnentory.
1783
entry = self._get_entry(path=path)
1784
if entry == (None, None):
1789
"""Unlock, freeing any cache memory used during the lock."""
1790
# outside of a lock, the inventory is suspect: release it.
1792
if not self._locked:
1793
self._inventory = None
1795
if self._dirstate_locked:
1796
self._dirstate.unlock()
1797
self._dirstate_locked = False
1798
self._repository.unlock()
1801
def supports_tree_reference(self):
1802
return self._repo_supports_tree_reference
1804
def walkdirs(self, prefix=""):
1805
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
1806
# implementation based on an inventory.
1807
# This should be cleaned up to use the much faster Dirstate code
1808
# So for now, we just build up the parent inventory, and extract
1809
# it the same way RevisionTree does.
1810
_directory = 'directory'
1811
inv = self._get_inventory()
1812
top_id = inv.path2id(prefix)
1816
pending = [(prefix, top_id)]
1819
relpath, file_id = pending.pop()
1820
# 0 - relpath, 1- file-id
1822
relroot = relpath + '/'
1825
# FIXME: stash the node in pending
1826
entry = inv[file_id]
1827
for name, child in entry.sorted_children():
1828
toppath = relroot + name
1829
dirblock.append((toppath, name, child.kind, None,
1830
child.file_id, child.kind
1832
yield (relpath, entry.file_id), dirblock
1833
# push the user specified dirs from dirblock
1834
for dir in reversed(dirblock):
1835
if dir[2] == _directory:
1836
pending.append((dir[0], dir[4]))
1839
class InterDirStateTree(InterTree):
1840
"""Fast path optimiser for changes_from with dirstate trees.
1842
This is used only when both trees are in the dirstate working file, and
1843
the source is any parent within the dirstate, and the destination is
1844
the current working tree of the same dirstate.
1846
# this could be generalized to allow comparisons between any trees in the
1847
# dirstate, and possibly between trees stored in different dirstates.
1849
def __init__(self, source, target):
1850
super(InterDirStateTree, self).__init__(source, target)
1851
if not InterDirStateTree.is_compatible(source, target):
1852
raise Exception, "invalid source %r and target %r" % (source, target)
1855
def make_source_parent_tree(source, target):
1856
"""Change the source tree into a parent of the target."""
1857
revid = source.commit('record tree')
1858
target.branch.repository.fetch(source.branch.repository, revid)
1859
target.set_parent_ids([revid])
1860
return target.basis_tree(), target
1863
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
1864
result = klass.make_source_parent_tree(source, target)
1865
result[1]._iter_changes = dirstate.ProcessEntryPython
1869
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1870
from bzrlib.tests.test__dirstate_helpers import \
1871
CompiledDirstateHelpersFeature
1872
if not CompiledDirstateHelpersFeature.available():
1873
from bzrlib.tests import UnavailableFeature
1874
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1875
from bzrlib._dirstate_helpers_c import ProcessEntryC
1876
result = klass.make_source_parent_tree(source, target)
1877
result[1]._iter_changes = ProcessEntryC
1880
_matching_from_tree_format = WorkingTreeFormat4()
1881
_matching_to_tree_format = WorkingTreeFormat4()
1884
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
1885
# This method shouldn't be called, because we have python and C
1886
# specific flavours.
1887
raise NotImplementedError
1889
def iter_changes(self, include_unchanged=False,
1890
specific_files=None, pb=None, extra_trees=[],
1891
require_versioned=True, want_unversioned=False):
1892
"""Return the changes from source to target.
1894
:return: An iterator that yields tuples. See InterTree.iter_changes
1896
:param specific_files: An optional list of file paths to restrict the
1897
comparison to. When mapping filenames to ids, all matches in all
1898
trees (including optional extra_trees) are used, and all children of
1899
matched directories are included.
1900
:param include_unchanged: An optional boolean requesting the inclusion of
1901
unchanged entries in the result.
1902
:param extra_trees: An optional list of additional trees to use when
1903
mapping the contents of specific_files (paths) to file_ids.
1904
:param require_versioned: If True, all files in specific_files must be
1905
versioned in one of source, target, extra_trees or
1906
PathsNotVersionedError is raised.
1907
:param want_unversioned: Should unversioned files be returned in the
1908
output. An unversioned file is defined as one with (False, False)
1909
for the versioned pair.
1911
# NB: show_status depends on being able to pass in non-versioned files
1912
# and report them as unknown
1913
# TODO: handle extra trees in the dirstate.
1914
if (extra_trees or specific_files == []):
1915
# we can't fast-path these cases (yet)
1916
return super(InterDirStateTree, self).iter_changes(
1917
include_unchanged, specific_files, pb, extra_trees,
1918
require_versioned, want_unversioned=want_unversioned)
1919
parent_ids = self.target.get_parent_ids()
1920
if not (self.source._revision_id in parent_ids
1921
or self.source._revision_id == NULL_REVISION):
1922
raise AssertionError(
1923
"revision {%s} is not stored in {%s}, but %s "
1924
"can only be used for trees stored in the dirstate"
1925
% (self.source._revision_id, self.target, self.iter_changes))
1927
if self.source._revision_id == NULL_REVISION:
1929
indices = (target_index,)
1931
if not (self.source._revision_id in parent_ids):
1932
raise AssertionError(
1933
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1934
self.source._revision_id, parent_ids))
1935
source_index = 1 + parent_ids.index(self.source._revision_id)
1936
indices = (source_index, target_index)
1937
# -- make all specific_files utf8 --
1939
specific_files_utf8 = set()
1940
for path in specific_files:
1941
# Note, if there are many specific files, using cache_utf8
1942
# would be good here.
1943
specific_files_utf8.add(path.encode('utf8'))
1944
specific_files = specific_files_utf8
1946
specific_files = set([''])
1947
# -- specific_files is now a utf8 path set --
1948
search_specific_files = set()
1949
# -- get the state object and prepare it.
1950
state = self.target.current_dirstate()
1951
state._read_dirblocks_if_needed()
1952
if require_versioned:
1953
# -- check all supplied paths are versioned in a search tree. --
1954
all_versioned = True
1955
for path in specific_files:
1956
path_entries = state._entries_for_path(path)
1957
if not path_entries:
1958
# this specified path is not present at all: error
1959
all_versioned = False
1961
found_versioned = False
1962
# for each id at this path
1963
for entry in path_entries:
1965
for index in indices:
1966
if entry[1][index][0] != 'a': # absent
1967
found_versioned = True
1968
# all good: found a versioned cell
1970
if not found_versioned:
1971
# none of the indexes was not 'absent' at all ids for this
1973
all_versioned = False
1975
if not all_versioned:
1976
raise errors.PathsNotVersionedError(specific_files)
1977
# -- remove redundancy in supplied specific_files to prevent over-scanning --
1978
for path in specific_files:
1979
other_specific_files = specific_files.difference(set([path]))
1980
if not osutils.is_inside_any(other_specific_files, path):
1981
# this is a top level path, we must check it.
1982
search_specific_files.add(path)
1984
use_filesystem_for_exec = (sys.platform != 'win32')
1985
iter_changes = self.target._iter_changes(include_unchanged,
1986
use_filesystem_for_exec, search_specific_files, state,
1987
source_index, target_index, want_unversioned, self.target)
1988
return iter_changes.iter_changes()
1991
def is_compatible(source, target):
1992
# the target must be a dirstate working tree
1993
if not isinstance(target, DirStateWorkingTree):
1995
# the source must be a revtree or dirstate rev tree.
1996
if not isinstance(source,
1997
(revisiontree.RevisionTree, DirStateRevisionTree)):
1999
# the source revid must be in the target dirstate
2000
if not (source._revision_id == NULL_REVISION or
2001
source._revision_id in target.get_parent_ids()):
2002
# TODO: what about ghosts? it may well need to
2003
# check for them explicitly.
2007
InterTree.register_optimiser(InterDirStateTree)
2010
class Converter3to4(object):
2011
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2014
self.target_format = WorkingTreeFormat4()
2016
def convert(self, tree):
2017
# lock the control files not the tree, so that we dont get tree
2018
# on-unlock behaviours, and so that noone else diddles with the
2019
# tree during upgrade.
2020
tree._control_files.lock_write()
2022
tree.read_working_inventory()
2023
self.create_dirstate_data(tree)
2024
self.update_format(tree)
2025
self.remove_xml_files(tree)
2027
tree._control_files.unlock()
2029
def create_dirstate_data(self, tree):
2030
"""Create the dirstate based data for tree."""
2031
local_path = tree.bzrdir.get_workingtree_transport(None
2032
).local_abspath('dirstate')
2033
state = dirstate.DirState.from_tree(tree, local_path)
2037
def remove_xml_files(self, tree):
2038
"""Remove the oldformat 3 data."""
2039
transport = tree.bzrdir.get_workingtree_transport(None)
2040
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2041
'pending-merges', 'stat-cache']:
2043
transport.delete(path)
2044
except errors.NoSuchFile:
2045
# some files are optional - just deal.
2048
def update_format(self, tree):
2049
"""Change the format marker."""
2050
tree._transport.put_bytes('format',
2051
self.target_format.get_format_string(),
2052
mode=tree.bzrdir._get_file_mode())
2055
class Converter4to5(object):
2056
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2059
self.target_format = WorkingTreeFormat5()
2061
def convert(self, tree):
2062
# lock the control files not the tree, so that we don't get tree
2063
# on-unlock behaviours, and so that no-one else diddles with the
2064
# tree during upgrade.
2065
tree._control_files.lock_write()
2067
self.init_custom_control_files(tree)
2068
self.update_format(tree)
2070
tree._control_files.unlock()
2072
def init_custom_control_files(self, tree):
2073
"""Initialize custom control files."""
2074
tree._transport.put_bytes('views', '',
2075
mode=tree.bzrdir._get_file_mode())
2077
def update_format(self, tree):
2078
"""Change the format marker."""
2079
tree._transport.put_bytes('format',
2080
self.target_format.get_format_string(),
2081
mode=tree.bzrdir._get_file_mode())