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
"""Return the sha1 of a file given its absolute path."""
1305
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1306
return internal_size_sha_file_byname(abspath, filters)[1]
1308
def stat_and_sha1(self, abspath):
1309
"""Return the stat and sha1 of a file given its absolute path."""
1310
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1311
file_obj = file(abspath, 'rb', 65000)
1313
statvalue = os.fstat(file_obj.fileno())
1315
file_obj = filtered_input_file(file_obj, filters)
1316
sha1 = osutils.size_sha_file(file_obj)[1]
1319
return statvalue, sha1
1322
class WorkingTree4(DirStateWorkingTree):
1323
"""This is the Format 4 working tree.
1325
This differs from WorkingTree3 by:
1326
- Having a consolidated internal dirstate, stored in a
1327
randomly-accessible sorted file on disk.
1328
- Not having a regular inventory attribute. One can be synthesized
1329
on demand but this is expensive and should be avoided.
1331
This is new in bzr 0.15.
1335
class WorkingTree5(DirStateWorkingTree):
1336
"""This is the Format 5 working tree.
1338
This differs from WorkingTree4 by:
1339
- Supporting content filtering.
1341
This is new in bzr 1.11.
1345
class WorkingTree6(DirStateWorkingTree):
1346
"""This is the Format 6 working tree.
1348
This differs from WorkingTree5 by:
1349
- Supporting a current view that may mask the set of files in a tree
1350
impacted by most user operations.
1352
This is new in bzr 1.14.
1355
def _make_views(self):
1356
return views.PathBasedViews(self)
1359
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1360
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1361
accelerator_tree=None, hardlink=False):
1362
"""See WorkingTreeFormat.initialize().
1364
:param revision_id: allows creating a working tree at a different
1365
revision than the branch is at.
1366
:param accelerator_tree: A tree which can be used for retrieving file
1367
contents more quickly than the revision tree, i.e. a workingtree.
1368
The revision tree will be used for cases where accelerator_tree's
1369
content is different.
1370
:param hardlink: If true, hard-link files from accelerator_tree,
1373
These trees get an initial random root id, if their repository supports
1374
rich root data, TREE_ROOT otherwise.
1376
if not isinstance(a_bzrdir.transport, LocalTransport):
1377
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1378
transport = a_bzrdir.get_workingtree_transport(self)
1379
control_files = self._open_control_files(a_bzrdir)
1380
control_files.create_lock()
1381
control_files.lock_write()
1382
transport.put_bytes('format', self.get_format_string(),
1383
mode=a_bzrdir._get_file_mode())
1384
if from_branch is not None:
1385
branch = from_branch
1387
branch = a_bzrdir.open_branch()
1388
if revision_id is None:
1389
revision_id = branch.last_revision()
1390
local_path = transport.local_abspath('dirstate')
1391
# write out new dirstate (must exist when we create the tree)
1392
state = dirstate.DirState.initialize(local_path)
1395
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1399
_control_files=control_files)
1401
wt.lock_tree_write()
1403
self._init_custom_control_files(wt)
1404
if revision_id in (None, NULL_REVISION):
1405
if branch.repository.supports_rich_root():
1406
wt._set_root_id(generate_ids.gen_root_id())
1408
wt._set_root_id(ROOT_ID)
1411
# frequently, we will get here due to branching. The accelerator
1412
# tree will be the tree from the branch, so the desired basis
1413
# tree will often be a parent of the accelerator tree.
1414
if accelerator_tree is not None:
1416
basis = accelerator_tree.revision_tree(revision_id)
1417
except errors.NoSuchRevision:
1420
basis = branch.repository.revision_tree(revision_id)
1421
if revision_id == NULL_REVISION:
1424
parents_list = [(revision_id, basis)]
1427
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1429
# if the basis has a root id we have to use that; otherwise we
1430
# use a new random one
1431
basis_root_id = basis.get_root_id()
1432
if basis_root_id is not None:
1433
wt._set_root_id(basis_root_id)
1435
# delta_from_tree is safe even for DirStateRevisionTrees,
1436
# because wt4.apply_inventory_delta does not mutate the input
1437
# inventory entries.
1438
transform.build_tree(basis, wt, accelerator_tree,
1439
hardlink=hardlink, delta_from_tree=True)
1443
control_files.unlock()
1447
def _init_custom_control_files(self, wt):
1448
"""Subclasses with custom control files should override this method.
1450
The working tree and control files are locked for writing when this
1453
:param wt: the WorkingTree object
1456
def _open(self, a_bzrdir, control_files):
1457
"""Open the tree itself.
1459
:param a_bzrdir: the dir for the tree.
1460
:param control_files: the control files for the tree.
1462
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1463
branch=a_bzrdir.open_branch(),
1466
_control_files=control_files)
1468
def __get_matchingbzrdir(self):
1469
# please test against something that will let us do tree references
1470
return bzrdir.format_registry.make_bzrdir(
1471
'dirstate-with-subtree')
1473
_matchingbzrdir = property(__get_matchingbzrdir)
1476
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1477
"""The first consolidated dirstate working tree format.
1480
- exists within a metadir controlling .bzr
1481
- includes an explicit version marker for the workingtree control
1482
files, separate from the BzrDir format
1483
- modifies the hash cache format
1484
- is new in bzr 0.15
1485
- uses a LockDir to guard access to it.
1488
upgrade_recommended = False
1490
_tree_class = WorkingTree4
1492
def get_format_string(self):
1493
"""See WorkingTreeFormat.get_format_string()."""
1494
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1496
def get_format_description(self):
1497
"""See WorkingTreeFormat.get_format_description()."""
1498
return "Working tree format 4"
1501
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1502
"""WorkingTree format supporting content filtering.
1505
upgrade_recommended = False
1507
_tree_class = WorkingTree5
1509
def get_format_string(self):
1510
"""See WorkingTreeFormat.get_format_string()."""
1511
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1513
def get_format_description(self):
1514
"""See WorkingTreeFormat.get_format_description()."""
1515
return "Working tree format 5"
1517
def supports_content_filtering(self):
1521
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1522
"""WorkingTree format supporting views.
1525
upgrade_recommended = False
1527
_tree_class = WorkingTree6
1529
def get_format_string(self):
1530
"""See WorkingTreeFormat.get_format_string()."""
1531
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1533
def get_format_description(self):
1534
"""See WorkingTreeFormat.get_format_description()."""
1535
return "Working tree format 6"
1537
def _init_custom_control_files(self, wt):
1538
"""Subclasses with custom control files should override this method."""
1539
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1541
def supports_content_filtering(self):
1544
def supports_views(self):
1548
class DirStateRevisionTree(Tree):
1549
"""A revision tree pulling the inventory from a dirstate."""
1551
def __init__(self, dirstate, revision_id, repository):
1552
self._dirstate = dirstate
1553
self._revision_id = revision_id
1554
self._repository = repository
1555
self._inventory = None
1557
self._dirstate_locked = False
1558
self._repo_supports_tree_reference = getattr(
1559
repository._format, "supports_tree_reference",
1563
return "<%s of %s in %s>" % \
1564
(self.__class__.__name__, self._revision_id, self._dirstate)
1566
def annotate_iter(self, file_id,
1567
default_revision=_mod_revision.CURRENT_REVISION):
1568
"""See Tree.annotate_iter"""
1569
text_key = (file_id, self.inventory[file_id].revision)
1570
annotations = self._repository.texts.annotate(text_key)
1571
return [(key[-1], line) for (key, line) in annotations]
1573
def _get_ancestors(self, default_revision):
1574
return set(self._repository.get_ancestry(self._revision_id,
1576
def _comparison_data(self, entry, path):
1577
"""See Tree._comparison_data."""
1579
return None, False, None
1580
# trust the entry as RevisionTree does, but this may not be
1581
# sensible: the entry might not have come from us?
1582
return entry.kind, entry.executable, None
1584
def _file_size(self, entry, stat_value):
1585
return entry.text_size
1587
def filter_unversioned_files(self, paths):
1588
"""Filter out paths that are not versioned.
1590
:return: set of paths.
1592
pred = self.has_filename
1593
return set((p for p in paths if not pred(p)))
1595
def get_root_id(self):
1596
return self.path2id('')
1598
def id2path(self, file_id):
1599
"Convert a file-id to a path."
1600
entry = self._get_entry(file_id=file_id)
1601
if entry == (None, None):
1602
raise errors.NoSuchId(tree=self, file_id=file_id)
1603
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1604
return path_utf8.decode('utf8')
1606
def iter_references(self):
1607
if not self._repo_supports_tree_reference:
1608
# When the repo doesn't support references, we will have nothing to
1611
# Otherwise, fall back to the default implementation
1612
return super(DirStateRevisionTree, self).iter_references()
1614
def _get_parent_index(self):
1615
"""Return the index in the dirstate referenced by this tree."""
1616
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1618
def _get_entry(self, file_id=None, path=None):
1619
"""Get the dirstate row for file_id or path.
1621
If either file_id or path is supplied, it is used as the key to lookup.
1622
If both are supplied, the fastest lookup is used, and an error is
1623
raised if they do not both point at the same row.
1625
:param file_id: An optional unicode file_id to be looked up.
1626
:param path: An optional unicode path to be looked up.
1627
:return: The dirstate row tuple for path/file_id, or (None, None)
1629
if file_id is None and path is None:
1630
raise errors.BzrError('must supply file_id or path')
1631
if path is not None:
1632
path = path.encode('utf8')
1633
parent_index = self._get_parent_index()
1634
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1636
def _generate_inventory(self):
1637
"""Create and set self.inventory from the dirstate object.
1639
(So this is only called the first time the inventory is requested for
1640
this tree; it then remains in memory until it's out of date.)
1642
This is relatively expensive: we have to walk the entire dirstate.
1644
if not self._locked:
1645
raise AssertionError(
1646
'cannot generate inventory of an unlocked '
1647
'dirstate revision tree')
1648
# separate call for profiling - makes it clear where the costs are.
1649
self._dirstate._read_dirblocks_if_needed()
1650
if self._revision_id not in self._dirstate.get_parent_ids():
1651
raise AssertionError(
1652
'parent %s has disappeared from %s' % (
1653
self._revision_id, self._dirstate.get_parent_ids()))
1654
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1655
# This is identical now to the WorkingTree _generate_inventory except
1656
# for the tree index use.
1657
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1658
current_id = root_key[2]
1659
if current_entry[parent_index][0] != 'd':
1660
raise AssertionError()
1661
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1662
inv.root.revision = current_entry[parent_index][4]
1663
# Turn some things into local variables
1664
minikind_to_kind = dirstate.DirState._minikind_to_kind
1665
factory = entry_factory
1666
utf8_decode = cache_utf8._utf8_decode
1667
inv_byid = inv._byid
1668
# we could do this straight out of the dirstate; it might be fast
1669
# and should be profiled - RBC 20070216
1670
parent_ies = {'' : inv.root}
1671
for block in self._dirstate._dirblocks[1:]: #skip root
1674
parent_ie = parent_ies[dirname]
1676
# all the paths in this block are not versioned in this tree
1678
for key, entry in block[1]:
1679
minikind, fingerprint, size, executable, revid = entry[parent_index]
1680
if minikind in ('a', 'r'): # absent, relocated
1684
name_unicode = utf8_decode(name)[0]
1686
kind = minikind_to_kind[minikind]
1687
inv_entry = factory[kind](file_id, name_unicode,
1689
inv_entry.revision = revid
1691
inv_entry.executable = executable
1692
inv_entry.text_size = size
1693
inv_entry.text_sha1 = fingerprint
1694
elif kind == 'directory':
1695
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1696
elif kind == 'symlink':
1697
inv_entry.executable = False
1698
inv_entry.text_size = None
1699
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1700
elif kind == 'tree-reference':
1701
inv_entry.reference_revision = fingerprint or None
1703
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1705
# These checks cost us around 40ms on a 55k entry tree
1706
if file_id in inv_byid:
1707
raise AssertionError('file_id %s already in'
1708
' inventory as %s' % (file_id, inv_byid[file_id]))
1709
if name_unicode in parent_ie.children:
1710
raise AssertionError('name %r already in parent'
1712
inv_byid[file_id] = inv_entry
1713
parent_ie.children[name_unicode] = inv_entry
1714
self._inventory = inv
1716
def get_file_mtime(self, file_id, path=None):
1717
"""Return the modification time for this record.
1719
We return the timestamp of the last-changed revision.
1721
# Make sure the file exists
1722
entry = self._get_entry(file_id, path=path)
1723
if entry == (None, None): # do we raise?
1725
parent_index = self._get_parent_index()
1726
last_changed_revision = entry[1][parent_index][4]
1727
return self._repository.get_revision(last_changed_revision).timestamp
1729
def get_file_sha1(self, file_id, path=None, stat_value=None):
1730
entry = self._get_entry(file_id=file_id, path=path)
1731
parent_index = self._get_parent_index()
1732
parent_details = entry[1][parent_index]
1733
if parent_details[0] == 'f':
1734
return parent_details[1]
1737
def get_file(self, file_id, path=None):
1738
return StringIO(self.get_file_text(file_id))
1740
def get_file_size(self, file_id):
1741
"""See Tree.get_file_size"""
1742
return self.inventory[file_id].text_size
1744
def get_file_text(self, file_id, path=None):
1745
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1746
return ''.join(content)
1748
def get_reference_revision(self, file_id, path=None):
1749
return self.inventory[file_id].reference_revision
1751
def iter_files_bytes(self, desired_files):
1752
"""See Tree.iter_files_bytes.
1754
This version is implemented on top of Repository.iter_files_bytes"""
1755
parent_index = self._get_parent_index()
1756
repo_desired_files = []
1757
for file_id, identifier in desired_files:
1758
entry = self._get_entry(file_id)
1759
if entry == (None, None):
1760
raise errors.NoSuchId(self, file_id)
1761
repo_desired_files.append((file_id, entry[1][parent_index][4],
1763
return self._repository.iter_files_bytes(repo_desired_files)
1765
def get_symlink_target(self, file_id):
1766
entry = self._get_entry(file_id=file_id)
1767
parent_index = self._get_parent_index()
1768
if entry[1][parent_index][0] != 'l':
1771
target = entry[1][parent_index][1]
1772
target = target.decode('utf8')
1775
def get_revision_id(self):
1776
"""Return the revision id for this tree."""
1777
return self._revision_id
1779
def _get_inventory(self):
1780
if self._inventory is not None:
1781
return self._inventory
1782
self._must_be_locked()
1783
self._generate_inventory()
1784
return self._inventory
1786
inventory = property(_get_inventory,
1787
doc="Inventory of this Tree")
1789
def get_parent_ids(self):
1790
"""The parents of a tree in the dirstate are not cached."""
1791
return self._repository.get_revision(self._revision_id).parent_ids
1793
def has_filename(self, filename):
1794
return bool(self.path2id(filename))
1796
def kind(self, file_id):
1797
entry = self._get_entry(file_id=file_id)[1]
1799
raise errors.NoSuchId(tree=self, file_id=file_id)
1800
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1802
def stored_kind(self, file_id):
1803
"""See Tree.stored_kind"""
1804
return self.kind(file_id)
1806
def path_content_summary(self, path):
1807
"""See Tree.path_content_summary."""
1808
id = self.inventory.path2id(path)
1810
return ('missing', None, None, None)
1811
entry = self._inventory[id]
1814
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1815
elif kind == 'symlink':
1816
return (kind, None, None, entry.symlink_target)
1818
return (kind, None, None, None)
1820
def is_executable(self, file_id, path=None):
1821
ie = self.inventory[file_id]
1822
if ie.kind != "file":
1824
return ie.executable
1826
def list_files(self, include_root=False):
1827
# We use a standard implementation, because DirStateRevisionTree is
1828
# dealing with one of the parents of the current state
1829
inv = self._get_inventory()
1830
entries = inv.iter_entries()
1831
if self.inventory.root is not None and not include_root:
1833
for path, entry in entries:
1834
yield path, 'V', entry.kind, entry.file_id, entry
1836
def lock_read(self):
1837
"""Lock the tree for a set of operations."""
1838
if not self._locked:
1839
self._repository.lock_read()
1840
if self._dirstate._lock_token is None:
1841
self._dirstate.lock_read()
1842
self._dirstate_locked = True
1845
def _must_be_locked(self):
1846
if not self._locked:
1847
raise errors.ObjectNotLocked(self)
1850
def path2id(self, path):
1851
"""Return the id for path in this tree."""
1852
# lookup by path: faster than splitting and walking the ivnentory.
1853
entry = self._get_entry(path=path)
1854
if entry == (None, None):
1859
"""Unlock, freeing any cache memory used during the lock."""
1860
# outside of a lock, the inventory is suspect: release it.
1862
if not self._locked:
1863
self._inventory = None
1865
if self._dirstate_locked:
1866
self._dirstate.unlock()
1867
self._dirstate_locked = False
1868
self._repository.unlock()
1871
def supports_tree_reference(self):
1872
return self._repo_supports_tree_reference
1874
def walkdirs(self, prefix=""):
1875
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
1876
# implementation based on an inventory.
1877
# This should be cleaned up to use the much faster Dirstate code
1878
# So for now, we just build up the parent inventory, and extract
1879
# it the same way RevisionTree does.
1880
_directory = 'directory'
1881
inv = self._get_inventory()
1882
top_id = inv.path2id(prefix)
1886
pending = [(prefix, top_id)]
1889
relpath, file_id = pending.pop()
1890
# 0 - relpath, 1- file-id
1892
relroot = relpath + '/'
1895
# FIXME: stash the node in pending
1896
entry = inv[file_id]
1897
for name, child in entry.sorted_children():
1898
toppath = relroot + name
1899
dirblock.append((toppath, name, child.kind, None,
1900
child.file_id, child.kind
1902
yield (relpath, entry.file_id), dirblock
1903
# push the user specified dirs from dirblock
1904
for dir in reversed(dirblock):
1905
if dir[2] == _directory:
1906
pending.append((dir[0], dir[4]))
1909
class InterDirStateTree(InterTree):
1910
"""Fast path optimiser for changes_from with dirstate trees.
1912
This is used only when both trees are in the dirstate working file, and
1913
the source is any parent within the dirstate, and the destination is
1914
the current working tree of the same dirstate.
1916
# this could be generalized to allow comparisons between any trees in the
1917
# dirstate, and possibly between trees stored in different dirstates.
1919
def __init__(self, source, target):
1920
super(InterDirStateTree, self).__init__(source, target)
1921
if not InterDirStateTree.is_compatible(source, target):
1922
raise Exception, "invalid source %r and target %r" % (source, target)
1925
def make_source_parent_tree(source, target):
1926
"""Change the source tree into a parent of the target."""
1927
revid = source.commit('record tree')
1928
target.branch.repository.fetch(source.branch.repository, revid)
1929
target.set_parent_ids([revid])
1930
return target.basis_tree(), target
1933
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
1934
result = klass.make_source_parent_tree(source, target)
1935
result[1]._iter_changes = dirstate.ProcessEntryPython
1939
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1940
from bzrlib.tests.test__dirstate_helpers import \
1941
CompiledDirstateHelpersFeature
1942
if not CompiledDirstateHelpersFeature.available():
1943
from bzrlib.tests import UnavailableFeature
1944
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1945
from bzrlib._dirstate_helpers_c import ProcessEntryC
1946
result = klass.make_source_parent_tree(source, target)
1947
result[1]._iter_changes = ProcessEntryC
1950
_matching_from_tree_format = WorkingTreeFormat4()
1951
_matching_to_tree_format = WorkingTreeFormat4()
1954
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
1955
# This method shouldn't be called, because we have python and C
1956
# specific flavours.
1957
raise NotImplementedError
1959
def iter_changes(self, include_unchanged=False,
1960
specific_files=None, pb=None, extra_trees=[],
1961
require_versioned=True, want_unversioned=False):
1962
"""Return the changes from source to target.
1964
:return: An iterator that yields tuples. See InterTree.iter_changes
1966
:param specific_files: An optional list of file paths to restrict the
1967
comparison to. When mapping filenames to ids, all matches in all
1968
trees (including optional extra_trees) are used, and all children of
1969
matched directories are included.
1970
:param include_unchanged: An optional boolean requesting the inclusion of
1971
unchanged entries in the result.
1972
:param extra_trees: An optional list of additional trees to use when
1973
mapping the contents of specific_files (paths) to file_ids.
1974
:param require_versioned: If True, all files in specific_files must be
1975
versioned in one of source, target, extra_trees or
1976
PathsNotVersionedError is raised.
1977
:param want_unversioned: Should unversioned files be returned in the
1978
output. An unversioned file is defined as one with (False, False)
1979
for the versioned pair.
1981
# NB: show_status depends on being able to pass in non-versioned files
1982
# and report them as unknown
1983
# TODO: handle extra trees in the dirstate.
1984
if (extra_trees or specific_files == []):
1985
# we can't fast-path these cases (yet)
1986
return super(InterDirStateTree, self).iter_changes(
1987
include_unchanged, specific_files, pb, extra_trees,
1988
require_versioned, want_unversioned=want_unversioned)
1989
parent_ids = self.target.get_parent_ids()
1990
if not (self.source._revision_id in parent_ids
1991
or self.source._revision_id == NULL_REVISION):
1992
raise AssertionError(
1993
"revision {%s} is not stored in {%s}, but %s "
1994
"can only be used for trees stored in the dirstate"
1995
% (self.source._revision_id, self.target, self.iter_changes))
1997
if self.source._revision_id == NULL_REVISION:
1999
indices = (target_index,)
2001
if not (self.source._revision_id in parent_ids):
2002
raise AssertionError(
2003
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2004
self.source._revision_id, parent_ids))
2005
source_index = 1 + parent_ids.index(self.source._revision_id)
2006
indices = (source_index, target_index)
2007
# -- make all specific_files utf8 --
2009
specific_files_utf8 = set()
2010
for path in specific_files:
2011
# Note, if there are many specific files, using cache_utf8
2012
# would be good here.
2013
specific_files_utf8.add(path.encode('utf8'))
2014
specific_files = specific_files_utf8
2016
specific_files = set([''])
2017
# -- specific_files is now a utf8 path set --
2018
search_specific_files = set()
2019
# -- get the state object and prepare it.
2020
state = self.target.current_dirstate()
2021
state._read_dirblocks_if_needed()
2022
if require_versioned:
2023
# -- check all supplied paths are versioned in a search tree. --
2024
all_versioned = True
2025
for path in specific_files:
2026
path_entries = state._entries_for_path(path)
2027
if not path_entries:
2028
# this specified path is not present at all: error
2029
all_versioned = False
2031
found_versioned = False
2032
# for each id at this path
2033
for entry in path_entries:
2035
for index in indices:
2036
if entry[1][index][0] != 'a': # absent
2037
found_versioned = True
2038
# all good: found a versioned cell
2040
if not found_versioned:
2041
# none of the indexes was not 'absent' at all ids for this
2043
all_versioned = False
2045
if not all_versioned:
2046
raise errors.PathsNotVersionedError(specific_files)
2047
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2048
for path in specific_files:
2049
other_specific_files = specific_files.difference(set([path]))
2050
if not osutils.is_inside_any(other_specific_files, path):
2051
# this is a top level path, we must check it.
2052
search_specific_files.add(path)
2054
use_filesystem_for_exec = (sys.platform != 'win32')
2055
iter_changes = self.target._iter_changes(include_unchanged,
2056
use_filesystem_for_exec, search_specific_files, state,
2057
source_index, target_index, want_unversioned, self.target)
2058
return iter_changes.iter_changes()
2061
def is_compatible(source, target):
2062
# the target must be a dirstate working tree
2063
if not isinstance(target, DirStateWorkingTree):
2065
# the source must be a revtree or dirstate rev tree.
2066
if not isinstance(source,
2067
(revisiontree.RevisionTree, DirStateRevisionTree)):
2069
# the source revid must be in the target dirstate
2070
if not (source._revision_id == NULL_REVISION or
2071
source._revision_id in target.get_parent_ids()):
2072
# TODO: what about ghosts? it may well need to
2073
# check for them explicitly.
2077
InterTree.register_optimiser(InterDirStateTree)
2080
class Converter3to4(object):
2081
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2084
self.target_format = WorkingTreeFormat4()
2086
def convert(self, tree):
2087
# lock the control files not the tree, so that we dont get tree
2088
# on-unlock behaviours, and so that noone else diddles with the
2089
# tree during upgrade.
2090
tree._control_files.lock_write()
2092
tree.read_working_inventory()
2093
self.create_dirstate_data(tree)
2094
self.update_format(tree)
2095
self.remove_xml_files(tree)
2097
tree._control_files.unlock()
2099
def create_dirstate_data(self, tree):
2100
"""Create the dirstate based data for tree."""
2101
local_path = tree.bzrdir.get_workingtree_transport(None
2102
).local_abspath('dirstate')
2103
state = dirstate.DirState.from_tree(tree, local_path)
2107
def remove_xml_files(self, tree):
2108
"""Remove the oldformat 3 data."""
2109
transport = tree.bzrdir.get_workingtree_transport(None)
2110
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2111
'pending-merges', 'stat-cache']:
2113
transport.delete(path)
2114
except errors.NoSuchFile:
2115
# some files are optional - just deal.
2118
def update_format(self, tree):
2119
"""Change the format marker."""
2120
tree._transport.put_bytes('format',
2121
self.target_format.get_format_string(),
2122
mode=tree.bzrdir._get_file_mode())
2125
class Converter4to5(object):
2126
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2129
self.target_format = WorkingTreeFormat5()
2131
def convert(self, tree):
2132
# lock the control files not the tree, so that we don't get tree
2133
# on-unlock behaviours, and so that no-one else diddles with the
2134
# tree during upgrade.
2135
tree._control_files.lock_write()
2137
self.update_format(tree)
2139
tree._control_files.unlock()
2141
def update_format(self, tree):
2142
"""Change the format marker."""
2143
tree._transport.put_bytes('format',
2144
self.target_format.get_format_string(),
2145
mode=tree.bzrdir._get_file_mode())
2148
class Converter4or5to6(object):
2149
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2152
self.target_format = WorkingTreeFormat6()
2154
def convert(self, tree):
2155
# lock the control files not the tree, so that we don't get tree
2156
# on-unlock behaviours, and so that no-one else diddles with the
2157
# tree during upgrade.
2158
tree._control_files.lock_write()
2160
self.init_custom_control_files(tree)
2161
self.update_format(tree)
2163
tree._control_files.unlock()
2165
def init_custom_control_files(self, tree):
2166
"""Initialize custom control files."""
2167
tree._transport.put_bytes('views', '',
2168
mode=tree.bzrdir._get_file_mode())
2170
def update_format(self, tree):
2171
"""Change the format marker."""
2172
tree._transport.put_bytes('format',
2173
self.target_format.get_format_string(),
2174
mode=tree.bzrdir._get_file_mode())