1
# Copyright (C) 2005-2011 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
from __future__ import absolute_import
19
"""WorkingTree object and friends.
21
A WorkingTree represents the editable working copy of a branch.
22
Operations which represent the WorkingTree are also done here,
23
such as renaming or adding files. The WorkingTree has an inventory
24
which is updated by these operations. A commit produces a
25
new revision based on the workingtree and its inventory.
27
At the moment every WorkingTree has its own branch. Remote
28
WorkingTrees aren't supported.
30
To get a WorkingTree, call bzrdir.open_workingtree() or
31
WorkingTree.open(dir).
35
from cStringIO import StringIO
39
from bzrlib.lazy_import import lazy_import
40
lazy_import(globals(), """
41
from bisect import bisect_left
52
conflicts as _mod_conflicts,
55
filters as _mod_filters,
62
revision as _mod_revision,
74
from bzrlib import symbol_versioning
75
from bzrlib.decorators import needs_read_lock, needs_write_lock
76
from bzrlib.i18n import gettext
77
from bzrlib.lock import LogicalLockResult
78
import bzrlib.mutabletree
79
from bzrlib.mutabletree import needs_tree_write_lock
80
from bzrlib import osutils
81
from bzrlib.osutils import (
91
from bzrlib.trace import mutter, note
92
from bzrlib.revision import CURRENT_REVISION
93
from bzrlib.symbol_versioning import (
99
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
100
# TODO: Modifying the conflict objects or their type is currently nearly
101
# impossible as there is no clear relationship between the working tree format
102
# and the conflict list file format.
103
CONFLICT_HEADER_1 = "BZR conflict list format 1"
105
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
108
class TreeEntry(object):
109
"""An entry that implements the minimum interface used by commands.
111
This needs further inspection, it may be better to have
112
InventoryEntries without ids - though that seems wrong. For now,
113
this is a parallel hierarchy to InventoryEntry, and needs to become
114
one of several things: decorates to that hierarchy, children of, or
116
Another note is that these objects are currently only used when there is
117
no InventoryEntry available - i.e. for unversioned objects.
118
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
121
def __eq__(self, other):
122
# yes, this us ugly, TODO: best practice __eq__ style.
123
return (isinstance(other, TreeEntry)
124
and other.__class__ == self.__class__)
126
def kind_character(self):
130
class TreeDirectory(TreeEntry):
131
"""See TreeEntry. This is a directory in a working tree."""
133
def __eq__(self, other):
134
return (isinstance(other, TreeDirectory)
135
and other.__class__ == self.__class__)
137
def kind_character(self):
141
class TreeFile(TreeEntry):
142
"""See TreeEntry. This is a regular file in a working tree."""
144
def __eq__(self, other):
145
return (isinstance(other, TreeFile)
146
and other.__class__ == self.__class__)
148
def kind_character(self):
152
class TreeLink(TreeEntry):
153
"""See TreeEntry. This is a symlink in a working tree."""
155
def __eq__(self, other):
156
return (isinstance(other, TreeLink)
157
and other.__class__ == self.__class__)
159
def kind_character(self):
163
class WorkingTree(bzrlib.mutabletree.MutableTree,
164
controldir.ControlComponent):
165
"""Working copy tree.
167
:ivar basedir: The root of the tree on disk. This is a unicode path object
168
(as opposed to a URL).
171
# override this to set the strategy for storing views
172
def _make_views(self):
173
return views.DisabledViews(self)
175
def __init__(self, basedir='.',
176
branch=DEPRECATED_PARAMETER,
181
"""Construct a WorkingTree instance. This is not a public API.
183
:param branch: A branch to override probing for the branch.
185
self._format = _format
186
self.bzrdir = _bzrdir
188
raise errors.BzrError("Please use bzrdir.open_workingtree or "
189
"WorkingTree.open() to obtain a WorkingTree.")
190
basedir = safe_unicode(basedir)
191
mutter("opening working tree %r", basedir)
192
if deprecated_passed(branch):
193
self._branch = branch
195
self._branch = self.bzrdir.open_branch()
196
self.basedir = realpath(basedir)
197
self._transport = _transport
198
self._rules_searcher = None
199
self.views = self._make_views()
202
def user_transport(self):
203
return self.bzrdir.user_transport
206
def control_transport(self):
207
return self._transport
209
def is_control_filename(self, filename):
210
"""True if filename is the name of a control file in this tree.
212
:param filename: A filename within the tree. This is a relative path
213
from the root of this tree.
215
This is true IF and ONLY IF the filename is part of the meta data
216
that bzr controls in this tree. I.E. a random .bzr directory placed
217
on disk will not be a control file for this tree.
219
return self.bzrdir.is_control_filename(filename)
222
fget=lambda self: self._branch,
223
doc="""The branch this WorkingTree is connected to.
225
This cannot be set - it is reflective of the actual disk structure
226
the working tree has been constructed from.
229
def has_versioned_directories(self):
230
"""See `Tree.has_versioned_directories`."""
231
return self._format.supports_versioned_directories
233
def break_lock(self):
234
"""Break a lock if one is present from another instance.
236
Uses the ui factory to ask for confirmation if the lock may be from
239
This will probe the repository for its lock as well.
241
raise NotImplementedError(self.break_lock)
243
def requires_rich_root(self):
244
return self._format.requires_rich_root
246
def supports_tree_reference(self):
249
def supports_content_filtering(self):
250
return self._format.supports_content_filtering()
252
def supports_views(self):
253
return self.views.supports_views()
256
def open(path=None, _unsupported=False):
257
"""Open an existing working tree at path.
261
path = osutils.getcwd()
262
control = controldir.ControlDir.open(path, _unsupported)
263
return control.open_workingtree(_unsupported)
266
def open_containing(path=None):
267
"""Open an existing working tree which has its root about path.
269
This probes for a working tree at path and searches upwards from there.
271
Basically we keep looking up until we find the control directory or
272
run into /. If there isn't one, raises NotBranchError.
273
TODO: give this a new exception.
274
If there is one, it is returned, along with the unused portion of path.
276
:return: The WorkingTree that contains 'path', and the rest of path
279
path = osutils.getcwd()
280
control, relpath = controldir.ControlDir.open_containing(path)
281
return control.open_workingtree(), relpath
284
def open_containing_paths(file_list, default_directory=None,
285
canonicalize=True, apply_view=True):
286
"""Open the WorkingTree that contains a set of paths.
288
Fail if the paths given are not all in a single tree.
290
This is used for the many command-line interfaces that take a list of
291
any number of files and that require they all be in the same tree.
293
if default_directory is None:
294
default_directory = u'.'
295
# recommended replacement for builtins.internal_tree_files
296
if file_list is None or len(file_list) == 0:
297
tree = WorkingTree.open_containing(default_directory)[0]
298
# XXX: doesn't really belong here, and seems to have the strange
299
# side effect of making it return a bunch of files, not the whole
300
# tree -- mbp 20100716
301
if tree.supports_views() and apply_view:
302
view_files = tree.views.lookup_view()
304
file_list = view_files
305
view_str = views.view_display_str(view_files)
306
note(gettext("Ignoring files outside view. View is %s") % view_str)
307
return tree, file_list
308
if default_directory == u'.':
311
seed = default_directory
312
file_list = [osutils.pathjoin(default_directory, f)
314
tree = WorkingTree.open_containing(seed)[0]
315
return tree, tree.safe_relpath_files(file_list, canonicalize,
316
apply_view=apply_view)
318
def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
319
"""Convert file_list into a list of relpaths in tree.
321
:param self: A tree to operate on.
322
:param file_list: A list of user provided paths or None.
323
:param apply_view: if True and a view is set, apply it or check that
324
specified files are within it
325
:return: A list of relative paths.
326
:raises errors.PathNotChild: When a provided path is in a different self
329
if file_list is None:
331
if self.supports_views() and apply_view:
332
view_files = self.views.lookup_view()
336
# self.relpath exists as a "thunk" to osutils, but canonical_relpath
337
# doesn't - fix that up here before we enter the loop.
339
fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
342
for filename in file_list:
343
relpath = fixer(osutils.dereference_path(filename))
344
if view_files and not osutils.is_inside_any(view_files, relpath):
345
raise errors.FileOutsideView(filename, view_files)
346
new_list.append(relpath)
350
def open_downlevel(path=None):
351
"""Open an unsupported working tree.
353
Only intended for advanced situations like upgrading part of a bzrdir.
355
return WorkingTree.open(path, _unsupported=True)
358
def find_trees(location):
359
def list_current(transport):
360
return [d for d in transport.list_dir('') if d != '.bzr']
361
def evaluate(bzrdir):
363
tree = bzrdir.open_workingtree()
364
except errors.NoWorkingTree:
368
t = transport.get_transport(location)
369
iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
370
list_current=list_current)
371
return [tr for tr in iterator if tr is not None]
373
def all_file_ids(self):
374
"""See Tree.iter_all_file_ids"""
375
raise NotImplementedError(self.all_file_ids)
378
return "<%s of %s>" % (self.__class__.__name__,
379
getattr(self, 'basedir', None))
381
def abspath(self, filename):
382
return pathjoin(self.basedir, filename)
384
def basis_tree(self):
385
"""Return RevisionTree for the current last revision.
387
If the left most parent is a ghost then the returned tree will be an
388
empty tree - one obtained by calling
389
repository.revision_tree(NULL_REVISION).
392
revision_id = self.get_parent_ids()[0]
394
# no parents, return an empty revision tree.
395
# in the future this should return the tree for
396
# 'empty:' - the implicit root empty tree.
397
return self.branch.repository.revision_tree(
398
_mod_revision.NULL_REVISION)
400
return self.revision_tree(revision_id)
401
except errors.NoSuchRevision:
403
# No cached copy available, retrieve from the repository.
404
# FIXME? RBC 20060403 should we cache the inventory locally
407
return self.branch.repository.revision_tree(revision_id)
408
except (errors.RevisionNotPresent, errors.NoSuchRevision):
409
# the basis tree *may* be a ghost or a low level error may have
410
# occurred. If the revision is present, its a problem, if its not
412
if self.branch.repository.has_revision(revision_id):
414
# the basis tree is a ghost so return an empty tree.
415
return self.branch.repository.revision_tree(
416
_mod_revision.NULL_REVISION)
419
self._flush_ignore_list_cache()
421
def relpath(self, path):
422
"""Return the local path portion from a given path.
424
The path may be absolute or relative. If its a relative path it is
425
interpreted relative to the python current working directory.
427
return osutils.relpath(self.basedir, path)
429
def has_filename(self, filename):
430
return osutils.lexists(self.abspath(filename))
432
def get_file(self, file_id, path=None, filtered=True):
433
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
435
def get_file_with_stat(self, file_id, path=None, filtered=True,
436
_fstat=osutils.fstat):
437
"""See Tree.get_file_with_stat."""
439
path = self.id2path(file_id)
440
file_obj = self.get_file_byname(path, filtered=False)
441
stat_value = _fstat(file_obj.fileno())
442
if filtered and self.supports_content_filtering():
443
filters = self._content_filter_stack(path)
444
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
445
return (file_obj, stat_value)
447
def get_file_text(self, file_id, path=None, filtered=True):
448
my_file = self.get_file(file_id, path=path, filtered=filtered)
450
return my_file.read()
454
def get_file_byname(self, filename, filtered=True):
455
path = self.abspath(filename)
457
if filtered and self.supports_content_filtering():
458
filters = self._content_filter_stack(filename)
459
return _mod_filters.filtered_input_file(f, filters)
463
def get_file_lines(self, file_id, path=None, filtered=True):
464
"""See Tree.get_file_lines()"""
465
file = self.get_file(file_id, path, filtered=filtered)
467
return file.readlines()
471
def get_parent_ids(self):
472
"""See Tree.get_parent_ids.
474
This implementation reads the pending merges list and last_revision
475
value and uses that to decide what the parents list should be.
477
last_rev = _mod_revision.ensure_null(self._last_revision())
478
if _mod_revision.NULL_REVISION == last_rev:
483
merges_bytes = self._transport.get_bytes('pending-merges')
484
except errors.NoSuchFile:
487
for l in osutils.split_lines(merges_bytes):
488
revision_id = l.rstrip('\n')
489
parents.append(revision_id)
492
def get_root_id(self):
493
"""Return the id of this trees root"""
494
raise NotImplementedError(self.get_root_id)
497
def clone(self, to_controldir, revision_id=None):
498
"""Duplicate this working tree into to_bzr, including all state.
500
Specifically modified files are kept as modified, but
501
ignored and unknown files are discarded.
503
If you want to make a new line of development, see ControlDir.sprout()
506
If not None, the cloned tree will have its last revision set to
507
revision, and difference between the source trees last revision
508
and this one merged in.
510
# assumes the target bzr dir format is compatible.
511
result = to_controldir.create_workingtree()
512
self.copy_content_into(result, revision_id)
516
def copy_content_into(self, tree, revision_id=None):
517
"""Copy the current content and user files of this tree into tree."""
518
tree.set_root_id(self.get_root_id())
519
if revision_id is None:
520
merge.transform_tree(tree, self)
522
# TODO now merge from tree.last_revision to revision (to preserve
523
# user local changes)
524
merge.transform_tree(tree, self)
525
if revision_id == _mod_revision.NULL_REVISION:
528
new_parents = [revision_id]
529
tree.set_parent_ids(new_parents)
531
def id2abspath(self, file_id):
532
return self.abspath(self.id2path(file_id))
534
def _check_for_tree_references(self, iterator):
535
"""See if directories have become tree-references."""
536
blocked_parent_ids = set()
537
for path, ie in iterator:
538
if ie.parent_id in blocked_parent_ids:
539
# This entry was pruned because one of its parents became a
540
# TreeReference. If this is a directory, mark it as blocked.
541
if ie.kind == 'directory':
542
blocked_parent_ids.add(ie.file_id)
544
if ie.kind == 'directory' and self._directory_is_tree_reference(path):
545
# This InventoryDirectory needs to be a TreeReference
546
ie = inventory.TreeReference(ie.file_id, ie.name, ie.parent_id)
547
blocked_parent_ids.add(ie.file_id)
550
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
551
"""See Tree.iter_entries_by_dir()"""
552
# The only trick here is that if we supports_tree_reference then we
553
# need to detect if a directory becomes a tree-reference.
554
iterator = super(WorkingTree, self).iter_entries_by_dir(
555
specific_file_ids=specific_file_ids,
556
yield_parents=yield_parents)
557
if not self.supports_tree_reference():
560
return self._check_for_tree_references(iterator)
562
def get_file_size(self, file_id):
563
"""See Tree.get_file_size"""
564
# XXX: this returns the on-disk size; it should probably return the
567
return os.path.getsize(self.id2abspath(file_id))
569
if e.errno != errno.ENOENT:
574
@needs_tree_write_lock
575
def _gather_kinds(self, files, kinds):
576
"""See MutableTree._gather_kinds."""
577
for pos, f in enumerate(files):
578
if kinds[pos] is None:
579
fullpath = normpath(self.abspath(f))
581
kinds[pos] = file_kind(fullpath)
583
if e.errno == errno.ENOENT:
584
raise errors.NoSuchFile(fullpath)
587
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
588
"""Add revision_id as a parent.
590
This is equivalent to retrieving the current list of parent ids
591
and setting the list to its value plus revision_id.
593
:param revision_id: The revision id to add to the parent list. It may
594
be a ghost revision as long as its not the first parent to be
595
added, or the allow_leftmost_as_ghost parameter is set True.
596
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
598
parents = self.get_parent_ids() + [revision_id]
599
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
600
or allow_leftmost_as_ghost)
602
@needs_tree_write_lock
603
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
604
"""Add revision_id, tree tuple as a parent.
606
This is equivalent to retrieving the current list of parent trees
607
and setting the list to its value plus parent_tuple. See also
608
add_parent_tree_id - if you only have a parent id available it will be
609
simpler to use that api. If you have the parent already available, using
610
this api is preferred.
612
:param parent_tuple: The (revision id, tree) to add to the parent list.
613
If the revision_id is a ghost, pass None for the tree.
614
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
616
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
617
if len(parent_ids) > 1:
618
# the leftmost may have already been a ghost, preserve that if it
620
allow_leftmost_as_ghost = True
621
self.set_parent_ids(parent_ids,
622
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
624
@needs_tree_write_lock
625
def add_pending_merge(self, *revision_ids):
626
# TODO: Perhaps should check at this point that the
627
# history of the revision is actually present?
628
parents = self.get_parent_ids()
630
for rev_id in revision_ids:
631
if rev_id in parents:
633
parents.append(rev_id)
636
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
638
def path_content_summary(self, path, _lstat=os.lstat,
639
_mapper=osutils.file_kind_from_stat_mode):
640
"""See Tree.path_content_summary."""
641
abspath = self.abspath(path)
643
stat_result = _lstat(abspath)
645
if getattr(e, 'errno', None) == errno.ENOENT:
647
return ('missing', None, None, None)
648
# propagate other errors
650
kind = _mapper(stat_result.st_mode)
652
return self._file_content_summary(path, stat_result)
653
elif kind == 'directory':
654
# perhaps it looks like a plain directory, but it's really a
656
if self._directory_is_tree_reference(path):
657
kind = 'tree-reference'
658
return kind, None, None, None
659
elif kind == 'symlink':
660
target = osutils.readlink(abspath)
661
return ('symlink', None, None, target)
663
return (kind, None, None, None)
665
def _file_content_summary(self, path, stat_result):
666
size = stat_result.st_size
667
executable = self._is_executable_from_path_and_stat(path, stat_result)
668
# try for a stat cache lookup
669
return ('file', size, executable, self._sha_from_stat(
672
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
673
"""Common ghost checking functionality from set_parent_*.
675
This checks that the left hand-parent exists if there are any
678
if len(revision_ids) > 0:
679
leftmost_id = revision_ids[0]
680
if (not allow_leftmost_as_ghost and not
681
self.branch.repository.has_revision(leftmost_id)):
682
raise errors.GhostRevisionUnusableHere(leftmost_id)
684
def _set_merges_from_parent_ids(self, parent_ids):
685
merges = parent_ids[1:]
686
self._transport.put_bytes('pending-merges', '\n'.join(merges),
687
mode=self.bzrdir._get_file_mode())
689
def _filter_parent_ids_by_ancestry(self, revision_ids):
690
"""Check that all merged revisions are proper 'heads'.
692
This will always return the first revision_id, and any merged revisions
695
if len(revision_ids) == 0:
697
graph = self.branch.repository.get_graph()
698
heads = graph.heads(revision_ids)
699
new_revision_ids = revision_ids[:1]
700
for revision_id in revision_ids[1:]:
701
if revision_id in heads and revision_id not in new_revision_ids:
702
new_revision_ids.append(revision_id)
703
if new_revision_ids != revision_ids:
704
mutter('requested to set revision_ids = %s,'
705
' but filtered to %s', revision_ids, new_revision_ids)
706
return new_revision_ids
708
@needs_tree_write_lock
709
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
710
"""Set the parent ids to revision_ids.
712
See also set_parent_trees. This api will try to retrieve the tree data
713
for each element of revision_ids from the trees repository. If you have
714
tree data already available, it is more efficient to use
715
set_parent_trees rather than set_parent_ids. set_parent_ids is however
716
an easier API to use.
718
:param revision_ids: The revision_ids to set as the parent ids of this
719
working tree. Any of these may be ghosts.
721
self._check_parents_for_ghosts(revision_ids,
722
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
723
for revision_id in revision_ids:
724
_mod_revision.check_not_reserved_id(revision_id)
726
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
728
if len(revision_ids) > 0:
729
self.set_last_revision(revision_ids[0])
731
self.set_last_revision(_mod_revision.NULL_REVISION)
733
self._set_merges_from_parent_ids(revision_ids)
735
@needs_tree_write_lock
736
def set_pending_merges(self, rev_list):
737
parents = self.get_parent_ids()
738
leftmost = parents[:1]
739
new_parents = leftmost + rev_list
740
self.set_parent_ids(new_parents)
742
@needs_tree_write_lock
743
def set_merge_modified(self, modified_hashes):
744
"""Set the merge modified hashes."""
745
raise NotImplementedError(self.set_merge_modified)
747
def _sha_from_stat(self, path, stat_result):
748
"""Get a sha digest from the tree's stat cache.
750
The default implementation assumes no stat cache is present.
752
:param path: The path.
753
:param stat_result: The stat result being looked up.
757
@needs_write_lock # because merge pulls data into the branch.
758
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
759
merge_type=None, force=False):
760
"""Merge from a branch into this working tree.
762
:param branch: The branch to merge from.
763
:param to_revision: If non-None, the merge will merge to to_revision,
764
but not beyond it. to_revision does not need to be in the history
765
of the branch when it is supplied. If None, to_revision defaults to
766
branch.last_revision().
768
from bzrlib.merge import Merger, Merge3Merger
769
merger = Merger(self.branch, this_tree=self)
770
# check that there are no local alterations
771
if not force and self.has_changes():
772
raise errors.UncommittedChanges(self)
773
if to_revision is None:
774
to_revision = _mod_revision.ensure_null(branch.last_revision())
775
merger.other_rev_id = to_revision
776
if _mod_revision.is_null(merger.other_rev_id):
777
raise errors.NoCommits(branch)
778
self.branch.fetch(branch, last_revision=merger.other_rev_id)
779
merger.other_basis = merger.other_rev_id
780
merger.other_tree = self.branch.repository.revision_tree(
782
merger.other_branch = branch
783
if from_revision is None:
786
merger.set_base_revision(from_revision, branch)
787
if merger.base_rev_id == merger.other_rev_id:
788
raise errors.PointlessMerge
789
merger.backup_files = False
790
if merge_type is None:
791
merger.merge_type = Merge3Merger
793
merger.merge_type = merge_type
794
merger.set_interesting_files(None)
795
merger.show_base = False
796
merger.reprocess = False
797
conflicts = merger.do_merge()
801
def merge_modified(self):
802
"""Return a dictionary of files modified by a merge.
804
The list is initialized by WorkingTree.set_merge_modified, which is
805
typically called after we make some automatic updates to the tree
808
This returns a map of file_id->sha1, containing only files which are
809
still in the working inventory and have that text hash.
811
raise NotImplementedError(self.merge_modified)
814
def mkdir(self, path, file_id=None):
815
"""See MutableTree.mkdir()."""
817
file_id = generate_ids.gen_file_id(os.path.basename(path))
818
os.mkdir(self.abspath(path))
819
self.add(path, file_id, 'directory')
822
def get_symlink_target(self, file_id, path=None):
824
abspath = self.abspath(path)
826
abspath = self.id2abspath(file_id)
827
target = osutils.readlink(abspath)
830
def subsume(self, other_tree):
831
raise NotImplementedError(self.subsume)
833
def _setup_directory_is_tree_reference(self):
834
if self._branch.repository._format.supports_tree_reference:
835
self._directory_is_tree_reference = \
836
self._directory_may_be_tree_reference
838
self._directory_is_tree_reference = \
839
self._directory_is_never_tree_reference
841
def _directory_is_never_tree_reference(self, relpath):
844
def _directory_may_be_tree_reference(self, relpath):
845
# as a special case, if a directory contains control files then
846
# it's a tree reference, except that the root of the tree is not
847
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
848
# TODO: We could ask all the control formats whether they
849
# recognize this directory, but at the moment there's no cheap api
850
# to do that. Since we probably can only nest bzr checkouts and
851
# they always use this name it's ok for now. -- mbp 20060306
853
# FIXME: There is an unhandled case here of a subdirectory
854
# containing .bzr but not a branch; that will probably blow up
855
# when you try to commit it. It might happen if there is a
856
# checkout in a subdirectory. This can be avoided by not adding
859
def extract(self, file_id, format=None):
860
"""Extract a subtree from this tree.
862
A new branch will be created, relative to the path for this tree.
864
raise NotImplementedError(self.extract)
867
"""Write the in memory meta data to disk."""
868
raise NotImplementedError(self.flush)
870
def _kind(self, relpath):
871
return osutils.file_kind(self.abspath(relpath))
873
def list_files(self, include_root=False, from_dir=None, recursive=True):
874
"""List all files as (path, class, kind, id, entry).
876
Lists, but does not descend into unversioned directories.
877
This does not include files that have been deleted in this
878
tree. Skips the control directory.
880
:param include_root: if True, return an entry for the root
881
:param from_dir: start from this directory or None for the root
882
:param recursive: whether to recurse into subdirectories or not
884
raise NotImplementedError(self.list_files)
886
def move(self, from_paths, to_dir=None, after=False):
889
to_dir must be known to the working tree.
891
If to_dir exists and is a directory, the files are moved into
892
it, keeping their old names.
894
Note that to_dir is only the last component of the new name;
895
this doesn't change the directory.
897
For each entry in from_paths the move mode will be determined
900
The first mode moves the file in the filesystem and updates the
901
working tree metadata. The second mode only updates the working tree
902
metadata without touching the file on the filesystem.
904
move uses the second mode if 'after == True' and the target is not
905
versioned but present in the working tree.
907
move uses the second mode if 'after == False' and the source is
908
versioned but no longer in the working tree, and the target is not
909
versioned but present in the working tree.
911
move uses the first mode if 'after == False' and the source is
912
versioned and present in the working tree, and the target is not
913
versioned and not present in the working tree.
915
Everything else results in an error.
917
This returns a list of (from_path, to_path) pairs for each
920
raise NotImplementedError(self.move)
922
@needs_tree_write_lock
923
def rename_one(self, from_rel, to_rel, after=False):
926
This can change the directory or the filename or both.
928
rename_one has several 'modes' to work. First, it can rename a physical
929
file and change the file_id. That is the normal mode. Second, it can
930
only change the file_id without touching any physical file.
932
rename_one uses the second mode if 'after == True' and 'to_rel' is
933
either not versioned or newly added, and present in the working tree.
935
rename_one uses the second mode if 'after == False' and 'from_rel' is
936
versioned but no longer in the working tree, and 'to_rel' is not
937
versioned but present in the working tree.
939
rename_one uses the first mode if 'after == False' and 'from_rel' is
940
versioned and present in the working tree, and 'to_rel' is not
941
versioned and not present in the working tree.
943
Everything else results in an error.
945
raise NotImplementedError(self.rename_one)
949
"""Return all unknown files.
951
These are files in the working directory that are not versioned or
952
control files or ignored.
954
# force the extras method to be fully executed before returning, to
955
# prevent race conditions with the lock
957
[subp for subp in self.extras() if not self.is_ignored(subp)])
959
def unversion(self, file_ids):
960
"""Remove the file ids in file_ids from the current versioned set.
962
When a file_id is unversioned, all of its children are automatically
965
:param file_ids: The file ids to stop versioning.
966
:raises: NoSuchId if any fileid is not currently versioned.
968
raise NotImplementedError(self.unversion)
971
def pull(self, source, overwrite=False, stop_revision=None,
972
change_reporter=None, possible_transports=None, local=False,
976
old_revision_info = self.branch.last_revision_info()
977
basis_tree = self.basis_tree()
978
count = self.branch.pull(source, overwrite, stop_revision,
979
possible_transports=possible_transports,
981
new_revision_info = self.branch.last_revision_info()
982
if new_revision_info != old_revision_info:
983
repository = self.branch.repository
984
if repository._format.fast_deltas:
985
parent_ids = self.get_parent_ids()
987
basis_id = parent_ids[0]
988
basis_tree = repository.revision_tree(basis_id)
989
basis_tree.lock_read()
991
new_basis_tree = self.branch.basis_tree()
998
change_reporter=change_reporter,
1000
basis_root_id = basis_tree.get_root_id()
1001
new_root_id = new_basis_tree.get_root_id()
1002
if new_root_id is not None and basis_root_id != new_root_id:
1003
self.set_root_id(new_root_id)
1006
# TODO - dedup parents list with things merged by pull ?
1007
# reuse the revisiontree we merged against to set the new
1010
if self.branch.last_revision() != _mod_revision.NULL_REVISION:
1011
parent_trees.append(
1012
(self.branch.last_revision(), new_basis_tree))
1013
# we have to pull the merge trees out again, because
1014
# merge_inner has set the ids. - this corner is not yet
1015
# layered well enough to prevent double handling.
1016
# XXX TODO: Fix the double handling: telling the tree about
1017
# the already known parent data is wasteful.
1018
merges = self.get_parent_ids()[1:]
1019
parent_trees.extend([
1020
(parent, repository.revision_tree(parent)) for
1022
self.set_parent_trees(parent_trees)
1028
def put_file_bytes_non_atomic(self, file_id, bytes):
1029
"""See MutableTree.put_file_bytes_non_atomic."""
1030
stream = file(self.id2abspath(file_id), 'wb')
1037
"""Yield all unversioned files in this WorkingTree.
1039
If there are any unversioned directories then only the directory is
1040
returned, not all its children. But if there are unversioned files
1041
under a versioned subdirectory, they are returned.
1043
Currently returned depth-first, sorted by name within directories.
1044
This is the same order used by 'osutils.walkdirs'.
1046
raise NotImplementedError(self.extras)
1048
def ignored_files(self):
1049
"""Yield list of PATH, IGNORE_PATTERN"""
1050
for subp in self.extras():
1051
pat = self.is_ignored(subp)
1055
def get_ignore_list(self):
1056
"""Return list of ignore patterns.
1058
Cached in the Tree object after the first call.
1060
ignoreset = getattr(self, '_ignoreset', None)
1061
if ignoreset is not None:
1064
ignore_globs = set()
1065
ignore_globs.update(ignores.get_runtime_ignores())
1066
ignore_globs.update(ignores.get_user_ignores())
1067
if self.has_filename(bzrlib.IGNORE_FILENAME):
1068
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1070
ignore_globs.update(ignores.parse_ignore_file(f))
1073
self._ignoreset = ignore_globs
1076
def _flush_ignore_list_cache(self):
1077
"""Resets the cached ignore list to force a cache rebuild."""
1078
self._ignoreset = None
1079
self._ignoreglobster = None
1081
def is_ignored(self, filename):
1082
r"""Check whether the filename matches an ignore pattern.
1084
Patterns containing '/' or '\' need to match the whole path;
1085
others match against only the last component. Patterns starting
1086
with '!' are ignore exceptions. Exceptions take precedence
1087
over regular patterns and cause the filename to not be ignored.
1089
If the file is ignored, returns the pattern which caused it to
1090
be ignored, otherwise None. So this can simply be used as a
1091
boolean if desired."""
1092
if getattr(self, '_ignoreglobster', None) is None:
1093
self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1094
return self._ignoreglobster.match(filename)
1096
def kind(self, file_id):
1097
return file_kind(self.id2abspath(file_id))
1099
def stored_kind(self, file_id):
1100
"""See Tree.stored_kind"""
1101
raise NotImplementedError(self.stored_kind)
1103
def _comparison_data(self, entry, path):
1104
abspath = self.abspath(path)
1106
stat_value = os.lstat(abspath)
1108
if getattr(e, 'errno', None) == errno.ENOENT:
1115
mode = stat_value.st_mode
1116
kind = osutils.file_kind_from_stat_mode(mode)
1117
if not supports_executable():
1118
executable = entry is not None and entry.executable
1120
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1121
return kind, executable, stat_value
1123
def _file_size(self, entry, stat_value):
1124
return stat_value.st_size
1126
def last_revision(self):
1127
"""Return the last revision of the branch for this tree.
1129
This format tree does not support a separate marker for last-revision
1130
compared to the branch.
1132
See MutableTree.last_revision
1134
return self._last_revision()
1137
def _last_revision(self):
1138
"""helper for get_parent_ids."""
1139
return _mod_revision.ensure_null(self.branch.last_revision())
1141
def is_locked(self):
1142
"""Check if this tree is locked."""
1143
raise NotImplementedError(self.is_locked)
1145
def lock_read(self):
1146
"""Lock the tree for reading.
1148
This also locks the branch, and can be unlocked via self.unlock().
1150
:return: A bzrlib.lock.LogicalLockResult.
1152
raise NotImplementedError(self.lock_read)
1154
def lock_tree_write(self):
1155
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
1157
:return: A bzrlib.lock.LogicalLockResult.
1159
raise NotImplementedError(self.lock_tree_write)
1161
def lock_write(self):
1162
"""See MutableTree.lock_write, and WorkingTree.unlock.
1164
:return: A bzrlib.lock.LogicalLockResult.
1166
raise NotImplementedError(self.lock_write)
1168
def get_physical_lock_status(self):
1169
raise NotImplementedError(self.get_physical_lock_status)
1171
def set_last_revision(self, new_revision):
1172
"""Change the last revision in the working tree."""
1173
raise NotImplementedError(self.set_last_revision)
1175
def _change_last_revision(self, new_revision):
1176
"""Template method part of set_last_revision to perform the change.
1178
This is used to allow WorkingTree3 instances to not affect branch
1179
when their last revision is set.
1181
if _mod_revision.is_null(new_revision):
1182
self.branch.set_last_revision_info(0, new_revision)
1184
_mod_revision.check_not_reserved_id(new_revision)
1186
self.branch.generate_revision_history(new_revision)
1187
except errors.NoSuchRevision:
1188
# not present in the repo - dont try to set it deeper than the tip
1189
self.branch._set_revision_history([new_revision])
1192
@needs_tree_write_lock
1193
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1195
"""Remove nominated files from the working tree metadata.
1197
:files: File paths relative to the basedir.
1198
:keep_files: If true, the files will also be kept.
1199
:force: Delete files and directories, even if they are changed and
1200
even if the directories are not empty.
1202
if isinstance(files, basestring):
1207
all_files = set() # specified and nested files
1208
unknown_nested_files=set()
1210
to_file = sys.stdout
1212
files_to_backup = []
1214
def recurse_directory_to_add_files(directory):
1215
# Recurse directory and add all files
1216
# so we can check if they have changed.
1217
for parent_info, file_infos in self.walkdirs(directory):
1218
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1219
# Is it versioned or ignored?
1220
if self.path2id(relpath):
1221
# Add nested content for deletion.
1222
all_files.add(relpath)
1224
# Files which are not versioned
1225
# should be treated as unknown.
1226
files_to_backup.append(relpath)
1228
for filename in files:
1229
# Get file name into canonical form.
1230
abspath = self.abspath(filename)
1231
filename = self.relpath(abspath)
1232
if len(filename) > 0:
1233
all_files.add(filename)
1234
recurse_directory_to_add_files(filename)
1236
files = list(all_files)
1239
return # nothing to do
1241
# Sort needed to first handle directory content before the directory
1242
files.sort(reverse=True)
1244
# Bail out if we are going to delete files we shouldn't
1245
if not keep_files and not force:
1246
for (file_id, path, content_change, versioned, parent_id, name,
1247
kind, executable) in self.iter_changes(self.basis_tree(),
1248
include_unchanged=True, require_versioned=False,
1249
want_unversioned=True, specific_files=files):
1250
if versioned[0] == False:
1251
# The record is unknown or newly added
1252
files_to_backup.append(path[1])
1253
elif (content_change and (kind[1] is not None) and
1254
osutils.is_inside_any(files, path[1])):
1255
# Versioned and changed, but not deleted, and still
1256
# in one of the dirs to be deleted.
1257
files_to_backup.append(path[1])
1259
def backup(file_to_backup):
1260
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1261
osutils.rename(abs_path, self.abspath(backup_name))
1262
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1265
# Build inv_delta and delete files where applicable,
1266
# do this before any modifications to meta data.
1268
fid = self.path2id(f)
1271
message = "%s is not versioned." % (f,)
1274
# having removed it, it must be either ignored or unknown
1275
if self.is_ignored(f):
1279
# XXX: Really should be a more abstract reporter interface
1280
kind_ch = osutils.kind_marker(self.kind(fid))
1281
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1283
inv_delta.append((f, None, fid, None))
1284
message = "removed %s" % (f,)
1287
abs_path = self.abspath(f)
1288
if osutils.lexists(abs_path):
1289
if (osutils.isdir(abs_path) and
1290
len(os.listdir(abs_path)) > 0):
1292
osutils.rmtree(abs_path)
1293
message = "deleted %s" % (f,)
1297
if f in files_to_backup:
1300
osutils.delete_any(abs_path)
1301
message = "deleted %s" % (f,)
1302
elif message is not None:
1303
# Only care if we haven't done anything yet.
1304
message = "%s does not exist." % (f,)
1306
# Print only one message (if any) per file.
1307
if message is not None:
1309
self.apply_inventory_delta(inv_delta)
1311
@needs_tree_write_lock
1312
def revert(self, filenames=None, old_tree=None, backups=True,
1313
pb=None, report_changes=False):
1314
from bzrlib.conflicts import resolve
1315
if old_tree is None:
1316
basis_tree = self.basis_tree()
1317
basis_tree.lock_read()
1318
old_tree = basis_tree
1322
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1324
if filenames is None and len(self.get_parent_ids()) > 1:
1326
last_revision = self.last_revision()
1327
if last_revision != _mod_revision.NULL_REVISION:
1328
if basis_tree is None:
1329
basis_tree = self.basis_tree()
1330
basis_tree.lock_read()
1331
parent_trees.append((last_revision, basis_tree))
1332
self.set_parent_trees(parent_trees)
1335
resolve(self, filenames, ignore_misses=True, recursive=True)
1337
if basis_tree is not None:
1341
def revision_tree(self, revision_id):
1342
"""See Tree.revision_tree.
1344
WorkingTree can supply revision_trees for the basis revision only
1345
because there is only one cached inventory in the bzr directory.
1347
raise NotImplementedError(self.revision_tree)
1349
@needs_tree_write_lock
1350
def set_root_id(self, file_id):
1351
"""Set the root id for this tree."""
1355
'WorkingTree.set_root_id with fileid=None')
1356
file_id = osutils.safe_file_id(file_id)
1357
self._set_root_id(file_id)
1359
def _set_root_id(self, file_id):
1360
"""Set the root id for this tree, in a format specific manner.
1362
:param file_id: The file id to assign to the root. It must not be
1363
present in the current inventory or an error will occur. It must
1364
not be None, but rather a valid file id.
1366
raise NotImplementedError(self._set_root_id)
1369
"""See Branch.unlock.
1371
WorkingTree locking just uses the Branch locking facilities.
1372
This is current because all working trees have an embedded branch
1373
within them. IF in the future, we were to make branch data shareable
1374
between multiple working trees, i.e. via shared storage, then we
1375
would probably want to lock both the local tree, and the branch.
1377
raise NotImplementedError(self.unlock)
1381
def update(self, change_reporter=None, possible_transports=None,
1382
revision=None, old_tip=_marker, show_base=False):
1383
"""Update a working tree along its branch.
1385
This will update the branch if its bound too, which means we have
1386
multiple trees involved:
1388
- The new basis tree of the master.
1389
- The old basis tree of the branch.
1390
- The old basis tree of the working tree.
1391
- The current working tree state.
1393
Pathologically, all three may be different, and non-ancestors of each
1394
other. Conceptually we want to:
1396
- Preserve the wt.basis->wt.state changes
1397
- Transform the wt.basis to the new master basis.
1398
- Apply a merge of the old branch basis to get any 'local' changes from
1400
- Restore the wt.basis->wt.state changes.
1402
There isn't a single operation at the moment to do that, so we:
1404
- Merge current state -> basis tree of the master w.r.t. the old tree
1406
- Do a 'normal' merge of the old branch basis if it is relevant.
1408
:param revision: The target revision to update to. Must be in the
1410
:param old_tip: If branch.update() has already been run, the value it
1411
returned (old tip of the branch or None). _marker is used
1414
if self.branch.get_bound_location() is not None:
1416
update_branch = (old_tip is self._marker)
1418
self.lock_tree_write()
1419
update_branch = False
1422
old_tip = self.branch.update(possible_transports)
1424
if old_tip is self._marker:
1426
return self._update_tree(old_tip, change_reporter, revision, show_base)
1430
@needs_tree_write_lock
1431
def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1433
"""Update a tree to the master branch.
1435
:param old_tip: if supplied, the previous tip revision the branch,
1436
before it was changed to the master branch's tip.
1438
# here if old_tip is not None, it is the old tip of the branch before
1439
# it was updated from the master branch. This should become a pending
1440
# merge in the working tree to preserve the user existing work. we
1441
# cant set that until we update the working trees last revision to be
1442
# one from the new branch, because it will just get absorbed by the
1443
# parent de-duplication logic.
1445
# We MUST save it even if an error occurs, because otherwise the users
1446
# local work is unreferenced and will appear to have been lost.
1450
last_rev = self.get_parent_ids()[0]
1452
last_rev = _mod_revision.NULL_REVISION
1453
if revision is None:
1454
revision = self.branch.last_revision()
1456
old_tip = old_tip or _mod_revision.NULL_REVISION
1458
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1459
# the branch we are bound to was updated
1460
# merge those changes in first
1461
base_tree = self.basis_tree()
1462
other_tree = self.branch.repository.revision_tree(old_tip)
1463
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1464
base_tree, this_tree=self,
1465
change_reporter=change_reporter,
1466
show_base=show_base)
1468
self.add_parent_tree((old_tip, other_tree))
1469
note(gettext('Rerun update after fixing the conflicts.'))
1472
if last_rev != _mod_revision.ensure_null(revision):
1473
# the working tree is up to date with the branch
1474
# we can merge the specified revision from master
1475
to_tree = self.branch.repository.revision_tree(revision)
1476
to_root_id = to_tree.get_root_id()
1478
basis = self.basis_tree()
1481
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1482
self.set_root_id(to_root_id)
1487
# determine the branch point
1488
graph = self.branch.repository.get_graph()
1489
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1491
base_tree = self.branch.repository.revision_tree(base_rev_id)
1493
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1495
change_reporter=change_reporter,
1496
show_base=show_base)
1497
self.set_last_revision(revision)
1498
# TODO - dedup parents list with things merged by pull ?
1499
# reuse the tree we've updated to to set the basis:
1500
parent_trees = [(revision, to_tree)]
1501
merges = self.get_parent_ids()[1:]
1502
# Ideally we ask the tree for the trees here, that way the working
1503
# tree can decide whether to give us the entire tree or give us a
1504
# lazy initialised tree. dirstate for instance will have the trees
1505
# in ram already, whereas a last-revision + basis-inventory tree
1506
# will not, but also does not need them when setting parents.
1507
for parent in merges:
1508
parent_trees.append(
1509
(parent, self.branch.repository.revision_tree(parent)))
1510
if not _mod_revision.is_null(old_tip):
1511
parent_trees.append(
1512
(old_tip, self.branch.repository.revision_tree(old_tip)))
1513
self.set_parent_trees(parent_trees)
1514
last_rev = parent_trees[0][0]
1517
def set_conflicts(self, arg):
1518
raise errors.UnsupportedOperation(self.set_conflicts, self)
1520
def add_conflicts(self, arg):
1521
raise errors.UnsupportedOperation(self.add_conflicts, self)
1523
def conflicts(self):
1524
raise NotImplementedError(self.conflicts)
1526
def walkdirs(self, prefix=""):
1527
"""Walk the directories of this tree.
1529
returns a generator which yields items in the form:
1530
((curren_directory_path, fileid),
1531
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
1534
This API returns a generator, which is only valid during the current
1535
tree transaction - within a single lock_read or lock_write duration.
1537
If the tree is not locked, it may cause an error to be raised,
1538
depending on the tree implementation.
1540
disk_top = self.abspath(prefix)
1541
if disk_top.endswith('/'):
1542
disk_top = disk_top[:-1]
1543
top_strip_len = len(disk_top) + 1
1544
inventory_iterator = self._walkdirs(prefix)
1545
disk_iterator = osutils.walkdirs(disk_top, prefix)
1547
current_disk = disk_iterator.next()
1548
disk_finished = False
1550
if not (e.errno == errno.ENOENT or
1551
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1554
disk_finished = True
1556
current_inv = inventory_iterator.next()
1557
inv_finished = False
1558
except StopIteration:
1561
while not inv_finished or not disk_finished:
1563
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1564
cur_disk_dir_content) = current_disk
1566
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1567
cur_disk_dir_content) = ((None, None), None)
1568
if not disk_finished:
1569
# strip out .bzr dirs
1570
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1571
len(cur_disk_dir_content) > 0):
1572
# osutils.walkdirs can be made nicer -
1573
# yield the path-from-prefix rather than the pathjoined
1575
bzrdir_loc = bisect_left(cur_disk_dir_content,
1577
if (bzrdir_loc < len(cur_disk_dir_content)
1578
and self.bzrdir.is_control_filename(
1579
cur_disk_dir_content[bzrdir_loc][0])):
1580
# we dont yield the contents of, or, .bzr itself.
1581
del cur_disk_dir_content[bzrdir_loc]
1583
# everything is unknown
1586
# everything is missing
1589
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1591
# disk is before inventory - unknown
1592
dirblock = [(relpath, basename, kind, stat, None, None) for
1593
relpath, basename, kind, stat, top_path in
1594
cur_disk_dir_content]
1595
yield (cur_disk_dir_relpath, None), dirblock
1597
current_disk = disk_iterator.next()
1598
except StopIteration:
1599
disk_finished = True
1601
# inventory is before disk - missing.
1602
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1603
for relpath, basename, dkind, stat, fileid, kind in
1605
yield (current_inv[0][0], current_inv[0][1]), dirblock
1607
current_inv = inventory_iterator.next()
1608
except StopIteration:
1611
# versioned present directory
1612
# merge the inventory and disk data together
1614
for relpath, subiterator in itertools.groupby(sorted(
1615
current_inv[1] + cur_disk_dir_content,
1616
key=operator.itemgetter(0)), operator.itemgetter(1)):
1617
path_elements = list(subiterator)
1618
if len(path_elements) == 2:
1619
inv_row, disk_row = path_elements
1620
# versioned, present file
1621
dirblock.append((inv_row[0],
1622
inv_row[1], disk_row[2],
1623
disk_row[3], inv_row[4],
1625
elif len(path_elements[0]) == 5:
1627
dirblock.append((path_elements[0][0],
1628
path_elements[0][1], path_elements[0][2],
1629
path_elements[0][3], None, None))
1630
elif len(path_elements[0]) == 6:
1631
# versioned, absent file.
1632
dirblock.append((path_elements[0][0],
1633
path_elements[0][1], 'unknown', None,
1634
path_elements[0][4], path_elements[0][5]))
1636
raise NotImplementedError('unreachable code')
1637
yield current_inv[0], dirblock
1639
current_inv = inventory_iterator.next()
1640
except StopIteration:
1643
current_disk = disk_iterator.next()
1644
except StopIteration:
1645
disk_finished = True
1647
def _walkdirs(self, prefix=""):
1648
"""Walk the directories of this tree.
1650
:param prefix: is used as the directrory to start with.
1651
:returns: a generator which yields items in the form::
1653
((curren_directory_path, fileid),
1654
[(file1_path, file1_name, file1_kind, None, file1_id,
1657
raise NotImplementedError(self._walkdirs)
1659
@needs_tree_write_lock
1660
def auto_resolve(self):
1661
"""Automatically resolve text conflicts according to contents.
1663
Only text conflicts are auto_resolvable. Files with no conflict markers
1664
are considered 'resolved', because bzr always puts conflict markers
1665
into files that have text conflicts. The corresponding .THIS .BASE and
1666
.OTHER files are deleted, as per 'resolve'.
1668
:return: a tuple of ConflictLists: (un_resolved, resolved).
1670
un_resolved = _mod_conflicts.ConflictList()
1671
resolved = _mod_conflicts.ConflictList()
1672
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1673
for conflict in self.conflicts():
1674
if (conflict.typestring != 'text conflict' or
1675
self.kind(conflict.file_id) != 'file'):
1676
un_resolved.append(conflict)
1678
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1680
for line in my_file:
1681
if conflict_re.search(line):
1682
un_resolved.append(conflict)
1685
resolved.append(conflict)
1688
resolved.remove_files(self)
1689
self.set_conflicts(un_resolved)
1690
return un_resolved, resolved
1692
def _validate(self):
1693
"""Validate internal structures.
1695
This is meant mostly for the test suite. To give it a chance to detect
1696
corruption after actions have occurred. The default implementation is a
1699
:return: None. An exception should be raised if there is an error.
1703
def check_state(self):
1704
"""Check that the working state is/isn't valid."""
1705
raise NotImplementedError(self.check_state)
1707
def reset_state(self, revision_ids=None):
1708
"""Reset the state of the working tree.
1710
This does a hard-reset to a last-known-good state. This is a way to
1711
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1713
raise NotImplementedError(self.reset_state)
1715
def _get_rules_searcher(self, default_searcher):
1716
"""See Tree._get_rules_searcher."""
1717
if self._rules_searcher is None:
1718
self._rules_searcher = super(WorkingTree,
1719
self)._get_rules_searcher(default_searcher)
1720
return self._rules_searcher
1722
def get_shelf_manager(self):
1723
"""Return the ShelfManager for this WorkingTree."""
1724
from bzrlib.shelf import ShelfManager
1725
return ShelfManager(self, self._transport)
1728
class InventoryWorkingTree(WorkingTree,
1729
bzrlib.mutabletree.MutableInventoryTree):
1730
"""Base class for working trees that are inventory-oriented.
1732
The inventory is held in the `Branch` working-inventory, and the
1733
files are in a directory on disk.
1735
It is possible for a `WorkingTree` to have a filename which is
1736
not listed in the Inventory and vice versa.
1739
def __init__(self, basedir='.',
1740
branch=DEPRECATED_PARAMETER,
1742
_control_files=None,
1746
"""Construct a InventoryWorkingTree instance. This is not a public API.
1748
:param branch: A branch to override probing for the branch.
1750
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1751
branch=branch, _transport=_control_files._transport,
1752
_internal=_internal, _format=_format, _bzrdir=_bzrdir)
1754
self._control_files = _control_files
1755
self._detect_case_handling()
1757
if _inventory is None:
1758
# This will be acquired on lock_read() or lock_write()
1759
self._inventory_is_modified = False
1760
self._inventory = None
1762
# the caller of __init__ has provided an inventory,
1763
# we assume they know what they are doing - as its only
1764
# the Format factory and creation methods that are
1765
# permitted to do this.
1766
self._set_inventory(_inventory, dirty=False)
1768
def _set_inventory(self, inv, dirty):
1769
"""Set the internal cached inventory.
1771
:param inv: The inventory to set.
1772
:param dirty: A boolean indicating whether the inventory is the same
1773
logical inventory as whats on disk. If True the inventory is not
1774
the same and should be written to disk or data will be lost, if
1775
False then the inventory is the same as that on disk and any
1776
serialisation would be unneeded overhead.
1778
self._inventory = inv
1779
self._inventory_is_modified = dirty
1781
def _detect_case_handling(self):
1782
wt_trans = self.bzrdir.get_workingtree_transport(None)
1784
wt_trans.stat(self._format.case_sensitive_filename)
1785
except errors.NoSuchFile:
1786
self.case_sensitive = True
1788
self.case_sensitive = False
1790
self._setup_directory_is_tree_reference()
1792
def _serialize(self, inventory, out_file):
1793
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1796
def _deserialize(selt, in_file):
1797
return xml5.serializer_v5.read_inventory(in_file)
1799
def break_lock(self):
1800
"""Break a lock if one is present from another instance.
1802
Uses the ui factory to ask for confirmation if the lock may be from
1805
This will probe the repository for its lock as well.
1807
self._control_files.break_lock()
1808
self.branch.break_lock()
1810
def is_locked(self):
1811
return self._control_files.is_locked()
1813
def _must_be_locked(self):
1814
if not self.is_locked():
1815
raise errors.ObjectNotLocked(self)
1817
def lock_read(self):
1818
"""Lock the tree for reading.
1820
This also locks the branch, and can be unlocked via self.unlock().
1822
:return: A bzrlib.lock.LogicalLockResult.
1824
if not self.is_locked():
1826
self.branch.lock_read()
1828
self._control_files.lock_read()
1829
return LogicalLockResult(self.unlock)
1831
self.branch.unlock()
1834
def lock_tree_write(self):
1835
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
1837
:return: A bzrlib.lock.LogicalLockResult.
1839
if not self.is_locked():
1841
self.branch.lock_read()
1843
self._control_files.lock_write()
1844
return LogicalLockResult(self.unlock)
1846
self.branch.unlock()
1849
def lock_write(self):
1850
"""See MutableTree.lock_write, and WorkingTree.unlock.
1852
:return: A bzrlib.lock.LogicalLockResult.
1854
if not self.is_locked():
1856
self.branch.lock_write()
1858
self._control_files.lock_write()
1859
return LogicalLockResult(self.unlock)
1861
self.branch.unlock()
1864
def get_physical_lock_status(self):
1865
return self._control_files.get_physical_lock_status()
1867
@needs_tree_write_lock
1868
def _write_inventory(self, inv):
1869
"""Write inventory as the current inventory."""
1870
self._set_inventory(inv, dirty=True)
1873
# XXX: This method should be deprecated in favour of taking in a proper
1874
# new Inventory object.
1875
@needs_tree_write_lock
1876
def set_inventory(self, new_inventory_list):
1877
from bzrlib.inventory import (Inventory,
1881
inv = Inventory(self.get_root_id())
1882
for path, file_id, parent, kind in new_inventory_list:
1883
name = os.path.basename(path)
1886
# fixme, there should be a factory function inv,add_??
1887
if kind == 'directory':
1888
inv.add(InventoryDirectory(file_id, name, parent))
1889
elif kind == 'file':
1890
inv.add(InventoryFile(file_id, name, parent))
1891
elif kind == 'symlink':
1892
inv.add(InventoryLink(file_id, name, parent))
1894
raise errors.BzrError("unknown kind %r" % kind)
1895
self._write_inventory(inv)
1897
def _write_basis_inventory(self, xml):
1898
"""Write the basis inventory XML to the basis-inventory file"""
1899
path = self._basis_inventory_name()
1901
self._transport.put_file(path, sio,
1902
mode=self.bzrdir._get_file_mode())
1904
def _reset_data(self):
1905
"""Reset transient data that cannot be revalidated."""
1906
self._inventory_is_modified = False
1907
f = self._transport.get('inventory')
1909
result = self._deserialize(f)
1912
self._set_inventory(result, dirty=False)
1914
def _set_root_id(self, file_id):
1915
"""Set the root id for this tree, in a format specific manner.
1917
:param file_id: The file id to assign to the root. It must not be
1918
present in the current inventory or an error will occur. It must
1919
not be None, but rather a valid file id.
1921
inv = self._inventory
1922
orig_root_id = inv.root.file_id
1923
# TODO: it might be nice to exit early if there was nothing
1924
# to do, saving us from trigger a sync on unlock.
1925
self._inventory_is_modified = True
1926
# we preserve the root inventory entry object, but
1927
# unlinkit from the byid index
1928
del inv._byid[inv.root.file_id]
1929
inv.root.file_id = file_id
1930
# and link it into the index with the new changed id.
1931
inv._byid[inv.root.file_id] = inv.root
1932
# and finally update all children to reference the new id.
1933
# XXX: this should be safe to just look at the root.children
1934
# list, not the WHOLE INVENTORY.
1937
if entry.parent_id == orig_root_id:
1938
entry.parent_id = inv.root.file_id
1940
def all_file_ids(self):
1941
"""See Tree.iter_all_file_ids"""
1942
return set(self.inventory)
1944
@needs_tree_write_lock
1945
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
1946
"""See MutableTree.set_parent_trees."""
1947
parent_ids = [rev for (rev, tree) in parents_list]
1948
for revision_id in parent_ids:
1949
_mod_revision.check_not_reserved_id(revision_id)
1951
self._check_parents_for_ghosts(parent_ids,
1952
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
1954
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
1956
if len(parent_ids) == 0:
1957
leftmost_parent_id = _mod_revision.NULL_REVISION
1958
leftmost_parent_tree = None
1960
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1962
if self._change_last_revision(leftmost_parent_id):
1963
if leftmost_parent_tree is None:
1964
# If we don't have a tree, fall back to reading the
1965
# parent tree from the repository.
1966
self._cache_basis_inventory(leftmost_parent_id)
1968
inv = leftmost_parent_tree.inventory
1969
xml = self._create_basis_xml_from_inventory(
1970
leftmost_parent_id, inv)
1971
self._write_basis_inventory(xml)
1972
self._set_merges_from_parent_ids(parent_ids)
1974
def _cache_basis_inventory(self, new_revision):
1975
"""Cache new_revision as the basis inventory."""
1976
# TODO: this should allow the ready-to-use inventory to be passed in,
1977
# as commit already has that ready-to-use [while the format is the
1980
# this double handles the inventory - unpack and repack -
1981
# but is easier to understand. We can/should put a conditional
1982
# in here based on whether the inventory is in the latest format
1983
# - perhaps we should repack all inventories on a repository
1985
# the fast path is to copy the raw xml from the repository. If the
1986
# xml contains 'revision_id="', then we assume the right
1987
# revision_id is set. We must check for this full string, because a
1988
# root node id can legitimately look like 'revision_id' but cannot
1990
xml = self.branch.repository._get_inventory_xml(new_revision)
1991
firstline = xml.split('\n', 1)[0]
1992
if (not 'revision_id="' in firstline or
1993
'format="7"' not in firstline):
1994
inv = self.branch.repository._serializer.read_inventory_from_string(
1996
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1997
self._write_basis_inventory(xml)
1998
except (errors.NoSuchRevision, errors.RevisionNotPresent):
2001
def _basis_inventory_name(self):
2002
return 'basis-inventory-cache'
2004
def _create_basis_xml_from_inventory(self, revision_id, inventory):
2005
"""Create the text that will be saved in basis-inventory"""
2006
inventory.revision_id = revision_id
2007
return xml7.serializer_v7.write_inventory_to_string(inventory)
2009
@needs_tree_write_lock
2010
def set_conflicts(self, conflicts):
2011
self._put_rio('conflicts', conflicts.to_stanzas(),
2014
@needs_tree_write_lock
2015
def add_conflicts(self, new_conflicts):
2016
conflict_set = set(self.conflicts())
2017
conflict_set.update(set(list(new_conflicts)))
2018
self.set_conflicts(_mod_conflicts.ConflictList(sorted(conflict_set,
2019
key=_mod_conflicts.Conflict.sort_key)))
2022
def conflicts(self):
2024
confile = self._transport.get('conflicts')
2025
except errors.NoSuchFile:
2026
return _mod_conflicts.ConflictList()
2029
if confile.next() != CONFLICT_HEADER_1 + '\n':
2030
raise errors.ConflictFormatError()
2031
except StopIteration:
2032
raise errors.ConflictFormatError()
2033
reader = _mod_rio.RioReader(confile)
2034
return _mod_conflicts.ConflictList.from_stanzas(reader)
2038
def read_basis_inventory(self):
2039
"""Read the cached basis inventory."""
2040
path = self._basis_inventory_name()
2041
return self._transport.get_bytes(path)
2044
def read_working_inventory(self):
2045
"""Read the working inventory.
2047
:raises errors.InventoryModified: read_working_inventory will fail
2048
when the current in memory inventory has been modified.
2050
# conceptually this should be an implementation detail of the tree.
2051
# XXX: Deprecate this.
2052
# ElementTree does its own conversion from UTF-8, so open in
2054
if self._inventory_is_modified:
2055
raise errors.InventoryModified(self)
2056
f = self._transport.get('inventory')
2058
result = self._deserialize(f)
2061
self._set_inventory(result, dirty=False)
2065
def get_root_id(self):
2066
"""Return the id of this trees root"""
2067
return self._inventory.root.file_id
2069
def has_id(self, file_id):
2070
# files that have been deleted are excluded
2071
inv = self.inventory
2072
if not inv.has_id(file_id):
2074
path = inv.id2path(file_id)
2075
return osutils.lexists(self.abspath(path))
2077
def has_or_had_id(self, file_id):
2078
if file_id == self.inventory.root.file_id:
2080
return self.inventory.has_id(file_id)
2082
@symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2084
"""Iterate through file_ids for this tree.
2086
file_ids are in a WorkingTree if they are in the working inventory
2087
and the working file exists.
2089
inv = self._inventory
2090
for path, ie in inv.iter_entries():
2091
if osutils.lexists(self.abspath(path)):
2094
@needs_tree_write_lock
2095
def set_last_revision(self, new_revision):
2096
"""Change the last revision in the working tree."""
2097
if self._change_last_revision(new_revision):
2098
self._cache_basis_inventory(new_revision)
2100
def _get_check_refs(self):
2101
"""Return the references needed to perform a check of this tree.
2103
The default implementation returns no refs, and is only suitable for
2104
trees that have no local caching and can commit on ghosts at any time.
2106
:seealso: bzrlib.check for details about check_refs.
2111
def _check(self, references):
2112
"""Check the tree for consistency.
2114
:param references: A dict with keys matching the items returned by
2115
self._get_check_refs(), and values from looking those keys up in
2118
tree_basis = self.basis_tree()
2119
tree_basis.lock_read()
2121
repo_basis = references[('trees', self.last_revision())]
2122
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2123
raise errors.BzrCheckError(
2124
"Mismatched basis inventory content.")
2130
def check_state(self):
2131
"""Check that the working state is/isn't valid."""
2132
check_refs = self._get_check_refs()
2134
for ref in check_refs:
2137
refs[ref] = self.branch.repository.revision_tree(value)
2140
@needs_tree_write_lock
2141
def reset_state(self, revision_ids=None):
2142
"""Reset the state of the working tree.
2144
This does a hard-reset to a last-known-good state. This is a way to
2145
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2147
if revision_ids is None:
2148
revision_ids = self.get_parent_ids()
2149
if not revision_ids:
2150
rt = self.branch.repository.revision_tree(
2151
_mod_revision.NULL_REVISION)
2153
rt = self.branch.repository.revision_tree(revision_ids[0])
2154
self._write_inventory(rt.inventory)
2155
self.set_parent_ids(revision_ids)
2158
"""Write the in memory inventory to disk."""
2159
# TODO: Maybe this should only write on dirty ?
2160
if self._control_files._lock_mode != 'w':
2161
raise errors.NotWriteLocked(self)
2163
self._serialize(self._inventory, sio)
2165
self._transport.put_file('inventory', sio,
2166
mode=self.bzrdir._get_file_mode())
2167
self._inventory_is_modified = False
2169
def get_file_mtime(self, file_id, path=None):
2170
"""See Tree.get_file_mtime."""
2172
path = self.inventory.id2path(file_id)
2174
return os.lstat(self.abspath(path)).st_mtime
2176
if e.errno == errno.ENOENT:
2177
raise errors.FileTimestampUnavailable(path)
2180
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2181
file_id = self.path2id(path)
2183
# For unversioned files on win32, we just assume they are not
2186
return self._inventory[file_id].executable
2188
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2189
mode = stat_result.st_mode
2190
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2192
if not supports_executable():
2193
def is_executable(self, file_id, path=None):
2194
return self._inventory[file_id].executable
2196
_is_executable_from_path_and_stat = \
2197
_is_executable_from_path_and_stat_from_basis
2199
def is_executable(self, file_id, path=None):
2201
path = self.id2path(file_id)
2202
mode = os.lstat(self.abspath(path)).st_mode
2203
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2205
_is_executable_from_path_and_stat = \
2206
_is_executable_from_path_and_stat_from_stat
2208
@needs_tree_write_lock
2209
def _add(self, files, ids, kinds):
2210
"""See MutableTree._add."""
2211
# TODO: Re-adding a file that is removed in the working copy
2212
# should probably put it back with the previous ID.
2213
# the read and write working inventory should not occur in this
2214
# function - they should be part of lock_write and unlock.
2215
inv = self.inventory
2216
for f, file_id, kind in zip(files, ids, kinds):
2218
inv.add_path(f, kind=kind)
2220
inv.add_path(f, kind=kind, file_id=file_id)
2221
self._inventory_is_modified = True
2223
def revision_tree(self, revision_id):
2224
"""See WorkingTree.revision_id."""
2225
if revision_id == self.last_revision():
2227
xml = self.read_basis_inventory()
2228
except errors.NoSuchFile:
2232
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2233
# dont use the repository revision_tree api because we want
2234
# to supply the inventory.
2235
if inv.revision_id == revision_id:
2236
return revisiontree.InventoryRevisionTree(
2237
self.branch.repository, inv, revision_id)
2238
except errors.BadInventoryFormat:
2240
# raise if there was no inventory, or if we read the wrong inventory.
2241
raise errors.NoSuchRevisionInTree(self, revision_id)
2244
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2245
"""See Tree.annotate_iter
2247
This implementation will use the basis tree implementation if possible.
2248
Lines not in the basis are attributed to CURRENT_REVISION
2250
If there are pending merges, lines added by those merges will be
2251
incorrectly attributed to CURRENT_REVISION (but after committing, the
2252
attribution will be correct).
2254
maybe_file_parent_keys = []
2255
for parent_id in self.get_parent_ids():
2257
parent_tree = self.revision_tree(parent_id)
2258
except errors.NoSuchRevisionInTree:
2259
parent_tree = self.branch.repository.revision_tree(parent_id)
2260
parent_tree.lock_read()
2262
if not parent_tree.has_id(file_id):
2264
ie = parent_tree.inventory[file_id]
2265
if ie.kind != 'file':
2266
# Note: this is slightly unnecessary, because symlinks and
2267
# directories have a "text" which is the empty text, and we
2268
# know that won't mess up annotations. But it seems cleaner
2270
parent_text_key = (file_id, ie.revision)
2271
if parent_text_key not in maybe_file_parent_keys:
2272
maybe_file_parent_keys.append(parent_text_key)
2274
parent_tree.unlock()
2275
graph = _mod_graph.Graph(self.branch.repository.texts)
2276
heads = graph.heads(maybe_file_parent_keys)
2277
file_parent_keys = []
2278
for key in maybe_file_parent_keys:
2280
file_parent_keys.append(key)
2282
# Now we have the parents of this content
2283
annotator = self.branch.repository.texts.get_annotator()
2284
text = self.get_file_text(file_id)
2285
this_key =(file_id, default_revision)
2286
annotator.add_special_text(this_key, file_parent_keys, text)
2287
annotations = [(key[-1], line)
2288
for key, line in annotator.annotate_flat(this_key)]
2291
def _put_rio(self, filename, stanzas, header):
2292
self._must_be_locked()
2293
my_file = _mod_rio.rio_file(stanzas, header)
2294
self._transport.put_file(filename, my_file,
2295
mode=self.bzrdir._get_file_mode())
2297
@needs_tree_write_lock
2298
def set_merge_modified(self, modified_hashes):
2300
for file_id, hash in modified_hashes.iteritems():
2301
yield _mod_rio.Stanza(file_id=file_id.decode('utf8'),
2303
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
2306
def merge_modified(self):
2307
"""Return a dictionary of files modified by a merge.
2309
The list is initialized by WorkingTree.set_merge_modified, which is
2310
typically called after we make some automatic updates to the tree
2313
This returns a map of file_id->sha1, containing only files which are
2314
still in the working inventory and have that text hash.
2317
hashfile = self._transport.get('merge-hashes')
2318
except errors.NoSuchFile:
2323
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2324
raise errors.MergeModifiedFormatError()
2325
except StopIteration:
2326
raise errors.MergeModifiedFormatError()
2327
for s in _mod_rio.RioReader(hashfile):
2328
# RioReader reads in Unicode, so convert file_ids back to utf8
2329
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2330
if not self.inventory.has_id(file_id):
2332
text_hash = s.get("hash")
2333
if text_hash == self.get_file_sha1(file_id):
2334
merge_hashes[file_id] = text_hash
2340
def subsume(self, other_tree):
2341
def add_children(inventory, entry):
2342
for child_entry in entry.children.values():
2343
inventory._byid[child_entry.file_id] = child_entry
2344
if child_entry.kind == 'directory':
2345
add_children(inventory, child_entry)
2346
if other_tree.get_root_id() == self.get_root_id():
2347
raise errors.BadSubsumeSource(self, other_tree,
2348
'Trees have the same root')
2350
other_tree_path = self.relpath(other_tree.basedir)
2351
except errors.PathNotChild:
2352
raise errors.BadSubsumeSource(self, other_tree,
2353
'Tree is not contained by the other')
2354
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2355
if new_root_parent is None:
2356
raise errors.BadSubsumeSource(self, other_tree,
2357
'Parent directory is not versioned.')
2358
# We need to ensure that the result of a fetch will have a
2359
# versionedfile for the other_tree root, and only fetching into
2360
# RepositoryKnit2 guarantees that.
2361
if not self.branch.repository.supports_rich_root():
2362
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2363
other_tree.lock_tree_write()
2365
new_parents = other_tree.get_parent_ids()
2366
other_root = other_tree.inventory.root
2367
other_root.parent_id = new_root_parent
2368
other_root.name = osutils.basename(other_tree_path)
2369
self.inventory.add(other_root)
2370
add_children(self.inventory, other_root)
2371
self._write_inventory(self.inventory)
2372
# normally we don't want to fetch whole repositories, but i think
2373
# here we really do want to consolidate the whole thing.
2374
for parent_id in other_tree.get_parent_ids():
2375
self.branch.fetch(other_tree.branch, parent_id)
2376
self.add_parent_tree_id(parent_id)
2379
other_tree.bzrdir.retire_bzrdir()
2381
@needs_tree_write_lock
2382
def extract(self, file_id, format=None):
2383
"""Extract a subtree from this tree.
2385
A new branch will be created, relative to the path for this tree.
2389
segments = osutils.splitpath(path)
2390
transport = self.branch.bzrdir.root_transport
2391
for name in segments:
2392
transport = transport.clone(name)
2393
transport.ensure_base()
2396
sub_path = self.id2path(file_id)
2397
branch_transport = mkdirs(sub_path)
2399
format = self.bzrdir.cloning_metadir()
2400
branch_transport.ensure_base()
2401
branch_bzrdir = format.initialize_on_transport(branch_transport)
2403
repo = branch_bzrdir.find_repository()
2404
except errors.NoRepositoryPresent:
2405
repo = branch_bzrdir.create_repository()
2406
if not repo.supports_rich_root():
2407
raise errors.RootNotRich()
2408
new_branch = branch_bzrdir.create_branch()
2409
new_branch.pull(self.branch)
2410
for parent_id in self.get_parent_ids():
2411
new_branch.fetch(self.branch, parent_id)
2412
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2413
if tree_transport.base != branch_transport.base:
2414
tree_bzrdir = format.initialize_on_transport(tree_transport)
2415
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2416
target_branch=new_branch)
2418
tree_bzrdir = branch_bzrdir
2419
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2420
wt.set_parent_ids(self.get_parent_ids())
2421
my_inv = self.inventory
2422
child_inv = inventory.Inventory(root_id=None)
2423
new_root = my_inv[file_id]
2424
my_inv.remove_recursive_id(file_id)
2425
new_root.parent_id = None
2426
child_inv.add(new_root)
2427
self._write_inventory(my_inv)
2428
wt._write_inventory(child_inv)
2431
def list_files(self, include_root=False, from_dir=None, recursive=True):
2432
"""List all files as (path, class, kind, id, entry).
2434
Lists, but does not descend into unversioned directories.
2435
This does not include files that have been deleted in this
2436
tree. Skips the control directory.
2438
:param include_root: if True, return an entry for the root
2439
:param from_dir: start from this directory or None for the root
2440
:param recursive: whether to recurse into subdirectories or not
2442
# list_files is an iterator, so @needs_read_lock doesn't work properly
2443
# with it. So callers should be careful to always read_lock the tree.
2444
if not self.is_locked():
2445
raise errors.ObjectNotLocked(self)
2447
inv = self.inventory
2448
if from_dir is None and include_root is True:
2449
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2450
# Convert these into local objects to save lookup times
2451
pathjoin = osutils.pathjoin
2452
file_kind = self._kind
2454
# transport.base ends in a slash, we want the piece
2455
# between the last two slashes
2456
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2458
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2460
# directory file_id, relative path, absolute path, reverse sorted children
2461
if from_dir is not None:
2462
from_dir_id = inv.path2id(from_dir)
2463
if from_dir_id is None:
2464
# Directory not versioned
2466
from_dir_abspath = pathjoin(self.basedir, from_dir)
2468
from_dir_id = inv.root.file_id
2469
from_dir_abspath = self.basedir
2470
children = os.listdir(from_dir_abspath)
2472
# jam 20060527 The kernel sized tree seems equivalent whether we
2473
# use a deque and popleft to keep them sorted, or if we use a plain
2474
# list and just reverse() them.
2475
children = collections.deque(children)
2476
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2478
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2481
f = children.popleft()
2482
## TODO: If we find a subdirectory with its own .bzr
2483
## directory, then that is a separate tree and we
2484
## should exclude it.
2486
# the bzrdir for this tree
2487
if transport_base_dir == f:
2490
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2491
# and 'f' doesn't begin with one, we can do a string op, rather
2492
# than the checks of pathjoin(), all relative paths will have an extra slash
2494
fp = from_dir_relpath + '/' + f
2497
fap = from_dir_abspath + '/' + f
2499
dir_ie = inv[from_dir_id]
2500
if dir_ie.kind == 'directory':
2501
f_ie = dir_ie.children.get(f)
2506
elif self.is_ignored(fp[1:]):
2509
# we may not have found this file, because of a unicode
2510
# issue, or because the directory was actually a symlink.
2511
f_norm, can_access = osutils.normalized_filename(f)
2512
if f == f_norm or not can_access:
2513
# No change, so treat this file normally
2516
# this file can be accessed by a normalized path
2517
# check again if it is versioned
2518
# these lines are repeated here for performance
2520
fp = from_dir_relpath + '/' + f
2521
fap = from_dir_abspath + '/' + f
2522
f_ie = inv.get_child(from_dir_id, f)
2525
elif self.is_ignored(fp[1:]):
2532
# make a last minute entry
2534
yield fp[1:], c, fk, f_ie.file_id, f_ie
2537
yield fp[1:], c, fk, None, fk_entries[fk]()
2539
yield fp[1:], c, fk, None, TreeEntry()
2542
if fk != 'directory':
2545
# But do this child first if recursing down
2547
new_children = os.listdir(fap)
2549
new_children = collections.deque(new_children)
2550
stack.append((f_ie.file_id, fp, fap, new_children))
2551
# Break out of inner loop,
2552
# so that we start outer loop with child
2555
# if we finished all children, pop it off the stack
2558
@needs_tree_write_lock
2559
def move(self, from_paths, to_dir=None, after=False):
2562
to_dir must exist in the inventory.
2564
If to_dir exists and is a directory, the files are moved into
2565
it, keeping their old names.
2567
Note that to_dir is only the last component of the new name;
2568
this doesn't change the directory.
2570
For each entry in from_paths the move mode will be determined
2573
The first mode moves the file in the filesystem and updates the
2574
inventory. The second mode only updates the inventory without
2575
touching the file on the filesystem.
2577
move uses the second mode if 'after == True' and the target is
2578
either not versioned or newly added, and present in the working tree.
2580
move uses the second mode if 'after == False' and the source is
2581
versioned but no longer in the working tree, and the target is not
2582
versioned but present in the working tree.
2584
move uses the first mode if 'after == False' and the source is
2585
versioned and present in the working tree, and the target is not
2586
versioned and not present in the working tree.
2588
Everything else results in an error.
2590
This returns a list of (from_path, to_path) pairs for each
2591
entry that is moved.
2596
# check for deprecated use of signature
2598
raise TypeError('You must supply a target directory')
2599
# check destination directory
2600
if isinstance(from_paths, basestring):
2602
inv = self.inventory
2603
to_abs = self.abspath(to_dir)
2604
if not isdir(to_abs):
2605
raise errors.BzrMoveFailedError('',to_dir,
2606
errors.NotADirectory(to_abs))
2607
if not self.has_filename(to_dir):
2608
raise errors.BzrMoveFailedError('',to_dir,
2609
errors.NotInWorkingDirectory(to_dir))
2610
to_dir_id = inv.path2id(to_dir)
2611
if to_dir_id is None:
2612
raise errors.BzrMoveFailedError('',to_dir,
2613
errors.NotVersionedError(path=to_dir))
2615
to_dir_ie = inv[to_dir_id]
2616
if to_dir_ie.kind != 'directory':
2617
raise errors.BzrMoveFailedError('',to_dir,
2618
errors.NotADirectory(to_abs))
2620
# create rename entries and tuples
2621
for from_rel in from_paths:
2622
from_tail = splitpath(from_rel)[-1]
2623
from_id = inv.path2id(from_rel)
2625
raise errors.BzrMoveFailedError(from_rel,to_dir,
2626
errors.NotVersionedError(path=from_rel))
2628
from_entry = inv[from_id]
2629
from_parent_id = from_entry.parent_id
2630
to_rel = pathjoin(to_dir, from_tail)
2631
rename_entry = InventoryWorkingTree._RenameEntry(
2634
from_tail=from_tail,
2635
from_parent_id=from_parent_id,
2636
to_rel=to_rel, to_tail=from_tail,
2637
to_parent_id=to_dir_id)
2638
rename_entries.append(rename_entry)
2639
rename_tuples.append((from_rel, to_rel))
2641
# determine which move mode to use. checks also for movability
2642
rename_entries = self._determine_mv_mode(rename_entries, after)
2644
original_modified = self._inventory_is_modified
2647
self._inventory_is_modified = True
2648
self._move(rename_entries)
2650
# restore the inventory on error
2651
self._inventory_is_modified = original_modified
2653
self._write_inventory(inv)
2654
return rename_tuples
2656
@needs_tree_write_lock
2657
def rename_one(self, from_rel, to_rel, after=False):
2660
This can change the directory or the filename or both.
2662
rename_one has several 'modes' to work. First, it can rename a physical
2663
file and change the file_id. That is the normal mode. Second, it can
2664
only change the file_id without touching any physical file.
2666
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2667
versioned but present in the working tree.
2669
rename_one uses the second mode if 'after == False' and 'from_rel' is
2670
versioned but no longer in the working tree, and 'to_rel' is not
2671
versioned but present in the working tree.
2673
rename_one uses the first mode if 'after == False' and 'from_rel' is
2674
versioned and present in the working tree, and 'to_rel' is not
2675
versioned and not present in the working tree.
2677
Everything else results in an error.
2679
inv = self.inventory
2682
# create rename entries and tuples
2683
from_tail = splitpath(from_rel)[-1]
2684
from_id = inv.path2id(from_rel)
2686
# if file is missing in the inventory maybe it's in the basis_tree
2687
basis_tree = self.branch.basis_tree()
2688
from_id = basis_tree.path2id(from_rel)
2690
raise errors.BzrRenameFailedError(from_rel,to_rel,
2691
errors.NotVersionedError(path=from_rel))
2692
# put entry back in the inventory so we can rename it
2693
from_entry = basis_tree.inventory[from_id].copy()
2696
from_entry = inv[from_id]
2697
from_parent_id = from_entry.parent_id
2698
to_dir, to_tail = os.path.split(to_rel)
2699
to_dir_id = inv.path2id(to_dir)
2700
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2702
from_tail=from_tail,
2703
from_parent_id=from_parent_id,
2704
to_rel=to_rel, to_tail=to_tail,
2705
to_parent_id=to_dir_id)
2706
rename_entries.append(rename_entry)
2708
# determine which move mode to use. checks also for movability
2709
rename_entries = self._determine_mv_mode(rename_entries, after)
2711
# check if the target changed directory and if the target directory is
2713
if to_dir_id is None:
2714
raise errors.BzrMoveFailedError(from_rel,to_rel,
2715
errors.NotVersionedError(path=to_dir))
2717
# all checks done. now we can continue with our actual work
2718
mutter('rename_one:\n'
2723
' to_dir_id {%s}\n',
2724
from_id, from_rel, to_rel, to_dir, to_dir_id)
2726
self._move(rename_entries)
2727
self._write_inventory(inv)
2729
class _RenameEntry(object):
2730
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2731
to_rel, to_tail, to_parent_id, only_change_inv=False,
2733
self.from_rel = from_rel
2734
self.from_id = from_id
2735
self.from_tail = from_tail
2736
self.from_parent_id = from_parent_id
2737
self.to_rel = to_rel
2738
self.to_tail = to_tail
2739
self.to_parent_id = to_parent_id
2740
self.change_id = change_id
2741
self.only_change_inv = only_change_inv
2743
def _determine_mv_mode(self, rename_entries, after=False):
2744
"""Determines for each from-to pair if both inventory and working tree
2745
or only the inventory has to be changed.
2747
Also does basic plausability tests.
2749
inv = self.inventory
2751
for rename_entry in rename_entries:
2752
# store to local variables for easier reference
2753
from_rel = rename_entry.from_rel
2754
from_id = rename_entry.from_id
2755
to_rel = rename_entry.to_rel
2756
to_id = inv.path2id(to_rel)
2757
only_change_inv = False
2760
# check the inventory for source and destination
2762
raise errors.BzrMoveFailedError(from_rel,to_rel,
2763
errors.NotVersionedError(path=from_rel))
2764
if to_id is not None:
2766
# allow it with --after but only if dest is newly added
2768
basis = self.basis_tree()
2771
if not basis.has_id(to_id):
2772
rename_entry.change_id = True
2777
raise errors.BzrMoveFailedError(from_rel,to_rel,
2778
errors.AlreadyVersionedError(path=to_rel))
2780
# try to determine the mode for rename (only change inv or change
2781
# inv and file system)
2783
if not self.has_filename(to_rel):
2784
raise errors.BzrMoveFailedError(from_id,to_rel,
2785
errors.NoSuchFile(path=to_rel,
2786
extra="New file has not been created yet"))
2787
only_change_inv = True
2788
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2789
only_change_inv = True
2790
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2791
only_change_inv = False
2792
elif (not self.case_sensitive
2793
and from_rel.lower() == to_rel.lower()
2794
and self.has_filename(from_rel)):
2795
only_change_inv = False
2797
# something is wrong, so lets determine what exactly
2798
if not self.has_filename(from_rel) and \
2799
not self.has_filename(to_rel):
2800
raise errors.BzrRenameFailedError(from_rel, to_rel,
2801
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
2803
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2804
rename_entry.only_change_inv = only_change_inv
2805
return rename_entries
2807
def _move(self, rename_entries):
2808
"""Moves a list of files.
2810
Depending on the value of the flag 'only_change_inv', the
2811
file will be moved on the file system or not.
2813
inv = self.inventory
2816
for entry in rename_entries:
2818
self._move_entry(entry)
2820
self._rollback_move(moved)
2824
def _rollback_move(self, moved):
2825
"""Try to rollback a previous move in case of an filesystem error."""
2826
inv = self.inventory
2829
self._move_entry(WorkingTree._RenameEntry(
2830
entry.to_rel, entry.from_id,
2831
entry.to_tail, entry.to_parent_id, entry.from_rel,
2832
entry.from_tail, entry.from_parent_id,
2833
entry.only_change_inv))
2834
except errors.BzrMoveFailedError, e:
2835
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2836
" The working tree is in an inconsistent state."
2837
" Please consider doing a 'bzr revert'."
2838
" Error message is: %s" % e)
2840
def _move_entry(self, entry):
2841
inv = self.inventory
2842
from_rel_abs = self.abspath(entry.from_rel)
2843
to_rel_abs = self.abspath(entry.to_rel)
2844
if from_rel_abs == to_rel_abs:
2845
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2846
"Source and target are identical.")
2848
if not entry.only_change_inv:
2850
osutils.rename(from_rel_abs, to_rel_abs)
2852
raise errors.BzrMoveFailedError(entry.from_rel,
2855
to_id = inv.path2id(entry.to_rel)
2856
inv.remove_recursive_id(to_id)
2857
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2859
@needs_tree_write_lock
2860
def unversion(self, file_ids):
2861
"""Remove the file ids in file_ids from the current versioned set.
2863
When a file_id is unversioned, all of its children are automatically
2866
:param file_ids: The file ids to stop versioning.
2867
:raises: NoSuchId if any fileid is not currently versioned.
2869
for file_id in file_ids:
2870
if not self._inventory.has_id(file_id):
2871
raise errors.NoSuchId(self, file_id)
2872
for file_id in file_ids:
2873
if self._inventory.has_id(file_id):
2874
self._inventory.remove_recursive_id(file_id)
2876
# in the future this should just set a dirty bit to wait for the
2877
# final unlock. However, until all methods of workingtree start
2878
# with the current in -memory inventory rather than triggering
2879
# a read, it is more complex - we need to teach read_inventory
2880
# to know when to read, and when to not read first... and possibly
2881
# to save first when the in memory one may be corrupted.
2882
# so for now, we just only write it if it is indeed dirty.
2884
self._write_inventory(self._inventory)
2886
def stored_kind(self, file_id):
2887
"""See Tree.stored_kind"""
2888
return self.inventory[file_id].kind
2891
"""Yield all unversioned files in this WorkingTree.
2893
If there are any unversioned directories then only the directory is
2894
returned, not all its children. But if there are unversioned files
2895
under a versioned subdirectory, they are returned.
2897
Currently returned depth-first, sorted by name within directories.
2898
This is the same order used by 'osutils.walkdirs'.
2900
## TODO: Work from given directory downwards
2901
for path, dir_entry in self.inventory.directories():
2902
# mutter("search for unknowns in %r", path)
2903
dirabs = self.abspath(path)
2904
if not isdir(dirabs):
2905
# e.g. directory deleted
2909
for subf in os.listdir(dirabs):
2910
if self.bzrdir.is_control_filename(subf):
2912
if subf not in dir_entry.children:
2915
can_access) = osutils.normalized_filename(subf)
2916
except UnicodeDecodeError:
2917
path_os_enc = path.encode(osutils._fs_enc)
2918
relpath = path_os_enc + '/' + subf
2919
raise errors.BadFilenameEncoding(relpath,
2921
if subf_norm != subf and can_access:
2922
if subf_norm not in dir_entry.children:
2923
fl.append(subf_norm)
2929
subp = pathjoin(path, subf)
2932
def _walkdirs(self, prefix=""):
2933
"""Walk the directories of this tree.
2935
:param prefix: is used as the directrory to start with.
2936
:returns: a generator which yields items in the form::
2938
((curren_directory_path, fileid),
2939
[(file1_path, file1_name, file1_kind, None, file1_id,
2942
_directory = 'directory'
2943
# get the root in the inventory
2944
inv = self.inventory
2945
top_id = inv.path2id(prefix)
2949
pending = [(prefix, '', _directory, None, top_id, None)]
2952
currentdir = pending.pop()
2953
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2954
top_id = currentdir[4]
2956
relroot = currentdir[0] + '/'
2959
# FIXME: stash the node in pending
2961
if entry.kind == 'directory':
2962
for name, child in entry.sorted_children():
2963
dirblock.append((relroot + name, name, child.kind, None,
2964
child.file_id, child.kind
2966
yield (currentdir[0], entry.file_id), dirblock
2967
# push the user specified dirs from dirblock
2968
for dir in reversed(dirblock):
2969
if dir[2] == _directory:
2973
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
2974
"""Registry for working tree formats."""
2976
def __init__(self, other_registry=None):
2977
super(WorkingTreeFormatRegistry, self).__init__(other_registry)
2978
self._default_format = None
2979
self._default_format_key = None
2981
def get_default(self):
2982
"""Return the current default format."""
2983
if (self._default_format_key is not None and
2984
self._default_format is None):
2985
self._default_format = self.get(self._default_format_key)
2986
return self._default_format
2988
def set_default(self, format):
2989
"""Set the default format."""
2990
self._default_format = format
2991
self._default_format_key = None
2993
def set_default_key(self, format_string):
2994
"""Set the default format by its format string."""
2995
self._default_format_key = format_string
2996
self._default_format = None
2999
format_registry = WorkingTreeFormatRegistry()
3002
class WorkingTreeFormat(controldir.ControlComponentFormat):
3003
"""An encapsulation of the initialization and open routines for a format.
3005
Formats provide three things:
3006
* An initialization routine,
3010
Formats are placed in an dict by their format string for reference
3011
during workingtree opening. Its not required that these be instances, they
3012
can be classes themselves with class methods - it simply depends on
3013
whether state is needed for a given format or not.
3015
Once a format is deprecated, just deprecate the initialize and open
3016
methods on the format class. Do not deprecate the object, as the
3017
object will be created every time regardless.
3020
requires_rich_root = False
3022
upgrade_recommended = False
3024
requires_normalized_unicode_filenames = False
3026
case_sensitive_filename = "FoRMaT"
3028
missing_parent_conflicts = False
3029
"""If this format supports missing parent conflicts."""
3031
supports_versioned_directories = None
3033
def initialize(self, controldir, revision_id=None, from_branch=None,
3034
accelerator_tree=None, hardlink=False):
3035
"""Initialize a new working tree in controldir.
3037
:param controldir: ControlDir to initialize the working tree in.
3038
:param revision_id: allows creating a working tree at a different
3039
revision than the branch is at.
3040
:param from_branch: Branch to checkout
3041
:param accelerator_tree: A tree which can be used for retrieving file
3042
contents more quickly than the revision tree, i.e. a workingtree.
3043
The revision tree will be used for cases where accelerator_tree's
3044
content is different.
3045
:param hardlink: If true, hard-link files from accelerator_tree,
3048
raise NotImplementedError(self.initialize)
3050
def __eq__(self, other):
3051
return self.__class__ is other.__class__
3053
def __ne__(self, other):
3054
return not (self == other)
3057
@symbol_versioning.deprecated_method(
3058
symbol_versioning.deprecated_in((2, 4, 0)))
3059
def get_default_format(klass):
3060
"""Return the current default format."""
3061
return format_registry.get_default()
3063
def get_format_description(self):
3064
"""Return the short description for this format."""
3065
raise NotImplementedError(self.get_format_description)
3067
def is_supported(self):
3068
"""Is this format supported?
3070
Supported formats can be initialized and opened.
3071
Unsupported formats may not support initialization or committing or
3072
some other features depending on the reason for not being supported.
3076
def supports_content_filtering(self):
3077
"""True if this format supports content filtering."""
3080
def supports_views(self):
3081
"""True if this format supports stored views."""
3085
@symbol_versioning.deprecated_method(
3086
symbol_versioning.deprecated_in((2, 4, 0)))
3087
def register_format(klass, format):
3088
format_registry.register(format)
3091
@symbol_versioning.deprecated_method(
3092
symbol_versioning.deprecated_in((2, 4, 0)))
3093
def register_extra_format(klass, format):
3094
format_registry.register_extra(format)
3097
@symbol_versioning.deprecated_method(
3098
symbol_versioning.deprecated_in((2, 4, 0)))
3099
def unregister_extra_format(klass, format):
3100
format_registry.unregister_extra(format)
3103
@symbol_versioning.deprecated_method(
3104
symbol_versioning.deprecated_in((2, 4, 0)))
3105
def get_formats(klass):
3106
return format_registry._get_all()
3109
@symbol_versioning.deprecated_method(
3110
symbol_versioning.deprecated_in((2, 4, 0)))
3111
def set_default_format(klass, format):
3112
format_registry.set_default(format)
3115
@symbol_versioning.deprecated_method(
3116
symbol_versioning.deprecated_in((2, 4, 0)))
3117
def unregister_format(klass, format):
3118
format_registry.remove(format)
3120
def get_controldir_for_branch(self):
3121
"""Get the control directory format for creating branches.
3123
This is to support testing of working tree formats that can not exist
3124
in the same control directory as a branch.
3126
return self._matchingbzrdir
3129
class WorkingTreeFormatMetaDir(bzrdir.BzrDirMetaComponentFormat, WorkingTreeFormat):
3130
"""Base class for working trees that live in bzr meta directories."""
3133
WorkingTreeFormat.__init__(self)
3134
bzrdir.BzrDirMetaComponentFormat.__init__(self)
3137
def find_format_string(klass, controldir):
3138
"""Return format name for the working tree object in controldir."""
3140
transport = controldir.get_workingtree_transport(None)
3141
return transport.get_bytes("format")
3142
except errors.NoSuchFile:
3143
raise errors.NoWorkingTree(base=transport.base)
3146
def find_format(klass, controldir):
3147
"""Return the format for the working tree object in controldir."""
3148
format_string = klass.find_format_string(controldir)
3149
return klass._find_format(format_registry, 'working tree',
3153
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3154
"bzrlib.workingtree_4", "WorkingTreeFormat4")
3155
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
3156
"bzrlib.workingtree_4", "WorkingTreeFormat5")
3157
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
3158
"bzrlib.workingtree_4", "WorkingTreeFormat6")
3159
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
3160
"bzrlib.workingtree_3", "WorkingTreeFormat3")
3161
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")