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., 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(), """
31
from bisect import bisect_left
33
from copy import deepcopy
45
conflicts as _mod_conflicts,
55
revision as _mod_revision,
66
from bzrlib.transport import get_transport
70
from bzrlib import symbol_versioning
71
from bzrlib.decorators import needs_read_lock, needs_write_lock
72
from bzrlib.filters import filtered_input_file, internal_size_sha_file_byname
73
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, entry_factory
74
import bzrlib.mutabletree
75
from bzrlib.mutabletree import needs_tree_write_lock
76
from bzrlib.osutils import (
86
from bzrlib.trace import mutter, note
87
from bzrlib.transport.local import LocalTransport
88
from bzrlib.tree import InterTree
89
from bzrlib.progress import DummyProgress, ProgressPhase
90
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
91
from bzrlib.rio import RioReader, rio_file, Stanza
92
from bzrlib.symbol_versioning import (deprecated_passed,
97
from bzrlib.tree import Tree
98
from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
101
class DirStateWorkingTree(WorkingTree3):
102
def __init__(self, basedir,
107
"""Construct a WorkingTree for basedir.
109
If the branch is not supplied, it is opened automatically.
110
If the branch is supplied, it must be the branch for this basedir.
111
(branch.base is not cross checked, because for remote branches that
112
would be meaningless).
114
self._format = _format
115
self.bzrdir = _bzrdir
116
basedir = safe_unicode(basedir)
117
mutter("opening working tree %r", basedir)
118
self._branch = branch
119
self.basedir = realpath(basedir)
120
# if branch is at our basedir and is a format 6 or less
121
# assume all other formats have their own control files.
122
self._control_files = _control_files
123
self._transport = self._control_files._transport
126
# during a read or write lock these objects are set, and are
127
# None the rest of the time.
128
self._dirstate = None
129
self._inventory = None
131
self._setup_directory_is_tree_reference()
132
self._detect_case_handling()
133
self._rules_searcher = None
134
self.views = self._make_views()
135
#--- allow tests to select the dirstate iter_changes implementation
136
self._iter_changes = dirstate._process_entry
138
@needs_tree_write_lock
139
def _add(self, files, ids, kinds):
140
"""See MutableTree._add."""
141
state = self.current_dirstate()
142
for f, file_id, kind in zip(files, ids, kinds):
145
# special case tree root handling.
146
if f == '' and self.path2id(f) == ROOT_ID:
147
state.set_path_id('', generate_ids.gen_file_id(f))
150
file_id = generate_ids.gen_file_id(f)
151
# deliberately add the file with no cached stat or sha1
152
# - on the first access it will be gathered, and we can
153
# always change this once tests are all passing.
154
state.add(f, file_id, kind, None, '')
155
self._make_dirty(reset_inventory=True)
157
def _make_dirty(self, reset_inventory):
158
"""Make the tree state dirty.
160
:param reset_inventory: True if the cached inventory should be removed
161
(presuming there is one).
164
if reset_inventory and self._inventory is not None:
165
self._inventory = None
167
@needs_tree_write_lock
168
def add_reference(self, sub_tree):
169
# use standard implementation, which calls back to self._add
171
# So we don't store the reference_revision in the working dirstate,
172
# it's just recorded at the moment of commit.
173
self._add_reference(sub_tree)
175
def break_lock(self):
176
"""Break a lock if one is present from another instance.
178
Uses the ui factory to ask for confirmation if the lock may be from
181
This will probe the repository for its lock as well.
183
# if the dirstate is locked by an active process, reject the break lock
186
if self._dirstate is None:
190
state = self._current_dirstate()
191
if state._lock_token is not None:
192
# we already have it locked. sheese, cant break our own lock.
193
raise errors.LockActive(self.basedir)
196
# try for a write lock - need permission to get one anyhow
199
except errors.LockContention:
200
# oslocks fail when a process is still live: fail.
201
# TODO: get the locked lockdir info and give to the user to
202
# assist in debugging.
203
raise errors.LockActive(self.basedir)
208
self._dirstate = None
209
self._control_files.break_lock()
210
self.branch.break_lock()
212
def _comparison_data(self, entry, path):
213
kind, executable, stat_value = \
214
WorkingTree3._comparison_data(self, entry, path)
215
# it looks like a plain directory, but it's really a reference -- see
217
if (self._repo_supports_tree_reference and kind == 'directory'
218
and entry is not None and entry.kind == 'tree-reference'):
219
kind = 'tree-reference'
220
return kind, executable, stat_value
223
def commit(self, message=None, revprops=None, *args, **kwargs):
224
# mark the tree as dirty post commit - commit
225
# can change the current versioned list by doing deletes.
226
result = WorkingTree3.commit(self, message, revprops, *args, **kwargs)
227
self._make_dirty(reset_inventory=True)
230
def current_dirstate(self):
231
"""Return the current dirstate object.
233
This is not part of the tree interface and only exposed for ease of
236
:raises errors.NotWriteLocked: when not in a lock.
238
self._must_be_locked()
239
return self._current_dirstate()
241
def _current_dirstate(self):
242
"""Internal function that does not check lock status.
244
This is needed for break_lock which also needs the dirstate.
246
if self._dirstate is not None:
247
return self._dirstate
248
local_path = self.bzrdir.get_workingtree_transport(None
249
).local_abspath('dirstate')
250
self._dirstate = dirstate.DirState.on_file(local_path,
251
self._sha1_provider())
252
return self._dirstate
254
def _sha1_provider(self):
255
"""A function that returns a SHA1Provider suitable for this tree.
257
:return: None if content filtering is not supported by this tree.
258
Otherwise, a SHA1Provider is returned that sha's the canonical
259
form of files, i.e. after read filters are applied.
261
if self.supports_content_filtering():
262
return ContentFilterAwareSHA1Provider(self)
266
def filter_unversioned_files(self, paths):
267
"""Filter out paths that are versioned.
269
:return: set of paths.
271
# TODO: make a generic multi-bisect routine roughly that should list
272
# the paths, then process one half at a time recursively, and feed the
273
# results of each bisect in further still
274
paths = sorted(paths)
276
state = self.current_dirstate()
277
# TODO we want a paths_to_dirblocks helper I think
279
dirname, basename = os.path.split(path.encode('utf8'))
280
_, _, _, path_is_versioned = state._get_block_entry_index(
281
dirname, basename, 0)
282
if not path_is_versioned:
287
"""Write all cached data to disk."""
288
if self._control_files._lock_mode != 'w':
289
raise errors.NotWriteLocked(self)
290
self.current_dirstate().save()
291
self._inventory = None
294
@needs_tree_write_lock
295
def _gather_kinds(self, files, kinds):
296
"""See MutableTree._gather_kinds."""
297
for pos, f in enumerate(files):
298
if kinds[pos] is None:
299
kinds[pos] = self._kind(f)
301
def _generate_inventory(self):
302
"""Create and set self.inventory from the dirstate object.
304
This is relatively expensive: we have to walk the entire dirstate.
305
Ideally we would not, and can deprecate this function.
307
#: uncomment to trap on inventory requests.
308
# import pdb;pdb.set_trace()
309
state = self.current_dirstate()
310
state._read_dirblocks_if_needed()
311
root_key, current_entry = self._get_entry(path='')
312
current_id = root_key[2]
313
if not (current_entry[0][0] == 'd'): # directory
314
raise AssertionError(current_entry)
315
inv = Inventory(root_id=current_id)
316
# Turn some things into local variables
317
minikind_to_kind = dirstate.DirState._minikind_to_kind
318
factory = entry_factory
319
utf8_decode = cache_utf8._utf8_decode
321
# we could do this straight out of the dirstate; it might be fast
322
# and should be profiled - RBC 20070216
323
parent_ies = {'' : inv.root}
324
for block in state._dirblocks[1:]: # skip the root
327
parent_ie = parent_ies[dirname]
329
# all the paths in this block are not versioned in this tree
331
for key, entry in block[1]:
332
minikind, link_or_sha1, size, executable, stat = entry[0]
333
if minikind in ('a', 'r'): # absent, relocated
334
# a parent tree only entry
337
name_unicode = utf8_decode(name)[0]
339
kind = minikind_to_kind[minikind]
340
inv_entry = factory[kind](file_id, name_unicode,
343
# This is only needed on win32, where this is the only way
344
# we know the executable bit.
345
inv_entry.executable = executable
346
# not strictly needed: working tree
347
#inv_entry.text_size = size
348
#inv_entry.text_sha1 = sha1
349
elif kind == 'directory':
350
# add this entry to the parent map.
351
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
352
elif kind == 'tree-reference':
353
if not self._repo_supports_tree_reference:
354
raise errors.UnsupportedOperation(
355
self._generate_inventory,
356
self.branch.repository)
357
inv_entry.reference_revision = link_or_sha1 or None
358
elif kind != 'symlink':
359
raise AssertionError("unknown kind %r" % kind)
360
# These checks cost us around 40ms on a 55k entry tree
361
if file_id in inv_byid:
362
raise AssertionError('file_id %s already in'
363
' inventory as %s' % (file_id, inv_byid[file_id]))
364
if name_unicode in parent_ie.children:
365
raise AssertionError('name %r already in parent'
367
inv_byid[file_id] = inv_entry
368
parent_ie.children[name_unicode] = inv_entry
369
self._inventory = inv
371
def _get_entry(self, file_id=None, path=None):
372
"""Get the dirstate row for file_id or path.
374
If either file_id or path is supplied, it is used as the key to lookup.
375
If both are supplied, the fastest lookup is used, and an error is
376
raised if they do not both point at the same row.
378
:param file_id: An optional unicode file_id to be looked up.
379
:param path: An optional unicode path to be looked up.
380
:return: The dirstate row tuple for path/file_id, or (None, None)
382
if file_id is None and path is None:
383
raise errors.BzrError('must supply file_id or path')
384
state = self.current_dirstate()
386
path = path.encode('utf8')
387
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
389
def get_file_sha1(self, file_id, path=None, stat_value=None):
390
# check file id is valid unconditionally.
391
entry = self._get_entry(file_id=file_id, path=path)
393
raise errors.NoSuchId(self, file_id)
395
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
397
file_abspath = self.abspath(path)
398
state = self.current_dirstate()
399
if stat_value is None:
401
stat_value = os.lstat(file_abspath)
403
if e.errno == errno.ENOENT:
407
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
408
stat_value=stat_value)
409
if entry[1][0][0] == 'f':
410
if link_or_sha1 is None:
411
file_obj, statvalue = self.get_file_with_stat(file_id, path)
413
sha1 = osutils.sha_file(file_obj)
416
self._observed_sha1(file_id, path, (sha1, statvalue))
422
def _get_inventory(self):
423
"""Get the inventory for the tree. This is only valid within a lock."""
424
if 'evil' in debug.debug_flags:
425
trace.mutter_callsite(2,
426
"accessing .inventory forces a size of tree translation.")
427
if self._inventory is not None:
428
return self._inventory
429
self._must_be_locked()
430
self._generate_inventory()
431
return self._inventory
433
inventory = property(_get_inventory,
434
doc="Inventory of this Tree")
437
def get_parent_ids(self):
438
"""See Tree.get_parent_ids.
440
This implementation requests the ids list from the dirstate file.
442
return self.current_dirstate().get_parent_ids()
444
def get_reference_revision(self, file_id, path=None):
445
# referenced tree's revision is whatever's currently there
446
return self.get_nested_tree(file_id, path).last_revision()
448
def get_nested_tree(self, file_id, path=None):
450
path = self.id2path(file_id)
451
# else: check file_id is at path?
452
return WorkingTree.open(self.abspath(path))
455
def get_root_id(self):
456
"""Return the id of this trees root"""
457
return self._get_entry(path='')[0][2]
459
def has_id(self, file_id):
460
state = self.current_dirstate()
461
row, parents = self._get_entry(file_id=file_id)
464
return osutils.lexists(pathjoin(
465
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
468
def id2path(self, file_id):
469
"Convert a file-id to a path."
470
state = self.current_dirstate()
471
entry = self._get_entry(file_id=file_id)
472
if entry == (None, None):
473
raise errors.NoSuchId(tree=self, file_id=file_id)
474
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
475
return path_utf8.decode('utf8')
477
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
478
entry = self._get_entry(path=path)
479
if entry == (None, None):
480
return False # Missing entries are not executable
481
return entry[1][0][3] # Executable?
483
if not osutils.supports_executable():
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
entry = self._get_entry(file_id=file_id, path=path)
490
if entry == (None, None):
492
return entry[1][0][3]
494
_is_executable_from_path_and_stat = \
495
_is_executable_from_path_and_stat_from_basis
497
def is_executable(self, file_id, path=None):
498
"""Test if a file is executable or not.
500
Note: The caller is expected to take a read-lock before calling this.
502
self._must_be_locked()
504
path = self.id2path(file_id)
505
mode = os.lstat(self.abspath(path)).st_mode
506
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
508
def all_file_ids(self):
509
"""See Tree.iter_all_file_ids"""
510
self._must_be_locked()
512
for key, tree_details in self.current_dirstate()._iter_entries():
513
if tree_details[0][0] in ('a', 'r'): # relocated
520
"""Iterate through file_ids for this tree.
522
file_ids are in a WorkingTree if they are in the working inventory
523
and the working file exists.
526
for key, tree_details in self.current_dirstate()._iter_entries():
527
if tree_details[0][0] in ('a', 'r'): # absent, relocated
528
# not relevant to the working tree
530
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
531
if osutils.lexists(path):
532
result.append(key[2])
535
def iter_references(self):
536
if not self._repo_supports_tree_reference:
537
# When the repo doesn't support references, we will have nothing to
540
for key, tree_details in self.current_dirstate()._iter_entries():
541
if tree_details[0][0] in ('a', 'r'): # absent, relocated
542
# not relevant to the working tree
545
# the root is not a reference.
547
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
549
if self._kind(relpath) == 'tree-reference':
550
yield relpath, key[2]
551
except errors.NoSuchFile:
552
# path is missing on disk.
555
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
556
"""See MutableTree._observed_sha1."""
557
state = self.current_dirstate()
558
entry = self._get_entry(file_id=file_id, path=path)
559
state._observed_sha1(entry, sha1, statvalue)
561
def kind(self, file_id):
562
"""Return the kind of a file.
564
This is always the actual kind that's on disk, regardless of what it
567
Note: The caller is expected to take a read-lock before calling this.
569
relpath = self.id2path(file_id)
571
raise AssertionError(
572
"path for id {%s} is None!" % file_id)
573
return self._kind(relpath)
575
def _kind(self, relpath):
576
abspath = self.abspath(relpath)
577
kind = file_kind(abspath)
578
if (self._repo_supports_tree_reference and kind == 'directory'):
579
entry = self._get_entry(path=relpath)
580
if entry[1] is not None:
581
if entry[1][0][0] == 't':
582
kind = 'tree-reference'
586
def _last_revision(self):
587
"""See Mutable.last_revision."""
588
parent_ids = self.current_dirstate().get_parent_ids()
592
return _mod_revision.NULL_REVISION
595
"""See Branch.lock_read, and WorkingTree.unlock."""
596
self.branch.lock_read()
598
self._control_files.lock_read()
600
state = self.current_dirstate()
601
if not state._lock_token:
603
# set our support for tree references from the repository in
605
self._repo_supports_tree_reference = getattr(
606
self.branch.repository._format, "supports_tree_reference",
609
self._control_files.unlock()
615
def _lock_self_write(self):
616
"""This should be called after the branch is locked."""
618
self._control_files.lock_write()
620
state = self.current_dirstate()
621
if not state._lock_token:
623
# set our support for tree references from the repository in
625
self._repo_supports_tree_reference = getattr(
626
self.branch.repository._format, "supports_tree_reference",
629
self._control_files.unlock()
635
def lock_tree_write(self):
636
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
637
self.branch.lock_read()
638
self._lock_self_write()
640
def lock_write(self):
641
"""See MutableTree.lock_write, and WorkingTree.unlock."""
642
self.branch.lock_write()
643
self._lock_self_write()
645
@needs_tree_write_lock
646
def move(self, from_paths, to_dir, after=False):
647
"""See WorkingTree.move()."""
651
state = self.current_dirstate()
652
if isinstance(from_paths, basestring):
654
to_dir_utf8 = to_dir.encode('utf8')
655
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
656
id_index = state._get_id_index()
657
# check destination directory
658
# get the details for it
659
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
660
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
661
if not entry_present:
662
raise errors.BzrMoveFailedError('', to_dir,
663
errors.NotVersionedError(to_dir))
664
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
665
# get a handle on the block itself.
666
to_block_index = state._ensure_block(
667
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
668
to_block = state._dirblocks[to_block_index]
669
to_abs = self.abspath(to_dir)
670
if not isdir(to_abs):
671
raise errors.BzrMoveFailedError('',to_dir,
672
errors.NotADirectory(to_abs))
674
if to_entry[1][0][0] != 'd':
675
raise errors.BzrMoveFailedError('',to_dir,
676
errors.NotADirectory(to_abs))
678
if self._inventory is not None:
679
update_inventory = True
681
to_dir_id = to_entry[0][2]
682
to_dir_ie = inv[to_dir_id]
684
update_inventory = False
687
def move_one(old_entry, from_path_utf8, minikind, executable,
688
fingerprint, packed_stat, size,
689
to_block, to_key, to_path_utf8):
690
state._make_absent(old_entry)
691
from_key = old_entry[0]
693
lambda:state.update_minimal(from_key,
695
executable=executable,
696
fingerprint=fingerprint,
697
packed_stat=packed_stat,
699
path_utf8=from_path_utf8))
700
state.update_minimal(to_key,
702
executable=executable,
703
fingerprint=fingerprint,
704
packed_stat=packed_stat,
706
path_utf8=to_path_utf8)
707
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
708
new_entry = to_block[1][added_entry_index]
709
rollbacks.append(lambda:state._make_absent(new_entry))
711
for from_rel in from_paths:
712
# from_rel is 'pathinroot/foo/bar'
713
from_rel_utf8 = from_rel.encode('utf8')
714
from_dirname, from_tail = osutils.split(from_rel)
715
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
716
from_entry = self._get_entry(path=from_rel)
717
if from_entry == (None, None):
718
raise errors.BzrMoveFailedError(from_rel,to_dir,
719
errors.NotVersionedError(path=str(from_rel)))
721
from_id = from_entry[0][2]
722
to_rel = pathjoin(to_dir, from_tail)
723
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
724
item_to_entry = self._get_entry(path=to_rel)
725
if item_to_entry != (None, None):
726
raise errors.BzrMoveFailedError(from_rel, to_rel,
727
"Target is already versioned.")
729
if from_rel == to_rel:
730
raise errors.BzrMoveFailedError(from_rel, to_rel,
731
"Source and target are identical.")
733
from_missing = not self.has_filename(from_rel)
734
to_missing = not self.has_filename(to_rel)
741
raise errors.BzrMoveFailedError(from_rel, to_rel,
742
errors.NoSuchFile(path=to_rel,
743
extra="New file has not been created yet"))
745
# neither path exists
746
raise errors.BzrRenameFailedError(from_rel, to_rel,
747
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
749
if from_missing: # implicitly just update our path mapping
752
raise errors.RenameFailedFilesExist(from_rel, to_rel)
755
def rollback_rename():
756
"""A single rename has failed, roll it back."""
757
# roll back everything, even if we encounter trouble doing one
760
# TODO: at least log the other exceptions rather than just
761
# losing them mbp 20070307
763
for rollback in reversed(rollbacks):
767
exc_info = sys.exc_info()
769
raise exc_info[0], exc_info[1], exc_info[2]
771
# perform the disk move first - its the most likely failure point.
773
from_rel_abs = self.abspath(from_rel)
774
to_rel_abs = self.abspath(to_rel)
776
osutils.rename(from_rel_abs, to_rel_abs)
778
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
779
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
781
# perform the rename in the inventory next if needed: its easy
785
from_entry = inv[from_id]
786
current_parent = from_entry.parent_id
787
inv.rename(from_id, to_dir_id, from_tail)
789
lambda: inv.rename(from_id, current_parent, from_tail))
790
# finally do the rename in the dirstate, which is a little
791
# tricky to rollback, but least likely to need it.
792
old_block_index, old_entry_index, dir_present, file_present = \
793
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
794
old_block = state._dirblocks[old_block_index][1]
795
old_entry = old_block[old_entry_index]
796
from_key, old_entry_details = old_entry
797
cur_details = old_entry_details[0]
799
to_key = ((to_block[0],) + from_key[1:3])
800
minikind = cur_details[0]
801
move_one(old_entry, from_path_utf8=from_rel_utf8,
803
executable=cur_details[3],
804
fingerprint=cur_details[1],
805
packed_stat=cur_details[4],
809
to_path_utf8=to_rel_utf8)
812
def update_dirblock(from_dir, to_key, to_dir_utf8):
813
"""Recursively update all entries in this dirblock."""
815
raise AssertionError("renaming root not supported")
816
from_key = (from_dir, '')
817
from_block_idx, present = \
818
state._find_block_index_from_key(from_key)
820
# This is the old record, if it isn't present, then
821
# there is theoretically nothing to update.
822
# (Unless it isn't present because of lazy loading,
823
# but we don't do that yet)
825
from_block = state._dirblocks[from_block_idx]
826
to_block_index, to_entry_index, _, _ = \
827
state._get_block_entry_index(to_key[0], to_key[1], 0)
828
to_block_index = state._ensure_block(
829
to_block_index, to_entry_index, to_dir_utf8)
830
to_block = state._dirblocks[to_block_index]
832
# Grab a copy since move_one may update the list.
833
for entry in from_block[1][:]:
834
if not (entry[0][0] == from_dir):
835
raise AssertionError()
836
cur_details = entry[1][0]
837
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
838
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
839
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
840
minikind = cur_details[0]
842
# Deleted children of a renamed directory
843
# Do not need to be updated.
844
# Children that have been renamed out of this
845
# directory should also not be updated
847
move_one(entry, from_path_utf8=from_path_utf8,
849
executable=cur_details[3],
850
fingerprint=cur_details[1],
851
packed_stat=cur_details[4],
855
to_path_utf8=to_path_utf8)
857
# We need to move all the children of this
859
update_dirblock(from_path_utf8, to_key,
861
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
865
result.append((from_rel, to_rel))
866
state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
867
self._make_dirty(reset_inventory=False)
871
def _must_be_locked(self):
872
if not self._control_files._lock_count:
873
raise errors.ObjectNotLocked(self)
876
"""Initialize the state in this tree to be a new tree."""
880
def path2id(self, path):
881
"""Return the id for path in this tree."""
882
path = path.strip('/')
883
entry = self._get_entry(path=path)
884
if entry == (None, None):
888
def paths2ids(self, paths, trees=[], require_versioned=True):
889
"""See Tree.paths2ids().
891
This specialisation fast-paths the case where all the trees are in the
896
parents = self.get_parent_ids()
898
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
900
return super(DirStateWorkingTree, self).paths2ids(paths,
901
trees, require_versioned)
902
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
903
# -- make all paths utf8 --
906
paths_utf8.add(path.encode('utf8'))
908
# -- paths is now a utf8 path set --
909
# -- get the state object and prepare it.
910
state = self.current_dirstate()
911
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
912
and '' not in paths):
913
paths2ids = self._paths2ids_using_bisect
915
paths2ids = self._paths2ids_in_memory
916
return paths2ids(paths, search_indexes,
917
require_versioned=require_versioned)
919
def _paths2ids_in_memory(self, paths, search_indexes,
920
require_versioned=True):
921
state = self.current_dirstate()
922
state._read_dirblocks_if_needed()
923
def _entries_for_path(path):
924
"""Return a list with all the entries that match path for all ids.
926
dirname, basename = os.path.split(path)
927
key = (dirname, basename, '')
928
block_index, present = state._find_block_index_from_key(key)
930
# the block which should contain path is absent.
933
block = state._dirblocks[block_index][1]
934
entry_index, _ = state._find_entry_index(key, block)
935
# we may need to look at multiple entries at this path: walk while the paths match.
936
while (entry_index < len(block) and
937
block[entry_index][0][0:2] == key[0:2]):
938
result.append(block[entry_index])
941
if require_versioned:
942
# -- check all supplied paths are versioned in a search tree. --
945
path_entries = _entries_for_path(path)
947
# this specified path is not present at all: error
948
all_versioned = False
950
found_versioned = False
951
# for each id at this path
952
for entry in path_entries:
954
for index in search_indexes:
955
if entry[1][index][0] != 'a': # absent
956
found_versioned = True
957
# all good: found a versioned cell
959
if not found_versioned:
960
# none of the indexes was not 'absent' at all ids for this
962
all_versioned = False
964
if not all_versioned:
965
raise errors.PathsNotVersionedError(paths)
966
# -- remove redundancy in supplied paths to prevent over-scanning --
967
search_paths = osutils.minimum_path_selection(paths)
969
# for all search_indexs in each path at or under each element of
970
# search_paths, if the detail is relocated: add the id, and add the
971
# relocated path as one to search if its not searched already. If the
972
# detail is not relocated, add the id.
973
searched_paths = set()
975
def _process_entry(entry):
976
"""Look at search_indexes within entry.
978
If a specific tree's details are relocated, add the relocation
979
target to search_paths if not searched already. If it is absent, do
980
nothing. Otherwise add the id to found_ids.
982
for index in search_indexes:
983
if entry[1][index][0] == 'r': # relocated
984
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
985
search_paths.add(entry[1][index][1])
986
elif entry[1][index][0] != 'a': # absent
987
found_ids.add(entry[0][2])
989
current_root = search_paths.pop()
990
searched_paths.add(current_root)
991
# process the entries for this containing directory: the rest will be
992
# found by their parents recursively.
993
root_entries = _entries_for_path(current_root)
995
# this specified path is not present at all, skip it.
997
for entry in root_entries:
998
_process_entry(entry)
999
initial_key = (current_root, '', '')
1000
block_index, _ = state._find_block_index_from_key(initial_key)
1001
while (block_index < len(state._dirblocks) and
1002
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1003
for entry in state._dirblocks[block_index][1]:
1004
_process_entry(entry)
1008
def _paths2ids_using_bisect(self, paths, search_indexes,
1009
require_versioned=True):
1010
state = self.current_dirstate()
1013
split_paths = sorted(osutils.split(p) for p in paths)
1014
found = state._bisect_recursive(split_paths)
1016
if require_versioned:
1017
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1018
for dir_name in split_paths:
1019
if dir_name not in found_dir_names:
1020
raise errors.PathsNotVersionedError(paths)
1022
for dir_name_id, trees_info in found.iteritems():
1023
for index in search_indexes:
1024
if trees_info[index][0] not in ('r', 'a'):
1025
found_ids.add(dir_name_id[2])
1028
def read_working_inventory(self):
1029
"""Read the working inventory.
1031
This is a meaningless operation for dirstate, but we obey it anyhow.
1033
return self.inventory
1036
def revision_tree(self, revision_id):
1037
"""See Tree.revision_tree.
1039
WorkingTree4 supplies revision_trees for any basis tree.
1041
dirstate = self.current_dirstate()
1042
parent_ids = dirstate.get_parent_ids()
1043
if revision_id not in parent_ids:
1044
raise errors.NoSuchRevisionInTree(self, revision_id)
1045
if revision_id in dirstate.get_ghosts():
1046
raise errors.NoSuchRevisionInTree(self, revision_id)
1047
return DirStateRevisionTree(dirstate, revision_id,
1048
self.branch.repository)
1050
@needs_tree_write_lock
1051
def set_last_revision(self, new_revision):
1052
"""Change the last revision in the working tree."""
1053
parents = self.get_parent_ids()
1054
if new_revision in (NULL_REVISION, None):
1055
if len(parents) >= 2:
1056
raise AssertionError(
1057
"setting the last parent to none with a pending merge is "
1059
self.set_parent_ids([])
1061
self.set_parent_ids([new_revision] + parents[1:],
1062
allow_leftmost_as_ghost=True)
1064
@needs_tree_write_lock
1065
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1066
"""Set the parent ids to revision_ids.
1068
See also set_parent_trees. This api will try to retrieve the tree data
1069
for each element of revision_ids from the trees repository. If you have
1070
tree data already available, it is more efficient to use
1071
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1072
an easier API to use.
1074
:param revision_ids: The revision_ids to set as the parent ids of this
1075
working tree. Any of these may be ghosts.
1078
for revision_id in revision_ids:
1080
revtree = self.branch.repository.revision_tree(revision_id)
1081
# TODO: jam 20070213 KnitVersionedFile raises
1082
# RevisionNotPresent rather than NoSuchRevision if a
1083
# given revision_id is not present. Should Repository be
1084
# catching it and re-raising NoSuchRevision?
1085
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1087
trees.append((revision_id, revtree))
1088
self.set_parent_trees(trees,
1089
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1091
@needs_tree_write_lock
1092
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1093
"""Set the parents of the working tree.
1095
:param parents_list: A list of (revision_id, tree) tuples.
1096
If tree is None, then that element is treated as an unreachable
1097
parent tree - i.e. a ghost.
1099
dirstate = self.current_dirstate()
1100
if len(parents_list) > 0:
1101
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1102
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1106
parent_ids = [rev_id for rev_id, tree in parents_list]
1107
graph = self.branch.repository.get_graph()
1108
heads = graph.heads(parent_ids)
1109
accepted_revisions = set()
1111
# convert absent trees to the null tree, which we convert back to
1112
# missing on access.
1113
for rev_id, tree in parents_list:
1114
if len(accepted_revisions) > 0:
1115
# we always accept the first tree
1116
if rev_id in accepted_revisions or rev_id not in heads:
1117
# We have already included either this tree, or its
1118
# descendent, so we skip it.
1120
_mod_revision.check_not_reserved_id(rev_id)
1121
if tree is not None:
1122
real_trees.append((rev_id, tree))
1124
real_trees.append((rev_id,
1125
self.branch.repository.revision_tree(
1126
_mod_revision.NULL_REVISION)))
1127
ghosts.append(rev_id)
1128
accepted_revisions.add(rev_id)
1129
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1130
self._make_dirty(reset_inventory=False)
1132
def _set_root_id(self, file_id):
1133
"""See WorkingTree.set_root_id."""
1134
state = self.current_dirstate()
1135
state.set_path_id('', file_id)
1136
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1137
self._make_dirty(reset_inventory=True)
1139
def _sha_from_stat(self, path, stat_result):
1140
"""Get a sha digest from the tree's stat cache.
1142
The default implementation assumes no stat cache is present.
1144
:param path: The path.
1145
:param stat_result: The stat result being looked up.
1147
return self.current_dirstate().sha1_from_stat(path, stat_result)
1150
def supports_tree_reference(self):
1151
return self._repo_supports_tree_reference
1154
"""Unlock in format 4 trees needs to write the entire dirstate."""
1155
# do non-implementation specific cleanup
1158
if self._control_files._lock_count == 1:
1159
# eventually we should do signature checking during read locks for
1161
if self._control_files._lock_mode == 'w':
1164
if self._dirstate is not None:
1165
# This is a no-op if there are no modifications.
1166
self._dirstate.save()
1167
self._dirstate.unlock()
1168
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1169
# point. Instead, it could check if the header has been
1170
# modified when it is locked, and if not, it can hang on to
1171
# the data it has in memory.
1172
self._dirstate = None
1173
self._inventory = None
1174
# reverse order of locking.
1176
return self._control_files.unlock()
1178
self.branch.unlock()
1180
@needs_tree_write_lock
1181
def unversion(self, file_ids):
1182
"""Remove the file ids in file_ids from the current versioned set.
1184
When a file_id is unversioned, all of its children are automatically
1187
:param file_ids: The file ids to stop versioning.
1188
:raises: NoSuchId if any fileid is not currently versioned.
1192
state = self.current_dirstate()
1193
state._read_dirblocks_if_needed()
1194
ids_to_unversion = set(file_ids)
1195
paths_to_unversion = set()
1197
# check if the root is to be unversioned, if so, assert for now.
1198
# walk the state marking unversioned things as absent.
1199
# if there are any un-unversioned ids at the end, raise
1200
for key, details in state._dirblocks[0][1]:
1201
if (details[0][0] not in ('a', 'r') and # absent or relocated
1202
key[2] in ids_to_unversion):
1203
# I haven't written the code to unversion / yet - it should be
1205
raise errors.BzrError('Unversioning the / is not currently supported')
1207
while block_index < len(state._dirblocks):
1208
# process one directory at a time.
1209
block = state._dirblocks[block_index]
1210
# first check: is the path one to remove - it or its children
1211
delete_block = False
1212
for path in paths_to_unversion:
1213
if (block[0].startswith(path) and
1214
(len(block[0]) == len(path) or
1215
block[0][len(path)] == '/')):
1216
# this entire block should be deleted - its the block for a
1217
# path to unversion; or the child of one
1220
# TODO: trim paths_to_unversion as we pass by paths
1222
# this block is to be deleted: process it.
1223
# TODO: we can special case the no-parents case and
1224
# just forget the whole block.
1226
while entry_index < len(block[1]):
1227
# Mark this file id as having been removed
1228
entry = block[1][entry_index]
1229
ids_to_unversion.discard(entry[0][2])
1230
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1232
or not state._make_absent(entry)):
1234
# go to the next block. (At the moment we dont delete empty
1239
while entry_index < len(block[1]):
1240
entry = block[1][entry_index]
1241
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1242
# ^ some parent row.
1243
entry[0][2] not in ids_to_unversion):
1244
# ^ not an id to unversion
1247
if entry[1][0][0] == 'd':
1248
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1249
if not state._make_absent(entry):
1251
# we have unversioned this id
1252
ids_to_unversion.remove(entry[0][2])
1254
if ids_to_unversion:
1255
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1256
self._make_dirty(reset_inventory=False)
1257
# have to change the legacy inventory too.
1258
if self._inventory is not None:
1259
for file_id in file_ids:
1260
self._inventory.remove_recursive_id(file_id)
1262
@needs_tree_write_lock
1263
def rename_one(self, from_rel, to_rel, after=False):
1264
"""See WorkingTree.rename_one"""
1266
WorkingTree.rename_one(self, from_rel, to_rel, after)
1268
@needs_tree_write_lock
1269
def apply_inventory_delta(self, changes):
1270
"""See MutableTree.apply_inventory_delta"""
1271
state = self.current_dirstate()
1272
state.update_by_delta(changes)
1273
self._make_dirty(reset_inventory=True)
1275
def update_basis_by_delta(self, new_revid, delta):
1276
"""See MutableTree.update_basis_by_delta."""
1277
if self.last_revision() == new_revid:
1278
raise AssertionError()
1279
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1282
def _validate(self):
1283
self._dirstate._validate()
1285
@needs_tree_write_lock
1286
def _write_inventory(self, inv):
1287
"""Write inventory as the current inventory."""
1289
raise AssertionError("attempting to write an inventory when the "
1290
"dirstate is dirty will lose pending changes")
1291
self.current_dirstate().set_state_from_inventory(inv)
1292
self._make_dirty(reset_inventory=False)
1293
if self._inventory is not None:
1294
self._inventory = inv
1298
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1300
def __init__(self, tree):
1303
def sha1(self, abspath):
1304
"""See dirstate.SHA1Provider.sha1()."""
1305
filters = self.tree._content_filter_stack(
1306
self.tree.relpath(osutils.safe_unicode(abspath)))
1307
return internal_size_sha_file_byname(abspath, filters)[1]
1309
def stat_and_sha1(self, abspath):
1310
"""See dirstate.SHA1Provider.stat_and_sha1()."""
1311
filters = self.tree._content_filter_stack(
1312
self.tree.relpath(osutils.safe_unicode(abspath)))
1313
file_obj = file(abspath, 'rb', 65000)
1315
statvalue = os.fstat(file_obj.fileno())
1317
file_obj = filtered_input_file(file_obj, filters)
1318
sha1 = osutils.size_sha_file(file_obj)[1]
1321
return statvalue, sha1
1324
class WorkingTree4(DirStateWorkingTree):
1325
"""This is the Format 4 working tree.
1327
This differs from WorkingTree3 by:
1328
- Having a consolidated internal dirstate, stored in a
1329
randomly-accessible sorted file on disk.
1330
- Not having a regular inventory attribute. One can be synthesized
1331
on demand but this is expensive and should be avoided.
1333
This is new in bzr 0.15.
1337
class WorkingTree5(DirStateWorkingTree):
1338
"""This is the Format 5 working tree.
1340
This differs from WorkingTree4 by:
1341
- Supporting content filtering.
1343
This is new in bzr 1.11.
1347
class WorkingTree6(DirStateWorkingTree):
1348
"""This is the Format 6 working tree.
1350
This differs from WorkingTree5 by:
1351
- Supporting a current view that may mask the set of files in a tree
1352
impacted by most user operations.
1354
This is new in bzr 1.14.
1357
def _make_views(self):
1358
return views.PathBasedViews(self)
1361
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1362
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1363
accelerator_tree=None, hardlink=False):
1364
"""See WorkingTreeFormat.initialize().
1366
:param revision_id: allows creating a working tree at a different
1367
revision than the branch is at.
1368
:param accelerator_tree: A tree which can be used for retrieving file
1369
contents more quickly than the revision tree, i.e. a workingtree.
1370
The revision tree will be used for cases where accelerator_tree's
1371
content is different.
1372
:param hardlink: If true, hard-link files from accelerator_tree,
1375
These trees get an initial random root id, if their repository supports
1376
rich root data, TREE_ROOT otherwise.
1378
if not isinstance(a_bzrdir.transport, LocalTransport):
1379
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1380
transport = a_bzrdir.get_workingtree_transport(self)
1381
control_files = self._open_control_files(a_bzrdir)
1382
control_files.create_lock()
1383
control_files.lock_write()
1384
transport.put_bytes('format', self.get_format_string(),
1385
mode=a_bzrdir._get_file_mode())
1386
if from_branch is not None:
1387
branch = from_branch
1389
branch = a_bzrdir.open_branch()
1390
if revision_id is None:
1391
revision_id = branch.last_revision()
1392
local_path = transport.local_abspath('dirstate')
1393
# write out new dirstate (must exist when we create the tree)
1394
state = dirstate.DirState.initialize(local_path)
1397
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1401
_control_files=control_files)
1403
wt.lock_tree_write()
1405
self._init_custom_control_files(wt)
1406
if revision_id in (None, NULL_REVISION):
1407
if branch.repository.supports_rich_root():
1408
wt._set_root_id(generate_ids.gen_root_id())
1410
wt._set_root_id(ROOT_ID)
1413
# frequently, we will get here due to branching. The accelerator
1414
# tree will be the tree from the branch, so the desired basis
1415
# tree will often be a parent of the accelerator tree.
1416
if accelerator_tree is not None:
1418
basis = accelerator_tree.revision_tree(revision_id)
1419
except errors.NoSuchRevision:
1422
basis = branch.repository.revision_tree(revision_id)
1423
if revision_id == NULL_REVISION:
1426
parents_list = [(revision_id, basis)]
1429
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1431
# if the basis has a root id we have to use that; otherwise we
1432
# use a new random one
1433
basis_root_id = basis.get_root_id()
1434
if basis_root_id is not None:
1435
wt._set_root_id(basis_root_id)
1437
# delta_from_tree is safe even for DirStateRevisionTrees,
1438
# because wt4.apply_inventory_delta does not mutate the input
1439
# inventory entries.
1440
transform.build_tree(basis, wt, accelerator_tree,
1441
hardlink=hardlink, delta_from_tree=True)
1445
control_files.unlock()
1449
def _init_custom_control_files(self, wt):
1450
"""Subclasses with custom control files should override this method.
1452
The working tree and control files are locked for writing when this
1455
:param wt: the WorkingTree object
1458
def _open(self, a_bzrdir, control_files):
1459
"""Open the tree itself.
1461
:param a_bzrdir: the dir for the tree.
1462
:param control_files: the control files for the tree.
1464
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1465
branch=a_bzrdir.open_branch(),
1468
_control_files=control_files)
1470
def __get_matchingbzrdir(self):
1471
return self._get_matchingbzrdir()
1473
def _get_matchingbzrdir(self):
1474
"""Overrideable method to get a bzrdir for testing."""
1475
# please test against something that will let us do tree references
1476
return bzrdir.format_registry.make_bzrdir(
1477
'dirstate-with-subtree')
1479
_matchingbzrdir = property(__get_matchingbzrdir)
1482
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1483
"""The first consolidated dirstate working tree format.
1486
- exists within a metadir controlling .bzr
1487
- includes an explicit version marker for the workingtree control
1488
files, separate from the BzrDir format
1489
- modifies the hash cache format
1490
- is new in bzr 0.15
1491
- uses a LockDir to guard access to it.
1494
upgrade_recommended = False
1496
_tree_class = WorkingTree4
1498
def get_format_string(self):
1499
"""See WorkingTreeFormat.get_format_string()."""
1500
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1502
def get_format_description(self):
1503
"""See WorkingTreeFormat.get_format_description()."""
1504
return "Working tree format 4"
1507
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1508
"""WorkingTree format supporting content filtering.
1511
upgrade_recommended = False
1513
_tree_class = WorkingTree5
1515
def get_format_string(self):
1516
"""See WorkingTreeFormat.get_format_string()."""
1517
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1519
def get_format_description(self):
1520
"""See WorkingTreeFormat.get_format_description()."""
1521
return "Working tree format 5"
1523
def supports_content_filtering(self):
1527
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1528
"""WorkingTree format supporting views.
1531
upgrade_recommended = False
1533
_tree_class = WorkingTree6
1535
def get_format_string(self):
1536
"""See WorkingTreeFormat.get_format_string()."""
1537
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1539
def get_format_description(self):
1540
"""See WorkingTreeFormat.get_format_description()."""
1541
return "Working tree format 6"
1543
def _init_custom_control_files(self, wt):
1544
"""Subclasses with custom control files should override this method."""
1545
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1547
def supports_content_filtering(self):
1550
def supports_views(self):
1554
class DirStateRevisionTree(Tree):
1555
"""A revision tree pulling the inventory from a dirstate."""
1557
def __init__(self, dirstate, revision_id, repository):
1558
self._dirstate = dirstate
1559
self._revision_id = revision_id
1560
self._repository = repository
1561
self._inventory = None
1563
self._dirstate_locked = False
1564
self._repo_supports_tree_reference = getattr(
1565
repository._format, "supports_tree_reference",
1569
return "<%s of %s in %s>" % \
1570
(self.__class__.__name__, self._revision_id, self._dirstate)
1572
def annotate_iter(self, file_id,
1573
default_revision=_mod_revision.CURRENT_REVISION):
1574
"""See Tree.annotate_iter"""
1575
text_key = (file_id, self.inventory[file_id].revision)
1576
annotations = self._repository.texts.annotate(text_key)
1577
return [(key[-1], line) for (key, line) in annotations]
1579
def _get_ancestors(self, default_revision):
1580
return set(self._repository.get_ancestry(self._revision_id,
1582
def _comparison_data(self, entry, path):
1583
"""See Tree._comparison_data."""
1585
return None, False, None
1586
# trust the entry as RevisionTree does, but this may not be
1587
# sensible: the entry might not have come from us?
1588
return entry.kind, entry.executable, None
1590
def _file_size(self, entry, stat_value):
1591
return entry.text_size
1593
def filter_unversioned_files(self, paths):
1594
"""Filter out paths that are not versioned.
1596
:return: set of paths.
1598
pred = self.has_filename
1599
return set((p for p in paths if not pred(p)))
1601
def get_root_id(self):
1602
return self.path2id('')
1604
def id2path(self, file_id):
1605
"Convert a file-id to a path."
1606
entry = self._get_entry(file_id=file_id)
1607
if entry == (None, None):
1608
raise errors.NoSuchId(tree=self, file_id=file_id)
1609
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1610
return path_utf8.decode('utf8')
1612
def iter_references(self):
1613
if not self._repo_supports_tree_reference:
1614
# When the repo doesn't support references, we will have nothing to
1617
# Otherwise, fall back to the default implementation
1618
return super(DirStateRevisionTree, self).iter_references()
1620
def _get_parent_index(self):
1621
"""Return the index in the dirstate referenced by this tree."""
1622
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1624
def _get_entry(self, file_id=None, path=None):
1625
"""Get the dirstate row for file_id or path.
1627
If either file_id or path is supplied, it is used as the key to lookup.
1628
If both are supplied, the fastest lookup is used, and an error is
1629
raised if they do not both point at the same row.
1631
:param file_id: An optional unicode file_id to be looked up.
1632
:param path: An optional unicode path to be looked up.
1633
:return: The dirstate row tuple for path/file_id, or (None, None)
1635
if file_id is None and path is None:
1636
raise errors.BzrError('must supply file_id or path')
1637
if path is not None:
1638
path = path.encode('utf8')
1639
parent_index = self._get_parent_index()
1640
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1642
def _generate_inventory(self):
1643
"""Create and set self.inventory from the dirstate object.
1645
(So this is only called the first time the inventory is requested for
1646
this tree; it then remains in memory until it's out of date.)
1648
This is relatively expensive: we have to walk the entire dirstate.
1650
if not self._locked:
1651
raise AssertionError(
1652
'cannot generate inventory of an unlocked '
1653
'dirstate revision tree')
1654
# separate call for profiling - makes it clear where the costs are.
1655
self._dirstate._read_dirblocks_if_needed()
1656
if self._revision_id not in self._dirstate.get_parent_ids():
1657
raise AssertionError(
1658
'parent %s has disappeared from %s' % (
1659
self._revision_id, self._dirstate.get_parent_ids()))
1660
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1661
# This is identical now to the WorkingTree _generate_inventory except
1662
# for the tree index use.
1663
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1664
current_id = root_key[2]
1665
if current_entry[parent_index][0] != 'd':
1666
raise AssertionError()
1667
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1668
inv.root.revision = current_entry[parent_index][4]
1669
# Turn some things into local variables
1670
minikind_to_kind = dirstate.DirState._minikind_to_kind
1671
factory = entry_factory
1672
utf8_decode = cache_utf8._utf8_decode
1673
inv_byid = inv._byid
1674
# we could do this straight out of the dirstate; it might be fast
1675
# and should be profiled - RBC 20070216
1676
parent_ies = {'' : inv.root}
1677
for block in self._dirstate._dirblocks[1:]: #skip root
1680
parent_ie = parent_ies[dirname]
1682
# all the paths in this block are not versioned in this tree
1684
for key, entry in block[1]:
1685
minikind, fingerprint, size, executable, revid = entry[parent_index]
1686
if minikind in ('a', 'r'): # absent, relocated
1690
name_unicode = utf8_decode(name)[0]
1692
kind = minikind_to_kind[minikind]
1693
inv_entry = factory[kind](file_id, name_unicode,
1695
inv_entry.revision = revid
1697
inv_entry.executable = executable
1698
inv_entry.text_size = size
1699
inv_entry.text_sha1 = fingerprint
1700
elif kind == 'directory':
1701
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1702
elif kind == 'symlink':
1703
inv_entry.executable = False
1704
inv_entry.text_size = None
1705
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1706
elif kind == 'tree-reference':
1707
inv_entry.reference_revision = fingerprint or None
1709
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1711
# These checks cost us around 40ms on a 55k entry tree
1712
if file_id in inv_byid:
1713
raise AssertionError('file_id %s already in'
1714
' inventory as %s' % (file_id, inv_byid[file_id]))
1715
if name_unicode in parent_ie.children:
1716
raise AssertionError('name %r already in parent'
1718
inv_byid[file_id] = inv_entry
1719
parent_ie.children[name_unicode] = inv_entry
1720
self._inventory = inv
1722
def get_file_mtime(self, file_id, path=None):
1723
"""Return the modification time for this record.
1725
We return the timestamp of the last-changed revision.
1727
# Make sure the file exists
1728
entry = self._get_entry(file_id, path=path)
1729
if entry == (None, None): # do we raise?
1731
parent_index = self._get_parent_index()
1732
last_changed_revision = entry[1][parent_index][4]
1733
return self._repository.get_revision(last_changed_revision).timestamp
1735
def get_file_sha1(self, file_id, path=None, stat_value=None):
1736
entry = self._get_entry(file_id=file_id, path=path)
1737
parent_index = self._get_parent_index()
1738
parent_details = entry[1][parent_index]
1739
if parent_details[0] == 'f':
1740
return parent_details[1]
1743
def get_file(self, file_id, path=None):
1744
return StringIO(self.get_file_text(file_id))
1746
def get_file_size(self, file_id):
1747
"""See Tree.get_file_size"""
1748
return self.inventory[file_id].text_size
1750
def get_file_text(self, file_id, path=None):
1751
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1752
return ''.join(content)
1754
def get_reference_revision(self, file_id, path=None):
1755
return self.inventory[file_id].reference_revision
1757
def iter_files_bytes(self, desired_files):
1758
"""See Tree.iter_files_bytes.
1760
This version is implemented on top of Repository.iter_files_bytes"""
1761
parent_index = self._get_parent_index()
1762
repo_desired_files = []
1763
for file_id, identifier in desired_files:
1764
entry = self._get_entry(file_id)
1765
if entry == (None, None):
1766
raise errors.NoSuchId(self, file_id)
1767
repo_desired_files.append((file_id, entry[1][parent_index][4],
1769
return self._repository.iter_files_bytes(repo_desired_files)
1771
def get_symlink_target(self, file_id):
1772
entry = self._get_entry(file_id=file_id)
1773
parent_index = self._get_parent_index()
1774
if entry[1][parent_index][0] != 'l':
1777
target = entry[1][parent_index][1]
1778
target = target.decode('utf8')
1781
def get_revision_id(self):
1782
"""Return the revision id for this tree."""
1783
return self._revision_id
1785
def _get_inventory(self):
1786
if self._inventory is not None:
1787
return self._inventory
1788
self._must_be_locked()
1789
self._generate_inventory()
1790
return self._inventory
1792
inventory = property(_get_inventory,
1793
doc="Inventory of this Tree")
1795
def get_parent_ids(self):
1796
"""The parents of a tree in the dirstate are not cached."""
1797
return self._repository.get_revision(self._revision_id).parent_ids
1799
def has_filename(self, filename):
1800
return bool(self.path2id(filename))
1802
def kind(self, file_id):
1803
entry = self._get_entry(file_id=file_id)[1]
1805
raise errors.NoSuchId(tree=self, file_id=file_id)
1806
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1808
def stored_kind(self, file_id):
1809
"""See Tree.stored_kind"""
1810
return self.kind(file_id)
1812
def path_content_summary(self, path):
1813
"""See Tree.path_content_summary."""
1814
id = self.inventory.path2id(path)
1816
return ('missing', None, None, None)
1817
entry = self._inventory[id]
1820
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1821
elif kind == 'symlink':
1822
return (kind, None, None, entry.symlink_target)
1824
return (kind, None, None, None)
1826
def is_executable(self, file_id, path=None):
1827
ie = self.inventory[file_id]
1828
if ie.kind != "file":
1830
return ie.executable
1832
def list_files(self, include_root=False):
1833
# We use a standard implementation, because DirStateRevisionTree is
1834
# dealing with one of the parents of the current state
1835
inv = self._get_inventory()
1836
entries = inv.iter_entries()
1837
if self.inventory.root is not None and not include_root:
1839
for path, entry in entries:
1840
yield path, 'V', entry.kind, entry.file_id, entry
1842
def lock_read(self):
1843
"""Lock the tree for a set of operations."""
1844
if not self._locked:
1845
self._repository.lock_read()
1846
if self._dirstate._lock_token is None:
1847
self._dirstate.lock_read()
1848
self._dirstate_locked = True
1851
def _must_be_locked(self):
1852
if not self._locked:
1853
raise errors.ObjectNotLocked(self)
1856
def path2id(self, path):
1857
"""Return the id for path in this tree."""
1858
# lookup by path: faster than splitting and walking the ivnentory.
1859
entry = self._get_entry(path=path)
1860
if entry == (None, None):
1865
"""Unlock, freeing any cache memory used during the lock."""
1866
# outside of a lock, the inventory is suspect: release it.
1868
if not self._locked:
1869
self._inventory = None
1871
if self._dirstate_locked:
1872
self._dirstate.unlock()
1873
self._dirstate_locked = False
1874
self._repository.unlock()
1877
def supports_tree_reference(self):
1878
return self._repo_supports_tree_reference
1880
def walkdirs(self, prefix=""):
1881
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
1882
# implementation based on an inventory.
1883
# This should be cleaned up to use the much faster Dirstate code
1884
# So for now, we just build up the parent inventory, and extract
1885
# it the same way RevisionTree does.
1886
_directory = 'directory'
1887
inv = self._get_inventory()
1888
top_id = inv.path2id(prefix)
1892
pending = [(prefix, top_id)]
1895
relpath, file_id = pending.pop()
1896
# 0 - relpath, 1- file-id
1898
relroot = relpath + '/'
1901
# FIXME: stash the node in pending
1902
entry = inv[file_id]
1903
for name, child in entry.sorted_children():
1904
toppath = relroot + name
1905
dirblock.append((toppath, name, child.kind, None,
1906
child.file_id, child.kind
1908
yield (relpath, entry.file_id), dirblock
1909
# push the user specified dirs from dirblock
1910
for dir in reversed(dirblock):
1911
if dir[2] == _directory:
1912
pending.append((dir[0], dir[4]))
1915
class InterDirStateTree(InterTree):
1916
"""Fast path optimiser for changes_from with dirstate trees.
1918
This is used only when both trees are in the dirstate working file, and
1919
the source is any parent within the dirstate, and the destination is
1920
the current working tree of the same dirstate.
1922
# this could be generalized to allow comparisons between any trees in the
1923
# dirstate, and possibly between trees stored in different dirstates.
1925
def __init__(self, source, target):
1926
super(InterDirStateTree, self).__init__(source, target)
1927
if not InterDirStateTree.is_compatible(source, target):
1928
raise Exception, "invalid source %r and target %r" % (source, target)
1931
def make_source_parent_tree(source, target):
1932
"""Change the source tree into a parent of the target."""
1933
revid = source.commit('record tree')
1934
target.branch.repository.fetch(source.branch.repository, revid)
1935
target.set_parent_ids([revid])
1936
return target.basis_tree(), target
1939
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
1940
result = klass.make_source_parent_tree(source, target)
1941
result[1]._iter_changes = dirstate.ProcessEntryPython
1945
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1946
from bzrlib.tests.test__dirstate_helpers import \
1947
CompiledDirstateHelpersFeature
1948
if not CompiledDirstateHelpersFeature.available():
1949
from bzrlib.tests import UnavailableFeature
1950
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1951
from bzrlib._dirstate_helpers_c import ProcessEntryC
1952
result = klass.make_source_parent_tree(source, target)
1953
result[1]._iter_changes = ProcessEntryC
1956
_matching_from_tree_format = WorkingTreeFormat4()
1957
_matching_to_tree_format = WorkingTreeFormat4()
1960
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
1961
# This method shouldn't be called, because we have python and C
1962
# specific flavours.
1963
raise NotImplementedError
1965
def iter_changes(self, include_unchanged=False,
1966
specific_files=None, pb=None, extra_trees=[],
1967
require_versioned=True, want_unversioned=False):
1968
"""Return the changes from source to target.
1970
:return: An iterator that yields tuples. See InterTree.iter_changes
1972
:param specific_files: An optional list of file paths to restrict the
1973
comparison to. When mapping filenames to ids, all matches in all
1974
trees (including optional extra_trees) are used, and all children of
1975
matched directories are included.
1976
:param include_unchanged: An optional boolean requesting the inclusion of
1977
unchanged entries in the result.
1978
:param extra_trees: An optional list of additional trees to use when
1979
mapping the contents of specific_files (paths) to file_ids.
1980
:param require_versioned: If True, all files in specific_files must be
1981
versioned in one of source, target, extra_trees or
1982
PathsNotVersionedError is raised.
1983
:param want_unversioned: Should unversioned files be returned in the
1984
output. An unversioned file is defined as one with (False, False)
1985
for the versioned pair.
1987
# NB: show_status depends on being able to pass in non-versioned files
1988
# and report them as unknown
1989
# TODO: handle extra trees in the dirstate.
1990
if (extra_trees or specific_files == []):
1991
# we can't fast-path these cases (yet)
1992
return super(InterDirStateTree, self).iter_changes(
1993
include_unchanged, specific_files, pb, extra_trees,
1994
require_versioned, want_unversioned=want_unversioned)
1995
parent_ids = self.target.get_parent_ids()
1996
if not (self.source._revision_id in parent_ids
1997
or self.source._revision_id == NULL_REVISION):
1998
raise AssertionError(
1999
"revision {%s} is not stored in {%s}, but %s "
2000
"can only be used for trees stored in the dirstate"
2001
% (self.source._revision_id, self.target, self.iter_changes))
2003
if self.source._revision_id == NULL_REVISION:
2005
indices = (target_index,)
2007
if not (self.source._revision_id in parent_ids):
2008
raise AssertionError(
2009
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2010
self.source._revision_id, parent_ids))
2011
source_index = 1 + parent_ids.index(self.source._revision_id)
2012
indices = (source_index, target_index)
2013
# -- make all specific_files utf8 --
2015
specific_files_utf8 = set()
2016
for path in specific_files:
2017
# Note, if there are many specific files, using cache_utf8
2018
# would be good here.
2019
specific_files_utf8.add(path.encode('utf8'))
2020
specific_files = specific_files_utf8
2022
specific_files = set([''])
2023
# -- specific_files is now a utf8 path set --
2024
search_specific_files = set()
2025
# -- get the state object and prepare it.
2026
state = self.target.current_dirstate()
2027
state._read_dirblocks_if_needed()
2028
if require_versioned:
2029
# -- check all supplied paths are versioned in a search tree. --
2030
all_versioned = True
2031
for path in specific_files:
2032
path_entries = state._entries_for_path(path)
2033
if not path_entries:
2034
# this specified path is not present at all: error
2035
all_versioned = False
2037
found_versioned = False
2038
# for each id at this path
2039
for entry in path_entries:
2041
for index in indices:
2042
if entry[1][index][0] != 'a': # absent
2043
found_versioned = True
2044
# all good: found a versioned cell
2046
if not found_versioned:
2047
# none of the indexes was not 'absent' at all ids for this
2049
all_versioned = False
2051
if not all_versioned:
2052
raise errors.PathsNotVersionedError(specific_files)
2053
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2054
for path in specific_files:
2055
other_specific_files = specific_files.difference(set([path]))
2056
if not osutils.is_inside_any(other_specific_files, path):
2057
# this is a top level path, we must check it.
2058
search_specific_files.add(path)
2060
use_filesystem_for_exec = (sys.platform != 'win32')
2061
iter_changes = self.target._iter_changes(include_unchanged,
2062
use_filesystem_for_exec, search_specific_files, state,
2063
source_index, target_index, want_unversioned, self.target)
2064
return iter_changes.iter_changes()
2067
def is_compatible(source, target):
2068
# the target must be a dirstate working tree
2069
if not isinstance(target, DirStateWorkingTree):
2071
# the source must be a revtree or dirstate rev tree.
2072
if not isinstance(source,
2073
(revisiontree.RevisionTree, DirStateRevisionTree)):
2075
# the source revid must be in the target dirstate
2076
if not (source._revision_id == NULL_REVISION or
2077
source._revision_id in target.get_parent_ids()):
2078
# TODO: what about ghosts? it may well need to
2079
# check for them explicitly.
2083
InterTree.register_optimiser(InterDirStateTree)
2086
class Converter3to4(object):
2087
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2090
self.target_format = WorkingTreeFormat4()
2092
def convert(self, tree):
2093
# lock the control files not the tree, so that we dont get tree
2094
# on-unlock behaviours, and so that noone else diddles with the
2095
# tree during upgrade.
2096
tree._control_files.lock_write()
2098
tree.read_working_inventory()
2099
self.create_dirstate_data(tree)
2100
self.update_format(tree)
2101
self.remove_xml_files(tree)
2103
tree._control_files.unlock()
2105
def create_dirstate_data(self, tree):
2106
"""Create the dirstate based data for tree."""
2107
local_path = tree.bzrdir.get_workingtree_transport(None
2108
).local_abspath('dirstate')
2109
state = dirstate.DirState.from_tree(tree, local_path)
2113
def remove_xml_files(self, tree):
2114
"""Remove the oldformat 3 data."""
2115
transport = tree.bzrdir.get_workingtree_transport(None)
2116
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2117
'pending-merges', 'stat-cache']:
2119
transport.delete(path)
2120
except errors.NoSuchFile:
2121
# some files are optional - just deal.
2124
def update_format(self, tree):
2125
"""Change the format marker."""
2126
tree._transport.put_bytes('format',
2127
self.target_format.get_format_string(),
2128
mode=tree.bzrdir._get_file_mode())
2131
class Converter4to5(object):
2132
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2135
self.target_format = WorkingTreeFormat5()
2137
def convert(self, tree):
2138
# lock the control files not the tree, so that we don't get tree
2139
# on-unlock behaviours, and so that no-one else diddles with the
2140
# tree during upgrade.
2141
tree._control_files.lock_write()
2143
self.update_format(tree)
2145
tree._control_files.unlock()
2147
def update_format(self, tree):
2148
"""Change the format marker."""
2149
tree._transport.put_bytes('format',
2150
self.target_format.get_format_string(),
2151
mode=tree.bzrdir._get_file_mode())
2154
class Converter4or5to6(object):
2155
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2158
self.target_format = WorkingTreeFormat6()
2160
def convert(self, tree):
2161
# lock the control files not the tree, so that we don't get tree
2162
# on-unlock behaviours, and so that no-one else diddles with the
2163
# tree during upgrade.
2164
tree._control_files.lock_write()
2166
self.init_custom_control_files(tree)
2167
self.update_format(tree)
2169
tree._control_files.unlock()
2171
def init_custom_control_files(self, tree):
2172
"""Initialize custom control files."""
2173
tree._transport.put_bytes('views', '',
2174
mode=tree.bzrdir._get_file_mode())
2176
def update_format(self, tree):
2177
"""Change the format marker."""
2178
tree._transport.put_bytes('format',
2179
self.target_format.get_format_string(),
2180
mode=tree.bzrdir._get_file_mode())