27
27
WorkingTree.open(dir).
30
from __future__ import absolute_import
37
40
from .lazy_import import lazy_import
38
41
lazy_import(globals(), """
42
44
from breezy import (
43
46
conflicts as _mod_conflicts,
44
49
filters as _mod_filters,
46
52
revision as _mod_revision,
51
from breezy.bzr import (
59
from .controldir import (
61
ControlComponentFormatRegistry,
62
ControlComponentFormat,
69
63
from .i18n import gettext
70
64
from . import mutabletree
71
from .symbol_versioning import deprecated_method, deprecated_in
72
68
from .trace import mutter, note
71
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
75
74
class SettingFileIdUnsupported(errors.BzrError):
77
76
_fmt = "This format does not support setting file ids."
107
107
self.controldir = _controldir
108
108
if not _internal:
109
109
raise errors.BzrError("Please use controldir.open_workingtree or "
110
"WorkingTree.open() to obtain a WorkingTree.")
110
"WorkingTree.open() to obtain a WorkingTree.")
111
111
basedir = osutils.safe_unicode(basedir)
112
112
mutter("opening working tree %r", basedir)
113
113
if branch is not None:
279
279
# self.relpath exists as a "thunk" to osutils, but canonical_relpath
280
280
# doesn't - fix that up here before we enter the loop.
283
return osutils.canonical_relpath(self.basedir, p)
282
fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
285
284
fixer = self.relpath
286
285
for filename in file_list:
299
298
return WorkingTree.open(path, _unsupported=True)
301
def find_trees(location):
302
def list_current(transport):
303
return [d for d in transport.list_dir('')
304
if not controldir.is_control_filename(d)]
305
def evaluate(controldir):
307
tree = controldir.open_workingtree()
308
except errors.NoWorkingTree:
312
t = transport.get_transport(location)
313
iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
314
list_current=list_current)
315
return [tr for tr in iterator if tr is not None]
301
317
def __repr__(self):
302
318
return "<%s of %s>" % (self.__class__.__name__,
303
319
getattr(self, 'basedir', None))
319
335
# in the future this should return the tree for
320
336
# 'empty:' - the implicit root empty tree.
321
337
return self.branch.repository.revision_tree(
322
_mod_revision.NULL_REVISION)
338
_mod_revision.NULL_REVISION)
324
340
return self.revision_tree(revision_id)
325
341
except errors.NoSuchRevision:
350
366
def has_filename(self, filename):
351
367
return osutils.lexists(self.abspath(filename))
353
def get_file(self, path, filtered=True):
354
return self.get_file_with_stat(path, filtered=filtered)[0]
369
def get_file(self, path, file_id=None, filtered=True):
370
return self.get_file_with_stat(path, file_id, filtered=filtered)[0]
356
def get_file_with_stat(self, path, filtered=True,
372
def get_file_with_stat(self, path, file_id=None, filtered=True,
357
373
_fstat=osutils.fstat):
358
374
"""See Tree.get_file_with_stat."""
359
375
abspath = self.abspath(path)
366
382
stat_value = _fstat(file_obj.fileno())
367
383
if filtered and self.supports_content_filtering():
368
384
filters = self._content_filter_stack(path)
370
file_obj, size = _mod_filters.filtered_input_file(
372
stat_value = _mod_filters.FilteredStat(
373
stat_value, st_size=size)
385
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
386
return (file_obj, stat_value)
376
def get_file_text(self, path, filtered=True):
377
with self.get_file(path, filtered=filtered) as my_file:
388
def get_file_text(self, path, file_id=None, filtered=True):
389
with self.get_file(path, file_id, filtered=filtered) as my_file:
378
390
return my_file.read()
380
def get_file_lines(self, path, filtered=True):
392
def get_file_lines(self, path, file_id=None, filtered=True):
381
393
"""See Tree.get_file_lines()"""
382
with self.get_file(path, filtered=filtered) as file:
394
with self.get_file(path, file_id, filtered=filtered) as file:
383
395
return file.readlines()
385
397
def get_parent_ids(self):
401
413
for l in osutils.split_lines(merges_bytes):
402
revision_id = l.rstrip(b'\n')
414
revision_id = l.rstrip('\n')
403
415
parents.append(revision_id)
418
def get_root_id(self):
419
"""Return the id of this trees root"""
420
raise NotImplementedError(self.get_root_id)
406
422
def clone(self, to_controldir, revision_id=None):
407
423
"""Duplicate this working tree into to_bzr, including all state.
425
441
def copy_content_into(self, tree, revision_id=None):
426
442
"""Copy the current content and user files of this tree into tree."""
427
443
with self.lock_read():
428
tree.set_root_id(self.path2id(''))
444
tree.set_root_id(self.get_root_id())
429
445
if revision_id is None:
430
446
merge.transform_tree(tree, self)
444
460
new_parents = [revision_id]
445
461
tree.set_parent_ids(new_parents)
447
def get_file_size(self, path):
463
def get_file_size(self, path, file_id=None):
448
464
"""See Tree.get_file_size"""
449
465
# XXX: this returns the on-disk size; it should probably return the
482
498
with self.lock_write():
483
499
parents = self.get_parent_ids() + [revision_id]
484
500
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
485
or allow_leftmost_as_ghost)
501
or allow_leftmost_as_ghost)
487
503
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
488
504
"""Add revision_id, tree tuple as a parent.
505
521
allow_leftmost_as_ghost = True
506
522
self.set_parent_ids(parent_ids,
507
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
523
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
509
525
def add_pending_merge(self, *revision_ids):
510
526
with self.lock_tree_write():
521
537
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
523
539
def path_content_summary(self, path, _lstat=os.lstat,
524
_mapper=osutils.file_kind_from_stat_mode):
540
_mapper=osutils.file_kind_from_stat_mode):
525
541
"""See Tree.path_content_summary."""
526
542
abspath = self.abspath(path)
563
579
if len(revision_ids) > 0:
564
580
leftmost_id = revision_ids[0]
565
581
if (not allow_leftmost_as_ghost and not
566
self.branch.repository.has_revision(leftmost_id)):
582
self.branch.repository.has_revision(leftmost_id)):
567
583
raise errors.GhostRevisionUnusableHere(leftmost_id)
569
585
def _set_merges_from_parent_ids(self, parent_ids):
570
586
merges = parent_ids[1:]
571
587
self._transport.put_bytes('pending-merges', b'\n'.join(merges),
572
mode=self.controldir._get_file_mode())
588
mode=self.controldir._get_file_mode())
574
590
def _filter_parent_ids_by_ancestry(self, revision_ids):
575
591
"""Check that all merged revisions are proper 'heads'.
587
603
new_revision_ids.append(revision_id)
588
604
if new_revision_ids != revision_ids:
589
605
mutter('requested to set revision_ids = %s,'
590
' but filtered to %s', revision_ids, new_revision_ids)
606
' but filtered to %s', revision_ids, new_revision_ids)
591
607
return new_revision_ids
593
609
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
605
621
with self.lock_tree_write():
606
622
self._check_parents_for_ghosts(revision_ids,
607
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
623
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
608
624
for revision_id in revision_ids:
609
625
_mod_revision.check_not_reserved_id(revision_id)
659
675
merger.other_rev_id = to_revision
660
676
if _mod_revision.is_null(merger.other_rev_id):
661
677
raise errors.NoCommits(branch)
662
self.branch.fetch(branch, stop_revision=merger.other_rev_id)
678
self.branch.fetch(branch, last_revision=merger.other_rev_id)
663
679
merger.other_basis = merger.other_rev_id
664
680
merger.other_tree = self.branch.repository.revision_tree(
665
681
merger.other_rev_id)
707
723
self.add(path, file_id, 'directory')
710
def get_symlink_target(self, path):
726
def get_symlink_target(self, path, file_id=None):
711
727
abspath = self.abspath(path)
712
728
target = osutils.readlink(abspath)
715
731
def subsume(self, other_tree):
716
732
raise NotImplementedError(self.subsume)
718
def _directory_is_tree_reference(self, relpath):
719
raise NotImplementedError(self._directory_is_tree_reference)
721
def extract(self, path, format=None):
734
def _setup_directory_is_tree_reference(self):
735
if self._branch.repository._format.supports_tree_reference:
736
self._directory_is_tree_reference = \
737
self._directory_may_be_tree_reference
739
self._directory_is_tree_reference = \
740
self._directory_is_never_tree_reference
742
def _directory_is_never_tree_reference(self, relpath):
745
def _directory_may_be_tree_reference(self, relpath):
746
# as a special case, if a directory contains control files then
747
# it's a tree reference, except that the root of the tree is not
748
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
749
# TODO: We could ask all the control formats whether they
750
# recognize this directory, but at the moment there's no cheap api
751
# to do that. Since we probably can only nest bzr checkouts and
752
# they always use this name it's ok for now. -- mbp 20060306
754
# FIXME: There is an unhandled case here of a subdirectory
755
# containing .bzr but not a branch; that will probably blow up
756
# when you try to commit it. It might happen if there is a
757
# checkout in a subdirectory. This can be avoided by not adding
760
def extract(self, path, file_id=None, format=None):
722
761
"""Extract a subtree from this tree.
724
763
A new branch will be created, relative to the path for this tree.
729
768
"""Write the in memory meta data to disk."""
730
769
raise NotImplementedError(self.flush)
732
def kind(self, relpath):
771
def kind(self, relpath, file_id=None):
733
772
return osutils.file_kind(self.abspath(relpath))
735
def list_files(self, include_root=False, from_dir=None, recursive=True,
736
recurse_nested=False):
774
def list_files(self, include_root=False, from_dir=None, recursive=True):
737
775
"""List all files as (path, class, kind, id, entry).
739
777
Lists, but does not descend into unversioned directories.
820
858
def pull(self, source, overwrite=False, stop_revision=None,
821
859
change_reporter=None, possible_transports=None, local=False,
822
show_base=False, tag_selector=None):
823
861
with self.lock_write(), source.lock_read():
824
862
old_revision_info = self.branch.last_revision_info()
825
863
basis_tree = self.basis_tree()
826
864
count = self.branch.pull(source, overwrite, stop_revision,
827
865
possible_transports=possible_transports,
828
local=local, tag_selector=tag_selector)
829
867
new_revision_info = self.branch.last_revision_info()
830
868
if new_revision_info != old_revision_info:
831
869
repository = self.branch.repository
837
875
with basis_tree.lock_read():
838
876
new_basis_tree = self.branch.basis_tree()
839
877
merge.merge_inner(
844
change_reporter=change_reporter,
846
basis_root_id = basis_tree.path2id('')
847
new_root_id = new_basis_tree.path2id('')
882
change_reporter=change_reporter,
884
basis_root_id = basis_tree.get_root_id()
885
new_root_id = new_basis_tree.get_root_id()
848
886
if new_root_id is not None and basis_root_id != new_root_id:
849
887
self.set_root_id(new_root_id)
850
888
# TODO - dedup parents list with things merged by pull ?
862
900
merges = self.get_parent_ids()[1:]
863
901
parent_trees.extend([
864
902
(parent, repository.revision_tree(parent)) for
866
904
self.set_parent_trees(parent_trees)
869
def put_file_bytes_non_atomic(self, path, bytes):
907
def put_file_bytes_non_atomic(self, path, bytes, file_id=None):
870
908
"""See MutableTree.put_file_bytes_non_atomic."""
871
909
with self.lock_write(), open(self.abspath(path), 'wb') as stream:
874
912
def extras(self):
875
913
"""Yield all unversioned files in this WorkingTree.
877
If there are any unversioned directories and the file format
878
supports versioning directories, then only the directory is returned,
879
not all its children. But if there are unversioned files under a
880
versioned subdirectory, they are returned.
915
If there are any unversioned directories then only the directory is
916
returned, not all its children. But if there are unversioned files
917
under a versioned subdirectory, they are returned.
882
919
Currently returned depth-first, sorted by name within directories.
883
920
This is the same order used by 'osutils.walkdirs'.
1000
1037
def revert(self, filenames=None, old_tree=None, backups=True,
1001
1038
pb=None, report_changes=False):
1002
1039
from .conflicts import resolve
1003
with contextlib.ExitStack() as exit_stack:
1004
exit_stack.enter_context(self.lock_tree_write())
1040
with self.lock_tree_write():
1005
1041
if old_tree is None:
1006
1042
basis_tree = self.basis_tree()
1007
exit_stack.enter_context(basis_tree.lock_read())
1043
basis_tree.lock_read()
1008
1044
old_tree = basis_tree
1010
1046
basis_tree = None
1011
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1013
if filenames is None and len(self.get_parent_ids()) > 1:
1015
last_revision = self.last_revision()
1016
if last_revision != _mod_revision.NULL_REVISION:
1017
if basis_tree is None:
1018
basis_tree = self.basis_tree()
1019
exit_stack.enter_context(basis_tree.lock_read())
1020
parent_trees.append((last_revision, basis_tree))
1021
self.set_parent_trees(parent_trees)
1024
resolve(self, filenames, ignore_misses=True, recursive=True)
1048
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1050
if filenames is None and len(self.get_parent_ids()) > 1:
1052
last_revision = self.last_revision()
1053
if last_revision != _mod_revision.NULL_REVISION:
1054
if basis_tree is None:
1055
basis_tree = self.basis_tree()
1056
basis_tree.lock_read()
1057
parent_trees.append((last_revision, basis_tree))
1058
self.set_parent_trees(parent_trees)
1061
resolve(self, filenames, ignore_misses=True, recursive=True)
1063
if basis_tree is not None:
1025
1065
return conflicts
1027
1067
def store_uncommitted(self):
1045
1085
if not self.supports_setting_file_ids():
1046
1086
raise SettingFileIdUnsupported()
1047
1087
with self.lock_tree_write():
1049
1089
if file_id is None:
1050
1090
raise ValueError(
1051
1091
'WorkingTree.set_root_id with fileid=None')
1092
file_id = osutils.safe_file_id(file_id)
1052
1093
self._set_root_id(file_id)
1054
1095
def _set_root_id(self, file_id):
1153
1194
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1154
1195
# the branch we are bound to was updated
1155
1196
# merge those changes in first
1156
base_tree = self.basis_tree()
1197
base_tree = self.basis_tree()
1157
1198
other_tree = self.branch.repository.revision_tree(old_tip)
1158
1199
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1159
1200
base_tree, this_tree=self,
1168
1209
# the working tree is up to date with the branch
1169
1210
# we can merge the specified revision from master
1170
1211
to_tree = self.branch.repository.revision_tree(revision)
1171
to_root_id = to_tree.path2id('')
1212
to_root_id = to_tree.get_root_id()
1173
1214
basis = self.basis_tree()
1174
1215
with basis.lock_read():
1175
if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1216
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1176
1217
self.set_root_id(to_root_id)
1245
1285
with self.lock_tree_write():
1246
1286
un_resolved = _mod_conflicts.ConflictList()
1247
1287
resolved = _mod_conflicts.ConflictList()
1288
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1248
1289
for conflict in self.conflicts():
1250
conflict.action_auto(self)
1251
except NotImplementedError:
1290
path = self.id2path(conflict.file_id)
1291
if (conflict.typestring != 'text conflict' or
1292
self.kind(path, conflict.file_id) != 'file'):
1252
1293
un_resolved.append(conflict)
1254
conflict.cleanup(self)
1255
resolved.append(conflict)
1295
with open(self.abspath(path), 'rb') as my_file:
1296
for line in my_file:
1297
if conflict_re.search(line):
1298
un_resolved.append(conflict)
1301
resolved.append(conflict)
1302
resolved.remove_files(self)
1256
1303
self.set_conflicts(un_resolved)
1257
1304
return un_resolved, resolved
1283
1330
"""See Tree._get_rules_searcher."""
1284
1331
if self._rules_searcher is None:
1285
1332
self._rules_searcher = super(WorkingTree,
1286
self)._get_rules_searcher(default_searcher)
1333
self)._get_rules_searcher(default_searcher)
1287
1334
return self._rules_searcher
1289
1336
def get_shelf_manager(self):
1290
1337
"""Return the ShelfManager for this WorkingTree."""
1291
1338
raise NotImplementedError(self.get_shelf_manager)
1293
def get_canonical_paths(self, paths):
1294
"""Like get_canonical_path() but works on multiple items.
1296
:param paths: A sequence of paths relative to the root of the tree.
1297
:return: A list of paths, with each item the corresponding input path
1298
adjusted to account for existing elements that match case
1301
with self.lock_read():
1305
def get_canonical_path(self, path):
1306
"""Returns the first item in the tree that matches a path.
1308
This is meant to allow case-insensitive path lookups on e.g.
1311
If a path matches exactly, it is returned. If no path matches exactly
1312
but more than one path matches according to the underlying file system,
1313
it is implementation defined which is returned.
1315
If no path matches according to the file system, the input path is
1316
returned, but with as many path entries that do exist changed to their
1319
If you need to resolve many names from the same tree, you should
1320
use get_canonical_paths() to avoid O(N) behaviour.
1322
:param path: A paths relative to the root of the tree.
1323
:return: The input path adjusted to account for existing elements
1324
that match case insensitively.
1326
with self.lock_read():
1327
return next(self.get_canonical_paths([path]))
1329
def reference_parent(self, path, branch=None, possible_transports=None):
1330
raise errors.UnsupportedOperation(self.reference_parent, self)
1332
def get_reference_info(self, path, branch=None):
1333
raise errors.UnsupportedOperation(self.get_reference_info, self)
1335
def set_reference_info(self, tree_path, branch_location):
1336
raise errors.UnsupportedOperation(self.set_reference_info, self)
1339
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
1341
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1340
1342
"""Registry for working tree formats."""
1342
1344
def __init__(self, other_registry=None):
1468
1470
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1469
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1471
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1470
1472
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1471
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1473
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1472
1474
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1473
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1475
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1474
1476
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1475
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1477
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1476
1478
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")