1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
# If content filtering is supported, do not use the accelerator
1438
# tree - the cost of transforming the content both ways and
1439
# checking for changed content can outweight the gains it gives.
1440
# Note: do NOT move this logic up higher - using the basis from
1441
# the accelerator tree is still desirable because that can save
1442
# a minute or more of processing on large trees!
1443
# The original tree may not have the same content filters
1444
# applied so we can't safely build the inventory delta from
1446
if wt.supports_content_filtering():
1447
accelerator_tree = None
1448
delta_from_tree = False
1450
delta_from_tree = True
1451
# delta_from_tree is safe even for DirStateRevisionTrees,
1452
# because wt4.apply_inventory_delta does not mutate the input
1453
# inventory entries.
1454
transform.build_tree(basis, wt, accelerator_tree,
1456
delta_from_tree=delta_from_tree)
1460
control_files.unlock()
1464
def _init_custom_control_files(self, wt):
1465
"""Subclasses with custom control files should override this method.
1467
The working tree and control files are locked for writing when this
1470
:param wt: the WorkingTree object
1473
def _open(self, a_bzrdir, control_files):
1474
"""Open the tree itself.
1476
:param a_bzrdir: the dir for the tree.
1477
:param control_files: the control files for the tree.
1479
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1480
branch=a_bzrdir.open_branch(),
1483
_control_files=control_files)
1485
def __get_matchingbzrdir(self):
1486
return self._get_matchingbzrdir()
1488
def _get_matchingbzrdir(self):
1489
"""Overrideable method to get a bzrdir for testing."""
1490
# please test against something that will let us do tree references
1491
return bzrdir.format_registry.make_bzrdir(
1492
'dirstate-with-subtree')
1494
_matchingbzrdir = property(__get_matchingbzrdir)
1497
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1498
"""The first consolidated dirstate working tree format.
1501
- exists within a metadir controlling .bzr
1502
- includes an explicit version marker for the workingtree control
1503
files, separate from the BzrDir format
1504
- modifies the hash cache format
1505
- is new in bzr 0.15
1506
- uses a LockDir to guard access to it.
1509
upgrade_recommended = False
1511
_tree_class = WorkingTree4
1513
def get_format_string(self):
1514
"""See WorkingTreeFormat.get_format_string()."""
1515
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1517
def get_format_description(self):
1518
"""See WorkingTreeFormat.get_format_description()."""
1519
return "Working tree format 4"
1522
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1523
"""WorkingTree format supporting content filtering.
1526
upgrade_recommended = False
1528
_tree_class = WorkingTree5
1530
def get_format_string(self):
1531
"""See WorkingTreeFormat.get_format_string()."""
1532
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1534
def get_format_description(self):
1535
"""See WorkingTreeFormat.get_format_description()."""
1536
return "Working tree format 5"
1538
def supports_content_filtering(self):
1542
class WorkingTreeFormat6(DirStateWorkingTreeFormat):
1543
"""WorkingTree format supporting views.
1546
upgrade_recommended = False
1548
_tree_class = WorkingTree6
1550
def get_format_string(self):
1551
"""See WorkingTreeFormat.get_format_string()."""
1552
return "Bazaar Working Tree Format 6 (bzr 1.14)\n"
1554
def get_format_description(self):
1555
"""See WorkingTreeFormat.get_format_description()."""
1556
return "Working tree format 6"
1558
def _init_custom_control_files(self, wt):
1559
"""Subclasses with custom control files should override this method."""
1560
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1562
def supports_content_filtering(self):
1565
def supports_views(self):
1569
class DirStateRevisionTree(Tree):
1570
"""A revision tree pulling the inventory from a dirstate."""
1572
def __init__(self, dirstate, revision_id, repository):
1573
self._dirstate = dirstate
1574
self._revision_id = revision_id
1575
self._repository = repository
1576
self._inventory = None
1578
self._dirstate_locked = False
1579
self._repo_supports_tree_reference = getattr(
1580
repository._format, "supports_tree_reference",
1584
return "<%s of %s in %s>" % \
1585
(self.__class__.__name__, self._revision_id, self._dirstate)
1587
def annotate_iter(self, file_id,
1588
default_revision=_mod_revision.CURRENT_REVISION):
1589
"""See Tree.annotate_iter"""
1590
text_key = (file_id, self.inventory[file_id].revision)
1591
annotations = self._repository.texts.annotate(text_key)
1592
return [(key[-1], line) for (key, line) in annotations]
1594
def _get_ancestors(self, default_revision):
1595
return set(self._repository.get_ancestry(self._revision_id,
1597
def _comparison_data(self, entry, path):
1598
"""See Tree._comparison_data."""
1600
return None, False, None
1601
# trust the entry as RevisionTree does, but this may not be
1602
# sensible: the entry might not have come from us?
1603
return entry.kind, entry.executable, None
1605
def _file_size(self, entry, stat_value):
1606
return entry.text_size
1608
def filter_unversioned_files(self, paths):
1609
"""Filter out paths that are not versioned.
1611
:return: set of paths.
1613
pred = self.has_filename
1614
return set((p for p in paths if not pred(p)))
1616
def get_root_id(self):
1617
return self.path2id('')
1619
def id2path(self, file_id):
1620
"Convert a file-id to a path."
1621
entry = self._get_entry(file_id=file_id)
1622
if entry == (None, None):
1623
raise errors.NoSuchId(tree=self, file_id=file_id)
1624
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1625
return path_utf8.decode('utf8')
1627
def iter_references(self):
1628
if not self._repo_supports_tree_reference:
1629
# When the repo doesn't support references, we will have nothing to
1632
# Otherwise, fall back to the default implementation
1633
return super(DirStateRevisionTree, self).iter_references()
1635
def _get_parent_index(self):
1636
"""Return the index in the dirstate referenced by this tree."""
1637
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1639
def _get_entry(self, file_id=None, path=None):
1640
"""Get the dirstate row for file_id or path.
1642
If either file_id or path is supplied, it is used as the key to lookup.
1643
If both are supplied, the fastest lookup is used, and an error is
1644
raised if they do not both point at the same row.
1646
:param file_id: An optional unicode file_id to be looked up.
1647
:param path: An optional unicode path to be looked up.
1648
:return: The dirstate row tuple for path/file_id, or (None, None)
1650
if file_id is None and path is None:
1651
raise errors.BzrError('must supply file_id or path')
1652
if path is not None:
1653
path = path.encode('utf8')
1654
parent_index = self._get_parent_index()
1655
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1657
def _generate_inventory(self):
1658
"""Create and set self.inventory from the dirstate object.
1660
(So this is only called the first time the inventory is requested for
1661
this tree; it then remains in memory until it's out of date.)
1663
This is relatively expensive: we have to walk the entire dirstate.
1665
if not self._locked:
1666
raise AssertionError(
1667
'cannot generate inventory of an unlocked '
1668
'dirstate revision tree')
1669
# separate call for profiling - makes it clear where the costs are.
1670
self._dirstate._read_dirblocks_if_needed()
1671
if self._revision_id not in self._dirstate.get_parent_ids():
1672
raise AssertionError(
1673
'parent %s has disappeared from %s' % (
1674
self._revision_id, self._dirstate.get_parent_ids()))
1675
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1676
# This is identical now to the WorkingTree _generate_inventory except
1677
# for the tree index use.
1678
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1679
current_id = root_key[2]
1680
if current_entry[parent_index][0] != 'd':
1681
raise AssertionError()
1682
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1683
inv.root.revision = current_entry[parent_index][4]
1684
# Turn some things into local variables
1685
minikind_to_kind = dirstate.DirState._minikind_to_kind
1686
factory = entry_factory
1687
utf8_decode = cache_utf8._utf8_decode
1688
inv_byid = inv._byid
1689
# we could do this straight out of the dirstate; it might be fast
1690
# and should be profiled - RBC 20070216
1691
parent_ies = {'' : inv.root}
1692
for block in self._dirstate._dirblocks[1:]: #skip root
1695
parent_ie = parent_ies[dirname]
1697
# all the paths in this block are not versioned in this tree
1699
for key, entry in block[1]:
1700
minikind, fingerprint, size, executable, revid = entry[parent_index]
1701
if minikind in ('a', 'r'): # absent, relocated
1705
name_unicode = utf8_decode(name)[0]
1707
kind = minikind_to_kind[minikind]
1708
inv_entry = factory[kind](file_id, name_unicode,
1710
inv_entry.revision = revid
1712
inv_entry.executable = executable
1713
inv_entry.text_size = size
1714
inv_entry.text_sha1 = fingerprint
1715
elif kind == 'directory':
1716
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1717
elif kind == 'symlink':
1718
inv_entry.executable = False
1719
inv_entry.text_size = None
1720
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1721
elif kind == 'tree-reference':
1722
inv_entry.reference_revision = fingerprint or None
1724
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1726
# These checks cost us around 40ms on a 55k entry tree
1727
if file_id in inv_byid:
1728
raise AssertionError('file_id %s already in'
1729
' inventory as %s' % (file_id, inv_byid[file_id]))
1730
if name_unicode in parent_ie.children:
1731
raise AssertionError('name %r already in parent'
1733
inv_byid[file_id] = inv_entry
1734
parent_ie.children[name_unicode] = inv_entry
1735
self._inventory = inv
1737
def get_file_mtime(self, file_id, path=None):
1738
"""Return the modification time for this record.
1740
We return the timestamp of the last-changed revision.
1742
# Make sure the file exists
1743
entry = self._get_entry(file_id, path=path)
1744
if entry == (None, None): # do we raise?
1746
parent_index = self._get_parent_index()
1747
last_changed_revision = entry[1][parent_index][4]
1748
return self._repository.get_revision(last_changed_revision).timestamp
1750
def get_file_sha1(self, file_id, path=None, stat_value=None):
1751
entry = self._get_entry(file_id=file_id, path=path)
1752
parent_index = self._get_parent_index()
1753
parent_details = entry[1][parent_index]
1754
if parent_details[0] == 'f':
1755
return parent_details[1]
1758
def get_file(self, file_id, path=None):
1759
return StringIO(self.get_file_text(file_id))
1761
def get_file_size(self, file_id):
1762
"""See Tree.get_file_size"""
1763
return self.inventory[file_id].text_size
1765
def get_file_text(self, file_id, path=None):
1766
_, content = list(self.iter_files_bytes([(file_id, None)]))[0]
1767
return ''.join(content)
1769
def get_reference_revision(self, file_id, path=None):
1770
return self.inventory[file_id].reference_revision
1772
def iter_files_bytes(self, desired_files):
1773
"""See Tree.iter_files_bytes.
1775
This version is implemented on top of Repository.iter_files_bytes"""
1776
parent_index = self._get_parent_index()
1777
repo_desired_files = []
1778
for file_id, identifier in desired_files:
1779
entry = self._get_entry(file_id)
1780
if entry == (None, None):
1781
raise errors.NoSuchId(self, file_id)
1782
repo_desired_files.append((file_id, entry[1][parent_index][4],
1784
return self._repository.iter_files_bytes(repo_desired_files)
1786
def get_symlink_target(self, file_id):
1787
entry = self._get_entry(file_id=file_id)
1788
parent_index = self._get_parent_index()
1789
if entry[1][parent_index][0] != 'l':
1792
target = entry[1][parent_index][1]
1793
target = target.decode('utf8')
1796
def get_revision_id(self):
1797
"""Return the revision id for this tree."""
1798
return self._revision_id
1800
def _get_inventory(self):
1801
if self._inventory is not None:
1802
return self._inventory
1803
self._must_be_locked()
1804
self._generate_inventory()
1805
return self._inventory
1807
inventory = property(_get_inventory,
1808
doc="Inventory of this Tree")
1810
def get_parent_ids(self):
1811
"""The parents of a tree in the dirstate are not cached."""
1812
return self._repository.get_revision(self._revision_id).parent_ids
1814
def has_filename(self, filename):
1815
return bool(self.path2id(filename))
1817
def kind(self, file_id):
1818
entry = self._get_entry(file_id=file_id)[1]
1820
raise errors.NoSuchId(tree=self, file_id=file_id)
1821
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1823
def stored_kind(self, file_id):
1824
"""See Tree.stored_kind"""
1825
return self.kind(file_id)
1827
def path_content_summary(self, path):
1828
"""See Tree.path_content_summary."""
1829
id = self.inventory.path2id(path)
1831
return ('missing', None, None, None)
1832
entry = self._inventory[id]
1835
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1836
elif kind == 'symlink':
1837
return (kind, None, None, entry.symlink_target)
1839
return (kind, None, None, None)
1841
def is_executable(self, file_id, path=None):
1842
ie = self.inventory[file_id]
1843
if ie.kind != "file":
1845
return ie.executable
1847
def list_files(self, include_root=False, from_dir=None, recursive=True):
1848
# We use a standard implementation, because DirStateRevisionTree is
1849
# dealing with one of the parents of the current state
1850
inv = self._get_inventory()
1851
if from_dir is None:
1854
from_dir_id = inv.path2id(from_dir)
1855
if from_dir_id is None:
1856
# Directory not versioned
1858
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
1859
if inv.root is not None and not include_root and from_dir is None:
1861
for path, entry in entries:
1862
yield path, 'V', entry.kind, entry.file_id, entry
1864
def lock_read(self):
1865
"""Lock the tree for a set of operations."""
1866
if not self._locked:
1867
self._repository.lock_read()
1868
if self._dirstate._lock_token is None:
1869
self._dirstate.lock_read()
1870
self._dirstate_locked = True
1873
def _must_be_locked(self):
1874
if not self._locked:
1875
raise errors.ObjectNotLocked(self)
1878
def path2id(self, path):
1879
"""Return the id for path in this tree."""
1880
# lookup by path: faster than splitting and walking the ivnentory.
1881
entry = self._get_entry(path=path)
1882
if entry == (None, None):
1887
"""Unlock, freeing any cache memory used during the lock."""
1888
# outside of a lock, the inventory is suspect: release it.
1890
if not self._locked:
1891
self._inventory = None
1893
if self._dirstate_locked:
1894
self._dirstate.unlock()
1895
self._dirstate_locked = False
1896
self._repository.unlock()
1899
def supports_tree_reference(self):
1900
return self._repo_supports_tree_reference
1902
def walkdirs(self, prefix=""):
1903
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
1904
# implementation based on an inventory.
1905
# This should be cleaned up to use the much faster Dirstate code
1906
# So for now, we just build up the parent inventory, and extract
1907
# it the same way RevisionTree does.
1908
_directory = 'directory'
1909
inv = self._get_inventory()
1910
top_id = inv.path2id(prefix)
1914
pending = [(prefix, top_id)]
1917
relpath, file_id = pending.pop()
1918
# 0 - relpath, 1- file-id
1920
relroot = relpath + '/'
1923
# FIXME: stash the node in pending
1924
entry = inv[file_id]
1925
for name, child in entry.sorted_children():
1926
toppath = relroot + name
1927
dirblock.append((toppath, name, child.kind, None,
1928
child.file_id, child.kind
1930
yield (relpath, entry.file_id), dirblock
1931
# push the user specified dirs from dirblock
1932
for dir in reversed(dirblock):
1933
if dir[2] == _directory:
1934
pending.append((dir[0], dir[4]))
1937
class InterDirStateTree(InterTree):
1938
"""Fast path optimiser for changes_from with dirstate trees.
1940
This is used only when both trees are in the dirstate working file, and
1941
the source is any parent within the dirstate, and the destination is
1942
the current working tree of the same dirstate.
1944
# this could be generalized to allow comparisons between any trees in the
1945
# dirstate, and possibly between trees stored in different dirstates.
1947
def __init__(self, source, target):
1948
super(InterDirStateTree, self).__init__(source, target)
1949
if not InterDirStateTree.is_compatible(source, target):
1950
raise Exception, "invalid source %r and target %r" % (source, target)
1953
def make_source_parent_tree(source, target):
1954
"""Change the source tree into a parent of the target."""
1955
revid = source.commit('record tree')
1956
target.branch.repository.fetch(source.branch.repository, revid)
1957
target.set_parent_ids([revid])
1958
return target.basis_tree(), target
1961
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
1962
result = klass.make_source_parent_tree(source, target)
1963
result[1]._iter_changes = dirstate.ProcessEntryPython
1967
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1968
from bzrlib.tests.test__dirstate_helpers import \
1969
CompiledDirstateHelpersFeature
1970
if not CompiledDirstateHelpersFeature.available():
1971
from bzrlib.tests import UnavailableFeature
1972
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1973
from bzrlib._dirstate_helpers_c import ProcessEntryC
1974
result = klass.make_source_parent_tree(source, target)
1975
result[1]._iter_changes = ProcessEntryC
1978
_matching_from_tree_format = WorkingTreeFormat4()
1979
_matching_to_tree_format = WorkingTreeFormat4()
1982
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
1983
# This method shouldn't be called, because we have python and C
1984
# specific flavours.
1985
raise NotImplementedError
1987
def iter_changes(self, include_unchanged=False,
1988
specific_files=None, pb=None, extra_trees=[],
1989
require_versioned=True, want_unversioned=False):
1990
"""Return the changes from source to target.
1992
:return: An iterator that yields tuples. See InterTree.iter_changes
1994
:param specific_files: An optional list of file paths to restrict the
1995
comparison to. When mapping filenames to ids, all matches in all
1996
trees (including optional extra_trees) are used, and all children of
1997
matched directories are included.
1998
:param include_unchanged: An optional boolean requesting the inclusion of
1999
unchanged entries in the result.
2000
:param extra_trees: An optional list of additional trees to use when
2001
mapping the contents of specific_files (paths) to file_ids.
2002
:param require_versioned: If True, all files in specific_files must be
2003
versioned in one of source, target, extra_trees or
2004
PathsNotVersionedError is raised.
2005
:param want_unversioned: Should unversioned files be returned in the
2006
output. An unversioned file is defined as one with (False, False)
2007
for the versioned pair.
2009
# NB: show_status depends on being able to pass in non-versioned files
2010
# and report them as unknown
2011
# TODO: handle extra trees in the dirstate.
2012
if (extra_trees or specific_files == []):
2013
# we can't fast-path these cases (yet)
2014
return super(InterDirStateTree, self).iter_changes(
2015
include_unchanged, specific_files, pb, extra_trees,
2016
require_versioned, want_unversioned=want_unversioned)
2017
parent_ids = self.target.get_parent_ids()
2018
if not (self.source._revision_id in parent_ids
2019
or self.source._revision_id == NULL_REVISION):
2020
raise AssertionError(
2021
"revision {%s} is not stored in {%s}, but %s "
2022
"can only be used for trees stored in the dirstate"
2023
% (self.source._revision_id, self.target, self.iter_changes))
2025
if self.source._revision_id == NULL_REVISION:
2027
indices = (target_index,)
2029
if not (self.source._revision_id in parent_ids):
2030
raise AssertionError(
2031
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
2032
self.source._revision_id, parent_ids))
2033
source_index = 1 + parent_ids.index(self.source._revision_id)
2034
indices = (source_index, target_index)
2035
# -- make all specific_files utf8 --
2037
specific_files_utf8 = set()
2038
for path in specific_files:
2039
# Note, if there are many specific files, using cache_utf8
2040
# would be good here.
2041
specific_files_utf8.add(path.encode('utf8'))
2042
specific_files = specific_files_utf8
2044
specific_files = set([''])
2045
# -- specific_files is now a utf8 path set --
2047
# -- get the state object and prepare it.
2048
state = self.target.current_dirstate()
2049
state._read_dirblocks_if_needed()
2050
if require_versioned:
2051
# -- check all supplied paths are versioned in a search tree. --
2052
all_versioned = True
2053
for path in specific_files:
2054
path_entries = state._entries_for_path(path)
2055
if not path_entries:
2056
# this specified path is not present at all: error
2057
all_versioned = False
2059
found_versioned = False
2060
# for each id at this path
2061
for entry in path_entries:
2063
for index in indices:
2064
if entry[1][index][0] != 'a': # absent
2065
found_versioned = True
2066
# all good: found a versioned cell
2068
if not found_versioned:
2069
# none of the indexes was not 'absent' at all ids for this
2071
all_versioned = False
2073
if not all_versioned:
2074
raise errors.PathsNotVersionedError(specific_files)
2075
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2076
search_specific_files = osutils.minimum_path_selection(specific_files)
2078
use_filesystem_for_exec = (sys.platform != 'win32')
2079
iter_changes = self.target._iter_changes(include_unchanged,
2080
use_filesystem_for_exec, search_specific_files, state,
2081
source_index, target_index, want_unversioned, self.target)
2082
return iter_changes.iter_changes()
2085
def is_compatible(source, target):
2086
# the target must be a dirstate working tree
2087
if not isinstance(target, DirStateWorkingTree):
2089
# the source must be a revtree or dirstate rev tree.
2090
if not isinstance(source,
2091
(revisiontree.RevisionTree, DirStateRevisionTree)):
2093
# the source revid must be in the target dirstate
2094
if not (source._revision_id == NULL_REVISION or
2095
source._revision_id in target.get_parent_ids()):
2096
# TODO: what about ghosts? it may well need to
2097
# check for them explicitly.
2101
InterTree.register_optimiser(InterDirStateTree)
2104
class Converter3to4(object):
2105
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2108
self.target_format = WorkingTreeFormat4()
2110
def convert(self, tree):
2111
# lock the control files not the tree, so that we dont get tree
2112
# on-unlock behaviours, and so that noone else diddles with the
2113
# tree during upgrade.
2114
tree._control_files.lock_write()
2116
tree.read_working_inventory()
2117
self.create_dirstate_data(tree)
2118
self.update_format(tree)
2119
self.remove_xml_files(tree)
2121
tree._control_files.unlock()
2123
def create_dirstate_data(self, tree):
2124
"""Create the dirstate based data for tree."""
2125
local_path = tree.bzrdir.get_workingtree_transport(None
2126
).local_abspath('dirstate')
2127
state = dirstate.DirState.from_tree(tree, local_path)
2131
def remove_xml_files(self, tree):
2132
"""Remove the oldformat 3 data."""
2133
transport = tree.bzrdir.get_workingtree_transport(None)
2134
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2135
'pending-merges', 'stat-cache']:
2137
transport.delete(path)
2138
except errors.NoSuchFile:
2139
# some files are optional - just deal.
2142
def update_format(self, tree):
2143
"""Change the format marker."""
2144
tree._transport.put_bytes('format',
2145
self.target_format.get_format_string(),
2146
mode=tree.bzrdir._get_file_mode())
2149
class Converter4to5(object):
2150
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2153
self.target_format = WorkingTreeFormat5()
2155
def convert(self, tree):
2156
# lock the control files not the tree, so that we don't get tree
2157
# on-unlock behaviours, and so that no-one else diddles with the
2158
# tree during upgrade.
2159
tree._control_files.lock_write()
2161
self.update_format(tree)
2163
tree._control_files.unlock()
2165
def update_format(self, tree):
2166
"""Change the format marker."""
2167
tree._transport.put_bytes('format',
2168
self.target_format.get_format_string(),
2169
mode=tree.bzrdir._get_file_mode())
2172
class Converter4or5to6(object):
2173
"""Perform an in-place upgrade of format 4 or 5 to format 6 trees."""
2176
self.target_format = WorkingTreeFormat6()
2178
def convert(self, tree):
2179
# lock the control files not the tree, so that we don't get tree
2180
# on-unlock behaviours, and so that no-one else diddles with the
2181
# tree during upgrade.
2182
tree._control_files.lock_write()
2184
self.init_custom_control_files(tree)
2185
self.update_format(tree)
2187
tree._control_files.unlock()
2189
def init_custom_control_files(self, tree):
2190
"""Initialize custom control files."""
2191
tree._transport.put_bytes('views', '',
2192
mode=tree.bzrdir._get_file_mode())
2194
def update_format(self, tree):
2195
"""Change the format marker."""
2196
tree._transport.put_bytes('format',
2197
self.target_format.get_format_string(),
2198
mode=tree.bzrdir._get_file_mode())