1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""WorkingTree4 format and implementation.
19
WorkingTree4 provides the dirstate based working tree logic.
21
To get a WorkingTree, call bzrdir.open_workingtree() or
22
WorkingTree.open(dir).
25
from cStringIO import StringIO
29
from bzrlib.lazy_import import lazy_import
30
lazy_import(globals(), """
31
from bisect import bisect_left
33
from copy import deepcopy
45
conflicts as _mod_conflicts,
55
revision as _mod_revision,
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 AssertionError(
356
"doesn't support tree references "
357
"required by entry %r"
359
inv_entry.reference_revision = link_or_sha1 or None
360
elif kind != 'symlink':
361
raise AssertionError("unknown kind %r" % kind)
362
# These checks cost us around 40ms on a 55k entry tree
363
if file_id in inv_byid:
364
raise AssertionError('file_id %s already in'
365
' inventory as %s' % (file_id, inv_byid[file_id]))
366
if name_unicode in parent_ie.children:
367
raise AssertionError('name %r already in parent'
369
inv_byid[file_id] = inv_entry
370
parent_ie.children[name_unicode] = inv_entry
371
self._inventory = inv
373
def _get_entry(self, file_id=None, path=None):
374
"""Get the dirstate row for file_id or path.
376
If either file_id or path is supplied, it is used as the key to lookup.
377
If both are supplied, the fastest lookup is used, and an error is
378
raised if they do not both point at the same row.
380
:param file_id: An optional unicode file_id to be looked up.
381
:param path: An optional unicode path to be looked up.
382
:return: The dirstate row tuple for path/file_id, or (None, None)
384
if file_id is None and path is None:
385
raise errors.BzrError('must supply file_id or path')
386
state = self.current_dirstate()
388
path = path.encode('utf8')
389
return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
391
def get_file_sha1(self, file_id, path=None, stat_value=None):
392
# check file id is valid unconditionally.
393
entry = self._get_entry(file_id=file_id, path=path)
395
raise errors.NoSuchId(self, file_id)
397
path = pathjoin(entry[0][0], entry[0][1]).decode('utf8')
399
file_abspath = self.abspath(path)
400
state = self.current_dirstate()
401
if stat_value is None:
403
stat_value = os.lstat(file_abspath)
405
if e.errno == errno.ENOENT:
409
link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
410
stat_value=stat_value)
411
if entry[1][0][0] == 'f':
412
if link_or_sha1 is None:
413
file_obj, statvalue = self.get_file_with_stat(file_id, path)
415
sha1 = osutils.sha_file(file_obj)
418
self._observed_sha1(file_id, path, (sha1, statvalue))
424
def _get_inventory(self):
425
"""Get the inventory for the tree. This is only valid within a lock."""
426
if 'evil' in debug.debug_flags:
427
trace.mutter_callsite(2,
428
"accessing .inventory forces a size of tree translation.")
429
if self._inventory is not None:
430
return self._inventory
431
self._must_be_locked()
432
self._generate_inventory()
433
return self._inventory
435
inventory = property(_get_inventory,
436
doc="Inventory of this Tree")
439
def get_parent_ids(self):
440
"""See Tree.get_parent_ids.
442
This implementation requests the ids list from the dirstate file.
444
return self.current_dirstate().get_parent_ids()
446
def get_reference_revision(self, file_id, path=None):
447
# referenced tree's revision is whatever's currently there
448
return self.get_nested_tree(file_id, path).last_revision()
450
def get_nested_tree(self, file_id, path=None):
452
path = self.id2path(file_id)
453
# else: check file_id is at path?
454
return WorkingTree.open(self.abspath(path))
457
def get_root_id(self):
458
"""Return the id of this trees root"""
459
return self._get_entry(path='')[0][2]
461
def has_id(self, file_id):
462
state = self.current_dirstate()
463
row, parents = self._get_entry(file_id=file_id)
466
return osutils.lexists(pathjoin(
467
self.basedir, row[0].decode('utf8'), row[1].decode('utf8')))
470
def id2path(self, file_id):
471
"Convert a file-id to a path."
472
state = self.current_dirstate()
473
entry = self._get_entry(file_id=file_id)
474
if entry == (None, None):
475
raise errors.NoSuchId(tree=self, file_id=file_id)
476
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
477
return path_utf8.decode('utf8')
479
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
480
entry = self._get_entry(path=path)
481
if entry == (None, None):
482
return False # Missing entries are not executable
483
return entry[1][0][3] # Executable?
485
if not osutils.supports_executable():
486
def is_executable(self, file_id, path=None):
487
"""Test if a file is executable or not.
489
Note: The caller is expected to take a read-lock before calling this.
491
entry = self._get_entry(file_id=file_id, path=path)
492
if entry == (None, None):
494
return entry[1][0][3]
496
_is_executable_from_path_and_stat = \
497
_is_executable_from_path_and_stat_from_basis
499
def is_executable(self, file_id, path=None):
500
"""Test if a file is executable or not.
502
Note: The caller is expected to take a read-lock before calling this.
504
self._must_be_locked()
506
path = self.id2path(file_id)
507
mode = os.lstat(self.abspath(path)).st_mode
508
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
510
def all_file_ids(self):
511
"""See Tree.iter_all_file_ids"""
512
self._must_be_locked()
514
for key, tree_details in self.current_dirstate()._iter_entries():
515
if tree_details[0][0] in ('a', 'r'): # relocated
522
"""Iterate through file_ids for this tree.
524
file_ids are in a WorkingTree if they are in the working inventory
525
and the working file exists.
528
for key, tree_details in self.current_dirstate()._iter_entries():
529
if tree_details[0][0] in ('a', 'r'): # absent, relocated
530
# not relevant to the working tree
532
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
533
if osutils.lexists(path):
534
result.append(key[2])
537
def iter_references(self):
538
if not self._repo_supports_tree_reference:
539
# When the repo doesn't support references, we will have nothing to
542
for key, tree_details in self.current_dirstate()._iter_entries():
543
if tree_details[0][0] in ('a', 'r'): # absent, relocated
544
# not relevant to the working tree
547
# the root is not a reference.
549
relpath = pathjoin(key[0].decode('utf8'), key[1].decode('utf8'))
551
if self._kind(relpath) == 'tree-reference':
552
yield relpath, key[2]
553
except errors.NoSuchFile:
554
# path is missing on disk.
557
def _observed_sha1(self, file_id, path, (sha1, statvalue)):
558
"""See MutableTree._observed_sha1."""
559
state = self.current_dirstate()
560
entry = self._get_entry(file_id=file_id, path=path)
561
state._observed_sha1(entry, sha1, statvalue)
563
def kind(self, file_id):
564
"""Return the kind of a file.
566
This is always the actual kind that's on disk, regardless of what it
569
Note: The caller is expected to take a read-lock before calling this.
571
relpath = self.id2path(file_id)
573
raise AssertionError(
574
"path for id {%s} is None!" % file_id)
575
return self._kind(relpath)
577
def _kind(self, relpath):
578
abspath = self.abspath(relpath)
579
kind = file_kind(abspath)
580
if (self._repo_supports_tree_reference and kind == 'directory'):
581
entry = self._get_entry(path=relpath)
582
if entry[1] is not None:
583
if entry[1][0][0] == 't':
584
kind = 'tree-reference'
588
def _last_revision(self):
589
"""See Mutable.last_revision."""
590
parent_ids = self.current_dirstate().get_parent_ids()
594
return _mod_revision.NULL_REVISION
597
"""See Branch.lock_read, and WorkingTree.unlock."""
598
self.branch.lock_read()
600
self._control_files.lock_read()
602
state = self.current_dirstate()
603
if not state._lock_token:
605
# set our support for tree references from the repository in
607
self._repo_supports_tree_reference = getattr(
608
self.branch.repository._format, "supports_tree_reference",
611
self._control_files.unlock()
617
def _lock_self_write(self):
618
"""This should be called after the branch is locked."""
620
self._control_files.lock_write()
622
state = self.current_dirstate()
623
if not state._lock_token:
625
# set our support for tree references from the repository in
627
self._repo_supports_tree_reference = getattr(
628
self.branch.repository._format, "supports_tree_reference",
631
self._control_files.unlock()
637
def lock_tree_write(self):
638
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
639
self.branch.lock_read()
640
self._lock_self_write()
642
def lock_write(self):
643
"""See MutableTree.lock_write, and WorkingTree.unlock."""
644
self.branch.lock_write()
645
self._lock_self_write()
647
@needs_tree_write_lock
648
def move(self, from_paths, to_dir, after=False):
649
"""See WorkingTree.move()."""
653
state = self.current_dirstate()
654
if isinstance(from_paths, basestring):
656
to_dir_utf8 = to_dir.encode('utf8')
657
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
658
id_index = state._get_id_index()
659
# check destination directory
660
# get the details for it
661
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
662
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
663
if not entry_present:
664
raise errors.BzrMoveFailedError('', to_dir,
665
errors.NotVersionedError(to_dir))
666
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
667
# get a handle on the block itself.
668
to_block_index = state._ensure_block(
669
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
670
to_block = state._dirblocks[to_block_index]
671
to_abs = self.abspath(to_dir)
672
if not isdir(to_abs):
673
raise errors.BzrMoveFailedError('',to_dir,
674
errors.NotADirectory(to_abs))
676
if to_entry[1][0][0] != 'd':
677
raise errors.BzrMoveFailedError('',to_dir,
678
errors.NotADirectory(to_abs))
680
if self._inventory is not None:
681
update_inventory = True
683
to_dir_id = to_entry[0][2]
684
to_dir_ie = inv[to_dir_id]
686
update_inventory = False
689
def move_one(old_entry, from_path_utf8, minikind, executable,
690
fingerprint, packed_stat, size,
691
to_block, to_key, to_path_utf8):
692
state._make_absent(old_entry)
693
from_key = old_entry[0]
695
lambda:state.update_minimal(from_key,
697
executable=executable,
698
fingerprint=fingerprint,
699
packed_stat=packed_stat,
701
path_utf8=from_path_utf8))
702
state.update_minimal(to_key,
704
executable=executable,
705
fingerprint=fingerprint,
706
packed_stat=packed_stat,
708
path_utf8=to_path_utf8)
709
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
710
new_entry = to_block[1][added_entry_index]
711
rollbacks.append(lambda:state._make_absent(new_entry))
713
for from_rel in from_paths:
714
# from_rel is 'pathinroot/foo/bar'
715
from_rel_utf8 = from_rel.encode('utf8')
716
from_dirname, from_tail = osutils.split(from_rel)
717
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
718
from_entry = self._get_entry(path=from_rel)
719
if from_entry == (None, None):
720
raise errors.BzrMoveFailedError(from_rel,to_dir,
721
errors.NotVersionedError(path=str(from_rel)))
723
from_id = from_entry[0][2]
724
to_rel = pathjoin(to_dir, from_tail)
725
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
726
item_to_entry = self._get_entry(path=to_rel)
727
if item_to_entry != (None, None):
728
raise errors.BzrMoveFailedError(from_rel, to_rel,
729
"Target is already versioned.")
731
if from_rel == to_rel:
732
raise errors.BzrMoveFailedError(from_rel, to_rel,
733
"Source and target are identical.")
735
from_missing = not self.has_filename(from_rel)
736
to_missing = not self.has_filename(to_rel)
743
raise errors.BzrMoveFailedError(from_rel, to_rel,
744
errors.NoSuchFile(path=to_rel,
745
extra="New file has not been created yet"))
747
# neither path exists
748
raise errors.BzrRenameFailedError(from_rel, to_rel,
749
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
751
if from_missing: # implicitly just update our path mapping
754
raise errors.RenameFailedFilesExist(from_rel, to_rel)
757
def rollback_rename():
758
"""A single rename has failed, roll it back."""
759
# roll back everything, even if we encounter trouble doing one
762
# TODO: at least log the other exceptions rather than just
763
# losing them mbp 20070307
765
for rollback in reversed(rollbacks):
769
exc_info = sys.exc_info()
771
raise exc_info[0], exc_info[1], exc_info[2]
773
# perform the disk move first - its the most likely failure point.
775
from_rel_abs = self.abspath(from_rel)
776
to_rel_abs = self.abspath(to_rel)
778
osutils.rename(from_rel_abs, to_rel_abs)
780
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
781
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
783
# perform the rename in the inventory next if needed: its easy
787
from_entry = inv[from_id]
788
current_parent = from_entry.parent_id
789
inv.rename(from_id, to_dir_id, from_tail)
791
lambda: inv.rename(from_id, current_parent, from_tail))
792
# finally do the rename in the dirstate, which is a little
793
# tricky to rollback, but least likely to need it.
794
old_block_index, old_entry_index, dir_present, file_present = \
795
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
796
old_block = state._dirblocks[old_block_index][1]
797
old_entry = old_block[old_entry_index]
798
from_key, old_entry_details = old_entry
799
cur_details = old_entry_details[0]
801
to_key = ((to_block[0],) + from_key[1:3])
802
minikind = cur_details[0]
803
move_one(old_entry, from_path_utf8=from_rel_utf8,
805
executable=cur_details[3],
806
fingerprint=cur_details[1],
807
packed_stat=cur_details[4],
811
to_path_utf8=to_rel_utf8)
814
def update_dirblock(from_dir, to_key, to_dir_utf8):
815
"""Recursively update all entries in this dirblock."""
817
raise AssertionError("renaming root not supported")
818
from_key = (from_dir, '')
819
from_block_idx, present = \
820
state._find_block_index_from_key(from_key)
822
# This is the old record, if it isn't present, then
823
# there is theoretically nothing to update.
824
# (Unless it isn't present because of lazy loading,
825
# but we don't do that yet)
827
from_block = state._dirblocks[from_block_idx]
828
to_block_index, to_entry_index, _, _ = \
829
state._get_block_entry_index(to_key[0], to_key[1], 0)
830
to_block_index = state._ensure_block(
831
to_block_index, to_entry_index, to_dir_utf8)
832
to_block = state._dirblocks[to_block_index]
834
# Grab a copy since move_one may update the list.
835
for entry in from_block[1][:]:
836
if not (entry[0][0] == from_dir):
837
raise AssertionError()
838
cur_details = entry[1][0]
839
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
840
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
841
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
842
minikind = cur_details[0]
844
# Deleted children of a renamed directory
845
# Do not need to be updated.
846
# Children that have been renamed out of this
847
# directory should also not be updated
849
move_one(entry, from_path_utf8=from_path_utf8,
851
executable=cur_details[3],
852
fingerprint=cur_details[1],
853
packed_stat=cur_details[4],
857
to_path_utf8=to_path_utf8)
859
# We need to move all the children of this
861
update_dirblock(from_path_utf8, to_key,
863
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
867
result.append((from_rel, to_rel))
868
state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
869
self._make_dirty(reset_inventory=False)
873
def _must_be_locked(self):
874
if not self._control_files._lock_count:
875
raise errors.ObjectNotLocked(self)
878
"""Initialize the state in this tree to be a new tree."""
882
def path2id(self, path):
883
"""Return the id for path in this tree."""
884
path = path.strip('/')
885
entry = self._get_entry(path=path)
886
if entry == (None, None):
890
def paths2ids(self, paths, trees=[], require_versioned=True):
891
"""See Tree.paths2ids().
893
This specialisation fast-paths the case where all the trees are in the
898
parents = self.get_parent_ids()
900
if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
902
return super(DirStateWorkingTree, self).paths2ids(paths,
903
trees, require_versioned)
904
search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
905
# -- make all paths utf8 --
908
paths_utf8.add(path.encode('utf8'))
910
# -- paths is now a utf8 path set --
911
# -- get the state object and prepare it.
912
state = self.current_dirstate()
913
if False and (state._dirblock_state == dirstate.DirState.NOT_IN_MEMORY
914
and '' not in paths):
915
paths2ids = self._paths2ids_using_bisect
917
paths2ids = self._paths2ids_in_memory
918
return paths2ids(paths, search_indexes,
919
require_versioned=require_versioned)
921
def _paths2ids_in_memory(self, paths, search_indexes,
922
require_versioned=True):
923
state = self.current_dirstate()
924
state._read_dirblocks_if_needed()
925
def _entries_for_path(path):
926
"""Return a list with all the entries that match path for all ids.
928
dirname, basename = os.path.split(path)
929
key = (dirname, basename, '')
930
block_index, present = state._find_block_index_from_key(key)
932
# the block which should contain path is absent.
935
block = state._dirblocks[block_index][1]
936
entry_index, _ = state._find_entry_index(key, block)
937
# we may need to look at multiple entries at this path: walk while the paths match.
938
while (entry_index < len(block) and
939
block[entry_index][0][0:2] == key[0:2]):
940
result.append(block[entry_index])
943
if require_versioned:
944
# -- check all supplied paths are versioned in a search tree. --
947
path_entries = _entries_for_path(path)
949
# this specified path is not present at all: error
950
all_versioned = False
952
found_versioned = False
953
# for each id at this path
954
for entry in path_entries:
956
for index in search_indexes:
957
if entry[1][index][0] != 'a': # absent
958
found_versioned = True
959
# all good: found a versioned cell
961
if not found_versioned:
962
# none of the indexes was not 'absent' at all ids for this
964
all_versioned = False
966
if not all_versioned:
967
raise errors.PathsNotVersionedError(paths)
968
# -- remove redundancy in supplied paths to prevent over-scanning --
969
search_paths = osutils.minimum_path_selection(paths)
971
# for all search_indexs in each path at or under each element of
972
# search_paths, if the detail is relocated: add the id, and add the
973
# relocated path as one to search if its not searched already. If the
974
# detail is not relocated, add the id.
975
searched_paths = set()
977
def _process_entry(entry):
978
"""Look at search_indexes within entry.
980
If a specific tree's details are relocated, add the relocation
981
target to search_paths if not searched already. If it is absent, do
982
nothing. Otherwise add the id to found_ids.
984
for index in search_indexes:
985
if entry[1][index][0] == 'r': # relocated
986
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
987
search_paths.add(entry[1][index][1])
988
elif entry[1][index][0] != 'a': # absent
989
found_ids.add(entry[0][2])
991
current_root = search_paths.pop()
992
searched_paths.add(current_root)
993
# process the entries for this containing directory: the rest will be
994
# found by their parents recursively.
995
root_entries = _entries_for_path(current_root)
997
# this specified path is not present at all, skip it.
999
for entry in root_entries:
1000
_process_entry(entry)
1001
initial_key = (current_root, '', '')
1002
block_index, _ = state._find_block_index_from_key(initial_key)
1003
while (block_index < len(state._dirblocks) and
1004
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
1005
for entry in state._dirblocks[block_index][1]:
1006
_process_entry(entry)
1010
def _paths2ids_using_bisect(self, paths, search_indexes,
1011
require_versioned=True):
1012
state = self.current_dirstate()
1015
split_paths = sorted(osutils.split(p) for p in paths)
1016
found = state._bisect_recursive(split_paths)
1018
if require_versioned:
1019
found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
1020
for dir_name in split_paths:
1021
if dir_name not in found_dir_names:
1022
raise errors.PathsNotVersionedError(paths)
1024
for dir_name_id, trees_info in found.iteritems():
1025
for index in search_indexes:
1026
if trees_info[index][0] not in ('r', 'a'):
1027
found_ids.add(dir_name_id[2])
1030
def read_working_inventory(self):
1031
"""Read the working inventory.
1033
This is a meaningless operation for dirstate, but we obey it anyhow.
1035
return self.inventory
1038
def revision_tree(self, revision_id):
1039
"""See Tree.revision_tree.
1041
WorkingTree4 supplies revision_trees for any basis tree.
1043
dirstate = self.current_dirstate()
1044
parent_ids = dirstate.get_parent_ids()
1045
if revision_id not in parent_ids:
1046
raise errors.NoSuchRevisionInTree(self, revision_id)
1047
if revision_id in dirstate.get_ghosts():
1048
raise errors.NoSuchRevisionInTree(self, revision_id)
1049
return DirStateRevisionTree(dirstate, revision_id,
1050
self.branch.repository)
1052
@needs_tree_write_lock
1053
def set_last_revision(self, new_revision):
1054
"""Change the last revision in the working tree."""
1055
parents = self.get_parent_ids()
1056
if new_revision in (NULL_REVISION, None):
1057
if len(parents) >= 2:
1058
raise AssertionError(
1059
"setting the last parent to none with a pending merge is "
1061
self.set_parent_ids([])
1063
self.set_parent_ids([new_revision] + parents[1:],
1064
allow_leftmost_as_ghost=True)
1066
@needs_tree_write_lock
1067
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
1068
"""Set the parent ids to revision_ids.
1070
See also set_parent_trees. This api will try to retrieve the tree data
1071
for each element of revision_ids from the trees repository. If you have
1072
tree data already available, it is more efficient to use
1073
set_parent_trees rather than set_parent_ids. set_parent_ids is however
1074
an easier API to use.
1076
:param revision_ids: The revision_ids to set as the parent ids of this
1077
working tree. Any of these may be ghosts.
1080
for revision_id in revision_ids:
1082
revtree = self.branch.repository.revision_tree(revision_id)
1083
# TODO: jam 20070213 KnitVersionedFile raises
1084
# RevisionNotPresent rather than NoSuchRevision if a
1085
# given revision_id is not present. Should Repository be
1086
# catching it and re-raising NoSuchRevision?
1087
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1089
trees.append((revision_id, revtree))
1090
self.set_parent_trees(trees,
1091
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1093
@needs_tree_write_lock
1094
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1095
"""Set the parents of the working tree.
1097
:param parents_list: A list of (revision_id, tree) tuples.
1098
If tree is None, then that element is treated as an unreachable
1099
parent tree - i.e. a ghost.
1101
dirstate = self.current_dirstate()
1102
if len(parents_list) > 0:
1103
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1104
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1108
parent_ids = [rev_id for rev_id, tree in parents_list]
1109
graph = self.branch.repository.get_graph()
1110
heads = graph.heads(parent_ids)
1111
accepted_revisions = set()
1113
# convert absent trees to the null tree, which we convert back to
1114
# missing on access.
1115
for rev_id, tree in parents_list:
1116
if len(accepted_revisions) > 0:
1117
# we always accept the first tree
1118
if rev_id in accepted_revisions or rev_id not in heads:
1119
# We have already included either this tree, or its
1120
# descendent, so we skip it.
1122
_mod_revision.check_not_reserved_id(rev_id)
1123
if tree is not None:
1124
real_trees.append((rev_id, tree))
1126
real_trees.append((rev_id,
1127
self.branch.repository.revision_tree(
1128
_mod_revision.NULL_REVISION)))
1129
ghosts.append(rev_id)
1130
accepted_revisions.add(rev_id)
1131
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1132
self._make_dirty(reset_inventory=False)
1134
def _set_root_id(self, file_id):
1135
"""See WorkingTree.set_root_id."""
1136
state = self.current_dirstate()
1137
state.set_path_id('', file_id)
1138
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1139
self._make_dirty(reset_inventory=True)
1141
def _sha_from_stat(self, path, stat_result):
1142
"""Get a sha digest from the tree's stat cache.
1144
The default implementation assumes no stat cache is present.
1146
:param path: The path.
1147
:param stat_result: The stat result being looked up.
1149
return self.current_dirstate().sha1_from_stat(path, stat_result)
1152
def supports_tree_reference(self):
1153
return self._repo_supports_tree_reference
1156
"""Unlock in format 4 trees needs to write the entire dirstate."""
1157
# do non-implementation specific cleanup
1160
if self._control_files._lock_count == 1:
1161
# eventually we should do signature checking during read locks for
1163
if self._control_files._lock_mode == 'w':
1166
if self._dirstate is not None:
1167
# This is a no-op if there are no modifications.
1168
self._dirstate.save()
1169
self._dirstate.unlock()
1170
# TODO: jam 20070301 We shouldn't have to wipe the dirstate at this
1171
# point. Instead, it could check if the header has been
1172
# modified when it is locked, and if not, it can hang on to
1173
# the data it has in memory.
1174
self._dirstate = None
1175
self._inventory = None
1176
# reverse order of locking.
1178
return self._control_files.unlock()
1180
self.branch.unlock()
1182
@needs_tree_write_lock
1183
def unversion(self, file_ids):
1184
"""Remove the file ids in file_ids from the current versioned set.
1186
When a file_id is unversioned, all of its children are automatically
1189
:param file_ids: The file ids to stop versioning.
1190
:raises: NoSuchId if any fileid is not currently versioned.
1194
state = self.current_dirstate()
1195
state._read_dirblocks_if_needed()
1196
ids_to_unversion = set(file_ids)
1197
paths_to_unversion = set()
1199
# check if the root is to be unversioned, if so, assert for now.
1200
# walk the state marking unversioned things as absent.
1201
# if there are any un-unversioned ids at the end, raise
1202
for key, details in state._dirblocks[0][1]:
1203
if (details[0][0] not in ('a', 'r') and # absent or relocated
1204
key[2] in ids_to_unversion):
1205
# I haven't written the code to unversion / yet - it should be
1207
raise errors.BzrError('Unversioning the / is not currently supported')
1209
while block_index < len(state._dirblocks):
1210
# process one directory at a time.
1211
block = state._dirblocks[block_index]
1212
# first check: is the path one to remove - it or its children
1213
delete_block = False
1214
for path in paths_to_unversion:
1215
if (block[0].startswith(path) and
1216
(len(block[0]) == len(path) or
1217
block[0][len(path)] == '/')):
1218
# this entire block should be deleted - its the block for a
1219
# path to unversion; or the child of one
1222
# TODO: trim paths_to_unversion as we pass by paths
1224
# this block is to be deleted: process it.
1225
# TODO: we can special case the no-parents case and
1226
# just forget the whole block.
1228
while entry_index < len(block[1]):
1229
# Mark this file id as having been removed
1230
entry = block[1][entry_index]
1231
ids_to_unversion.discard(entry[0][2])
1232
if (entry[1][0][0] in 'ar' # don't remove absent or renamed
1234
or not state._make_absent(entry)):
1236
# go to the next block. (At the moment we dont delete empty
1241
while entry_index < len(block[1]):
1242
entry = block[1][entry_index]
1243
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1244
# ^ some parent row.
1245
entry[0][2] not in ids_to_unversion):
1246
# ^ not an id to unversion
1249
if entry[1][0][0] == 'd':
1250
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1251
if not state._make_absent(entry):
1253
# we have unversioned this id
1254
ids_to_unversion.remove(entry[0][2])
1256
if ids_to_unversion:
1257
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1258
self._make_dirty(reset_inventory=False)
1259
# have to change the legacy inventory too.
1260
if self._inventory is not None:
1261
for file_id in file_ids:
1262
self._inventory.remove_recursive_id(file_id)
1264
@needs_tree_write_lock
1265
def rename_one(self, from_rel, to_rel, after=False):
1266
"""See WorkingTree.rename_one"""
1268
WorkingTree.rename_one(self, from_rel, to_rel, after)
1270
@needs_tree_write_lock
1271
def apply_inventory_delta(self, changes):
1272
"""See MutableTree.apply_inventory_delta"""
1273
state = self.current_dirstate()
1274
state.update_by_delta(changes)
1275
self._make_dirty(reset_inventory=True)
1277
def update_basis_by_delta(self, new_revid, delta):
1278
"""See MutableTree.update_basis_by_delta."""
1279
if self.last_revision() == new_revid:
1280
raise AssertionError()
1281
self.current_dirstate().update_basis_by_delta(delta, new_revid)
1284
def _validate(self):
1285
self._dirstate._validate()
1287
@needs_tree_write_lock
1288
def _write_inventory(self, inv):
1289
"""Write inventory as the current inventory."""
1291
raise AssertionError("attempting to write an inventory when the "
1292
"dirstate is dirty will lose pending changes")
1293
self.current_dirstate().set_state_from_inventory(inv)
1294
self._make_dirty(reset_inventory=False)
1295
if self._inventory is not None:
1296
self._inventory = inv
1300
class ContentFilterAwareSHA1Provider(dirstate.SHA1Provider):
1302
def __init__(self, tree):
1305
def sha1(self, abspath):
1306
"""Return the sha1 of a file given its absolute path."""
1307
filters = self.tree._content_filter_stack(self.tree.relpath(abspath))
1308
return internal_size_sha_file_byname(abspath, filters)[1]
1310
def stat_and_sha1(self, abspath):
1311
"""Return the stat and sha1 of a file given its absolute path."""
1312
filters = self.tree._content_filter_stack(self.tree.relpath(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.
1342
- Supporting a current view that may mask the set of files in a tree
1343
impacted by most user operations.
1345
This is new in bzr 1.11.
1348
def _make_views(self):
1349
return views.PathBasedViews(self)
1352
class DirStateWorkingTreeFormat(WorkingTreeFormat3):
1353
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
1354
accelerator_tree=None, hardlink=False):
1355
"""See WorkingTreeFormat.initialize().
1357
:param revision_id: allows creating a working tree at a different
1358
revision than the branch is at.
1359
:param accelerator_tree: A tree which can be used for retrieving file
1360
contents more quickly than the revision tree, i.e. a workingtree.
1361
The revision tree will be used for cases where accelerator_tree's
1362
content is different.
1363
:param hardlink: If true, hard-link files from accelerator_tree,
1366
These trees get an initial random root id, if their repository supports
1367
rich root data, TREE_ROOT otherwise.
1369
if not isinstance(a_bzrdir.transport, LocalTransport):
1370
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1371
transport = a_bzrdir.get_workingtree_transport(self)
1372
control_files = self._open_control_files(a_bzrdir)
1373
control_files.create_lock()
1374
control_files.lock_write()
1375
transport.put_bytes('format', self.get_format_string(),
1376
mode=a_bzrdir._get_file_mode())
1377
if from_branch is not None:
1378
branch = from_branch
1380
branch = a_bzrdir.open_branch()
1381
if revision_id is None:
1382
revision_id = branch.last_revision()
1383
local_path = transport.local_abspath('dirstate')
1384
# write out new dirstate (must exist when we create the tree)
1385
state = dirstate.DirState.initialize(local_path)
1388
wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1392
_control_files=control_files)
1394
wt.lock_tree_write()
1396
self._init_custom_control_files(wt)
1397
if revision_id in (None, NULL_REVISION):
1398
if branch.repository.supports_rich_root():
1399
wt._set_root_id(generate_ids.gen_root_id())
1401
wt._set_root_id(ROOT_ID)
1404
# frequently, we will get here due to branching. The accelerator
1405
# tree will be the tree from the branch, so the desired basis
1406
# tree will often be a parent of the accelerator tree.
1407
if accelerator_tree is not None:
1409
basis = accelerator_tree.revision_tree(revision_id)
1410
except errors.NoSuchRevision:
1413
basis = branch.repository.revision_tree(revision_id)
1414
if revision_id == NULL_REVISION:
1417
parents_list = [(revision_id, basis)]
1420
wt.set_parent_trees(parents_list, allow_leftmost_as_ghost=True)
1422
# if the basis has a root id we have to use that; otherwise we
1423
# use a new random one
1424
basis_root_id = basis.get_root_id()
1425
if basis_root_id is not None:
1426
wt._set_root_id(basis_root_id)
1428
# delta_from_tree is safe even for DirStateRevisionTrees,
1429
# because wt4.apply_inventory_delta does not mutate the input
1430
# inventory entries.
1431
transform.build_tree(basis, wt, accelerator_tree,
1432
hardlink=hardlink, delta_from_tree=True)
1436
control_files.unlock()
1440
def _init_custom_control_files(self, wt):
1441
"""Subclasses with custom control files should override this method.
1443
The working tree and control files are locked for writing when this
1446
:param wt: the WorkingTree object
1449
def _open(self, a_bzrdir, control_files):
1450
"""Open the tree itself.
1452
:param a_bzrdir: the dir for the tree.
1453
:param control_files: the control files for the tree.
1455
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
1456
branch=a_bzrdir.open_branch(),
1459
_control_files=control_files)
1461
def __get_matchingbzrdir(self):
1462
# please test against something that will let us do tree references
1463
return bzrdir.format_registry.make_bzrdir(
1464
'dirstate-with-subtree')
1466
_matchingbzrdir = property(__get_matchingbzrdir)
1469
class WorkingTreeFormat4(DirStateWorkingTreeFormat):
1470
"""The first consolidated dirstate working tree format.
1473
- exists within a metadir controlling .bzr
1474
- includes an explicit version marker for the workingtree control
1475
files, separate from the BzrDir format
1476
- modifies the hash cache format
1477
- is new in bzr 0.15
1478
- uses a LockDir to guard access to it.
1481
upgrade_recommended = False
1483
_tree_class = WorkingTree4
1485
def get_format_string(self):
1486
"""See WorkingTreeFormat.get_format_string()."""
1487
return "Bazaar Working Tree Format 4 (bzr 0.15)\n"
1489
def get_format_description(self):
1490
"""See WorkingTreeFormat.get_format_description()."""
1491
return "Working tree format 4"
1494
class WorkingTreeFormat5(DirStateWorkingTreeFormat):
1495
"""WorkingTree format supporting views.
1498
upgrade_recommended = False
1500
_tree_class = WorkingTree5
1502
def get_format_string(self):
1503
"""See WorkingTreeFormat.get_format_string()."""
1504
return "Bazaar Working Tree Format 5 (bzr 1.11)\n"
1506
def get_format_description(self):
1507
"""See WorkingTreeFormat.get_format_description()."""
1508
return "Working tree format 5"
1510
def _init_custom_control_files(self, wt):
1511
"""Subclasses with custom control files should override this method."""
1512
wt._transport.put_bytes('views', '', mode=wt.bzrdir._get_file_mode())
1514
def supports_content_filtering(self):
1517
def supports_views(self):
1521
class DirStateRevisionTree(Tree):
1522
"""A revision tree pulling the inventory from a dirstate."""
1524
def __init__(self, dirstate, revision_id, repository):
1525
self._dirstate = dirstate
1526
self._revision_id = revision_id
1527
self._repository = repository
1528
self._inventory = None
1530
self._dirstate_locked = False
1531
self._repo_supports_tree_reference = getattr(
1532
repository._format, "supports_tree_reference",
1536
return "<%s of %s in %s>" % \
1537
(self.__class__.__name__, self._revision_id, self._dirstate)
1539
def annotate_iter(self, file_id,
1540
default_revision=_mod_revision.CURRENT_REVISION):
1541
"""See Tree.annotate_iter"""
1542
text_key = (file_id, self.inventory[file_id].revision)
1543
annotations = self._repository.texts.annotate(text_key)
1544
return [(key[-1], line) for (key, line) in annotations]
1546
def _get_ancestors(self, default_revision):
1547
return set(self._repository.get_ancestry(self._revision_id,
1549
def _comparison_data(self, entry, path):
1550
"""See Tree._comparison_data."""
1552
return None, False, None
1553
# trust the entry as RevisionTree does, but this may not be
1554
# sensible: the entry might not have come from us?
1555
return entry.kind, entry.executable, None
1557
def _file_size(self, entry, stat_value):
1558
return entry.text_size
1560
def filter_unversioned_files(self, paths):
1561
"""Filter out paths that are not versioned.
1563
:return: set of paths.
1565
pred = self.has_filename
1566
return set((p for p in paths if not pred(p)))
1568
def get_root_id(self):
1569
return self.path2id('')
1571
def id2path(self, file_id):
1572
"Convert a file-id to a path."
1573
entry = self._get_entry(file_id=file_id)
1574
if entry == (None, None):
1575
raise errors.NoSuchId(tree=self, file_id=file_id)
1576
path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
1577
return path_utf8.decode('utf8')
1579
def iter_references(self):
1580
if not self._repo_supports_tree_reference:
1581
# When the repo doesn't support references, we will have nothing to
1584
# Otherwise, fall back to the default implementation
1585
return super(DirStateRevisionTree, self).iter_references()
1587
def _get_parent_index(self):
1588
"""Return the index in the dirstate referenced by this tree."""
1589
return self._dirstate.get_parent_ids().index(self._revision_id) + 1
1591
def _get_entry(self, file_id=None, path=None):
1592
"""Get the dirstate row for file_id or path.
1594
If either file_id or path is supplied, it is used as the key to lookup.
1595
If both are supplied, the fastest lookup is used, and an error is
1596
raised if they do not both point at the same row.
1598
:param file_id: An optional unicode file_id to be looked up.
1599
:param path: An optional unicode path to be looked up.
1600
:return: The dirstate row tuple for path/file_id, or (None, None)
1602
if file_id is None and path is None:
1603
raise errors.BzrError('must supply file_id or path')
1604
if path is not None:
1605
path = path.encode('utf8')
1606
parent_index = self._get_parent_index()
1607
return self._dirstate._get_entry(parent_index, fileid_utf8=file_id, path_utf8=path)
1609
def _generate_inventory(self):
1610
"""Create and set self.inventory from the dirstate object.
1612
(So this is only called the first time the inventory is requested for
1613
this tree; it then remains in memory until it's out of date.)
1615
This is relatively expensive: we have to walk the entire dirstate.
1617
if not self._locked:
1618
raise AssertionError(
1619
'cannot generate inventory of an unlocked '
1620
'dirstate revision tree')
1621
# separate call for profiling - makes it clear where the costs are.
1622
self._dirstate._read_dirblocks_if_needed()
1623
if self._revision_id not in self._dirstate.get_parent_ids():
1624
raise AssertionError(
1625
'parent %s has disappeared from %s' % (
1626
self._revision_id, self._dirstate.get_parent_ids()))
1627
parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
1628
# This is identical now to the WorkingTree _generate_inventory except
1629
# for the tree index use.
1630
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1631
current_id = root_key[2]
1632
if current_entry[parent_index][0] != 'd':
1633
raise AssertionError()
1634
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1635
inv.root.revision = current_entry[parent_index][4]
1636
# Turn some things into local variables
1637
minikind_to_kind = dirstate.DirState._minikind_to_kind
1638
factory = entry_factory
1639
utf8_decode = cache_utf8._utf8_decode
1640
inv_byid = inv._byid
1641
# we could do this straight out of the dirstate; it might be fast
1642
# and should be profiled - RBC 20070216
1643
parent_ies = {'' : inv.root}
1644
for block in self._dirstate._dirblocks[1:]: #skip root
1647
parent_ie = parent_ies[dirname]
1649
# all the paths in this block are not versioned in this tree
1651
for key, entry in block[1]:
1652
minikind, fingerprint, size, executable, revid = entry[parent_index]
1653
if minikind in ('a', 'r'): # absent, relocated
1657
name_unicode = utf8_decode(name)[0]
1659
kind = minikind_to_kind[minikind]
1660
inv_entry = factory[kind](file_id, name_unicode,
1662
inv_entry.revision = revid
1664
inv_entry.executable = executable
1665
inv_entry.text_size = size
1666
inv_entry.text_sha1 = fingerprint
1667
elif kind == 'directory':
1668
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1669
elif kind == 'symlink':
1670
inv_entry.executable = False
1671
inv_entry.text_size = None
1672
inv_entry.symlink_target = utf8_decode(fingerprint)[0]
1673
elif kind == 'tree-reference':
1674
inv_entry.reference_revision = fingerprint or None
1676
raise AssertionError("cannot convert entry %r into an InventoryEntry"
1678
# These checks cost us around 40ms on a 55k entry tree
1679
if file_id in inv_byid:
1680
raise AssertionError('file_id %s already in'
1681
' inventory as %s' % (file_id, inv_byid[file_id]))
1682
if name_unicode in parent_ie.children:
1683
raise AssertionError('name %r already in parent'
1685
inv_byid[file_id] = inv_entry
1686
parent_ie.children[name_unicode] = inv_entry
1687
self._inventory = inv
1689
def get_file_mtime(self, file_id, path=None):
1690
"""Return the modification time for this record.
1692
We return the timestamp of the last-changed revision.
1694
# Make sure the file exists
1695
entry = self._get_entry(file_id, path=path)
1696
if entry == (None, None): # do we raise?
1698
parent_index = self._get_parent_index()
1699
last_changed_revision = entry[1][parent_index][4]
1700
return self._repository.get_revision(last_changed_revision).timestamp
1702
def get_file_sha1(self, file_id, path=None, stat_value=None):
1703
entry = self._get_entry(file_id=file_id, path=path)
1704
parent_index = self._get_parent_index()
1705
parent_details = entry[1][parent_index]
1706
if parent_details[0] == 'f':
1707
return parent_details[1]
1710
def get_file(self, file_id, path=None):
1711
return StringIO(self.get_file_text(file_id))
1713
def get_file_size(self, file_id):
1714
"""See Tree.get_file_size"""
1715
return self.inventory[file_id].text_size
1717
def get_file_text(self, file_id, path=None):
1718
return list(self.iter_files_bytes([(file_id, None)]))[0][1]
1720
def get_reference_revision(self, file_id, path=None):
1721
return self.inventory[file_id].reference_revision
1723
def iter_files_bytes(self, desired_files):
1724
"""See Tree.iter_files_bytes.
1726
This version is implemented on top of Repository.iter_files_bytes"""
1727
parent_index = self._get_parent_index()
1728
repo_desired_files = []
1729
for file_id, identifier in desired_files:
1730
entry = self._get_entry(file_id)
1731
if entry == (None, None):
1732
raise errors.NoSuchId(self, file_id)
1733
repo_desired_files.append((file_id, entry[1][parent_index][4],
1735
return self._repository.iter_files_bytes(repo_desired_files)
1737
def get_symlink_target(self, file_id):
1738
entry = self._get_entry(file_id=file_id)
1739
parent_index = self._get_parent_index()
1740
if entry[1][parent_index][0] != 'l':
1743
# At present, none of the tree implementations supports non-ascii
1744
# symlink targets. So we will just assume that the dirstate path is
1746
return entry[1][parent_index][1]
1748
def get_revision_id(self):
1749
"""Return the revision id for this tree."""
1750
return self._revision_id
1752
def _get_inventory(self):
1753
if self._inventory is not None:
1754
return self._inventory
1755
self._must_be_locked()
1756
self._generate_inventory()
1757
return self._inventory
1759
inventory = property(_get_inventory,
1760
doc="Inventory of this Tree")
1762
def get_parent_ids(self):
1763
"""The parents of a tree in the dirstate are not cached."""
1764
return self._repository.get_revision(self._revision_id).parent_ids
1766
def has_filename(self, filename):
1767
return bool(self.path2id(filename))
1769
def kind(self, file_id):
1770
entry = self._get_entry(file_id=file_id)[1]
1772
raise errors.NoSuchId(tree=self, file_id=file_id)
1773
return dirstate.DirState._minikind_to_kind[entry[1][0]]
1775
def stored_kind(self, file_id):
1776
"""See Tree.stored_kind"""
1777
return self.kind(file_id)
1779
def path_content_summary(self, path):
1780
"""See Tree.path_content_summary."""
1781
id = self.inventory.path2id(path)
1783
return ('missing', None, None, None)
1784
entry = self._inventory[id]
1787
return (kind, entry.text_size, entry.executable, entry.text_sha1)
1788
elif kind == 'symlink':
1789
return (kind, None, None, entry.symlink_target)
1791
return (kind, None, None, None)
1793
def is_executable(self, file_id, path=None):
1794
ie = self.inventory[file_id]
1795
if ie.kind != "file":
1797
return ie.executable
1799
def list_files(self, include_root=False):
1800
# We use a standard implementation, because DirStateRevisionTree is
1801
# dealing with one of the parents of the current state
1802
inv = self._get_inventory()
1803
entries = inv.iter_entries()
1804
if self.inventory.root is not None and not include_root:
1806
for path, entry in entries:
1807
yield path, 'V', entry.kind, entry.file_id, entry
1809
def lock_read(self):
1810
"""Lock the tree for a set of operations."""
1811
if not self._locked:
1812
self._repository.lock_read()
1813
if self._dirstate._lock_token is None:
1814
self._dirstate.lock_read()
1815
self._dirstate_locked = True
1818
def _must_be_locked(self):
1819
if not self._locked:
1820
raise errors.ObjectNotLocked(self)
1823
def path2id(self, path):
1824
"""Return the id for path in this tree."""
1825
# lookup by path: faster than splitting and walking the ivnentory.
1826
entry = self._get_entry(path=path)
1827
if entry == (None, None):
1832
"""Unlock, freeing any cache memory used during the lock."""
1833
# outside of a lock, the inventory is suspect: release it.
1835
if not self._locked:
1836
self._inventory = None
1838
if self._dirstate_locked:
1839
self._dirstate.unlock()
1840
self._dirstate_locked = False
1841
self._repository.unlock()
1844
def supports_tree_reference(self):
1845
return self._repo_supports_tree_reference
1847
def walkdirs(self, prefix=""):
1848
# TODO: jam 20070215 This is the lazy way by using the RevisionTree
1849
# implementation based on an inventory.
1850
# This should be cleaned up to use the much faster Dirstate code
1851
# So for now, we just build up the parent inventory, and extract
1852
# it the same way RevisionTree does.
1853
_directory = 'directory'
1854
inv = self._get_inventory()
1855
top_id = inv.path2id(prefix)
1859
pending = [(prefix, top_id)]
1862
relpath, file_id = pending.pop()
1863
# 0 - relpath, 1- file-id
1865
relroot = relpath + '/'
1868
# FIXME: stash the node in pending
1869
entry = inv[file_id]
1870
for name, child in entry.sorted_children():
1871
toppath = relroot + name
1872
dirblock.append((toppath, name, child.kind, None,
1873
child.file_id, child.kind
1875
yield (relpath, entry.file_id), dirblock
1876
# push the user specified dirs from dirblock
1877
for dir in reversed(dirblock):
1878
if dir[2] == _directory:
1879
pending.append((dir[0], dir[4]))
1882
class InterDirStateTree(InterTree):
1883
"""Fast path optimiser for changes_from with dirstate trees.
1885
This is used only when both trees are in the dirstate working file, and
1886
the source is any parent within the dirstate, and the destination is
1887
the current working tree of the same dirstate.
1889
# this could be generalized to allow comparisons between any trees in the
1890
# dirstate, and possibly between trees stored in different dirstates.
1892
def __init__(self, source, target):
1893
super(InterDirStateTree, self).__init__(source, target)
1894
if not InterDirStateTree.is_compatible(source, target):
1895
raise Exception, "invalid source %r and target %r" % (source, target)
1898
def make_source_parent_tree(source, target):
1899
"""Change the source tree into a parent of the target."""
1900
revid = source.commit('record tree')
1901
target.branch.repository.fetch(source.branch.repository, revid)
1902
target.set_parent_ids([revid])
1903
return target.basis_tree(), target
1906
def make_source_parent_tree_python_dirstate(klass, test_case, source, target):
1907
result = klass.make_source_parent_tree(source, target)
1908
result[1]._iter_changes = dirstate.ProcessEntryPython
1912
def make_source_parent_tree_compiled_dirstate(klass, test_case, source, target):
1913
from bzrlib.tests.test__dirstate_helpers import \
1914
CompiledDirstateHelpersFeature
1915
if not CompiledDirstateHelpersFeature.available():
1916
from bzrlib.tests import UnavailableFeature
1917
raise UnavailableFeature(CompiledDirstateHelpersFeature)
1918
from bzrlib._dirstate_helpers_c import ProcessEntryC
1919
result = klass.make_source_parent_tree(source, target)
1920
result[1]._iter_changes = ProcessEntryC
1923
_matching_from_tree_format = WorkingTreeFormat4()
1924
_matching_to_tree_format = WorkingTreeFormat4()
1927
def _test_mutable_trees_to_test_trees(klass, test_case, source, target):
1928
# This method shouldn't be called, because we have python and C
1929
# specific flavours.
1930
raise NotImplementedError
1932
def iter_changes(self, include_unchanged=False,
1933
specific_files=None, pb=None, extra_trees=[],
1934
require_versioned=True, want_unversioned=False):
1935
"""Return the changes from source to target.
1937
:return: An iterator that yields tuples. See InterTree.iter_changes
1939
:param specific_files: An optional list of file paths to restrict the
1940
comparison to. When mapping filenames to ids, all matches in all
1941
trees (including optional extra_trees) are used, and all children of
1942
matched directories are included.
1943
:param include_unchanged: An optional boolean requesting the inclusion of
1944
unchanged entries in the result.
1945
:param extra_trees: An optional list of additional trees to use when
1946
mapping the contents of specific_files (paths) to file_ids.
1947
:param require_versioned: If True, all files in specific_files must be
1948
versioned in one of source, target, extra_trees or
1949
PathsNotVersionedError is raised.
1950
:param want_unversioned: Should unversioned files be returned in the
1951
output. An unversioned file is defined as one with (False, False)
1952
for the versioned pair.
1954
# NB: show_status depends on being able to pass in non-versioned files
1955
# and report them as unknown
1956
# TODO: handle extra trees in the dirstate.
1957
if (extra_trees or specific_files == []):
1958
# we can't fast-path these cases (yet)
1959
return super(InterDirStateTree, self).iter_changes(
1960
include_unchanged, specific_files, pb, extra_trees,
1961
require_versioned, want_unversioned=want_unversioned)
1962
parent_ids = self.target.get_parent_ids()
1963
if not (self.source._revision_id in parent_ids
1964
or self.source._revision_id == NULL_REVISION):
1965
raise AssertionError(
1966
"revision {%s} is not stored in {%s}, but %s "
1967
"can only be used for trees stored in the dirstate"
1968
% (self.source._revision_id, self.target, self.iter_changes))
1970
if self.source._revision_id == NULL_REVISION:
1972
indices = (target_index,)
1974
if not (self.source._revision_id in parent_ids):
1975
raise AssertionError(
1976
"Failure: source._revision_id: %s not in target.parent_ids(%s)" % (
1977
self.source._revision_id, parent_ids))
1978
source_index = 1 + parent_ids.index(self.source._revision_id)
1979
indices = (source_index, target_index)
1980
# -- make all specific_files utf8 --
1982
specific_files_utf8 = set()
1983
for path in specific_files:
1984
# Note, if there are many specific files, using cache_utf8
1985
# would be good here.
1986
specific_files_utf8.add(path.encode('utf8'))
1987
specific_files = specific_files_utf8
1989
specific_files = set([''])
1990
# -- specific_files is now a utf8 path set --
1991
search_specific_files = set()
1992
# -- get the state object and prepare it.
1993
state = self.target.current_dirstate()
1994
state._read_dirblocks_if_needed()
1995
if require_versioned:
1996
# -- check all supplied paths are versioned in a search tree. --
1997
all_versioned = True
1998
for path in specific_files:
1999
path_entries = state._entries_for_path(path)
2000
if not path_entries:
2001
# this specified path is not present at all: error
2002
all_versioned = False
2004
found_versioned = False
2005
# for each id at this path
2006
for entry in path_entries:
2008
for index in indices:
2009
if entry[1][index][0] != 'a': # absent
2010
found_versioned = True
2011
# all good: found a versioned cell
2013
if not found_versioned:
2014
# none of the indexes was not 'absent' at all ids for this
2016
all_versioned = False
2018
if not all_versioned:
2019
raise errors.PathsNotVersionedError(specific_files)
2020
# -- remove redundancy in supplied specific_files to prevent over-scanning --
2021
for path in specific_files:
2022
other_specific_files = specific_files.difference(set([path]))
2023
if not osutils.is_inside_any(other_specific_files, path):
2024
# this is a top level path, we must check it.
2025
search_specific_files.add(path)
2027
use_filesystem_for_exec = (sys.platform != 'win32')
2028
iter_changes = self.target._iter_changes(include_unchanged,
2029
use_filesystem_for_exec, search_specific_files, state,
2030
source_index, target_index, want_unversioned, self.target)
2031
return iter_changes.iter_changes()
2034
def is_compatible(source, target):
2035
# the target must be a dirstate working tree
2036
if not isinstance(target, DirStateWorkingTree):
2038
# the source must be a revtree or dirstate rev tree.
2039
if not isinstance(source,
2040
(revisiontree.RevisionTree, DirStateRevisionTree)):
2042
# the source revid must be in the target dirstate
2043
if not (source._revision_id == NULL_REVISION or
2044
source._revision_id in target.get_parent_ids()):
2045
# TODO: what about ghosts? it may well need to
2046
# check for them explicitly.
2050
InterTree.register_optimiser(InterDirStateTree)
2053
class Converter3to4(object):
2054
"""Perform an in-place upgrade of format 3 to format 4 trees."""
2057
self.target_format = WorkingTreeFormat4()
2059
def convert(self, tree):
2060
# lock the control files not the tree, so that we dont get tree
2061
# on-unlock behaviours, and so that noone else diddles with the
2062
# tree during upgrade.
2063
tree._control_files.lock_write()
2065
tree.read_working_inventory()
2066
self.create_dirstate_data(tree)
2067
self.update_format(tree)
2068
self.remove_xml_files(tree)
2070
tree._control_files.unlock()
2072
def create_dirstate_data(self, tree):
2073
"""Create the dirstate based data for tree."""
2074
local_path = tree.bzrdir.get_workingtree_transport(None
2075
).local_abspath('dirstate')
2076
state = dirstate.DirState.from_tree(tree, local_path)
2080
def remove_xml_files(self, tree):
2081
"""Remove the oldformat 3 data."""
2082
transport = tree.bzrdir.get_workingtree_transport(None)
2083
for path in ['basis-inventory-cache', 'inventory', 'last-revision',
2084
'pending-merges', 'stat-cache']:
2086
transport.delete(path)
2087
except errors.NoSuchFile:
2088
# some files are optional - just deal.
2091
def update_format(self, tree):
2092
"""Change the format marker."""
2093
tree._transport.put_bytes('format',
2094
self.target_format.get_format_string(),
2095
mode=tree.bzrdir._get_file_mode())
2098
class Converter4to5(object):
2099
"""Perform an in-place upgrade of format 4 to format 5 trees."""
2102
self.target_format = WorkingTreeFormat5()
2104
def convert(self, tree):
2105
# lock the control files not the tree, so that we don't get tree
2106
# on-unlock behaviours, and so that no-one else diddles with the
2107
# tree during upgrade.
2108
tree._control_files.lock_write()
2110
self.init_custom_control_files(tree)
2111
self.update_format(tree)
2113
tree._control_files.unlock()
2115
def init_custom_control_files(self, tree):
2116
"""Initialize custom control files."""
2117
tree._transport.put_bytes('views', '',
2118
mode=tree.bzrdir._get_file_mode())
2120
def update_format(self, tree):
2121
"""Change the format marker."""
2122
tree._transport.put_bytes('format',
2123
self.target_format.get_format_string(),
2124
mode=tree.bzrdir._get_file_mode())