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
"""WorkingTree object and friends.
19
A WorkingTree represents the editable working copy of a branch.
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files.
23
At the moment every WorkingTree has its own branch. Remote
24
WorkingTrees aren't supported.
26
To get a WorkingTree, call bzrdir.open_workingtree() or
27
WorkingTree.open(dir).
30
from __future__ import absolute_import
39
from .lazy_import import lazy_import
40
lazy_import(globals(), """
41
from bisect import bisect_left
48
conflicts as _mod_conflicts,
51
filters as _mod_filters,
56
revision as _mod_revision,
68
from .decorators import needs_read_lock, needs_write_lock
69
from .i18n import gettext
70
from . import mutabletree
71
from .mutabletree import needs_tree_write_lock
72
from .trace import mutter, note
75
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
78
class TreeEntry(object):
79
"""An entry that implements the minimum interface used by commands.
81
This needs further inspection, it may be better to have
82
InventoryEntries without ids - though that seems wrong. For now,
83
this is a parallel hierarchy to InventoryEntry, and needs to become
84
one of several things: decorates to that hierarchy, children of, or
86
Another note is that these objects are currently only used when there is
87
no InventoryEntry available - i.e. for unversioned objects.
88
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
91
def __eq__(self, other):
92
# yes, this us ugly, TODO: best practice __eq__ style.
93
return (isinstance(other, TreeEntry)
94
and other.__class__ == self.__class__)
96
def kind_character(self):
100
class TreeDirectory(TreeEntry):
101
"""See TreeEntry. This is a directory in a working tree."""
103
def __eq__(self, other):
104
return (isinstance(other, TreeDirectory)
105
and other.__class__ == self.__class__)
107
def kind_character(self):
111
class TreeFile(TreeEntry):
112
"""See TreeEntry. This is a regular file in a working tree."""
114
def __eq__(self, other):
115
return (isinstance(other, TreeFile)
116
and other.__class__ == self.__class__)
118
def kind_character(self):
122
class TreeLink(TreeEntry):
123
"""See TreeEntry. This is a symlink in a working tree."""
125
def __eq__(self, other):
126
return (isinstance(other, TreeLink)
127
and other.__class__ == self.__class__)
129
def kind_character(self):
133
class WorkingTree(mutabletree.MutableTree,
134
controldir.ControlComponent):
135
"""Working copy tree.
137
:ivar basedir: The root of the tree on disk. This is a unicode path object
138
(as opposed to a URL).
141
# override this to set the strategy for storing views
142
def _make_views(self):
143
return views.DisabledViews(self)
145
def __init__(self, basedir='.',
151
"""Construct a WorkingTree instance. This is not a public API.
153
:param branch: A branch to override probing for the branch.
155
self._format = _format
156
self.bzrdir = _bzrdir
158
raise errors.BzrError("Please use bzrdir.open_workingtree or "
159
"WorkingTree.open() to obtain a WorkingTree.")
160
basedir = osutils.safe_unicode(basedir)
161
mutter("opening working tree %r", basedir)
162
if branch is not None:
163
self._branch = branch
165
self._branch = self.bzrdir.open_branch()
166
self.basedir = osutils.realpath(basedir)
167
self._transport = _transport
168
self._rules_searcher = None
169
self.views = self._make_views()
172
def user_transport(self):
173
return self.bzrdir.user_transport
176
def control_transport(self):
177
return self._transport
179
def is_control_filename(self, filename):
180
"""True if filename is the name of a control file in this tree.
182
:param filename: A filename within the tree. This is a relative path
183
from the root of this tree.
185
This is true IF and ONLY IF the filename is part of the meta data
186
that bzr controls in this tree. I.E. a random .bzr directory placed
187
on disk will not be a control file for this tree.
189
return self.bzrdir.is_control_filename(filename)
192
fget=lambda self: self._branch,
193
doc="""The branch this WorkingTree is connected to.
195
This cannot be set - it is reflective of the actual disk structure
196
the working tree has been constructed from.
199
def has_versioned_directories(self):
200
"""See `Tree.has_versioned_directories`."""
201
return self._format.supports_versioned_directories
203
def _supports_executable(self):
204
if sys.platform == 'win32':
206
# FIXME: Ideally this should check the file system
209
def break_lock(self):
210
"""Break a lock if one is present from another instance.
212
Uses the ui factory to ask for confirmation if the lock may be from
215
This will probe the repository for its lock as well.
217
raise NotImplementedError(self.break_lock)
219
def requires_rich_root(self):
220
return self._format.requires_rich_root
222
def supports_tree_reference(self):
225
def supports_content_filtering(self):
226
return self._format.supports_content_filtering()
228
def supports_views(self):
229
return self.views.supports_views()
231
def get_config_stack(self):
232
"""Retrieve the config stack for this tree.
234
:return: A ``breezy.config.Stack``
236
# For the moment, just provide the branch config stack.
237
return self.branch.get_config_stack()
240
def open(path=None, _unsupported=False):
241
"""Open an existing working tree at path.
245
path = osutils.getcwd()
246
control = controldir.ControlDir.open(path, _unsupported=_unsupported)
247
return control.open_workingtree(unsupported=_unsupported)
250
def open_containing(path=None):
251
"""Open an existing working tree which has its root about path.
253
This probes for a working tree at path and searches upwards from there.
255
Basically we keep looking up until we find the control directory or
256
run into /. If there isn't one, raises NotBranchError.
257
TODO: give this a new exception.
258
If there is one, it is returned, along with the unused portion of path.
260
:return: The WorkingTree that contains 'path', and the rest of path
263
path = osutils.getcwd()
264
control, relpath = controldir.ControlDir.open_containing(path)
265
return control.open_workingtree(), relpath
268
def open_containing_paths(file_list, default_directory=None,
269
canonicalize=True, apply_view=True):
270
"""Open the WorkingTree that contains a set of paths.
272
Fail if the paths given are not all in a single tree.
274
This is used for the many command-line interfaces that take a list of
275
any number of files and that require they all be in the same tree.
277
if default_directory is None:
278
default_directory = u'.'
279
# recommended replacement for builtins.internal_tree_files
280
if file_list is None or len(file_list) == 0:
281
tree = WorkingTree.open_containing(default_directory)[0]
282
# XXX: doesn't really belong here, and seems to have the strange
283
# side effect of making it return a bunch of files, not the whole
284
# tree -- mbp 20100716
285
if tree.supports_views() and apply_view:
286
view_files = tree.views.lookup_view()
288
file_list = view_files
289
view_str = views.view_display_str(view_files)
290
note(gettext("Ignoring files outside view. View is %s") % view_str)
291
return tree, file_list
292
if default_directory == u'.':
295
seed = default_directory
296
file_list = [osutils.pathjoin(default_directory, f)
298
tree = WorkingTree.open_containing(seed)[0]
299
return tree, tree.safe_relpath_files(file_list, canonicalize,
300
apply_view=apply_view)
302
def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
303
"""Convert file_list into a list of relpaths in tree.
305
:param self: A tree to operate on.
306
:param file_list: A list of user provided paths or None.
307
:param apply_view: if True and a view is set, apply it or check that
308
specified files are within it
309
:return: A list of relative paths.
310
:raises errors.PathNotChild: When a provided path is in a different self
313
if file_list is None:
315
if self.supports_views() and apply_view:
316
view_files = self.views.lookup_view()
320
# self.relpath exists as a "thunk" to osutils, but canonical_relpath
321
# doesn't - fix that up here before we enter the loop.
323
fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
326
for filename in file_list:
327
relpath = fixer(osutils.dereference_path(filename))
328
if view_files and not osutils.is_inside_any(view_files, relpath):
329
raise errors.FileOutsideView(filename, view_files)
330
new_list.append(relpath)
334
def open_downlevel(path=None):
335
"""Open an unsupported working tree.
337
Only intended for advanced situations like upgrading part of a bzrdir.
339
return WorkingTree.open(path, _unsupported=True)
342
def find_trees(location):
343
def list_current(transport):
344
return [d for d in transport.list_dir('') if d != '.bzr']
345
def evaluate(bzrdir):
347
tree = bzrdir.open_workingtree()
348
except errors.NoWorkingTree:
352
t = transport.get_transport(location)
353
iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
354
list_current=list_current)
355
return [tr for tr in iterator if tr is not None]
358
return "<%s of %s>" % (self.__class__.__name__,
359
getattr(self, 'basedir', None))
361
def abspath(self, filename):
362
return osutils.pathjoin(self.basedir, filename)
364
def basis_tree(self):
365
"""Return RevisionTree for the current last revision.
367
If the left most parent is a ghost then the returned tree will be an
368
empty tree - one obtained by calling
369
repository.revision_tree(NULL_REVISION).
372
revision_id = self.get_parent_ids()[0]
374
# no parents, return an empty revision tree.
375
# in the future this should return the tree for
376
# 'empty:' - the implicit root empty tree.
377
return self.branch.repository.revision_tree(
378
_mod_revision.NULL_REVISION)
380
return self.revision_tree(revision_id)
381
except errors.NoSuchRevision:
383
# No cached copy available, retrieve from the repository.
384
# FIXME? RBC 20060403 should we cache the inventory locally
387
return self.branch.repository.revision_tree(revision_id)
388
except (errors.RevisionNotPresent, errors.NoSuchRevision):
389
# the basis tree *may* be a ghost or a low level error may have
390
# occurred. If the revision is present, its a problem, if its not
392
if self.branch.repository.has_revision(revision_id):
394
# the basis tree is a ghost so return an empty tree.
395
return self.branch.repository.revision_tree(
396
_mod_revision.NULL_REVISION)
399
self._flush_ignore_list_cache()
401
def relpath(self, path):
402
"""Return the local path portion from a given path.
404
The path may be absolute or relative. If its a relative path it is
405
interpreted relative to the python current working directory.
407
return osutils.relpath(self.basedir, path)
409
def has_filename(self, filename):
410
return osutils.lexists(self.abspath(filename))
412
def get_file(self, file_id, path=None, filtered=True):
413
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
415
def get_file_with_stat(self, file_id, path=None, filtered=True,
416
_fstat=osutils.fstat):
417
"""See Tree.get_file_with_stat."""
419
path = self.id2path(file_id)
420
file_obj = self.get_file_byname(path, filtered=False)
421
stat_value = _fstat(file_obj.fileno())
422
if filtered and self.supports_content_filtering():
423
filters = self._content_filter_stack(path)
424
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
425
return (file_obj, stat_value)
427
def get_file_text(self, file_id, path=None, filtered=True):
428
my_file = self.get_file(file_id, path=path, filtered=filtered)
430
return my_file.read()
434
def get_file_byname(self, filename, filtered=True):
435
path = self.abspath(filename)
437
if filtered and self.supports_content_filtering():
438
filters = self._content_filter_stack(filename)
439
return _mod_filters.filtered_input_file(f, filters)
443
def get_file_lines(self, file_id, path=None, filtered=True):
444
"""See Tree.get_file_lines()"""
445
file = self.get_file(file_id, path, filtered=filtered)
447
return file.readlines()
451
def get_parent_ids(self):
452
"""See Tree.get_parent_ids.
454
This implementation reads the pending merges list and last_revision
455
value and uses that to decide what the parents list should be.
457
last_rev = _mod_revision.ensure_null(self._last_revision())
458
if _mod_revision.NULL_REVISION == last_rev:
463
merges_bytes = self._transport.get_bytes('pending-merges')
464
except errors.NoSuchFile:
467
for l in osutils.split_lines(merges_bytes):
468
revision_id = l.rstrip('\n')
469
parents.append(revision_id)
472
def get_root_id(self):
473
"""Return the id of this trees root"""
474
raise NotImplementedError(self.get_root_id)
477
def clone(self, to_controldir, revision_id=None):
478
"""Duplicate this working tree into to_bzr, including all state.
480
Specifically modified files are kept as modified, but
481
ignored and unknown files are discarded.
483
If you want to make a new line of development, see ControlDir.sprout()
486
If not None, the cloned tree will have its last revision set to
487
revision, and difference between the source trees last revision
488
and this one merged in.
490
# assumes the target bzr dir format is compatible.
491
result = to_controldir.create_workingtree()
492
self.copy_content_into(result, revision_id)
496
def copy_content_into(self, tree, revision_id=None):
497
"""Copy the current content and user files of this tree into tree."""
498
tree.set_root_id(self.get_root_id())
499
if revision_id is None:
500
merge.transform_tree(tree, self)
502
# TODO now merge from tree.last_revision to revision (to preserve
503
# user local changes)
505
other_tree = self.revision_tree(revision_id)
506
except errors.NoSuchRevision:
507
other_tree = self.branch.repository.revision_tree(revision_id)
509
merge.transform_tree(tree, other_tree)
510
if revision_id == _mod_revision.NULL_REVISION:
513
new_parents = [revision_id]
514
tree.set_parent_ids(new_parents)
516
def id2abspath(self, file_id):
517
return self.abspath(self.id2path(file_id))
519
def get_file_size(self, file_id):
520
"""See Tree.get_file_size"""
521
# XXX: this returns the on-disk size; it should probably return the
524
return os.path.getsize(self.id2abspath(file_id))
526
if e.errno != errno.ENOENT:
531
@needs_tree_write_lock
532
def _gather_kinds(self, files, kinds):
533
"""See MutableTree._gather_kinds."""
534
for pos, f in enumerate(files):
535
if kinds[pos] is None:
536
fullpath = osutils.normpath(self.abspath(f))
538
kinds[pos] = osutils.file_kind(fullpath)
540
if e.errno == errno.ENOENT:
541
raise errors.NoSuchFile(fullpath)
544
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
545
"""Add revision_id as a parent.
547
This is equivalent to retrieving the current list of parent ids
548
and setting the list to its value plus revision_id.
550
:param revision_id: The revision id to add to the parent list. It may
551
be a ghost revision as long as its not the first parent to be
552
added, or the allow_leftmost_as_ghost parameter is set True.
553
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
555
parents = self.get_parent_ids() + [revision_id]
556
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
557
or allow_leftmost_as_ghost)
559
@needs_tree_write_lock
560
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
561
"""Add revision_id, tree tuple as a parent.
563
This is equivalent to retrieving the current list of parent trees
564
and setting the list to its value plus parent_tuple. See also
565
add_parent_tree_id - if you only have a parent id available it will be
566
simpler to use that api. If you have the parent already available, using
567
this api is preferred.
569
:param parent_tuple: The (revision id, tree) to add to the parent list.
570
If the revision_id is a ghost, pass None for the tree.
571
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
573
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
574
if len(parent_ids) > 1:
575
# the leftmost may have already been a ghost, preserve that if it
577
allow_leftmost_as_ghost = True
578
self.set_parent_ids(parent_ids,
579
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
581
@needs_tree_write_lock
582
def add_pending_merge(self, *revision_ids):
583
# TODO: Perhaps should check at this point that the
584
# history of the revision is actually present?
585
parents = self.get_parent_ids()
587
for rev_id in revision_ids:
588
if rev_id in parents:
590
parents.append(rev_id)
593
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
595
def path_content_summary(self, path, _lstat=os.lstat,
596
_mapper=osutils.file_kind_from_stat_mode):
597
"""See Tree.path_content_summary."""
598
abspath = self.abspath(path)
600
stat_result = _lstat(abspath)
602
if getattr(e, 'errno', None) == errno.ENOENT:
604
return ('missing', None, None, None)
605
# propagate other errors
607
kind = _mapper(stat_result.st_mode)
609
return self._file_content_summary(path, stat_result)
610
elif kind == 'directory':
611
# perhaps it looks like a plain directory, but it's really a
613
if self._directory_is_tree_reference(path):
614
kind = 'tree-reference'
615
return kind, None, None, None
616
elif kind == 'symlink':
617
target = osutils.readlink(abspath)
618
return ('symlink', None, None, target)
620
return (kind, None, None, None)
622
def _file_content_summary(self, path, stat_result):
623
size = stat_result.st_size
624
executable = self._is_executable_from_path_and_stat(path, stat_result)
625
# try for a stat cache lookup
626
return ('file', size, executable, self._sha_from_stat(
629
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
630
"""Common ghost checking functionality from set_parent_*.
632
This checks that the left hand-parent exists if there are any
635
if len(revision_ids) > 0:
636
leftmost_id = revision_ids[0]
637
if (not allow_leftmost_as_ghost and not
638
self.branch.repository.has_revision(leftmost_id)):
639
raise errors.GhostRevisionUnusableHere(leftmost_id)
641
def _set_merges_from_parent_ids(self, parent_ids):
642
merges = parent_ids[1:]
643
self._transport.put_bytes('pending-merges', '\n'.join(merges),
644
mode=self.bzrdir._get_file_mode())
646
def _filter_parent_ids_by_ancestry(self, revision_ids):
647
"""Check that all merged revisions are proper 'heads'.
649
This will always return the first revision_id, and any merged revisions
652
if len(revision_ids) == 0:
654
graph = self.branch.repository.get_graph()
655
heads = graph.heads(revision_ids)
656
new_revision_ids = revision_ids[:1]
657
for revision_id in revision_ids[1:]:
658
if revision_id in heads and revision_id not in new_revision_ids:
659
new_revision_ids.append(revision_id)
660
if new_revision_ids != revision_ids:
661
mutter('requested to set revision_ids = %s,'
662
' but filtered to %s', revision_ids, new_revision_ids)
663
return new_revision_ids
665
@needs_tree_write_lock
666
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
667
"""Set the parent ids to revision_ids.
669
See also set_parent_trees. This api will try to retrieve the tree data
670
for each element of revision_ids from the trees repository. If you have
671
tree data already available, it is more efficient to use
672
set_parent_trees rather than set_parent_ids. set_parent_ids is however
673
an easier API to use.
675
:param revision_ids: The revision_ids to set as the parent ids of this
676
working tree. Any of these may be ghosts.
678
self._check_parents_for_ghosts(revision_ids,
679
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
680
for revision_id in revision_ids:
681
_mod_revision.check_not_reserved_id(revision_id)
683
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
685
if len(revision_ids) > 0:
686
self.set_last_revision(revision_ids[0])
688
self.set_last_revision(_mod_revision.NULL_REVISION)
690
self._set_merges_from_parent_ids(revision_ids)
692
@needs_tree_write_lock
693
def set_pending_merges(self, rev_list):
694
parents = self.get_parent_ids()
695
leftmost = parents[:1]
696
new_parents = leftmost + rev_list
697
self.set_parent_ids(new_parents)
699
@needs_tree_write_lock
700
def set_merge_modified(self, modified_hashes):
701
"""Set the merge modified hashes."""
702
raise NotImplementedError(self.set_merge_modified)
704
def _sha_from_stat(self, path, stat_result):
705
"""Get a sha digest from the tree's stat cache.
707
The default implementation assumes no stat cache is present.
709
:param path: The path.
710
:param stat_result: The stat result being looked up.
714
@needs_write_lock # because merge pulls data into the branch.
715
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
716
merge_type=None, force=False):
717
"""Merge from a branch into this working tree.
719
:param branch: The branch to merge from.
720
:param to_revision: If non-None, the merge will merge to to_revision,
721
but not beyond it. to_revision does not need to be in the history
722
of the branch when it is supplied. If None, to_revision defaults to
723
branch.last_revision().
725
from .merge import Merger, Merge3Merger
726
merger = Merger(self.branch, this_tree=self)
727
# check that there are no local alterations
728
if not force and self.has_changes():
729
raise errors.UncommittedChanges(self)
730
if to_revision is None:
731
to_revision = _mod_revision.ensure_null(branch.last_revision())
732
merger.other_rev_id = to_revision
733
if _mod_revision.is_null(merger.other_rev_id):
734
raise errors.NoCommits(branch)
735
self.branch.fetch(branch, last_revision=merger.other_rev_id)
736
merger.other_basis = merger.other_rev_id
737
merger.other_tree = self.branch.repository.revision_tree(
739
merger.other_branch = branch
740
if from_revision is None:
743
merger.set_base_revision(from_revision, branch)
744
if merger.base_rev_id == merger.other_rev_id:
745
raise errors.PointlessMerge
746
merger.backup_files = False
747
if merge_type is None:
748
merger.merge_type = Merge3Merger
750
merger.merge_type = merge_type
751
merger.set_interesting_files(None)
752
merger.show_base = False
753
merger.reprocess = False
754
conflicts = merger.do_merge()
758
def merge_modified(self):
759
"""Return a dictionary of files modified by a merge.
761
The list is initialized by WorkingTree.set_merge_modified, which is
762
typically called after we make some automatic updates to the tree
765
This returns a map of file_id->sha1, containing only files which are
766
still in the working inventory and have that text hash.
768
raise NotImplementedError(self.merge_modified)
771
def mkdir(self, path, file_id=None):
772
"""See MutableTree.mkdir()."""
774
file_id = generate_ids.gen_file_id(os.path.basename(path))
775
os.mkdir(self.abspath(path))
776
self.add(path, file_id, 'directory')
779
def get_symlink_target(self, file_id, path=None):
781
abspath = self.abspath(path)
783
abspath = self.id2abspath(file_id)
784
target = osutils.readlink(abspath)
787
def subsume(self, other_tree):
788
raise NotImplementedError(self.subsume)
790
def _setup_directory_is_tree_reference(self):
791
if self._branch.repository._format.supports_tree_reference:
792
self._directory_is_tree_reference = \
793
self._directory_may_be_tree_reference
795
self._directory_is_tree_reference = \
796
self._directory_is_never_tree_reference
798
def _directory_is_never_tree_reference(self, relpath):
801
def _directory_may_be_tree_reference(self, relpath):
802
# as a special case, if a directory contains control files then
803
# it's a tree reference, except that the root of the tree is not
804
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
805
# TODO: We could ask all the control formats whether they
806
# recognize this directory, but at the moment there's no cheap api
807
# to do that. Since we probably can only nest bzr checkouts and
808
# they always use this name it's ok for now. -- mbp 20060306
810
# FIXME: There is an unhandled case here of a subdirectory
811
# containing .bzr but not a branch; that will probably blow up
812
# when you try to commit it. It might happen if there is a
813
# checkout in a subdirectory. This can be avoided by not adding
816
def extract(self, file_id, format=None):
817
"""Extract a subtree from this tree.
819
A new branch will be created, relative to the path for this tree.
821
raise NotImplementedError(self.extract)
824
"""Write the in memory meta data to disk."""
825
raise NotImplementedError(self.flush)
827
def _kind(self, relpath):
828
return osutils.file_kind(self.abspath(relpath))
830
def list_files(self, include_root=False, from_dir=None, recursive=True):
831
"""List all files as (path, class, kind, id, entry).
833
Lists, but does not descend into unversioned directories.
834
This does not include files that have been deleted in this
835
tree. Skips the control directory.
837
:param include_root: if True, return an entry for the root
838
:param from_dir: start from this directory or None for the root
839
:param recursive: whether to recurse into subdirectories or not
841
raise NotImplementedError(self.list_files)
843
def move(self, from_paths, to_dir=None, after=False):
846
to_dir must be known to the working tree.
848
If to_dir exists and is a directory, the files are moved into
849
it, keeping their old names.
851
Note that to_dir is only the last component of the new name;
852
this doesn't change the directory.
854
For each entry in from_paths the move mode will be determined
857
The first mode moves the file in the filesystem and updates the
858
working tree metadata. The second mode only updates the working tree
859
metadata without touching the file on the filesystem.
861
move uses the second mode if 'after == True' and the target is not
862
versioned but present in the working tree.
864
move uses the second mode if 'after == False' and the source is
865
versioned but no longer in the working tree, and the target is not
866
versioned but present in the working tree.
868
move uses the first mode if 'after == False' and the source is
869
versioned and present in the working tree, and the target is not
870
versioned and not present in the working tree.
872
Everything else results in an error.
874
This returns a list of (from_path, to_path) pairs for each
877
raise NotImplementedError(self.move)
879
@needs_tree_write_lock
880
def rename_one(self, from_rel, to_rel, after=False):
883
This can change the directory or the filename or both.
885
rename_one has several 'modes' to work. First, it can rename a physical
886
file and change the file_id. That is the normal mode. Second, it can
887
only change the file_id without touching any physical file.
889
rename_one uses the second mode if 'after == True' and 'to_rel' is
890
either not versioned or newly added, and present in the working tree.
892
rename_one uses the second mode if 'after == False' and 'from_rel' is
893
versioned but no longer in the working tree, and 'to_rel' is not
894
versioned but present in the working tree.
896
rename_one uses the first mode if 'after == False' and 'from_rel' is
897
versioned and present in the working tree, and 'to_rel' is not
898
versioned and not present in the working tree.
900
Everything else results in an error.
902
raise NotImplementedError(self.rename_one)
906
"""Return all unknown files.
908
These are files in the working directory that are not versioned or
909
control files or ignored.
911
# force the extras method to be fully executed before returning, to
912
# prevent race conditions with the lock
914
[subp for subp in self.extras() if not self.is_ignored(subp)])
916
def unversion(self, file_ids):
917
"""Remove the file ids in file_ids from the current versioned set.
919
When a file_id is unversioned, all of its children are automatically
922
:param file_ids: The file ids to stop versioning.
923
:raises: NoSuchId if any fileid is not currently versioned.
925
raise NotImplementedError(self.unversion)
928
def pull(self, source, overwrite=False, stop_revision=None,
929
change_reporter=None, possible_transports=None, local=False,
933
old_revision_info = self.branch.last_revision_info()
934
basis_tree = self.basis_tree()
935
count = self.branch.pull(source, overwrite, stop_revision,
936
possible_transports=possible_transports,
938
new_revision_info = self.branch.last_revision_info()
939
if new_revision_info != old_revision_info:
940
repository = self.branch.repository
941
if repository._format.fast_deltas:
942
parent_ids = self.get_parent_ids()
944
basis_id = parent_ids[0]
945
basis_tree = repository.revision_tree(basis_id)
946
basis_tree.lock_read()
948
new_basis_tree = self.branch.basis_tree()
955
change_reporter=change_reporter,
957
basis_root_id = basis_tree.get_root_id()
958
new_root_id = new_basis_tree.get_root_id()
959
if new_root_id is not None and basis_root_id != new_root_id:
960
self.set_root_id(new_root_id)
963
# TODO - dedup parents list with things merged by pull ?
964
# reuse the revisiontree we merged against to set the new
967
if self.branch.last_revision() != _mod_revision.NULL_REVISION:
969
(self.branch.last_revision(), new_basis_tree))
970
# we have to pull the merge trees out again, because
971
# merge_inner has set the ids. - this corner is not yet
972
# layered well enough to prevent double handling.
973
# XXX TODO: Fix the double handling: telling the tree about
974
# the already known parent data is wasteful.
975
merges = self.get_parent_ids()[1:]
976
parent_trees.extend([
977
(parent, repository.revision_tree(parent)) for
979
self.set_parent_trees(parent_trees)
985
def put_file_bytes_non_atomic(self, file_id, bytes):
986
"""See MutableTree.put_file_bytes_non_atomic."""
987
stream = file(self.id2abspath(file_id), 'wb')
994
"""Yield all unversioned files in this WorkingTree.
996
If there are any unversioned directories then only the directory is
997
returned, not all its children. But if there are unversioned files
998
under a versioned subdirectory, they are returned.
1000
Currently returned depth-first, sorted by name within directories.
1001
This is the same order used by 'osutils.walkdirs'.
1003
raise NotImplementedError(self.extras)
1005
def ignored_files(self):
1006
"""Yield list of PATH, IGNORE_PATTERN"""
1007
for subp in self.extras():
1008
pat = self.is_ignored(subp)
1012
def get_ignore_list(self):
1013
"""Return list of ignore patterns.
1015
Cached in the Tree object after the first call.
1017
ignoreset = getattr(self, '_ignoreset', None)
1018
if ignoreset is not None:
1021
ignore_globs = set()
1022
ignore_globs.update(ignores.get_runtime_ignores())
1023
ignore_globs.update(ignores.get_user_ignores())
1024
if self.has_filename(breezy.IGNORE_FILENAME):
1025
f = self.get_file_byname(breezy.IGNORE_FILENAME)
1027
ignore_globs.update(ignores.parse_ignore_file(f))
1030
self._ignoreset = ignore_globs
1033
def _flush_ignore_list_cache(self):
1034
"""Resets the cached ignore list to force a cache rebuild."""
1035
self._ignoreset = None
1036
self._ignoreglobster = None
1038
def is_ignored(self, filename):
1039
r"""Check whether the filename matches an ignore pattern.
1041
Patterns containing '/' or '\' need to match the whole path;
1042
others match against only the last component. Patterns starting
1043
with '!' are ignore exceptions. Exceptions take precedence
1044
over regular patterns and cause the filename to not be ignored.
1046
If the file is ignored, returns the pattern which caused it to
1047
be ignored, otherwise None. So this can simply be used as a
1048
boolean if desired."""
1049
if getattr(self, '_ignoreglobster', None) is None:
1050
self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1051
return self._ignoreglobster.match(filename)
1053
def kind(self, file_id):
1054
return osutils.file_kind(self.id2abspath(file_id))
1056
def stored_kind(self, file_id):
1057
"""See Tree.stored_kind"""
1058
raise NotImplementedError(self.stored_kind)
1060
def _comparison_data(self, entry, path):
1061
abspath = self.abspath(path)
1063
stat_value = os.lstat(abspath)
1064
except OSError as e:
1065
if getattr(e, 'errno', None) == errno.ENOENT:
1072
mode = stat_value.st_mode
1073
kind = osutils.file_kind_from_stat_mode(mode)
1074
if not self._supports_executable():
1075
executable = entry is not None and entry.executable
1077
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
1078
return kind, executable, stat_value
1080
def _file_size(self, entry, stat_value):
1081
return stat_value.st_size
1083
def last_revision(self):
1084
"""Return the last revision of the branch for this tree.
1086
This format tree does not support a separate marker for last-revision
1087
compared to the branch.
1089
See MutableTree.last_revision
1091
return self._last_revision()
1094
def _last_revision(self):
1095
"""helper for get_parent_ids."""
1096
return _mod_revision.ensure_null(self.branch.last_revision())
1098
def is_locked(self):
1099
"""Check if this tree is locked."""
1100
raise NotImplementedError(self.is_locked)
1102
def lock_read(self):
1103
"""Lock the tree for reading.
1105
This also locks the branch, and can be unlocked via self.unlock().
1107
:return: A breezy.lock.LogicalLockResult.
1109
raise NotImplementedError(self.lock_read)
1111
def lock_tree_write(self):
1112
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
1114
:return: A breezy.lock.LogicalLockResult.
1116
raise NotImplementedError(self.lock_tree_write)
1118
def lock_write(self):
1119
"""See MutableTree.lock_write, and WorkingTree.unlock.
1121
:return: A breezy.lock.LogicalLockResult.
1123
raise NotImplementedError(self.lock_write)
1125
def get_physical_lock_status(self):
1126
raise NotImplementedError(self.get_physical_lock_status)
1128
def set_last_revision(self, new_revision):
1129
"""Change the last revision in the working tree."""
1130
raise NotImplementedError(self.set_last_revision)
1132
def _change_last_revision(self, new_revision):
1133
"""Template method part of set_last_revision to perform the change.
1135
This is used to allow WorkingTree3 instances to not affect branch
1136
when their last revision is set.
1138
if _mod_revision.is_null(new_revision):
1139
self.branch.set_last_revision_info(0, new_revision)
1141
_mod_revision.check_not_reserved_id(new_revision)
1143
self.branch.generate_revision_history(new_revision)
1144
except errors.NoSuchRevision:
1145
# not present in the repo - dont try to set it deeper than the tip
1146
self.branch._set_revision_history([new_revision])
1149
@needs_tree_write_lock
1150
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1152
"""Remove nominated files from the working tree metadata.
1154
:files: File paths relative to the basedir.
1155
:keep_files: If true, the files will also be kept.
1156
:force: Delete files and directories, even if they are changed and
1157
even if the directories are not empty.
1159
if isinstance(files, basestring):
1164
all_files = set() # specified and nested files
1165
unknown_nested_files=set()
1167
to_file = sys.stdout
1169
files_to_backup = []
1171
def recurse_directory_to_add_files(directory):
1172
# Recurse directory and add all files
1173
# so we can check if they have changed.
1174
for parent_info, file_infos in self.walkdirs(directory):
1175
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1176
# Is it versioned or ignored?
1177
if self.path2id(relpath):
1178
# Add nested content for deletion.
1179
all_files.add(relpath)
1181
# Files which are not versioned
1182
# should be treated as unknown.
1183
files_to_backup.append(relpath)
1185
for filename in files:
1186
# Get file name into canonical form.
1187
abspath = self.abspath(filename)
1188
filename = self.relpath(abspath)
1189
if len(filename) > 0:
1190
all_files.add(filename)
1191
recurse_directory_to_add_files(filename)
1193
files = list(all_files)
1196
return # nothing to do
1198
# Sort needed to first handle directory content before the directory
1199
files.sort(reverse=True)
1201
# Bail out if we are going to delete files we shouldn't
1202
if not keep_files and not force:
1203
for (file_id, path, content_change, versioned, parent_id, name,
1204
kind, executable) in self.iter_changes(self.basis_tree(),
1205
include_unchanged=True, require_versioned=False,
1206
want_unversioned=True, specific_files=files):
1207
if versioned[0] == False:
1208
# The record is unknown or newly added
1209
files_to_backup.append(path[1])
1210
elif (content_change and (kind[1] is not None) and
1211
osutils.is_inside_any(files, path[1])):
1212
# Versioned and changed, but not deleted, and still
1213
# in one of the dirs to be deleted.
1214
files_to_backup.append(path[1])
1216
def backup(file_to_backup):
1217
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1218
osutils.rename(abs_path, self.abspath(backup_name))
1219
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1222
# Build inv_delta and delete files where applicable,
1223
# do this before any modifications to meta data.
1225
fid = self.path2id(f)
1228
message = "%s is not versioned." % (f,)
1231
# having removed it, it must be either ignored or unknown
1232
if self.is_ignored(f):
1236
# XXX: Really should be a more abstract reporter interface
1237
kind_ch = osutils.kind_marker(self.kind(fid))
1238
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1240
inv_delta.append((f, None, fid, None))
1241
message = "removed %s" % (f,)
1244
abs_path = self.abspath(f)
1245
if osutils.lexists(abs_path):
1246
if (osutils.isdir(abs_path) and
1247
len(os.listdir(abs_path)) > 0):
1249
osutils.rmtree(abs_path)
1250
message = "deleted %s" % (f,)
1254
if f in files_to_backup:
1257
osutils.delete_any(abs_path)
1258
message = "deleted %s" % (f,)
1259
elif message is not None:
1260
# Only care if we haven't done anything yet.
1261
message = "%s does not exist." % (f,)
1263
# Print only one message (if any) per file.
1264
if message is not None:
1266
self.apply_inventory_delta(inv_delta)
1268
@needs_tree_write_lock
1269
def revert(self, filenames=None, old_tree=None, backups=True,
1270
pb=None, report_changes=False):
1271
from .conflicts import resolve
1272
if old_tree is None:
1273
basis_tree = self.basis_tree()
1274
basis_tree.lock_read()
1275
old_tree = basis_tree
1279
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1281
if filenames is None and len(self.get_parent_ids()) > 1:
1283
last_revision = self.last_revision()
1284
if last_revision != _mod_revision.NULL_REVISION:
1285
if basis_tree is None:
1286
basis_tree = self.basis_tree()
1287
basis_tree.lock_read()
1288
parent_trees.append((last_revision, basis_tree))
1289
self.set_parent_trees(parent_trees)
1292
resolve(self, filenames, ignore_misses=True, recursive=True)
1294
if basis_tree is not None:
1299
def store_uncommitted(self):
1300
"""Store uncommitted changes from the tree in the branch."""
1301
target_tree = self.basis_tree()
1302
shelf_creator = shelf.ShelfCreator(self, target_tree)
1304
if not shelf_creator.shelve_all():
1306
self.branch.store_uncommitted(shelf_creator)
1307
shelf_creator.transform()
1309
shelf_creator.finalize()
1310
note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1313
def restore_uncommitted(self):
1314
"""Restore uncommitted changes from the branch into the tree."""
1315
unshelver = self.branch.get_unshelver(self)
1316
if unshelver is None:
1319
merger = unshelver.make_merger()
1320
merger.ignore_zero = True
1322
self.branch.store_uncommitted(None)
1324
unshelver.finalize()
1326
def revision_tree(self, revision_id):
1327
"""See Tree.revision_tree.
1329
WorkingTree can supply revision_trees for the basis revision only
1330
because there is only one cached inventory in the bzr directory.
1332
raise NotImplementedError(self.revision_tree)
1334
@needs_tree_write_lock
1335
def set_root_id(self, file_id):
1336
"""Set the root id for this tree."""
1340
'WorkingTree.set_root_id with fileid=None')
1341
file_id = osutils.safe_file_id(file_id)
1342
self._set_root_id(file_id)
1344
def _set_root_id(self, file_id):
1345
"""Set the root id for this tree, in a format specific manner.
1347
:param file_id: The file id to assign to the root. It must not be
1348
present in the current inventory or an error will occur. It must
1349
not be None, but rather a valid file id.
1351
raise NotImplementedError(self._set_root_id)
1354
"""See Branch.unlock.
1356
WorkingTree locking just uses the Branch locking facilities.
1357
This is current because all working trees have an embedded branch
1358
within them. IF in the future, we were to make branch data shareable
1359
between multiple working trees, i.e. via shared storage, then we
1360
would probably want to lock both the local tree, and the branch.
1362
raise NotImplementedError(self.unlock)
1366
def update(self, change_reporter=None, possible_transports=None,
1367
revision=None, old_tip=_marker, show_base=False):
1368
"""Update a working tree along its branch.
1370
This will update the branch if its bound too, which means we have
1371
multiple trees involved:
1373
- The new basis tree of the master.
1374
- The old basis tree of the branch.
1375
- The old basis tree of the working tree.
1376
- The current working tree state.
1378
Pathologically, all three may be different, and non-ancestors of each
1379
other. Conceptually we want to:
1381
- Preserve the wt.basis->wt.state changes
1382
- Transform the wt.basis to the new master basis.
1383
- Apply a merge of the old branch basis to get any 'local' changes from
1385
- Restore the wt.basis->wt.state changes.
1387
There isn't a single operation at the moment to do that, so we:
1389
- Merge current state -> basis tree of the master w.r.t. the old tree
1391
- Do a 'normal' merge of the old branch basis if it is relevant.
1393
:param revision: The target revision to update to. Must be in the
1395
:param old_tip: If branch.update() has already been run, the value it
1396
returned (old tip of the branch or None). _marker is used
1399
if self.branch.get_bound_location() is not None:
1401
update_branch = (old_tip is self._marker)
1403
self.lock_tree_write()
1404
update_branch = False
1407
old_tip = self.branch.update(possible_transports)
1409
if old_tip is self._marker:
1411
return self._update_tree(old_tip, change_reporter, revision, show_base)
1415
@needs_tree_write_lock
1416
def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1418
"""Update a tree to the master branch.
1420
:param old_tip: if supplied, the previous tip revision the branch,
1421
before it was changed to the master branch's tip.
1423
# here if old_tip is not None, it is the old tip of the branch before
1424
# it was updated from the master branch. This should become a pending
1425
# merge in the working tree to preserve the user existing work. we
1426
# cant set that until we update the working trees last revision to be
1427
# one from the new branch, because it will just get absorbed by the
1428
# parent de-duplication logic.
1430
# We MUST save it even if an error occurs, because otherwise the users
1431
# local work is unreferenced and will appear to have been lost.
1435
last_rev = self.get_parent_ids()[0]
1437
last_rev = _mod_revision.NULL_REVISION
1438
if revision is None:
1439
revision = self.branch.last_revision()
1441
old_tip = old_tip or _mod_revision.NULL_REVISION
1443
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1444
# the branch we are bound to was updated
1445
# merge those changes in first
1446
base_tree = self.basis_tree()
1447
other_tree = self.branch.repository.revision_tree(old_tip)
1448
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1449
base_tree, this_tree=self,
1450
change_reporter=change_reporter,
1451
show_base=show_base)
1453
self.add_parent_tree((old_tip, other_tree))
1454
note(gettext('Rerun update after fixing the conflicts.'))
1457
if last_rev != _mod_revision.ensure_null(revision):
1458
# the working tree is up to date with the branch
1459
# we can merge the specified revision from master
1460
to_tree = self.branch.repository.revision_tree(revision)
1461
to_root_id = to_tree.get_root_id()
1463
basis = self.basis_tree()
1466
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1467
self.set_root_id(to_root_id)
1472
# determine the branch point
1473
graph = self.branch.repository.get_graph()
1474
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1476
base_tree = self.branch.repository.revision_tree(base_rev_id)
1478
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1480
change_reporter=change_reporter,
1481
show_base=show_base)
1482
self.set_last_revision(revision)
1483
# TODO - dedup parents list with things merged by pull ?
1484
# reuse the tree we've updated to to set the basis:
1485
parent_trees = [(revision, to_tree)]
1486
merges = self.get_parent_ids()[1:]
1487
# Ideally we ask the tree for the trees here, that way the working
1488
# tree can decide whether to give us the entire tree or give us a
1489
# lazy initialised tree. dirstate for instance will have the trees
1490
# in ram already, whereas a last-revision + basis-inventory tree
1491
# will not, but also does not need them when setting parents.
1492
for parent in merges:
1493
parent_trees.append(
1494
(parent, self.branch.repository.revision_tree(parent)))
1495
if not _mod_revision.is_null(old_tip):
1496
parent_trees.append(
1497
(old_tip, self.branch.repository.revision_tree(old_tip)))
1498
self.set_parent_trees(parent_trees)
1499
last_rev = parent_trees[0][0]
1502
def set_conflicts(self, arg):
1503
raise errors.UnsupportedOperation(self.set_conflicts, self)
1505
def add_conflicts(self, arg):
1506
raise errors.UnsupportedOperation(self.add_conflicts, self)
1508
def conflicts(self):
1509
raise NotImplementedError(self.conflicts)
1511
def walkdirs(self, prefix=""):
1512
"""Walk the directories of this tree.
1514
returns a generator which yields items in the form:
1515
((curren_directory_path, fileid),
1516
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
1519
This API returns a generator, which is only valid during the current
1520
tree transaction - within a single lock_read or lock_write duration.
1522
If the tree is not locked, it may cause an error to be raised,
1523
depending on the tree implementation.
1525
disk_top = self.abspath(prefix)
1526
if disk_top.endswith('/'):
1527
disk_top = disk_top[:-1]
1528
top_strip_len = len(disk_top) + 1
1529
inventory_iterator = self._walkdirs(prefix)
1530
disk_iterator = osutils.walkdirs(disk_top, prefix)
1532
current_disk = next(disk_iterator)
1533
disk_finished = False
1534
except OSError as e:
1535
if not (e.errno == errno.ENOENT or
1536
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1539
disk_finished = True
1541
current_inv = next(inventory_iterator)
1542
inv_finished = False
1543
except StopIteration:
1546
while not inv_finished or not disk_finished:
1548
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1549
cur_disk_dir_content) = current_disk
1551
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1552
cur_disk_dir_content) = ((None, None), None)
1553
if not disk_finished:
1554
# strip out .bzr dirs
1555
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1556
len(cur_disk_dir_content) > 0):
1557
# osutils.walkdirs can be made nicer -
1558
# yield the path-from-prefix rather than the pathjoined
1560
bzrdir_loc = bisect_left(cur_disk_dir_content,
1562
if (bzrdir_loc < len(cur_disk_dir_content)
1563
and self.bzrdir.is_control_filename(
1564
cur_disk_dir_content[bzrdir_loc][0])):
1565
# we dont yield the contents of, or, .bzr itself.
1566
del cur_disk_dir_content[bzrdir_loc]
1568
# everything is unknown
1571
# everything is missing
1574
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1576
# disk is before inventory - unknown
1577
dirblock = [(relpath, basename, kind, stat, None, None) for
1578
relpath, basename, kind, stat, top_path in
1579
cur_disk_dir_content]
1580
yield (cur_disk_dir_relpath, None), dirblock
1582
current_disk = next(disk_iterator)
1583
except StopIteration:
1584
disk_finished = True
1586
# inventory is before disk - missing.
1587
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1588
for relpath, basename, dkind, stat, fileid, kind in
1590
yield (current_inv[0][0], current_inv[0][1]), dirblock
1592
current_inv = next(inventory_iterator)
1593
except StopIteration:
1596
# versioned present directory
1597
# merge the inventory and disk data together
1599
for relpath, subiterator in itertools.groupby(sorted(
1600
current_inv[1] + cur_disk_dir_content,
1601
key=operator.itemgetter(0)), operator.itemgetter(1)):
1602
path_elements = list(subiterator)
1603
if len(path_elements) == 2:
1604
inv_row, disk_row = path_elements
1605
# versioned, present file
1606
dirblock.append((inv_row[0],
1607
inv_row[1], disk_row[2],
1608
disk_row[3], inv_row[4],
1610
elif len(path_elements[0]) == 5:
1612
dirblock.append((path_elements[0][0],
1613
path_elements[0][1], path_elements[0][2],
1614
path_elements[0][3], None, None))
1615
elif len(path_elements[0]) == 6:
1616
# versioned, absent file.
1617
dirblock.append((path_elements[0][0],
1618
path_elements[0][1], 'unknown', None,
1619
path_elements[0][4], path_elements[0][5]))
1621
raise NotImplementedError('unreachable code')
1622
yield current_inv[0], dirblock
1624
current_inv = next(inventory_iterator)
1625
except StopIteration:
1628
current_disk = next(disk_iterator)
1629
except StopIteration:
1630
disk_finished = True
1632
def _walkdirs(self, prefix=""):
1633
"""Walk the directories of this tree.
1635
:param prefix: is used as the directrory to start with.
1636
:returns: a generator which yields items in the form::
1638
((curren_directory_path, fileid),
1639
[(file1_path, file1_name, file1_kind, None, file1_id,
1642
raise NotImplementedError(self._walkdirs)
1644
@needs_tree_write_lock
1645
def auto_resolve(self):
1646
"""Automatically resolve text conflicts according to contents.
1648
Only text conflicts are auto_resolvable. Files with no conflict markers
1649
are considered 'resolved', because bzr always puts conflict markers
1650
into files that have text conflicts. The corresponding .THIS .BASE and
1651
.OTHER files are deleted, as per 'resolve'.
1653
:return: a tuple of ConflictLists: (un_resolved, resolved).
1655
un_resolved = _mod_conflicts.ConflictList()
1656
resolved = _mod_conflicts.ConflictList()
1657
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1658
for conflict in self.conflicts():
1659
if (conflict.typestring != 'text conflict' or
1660
self.kind(conflict.file_id) != 'file'):
1661
un_resolved.append(conflict)
1663
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1665
for line in my_file:
1666
if conflict_re.search(line):
1667
un_resolved.append(conflict)
1670
resolved.append(conflict)
1673
resolved.remove_files(self)
1674
self.set_conflicts(un_resolved)
1675
return un_resolved, resolved
1677
def _validate(self):
1678
"""Validate internal structures.
1680
This is meant mostly for the test suite. To give it a chance to detect
1681
corruption after actions have occurred. The default implementation is a
1684
:return: None. An exception should be raised if there is an error.
1688
def check_state(self):
1689
"""Check that the working state is/isn't valid."""
1690
raise NotImplementedError(self.check_state)
1692
def reset_state(self, revision_ids=None):
1693
"""Reset the state of the working tree.
1695
This does a hard-reset to a last-known-good state. This is a way to
1696
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1698
raise NotImplementedError(self.reset_state)
1700
def _get_rules_searcher(self, default_searcher):
1701
"""See Tree._get_rules_searcher."""
1702
if self._rules_searcher is None:
1703
self._rules_searcher = super(WorkingTree,
1704
self)._get_rules_searcher(default_searcher)
1705
return self._rules_searcher
1707
def get_shelf_manager(self):
1708
"""Return the ShelfManager for this WorkingTree."""
1709
from .shelf import ShelfManager
1710
return ShelfManager(self, self._transport)
1713
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1714
"""Registry for working tree formats."""
1716
def __init__(self, other_registry=None):
1717
super(WorkingTreeFormatRegistry, self).__init__(other_registry)
1718
self._default_format = None
1719
self._default_format_key = None
1721
def get_default(self):
1722
"""Return the current default format."""
1723
if (self._default_format_key is not None and
1724
self._default_format is None):
1725
self._default_format = self.get(self._default_format_key)
1726
return self._default_format
1728
def set_default(self, format):
1729
"""Set the default format."""
1730
self._default_format = format
1731
self._default_format_key = None
1733
def set_default_key(self, format_string):
1734
"""Set the default format by its format string."""
1735
self._default_format_key = format_string
1736
self._default_format = None
1739
format_registry = WorkingTreeFormatRegistry()
1742
class WorkingTreeFormat(controldir.ControlComponentFormat):
1743
"""An encapsulation of the initialization and open routines for a format.
1745
Formats provide three things:
1746
* An initialization routine,
1750
Formats are placed in an dict by their format string for reference
1751
during workingtree opening. Its not required that these be instances, they
1752
can be classes themselves with class methods - it simply depends on
1753
whether state is needed for a given format or not.
1755
Once a format is deprecated, just deprecate the initialize and open
1756
methods on the format class. Do not deprecate the object, as the
1757
object will be created every time regardless.
1760
requires_rich_root = False
1762
upgrade_recommended = False
1764
requires_normalized_unicode_filenames = False
1766
case_sensitive_filename = "FoRMaT"
1768
missing_parent_conflicts = False
1769
"""If this format supports missing parent conflicts."""
1771
supports_versioned_directories = None
1773
def initialize(self, controldir, revision_id=None, from_branch=None,
1774
accelerator_tree=None, hardlink=False):
1775
"""Initialize a new working tree in controldir.
1777
:param controldir: ControlDir to initialize the working tree in.
1778
:param revision_id: allows creating a working tree at a different
1779
revision than the branch is at.
1780
:param from_branch: Branch to checkout
1781
:param accelerator_tree: A tree which can be used for retrieving file
1782
contents more quickly than the revision tree, i.e. a workingtree.
1783
The revision tree will be used for cases where accelerator_tree's
1784
content is different.
1785
:param hardlink: If true, hard-link files from accelerator_tree,
1788
raise NotImplementedError(self.initialize)
1790
def __eq__(self, other):
1791
return self.__class__ is other.__class__
1793
def __ne__(self, other):
1794
return not (self == other)
1796
def get_format_description(self):
1797
"""Return the short description for this format."""
1798
raise NotImplementedError(self.get_format_description)
1800
def is_supported(self):
1801
"""Is this format supported?
1803
Supported formats can be initialized and opened.
1804
Unsupported formats may not support initialization or committing or
1805
some other features depending on the reason for not being supported.
1809
def supports_content_filtering(self):
1810
"""True if this format supports content filtering."""
1813
def supports_views(self):
1814
"""True if this format supports stored views."""
1817
def get_controldir_for_branch(self):
1818
"""Get the control directory format for creating branches.
1820
This is to support testing of working tree formats that can not exist
1821
in the same control directory as a branch.
1823
return self._matchingbzrdir
1826
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
1827
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1828
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
1829
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1830
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
1831
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1832
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
1833
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1834
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")