38
40
from .lazy_import import lazy_import
39
41
lazy_import(globals(), """
43
44
from breezy import (
45
46
conflicts as _mod_conflicts,
46
49
filters as _mod_filters,
48
52
revision as _mod_revision,
53
from breezy.bzr import (
61
from .controldir import (
63
ControlComponentFormatRegistry,
64
ControlComponentFormat,
71
63
from .i18n import gettext
72
64
from . import mutabletree
73
from .symbol_versioning import deprecated_method, deprecated_in
74
68
from .trace import mutter, note
71
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
77
74
class SettingFileIdUnsupported(errors.BzrError):
79
76
_fmt = "This format does not support setting file ids."
109
107
self.controldir = _controldir
110
108
if not _internal:
111
109
raise errors.BzrError("Please use controldir.open_workingtree or "
112
"WorkingTree.open() to obtain a WorkingTree.")
110
"WorkingTree.open() to obtain a WorkingTree.")
113
111
basedir = osutils.safe_unicode(basedir)
114
112
mutter("opening working tree %r", basedir)
115
113
if branch is not None:
281
279
# self.relpath exists as a "thunk" to osutils, but canonical_relpath
282
280
# doesn't - fix that up here before we enter the loop.
285
return osutils.canonical_relpath(self.basedir, p)
282
fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
287
284
fixer = self.relpath
288
285
for filename in file_list:
301
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]
303
317
def __repr__(self):
304
318
return "<%s of %s>" % (self.__class__.__name__,
305
319
getattr(self, 'basedir', None))
321
335
# in the future this should return the tree for
322
336
# 'empty:' - the implicit root empty tree.
323
337
return self.branch.repository.revision_tree(
324
_mod_revision.NULL_REVISION)
338
_mod_revision.NULL_REVISION)
326
340
return self.revision_tree(revision_id)
327
341
except errors.NoSuchRevision:
352
366
def has_filename(self, filename):
353
367
return osutils.lexists(self.abspath(filename))
355
def get_file(self, path, filtered=True):
356
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]
358
def get_file_with_stat(self, path, filtered=True,
372
def get_file_with_stat(self, path, file_id=None, filtered=True,
359
373
_fstat=osutils.fstat):
360
374
"""See Tree.get_file_with_stat."""
361
375
abspath = self.abspath(path)
368
382
stat_value = _fstat(file_obj.fileno())
369
383
if filtered and self.supports_content_filtering():
370
384
filters = self._content_filter_stack(path)
372
file_obj, size = _mod_filters.filtered_input_file(
374
stat_value = _mod_filters.FilteredStat(
375
stat_value, st_size=size)
385
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
376
386
return (file_obj, stat_value)
378
def get_file_text(self, path, filtered=True):
379
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:
380
390
return my_file.read()
382
def get_file_lines(self, path, filtered=True):
392
def get_file_lines(self, path, file_id=None, filtered=True):
383
393
"""See Tree.get_file_lines()"""
384
with self.get_file(path, filtered=filtered) as file:
394
with self.get_file(path, file_id, filtered=filtered) as file:
385
395
return file.readlines()
387
397
def get_parent_ids(self):
403
413
for l in osutils.split_lines(merges_bytes):
404
revision_id = l.rstrip(b'\n')
414
revision_id = l.rstrip('\n')
405
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)
408
422
def clone(self, to_controldir, revision_id=None):
409
423
"""Duplicate this working tree into to_bzr, including all state.
427
441
def copy_content_into(self, tree, revision_id=None):
428
442
"""Copy the current content and user files of this tree into tree."""
429
443
with self.lock_read():
430
tree.set_root_id(self.path2id(''))
444
tree.set_root_id(self.get_root_id())
431
445
if revision_id is None:
432
446
merge.transform_tree(tree, self)
446
460
new_parents = [revision_id]
447
461
tree.set_parent_ids(new_parents)
449
def get_file_size(self, path):
463
def get_file_size(self, path, file_id=None):
450
464
"""See Tree.get_file_size"""
451
465
# XXX: this returns the on-disk size; it should probably return the
484
498
with self.lock_write():
485
499
parents = self.get_parent_ids() + [revision_id]
486
500
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
487
or allow_leftmost_as_ghost)
501
or allow_leftmost_as_ghost)
489
503
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
490
504
"""Add revision_id, tree tuple as a parent.
507
521
allow_leftmost_as_ghost = True
508
522
self.set_parent_ids(parent_ids,
509
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
523
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
511
525
def add_pending_merge(self, *revision_ids):
512
526
with self.lock_tree_write():
523
537
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
525
539
def path_content_summary(self, path, _lstat=os.lstat,
526
_mapper=osutils.file_kind_from_stat_mode):
540
_mapper=osutils.file_kind_from_stat_mode):
527
541
"""See Tree.path_content_summary."""
528
542
abspath = self.abspath(path)
565
579
if len(revision_ids) > 0:
566
580
leftmost_id = revision_ids[0]
567
581
if (not allow_leftmost_as_ghost and not
568
self.branch.repository.has_revision(leftmost_id)):
582
self.branch.repository.has_revision(leftmost_id)):
569
583
raise errors.GhostRevisionUnusableHere(leftmost_id)
571
585
def _set_merges_from_parent_ids(self, parent_ids):
572
586
merges = parent_ids[1:]
573
587
self._transport.put_bytes('pending-merges', b'\n'.join(merges),
574
mode=self.controldir._get_file_mode())
588
mode=self.controldir._get_file_mode())
576
590
def _filter_parent_ids_by_ancestry(self, revision_ids):
577
591
"""Check that all merged revisions are proper 'heads'.
589
603
new_revision_ids.append(revision_id)
590
604
if new_revision_ids != revision_ids:
591
605
mutter('requested to set revision_ids = %s,'
592
' but filtered to %s', revision_ids, new_revision_ids)
606
' but filtered to %s', revision_ids, new_revision_ids)
593
607
return new_revision_ids
595
609
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
607
621
with self.lock_tree_write():
608
622
self._check_parents_for_ghosts(revision_ids,
609
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
623
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
610
624
for revision_id in revision_ids:
611
625
_mod_revision.check_not_reserved_id(revision_id)
661
675
merger.other_rev_id = to_revision
662
676
if _mod_revision.is_null(merger.other_rev_id):
663
677
raise errors.NoCommits(branch)
664
self.branch.fetch(branch, stop_revision=merger.other_rev_id)
678
self.branch.fetch(branch, last_revision=merger.other_rev_id)
665
679
merger.other_basis = merger.other_rev_id
666
680
merger.other_tree = self.branch.repository.revision_tree(
667
681
merger.other_rev_id)
709
723
self.add(path, file_id, 'directory')
712
def get_symlink_target(self, path):
726
def get_symlink_target(self, path, file_id=None):
713
727
abspath = self.abspath(path)
714
728
target = osutils.readlink(abspath)
717
731
def subsume(self, other_tree):
718
732
raise NotImplementedError(self.subsume)
720
def _directory_is_tree_reference(self, relpath):
721
raise NotImplementedError(self._directory_is_tree_reference)
723
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):
724
761
"""Extract a subtree from this tree.
726
763
A new branch will be created, relative to the path for this tree.
731
768
"""Write the in memory meta data to disk."""
732
769
raise NotImplementedError(self.flush)
734
def kind(self, relpath):
771
def kind(self, relpath, file_id=None):
735
772
return osutils.file_kind(self.abspath(relpath))
737
def list_files(self, include_root=False, from_dir=None, recursive=True,
738
recurse_nested=False):
774
def list_files(self, include_root=False, from_dir=None, recursive=True):
739
775
"""List all files as (path, class, kind, id, entry).
741
777
Lists, but does not descend into unversioned directories.
822
858
def pull(self, source, overwrite=False, stop_revision=None,
823
859
change_reporter=None, possible_transports=None, local=False,
824
show_base=False, tag_selector=None):
825
861
with self.lock_write(), source.lock_read():
826
862
old_revision_info = self.branch.last_revision_info()
827
863
basis_tree = self.basis_tree()
828
864
count = self.branch.pull(source, overwrite, stop_revision,
829
865
possible_transports=possible_transports,
830
local=local, tag_selector=tag_selector)
831
867
new_revision_info = self.branch.last_revision_info()
832
868
if new_revision_info != old_revision_info:
833
869
repository = self.branch.repository
839
875
with basis_tree.lock_read():
840
876
new_basis_tree = self.branch.basis_tree()
841
877
merge.merge_inner(
846
change_reporter=change_reporter,
848
basis_root_id = basis_tree.path2id('')
849
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()
850
886
if new_root_id is not None and basis_root_id != new_root_id:
851
887
self.set_root_id(new_root_id)
852
888
# TODO - dedup parents list with things merged by pull ?
864
900
merges = self.get_parent_ids()[1:]
865
901
parent_trees.extend([
866
902
(parent, repository.revision_tree(parent)) for
868
904
self.set_parent_trees(parent_trees)
871
def put_file_bytes_non_atomic(self, path, bytes):
907
def put_file_bytes_non_atomic(self, path, bytes, file_id=None):
872
908
"""See MutableTree.put_file_bytes_non_atomic."""
873
with self.lock_write(), open(self.abspath(path), 'wb') as stream:
909
with self.lock_write(), file(self.abspath(path), 'wb') as stream:
876
912
def extras(self):
877
913
"""Yield all unversioned files in this WorkingTree.
879
If there are any unversioned directories and the file format
880
supports versioning directories, then only the directory is returned,
881
not all its children. But if there are unversioned files under a
882
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.
884
919
Currently returned depth-first, sorted by name within directories.
885
920
This is the same order used by 'osutils.walkdirs'.
1002
1037
def revert(self, filenames=None, old_tree=None, backups=True,
1003
1038
pb=None, report_changes=False):
1004
1039
from .conflicts import resolve
1005
with cleanup.ExitStack() as exit_stack:
1006
exit_stack.enter_context(self.lock_tree_write())
1040
with self.lock_tree_write():
1007
1041
if old_tree is None:
1008
1042
basis_tree = self.basis_tree()
1009
exit_stack.enter_context(basis_tree.lock_read())
1043
basis_tree.lock_read()
1010
1044
old_tree = basis_tree
1012
1046
basis_tree = None
1013
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1015
if filenames is None and len(self.get_parent_ids()) > 1:
1017
last_revision = self.last_revision()
1018
if last_revision != _mod_revision.NULL_REVISION:
1019
if basis_tree is None:
1020
basis_tree = self.basis_tree()
1021
exit_stack.enter_context(basis_tree.lock_read())
1022
parent_trees.append((last_revision, basis_tree))
1023
self.set_parent_trees(parent_trees)
1026
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:
1027
1065
return conflicts
1029
1067
def store_uncommitted(self):
1156
1194
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1157
1195
# the branch we are bound to was updated
1158
1196
# merge those changes in first
1159
base_tree = self.basis_tree()
1197
base_tree = self.basis_tree()
1160
1198
other_tree = self.branch.repository.revision_tree(old_tip)
1161
1199
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1162
1200
base_tree, this_tree=self,
1171
1209
# the working tree is up to date with the branch
1172
1210
# we can merge the specified revision from master
1173
1211
to_tree = self.branch.repository.revision_tree(revision)
1174
to_root_id = to_tree.path2id('')
1212
to_root_id = to_tree.get_root_id()
1176
1214
basis = self.basis_tree()
1177
1215
with basis.lock_read():
1178
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):
1179
1217
self.set_root_id(to_root_id)
1248
1285
with self.lock_tree_write():
1249
1286
un_resolved = _mod_conflicts.ConflictList()
1250
1287
resolved = _mod_conflicts.ConflictList()
1288
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1251
1289
for conflict in self.conflicts():
1253
conflict.action_auto(self)
1254
except NotImplementedError:
1290
path = self.id2path(conflict.file_id)
1291
if (conflict.typestring != 'text conflict' or
1292
self.kind(path, conflict.file_id) != 'file'):
1255
1293
un_resolved.append(conflict)
1257
conflict.cleanup(self)
1258
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)
1259
1303
self.set_conflicts(un_resolved)
1260
1304
return un_resolved, resolved
1286
1330
"""See Tree._get_rules_searcher."""
1287
1331
if self._rules_searcher is None:
1288
1332
self._rules_searcher = super(WorkingTree,
1289
self)._get_rules_searcher(default_searcher)
1333
self)._get_rules_searcher(default_searcher)
1290
1334
return self._rules_searcher
1292
1336
def get_shelf_manager(self):
1293
1337
"""Return the ShelfManager for this WorkingTree."""
1294
1338
raise NotImplementedError(self.get_shelf_manager)
1296
def get_canonical_paths(self, paths):
1297
"""Like get_canonical_path() but works on multiple items.
1299
:param paths: A sequence of paths relative to the root of the tree.
1300
:return: A list of paths, with each item the corresponding input path
1301
adjusted to account for existing elements that match case
1304
with self.lock_read():
1308
def get_canonical_path(self, path):
1309
"""Returns the first item in the tree that matches a path.
1311
This is meant to allow case-insensitive path lookups on e.g.
1314
If a path matches exactly, it is returned. If no path matches exactly
1315
but more than one path matches according to the underlying file system,
1316
it is implementation defined which is returned.
1318
If no path matches according to the file system, the input path is
1319
returned, but with as many path entries that do exist changed to their
1322
If you need to resolve many names from the same tree, you should
1323
use get_canonical_paths() to avoid O(N) behaviour.
1325
:param path: A paths relative to the root of the tree.
1326
:return: The input path adjusted to account for existing elements
1327
that match case insensitively.
1329
with self.lock_read():
1330
return next(self.get_canonical_paths([path]))
1332
def reference_parent(self, path, branch=None, possible_transports=None):
1333
raise errors.UnsupportedOperation(self.reference_parent, self)
1335
def get_reference_info(self, path, branch=None):
1336
raise errors.UnsupportedOperation(self.get_reference_info, self)
1338
def set_reference_info(self, tree_path, branch_location):
1339
raise errors.UnsupportedOperation(self.set_reference_info, self)
1342
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
1341
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1343
1342
"""Registry for working tree formats."""
1345
1344
def __init__(self, other_registry=None):
1471
1470
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1472
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1471
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1473
1472
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1474
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1473
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1475
1474
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1476
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1475
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1477
1476
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1478
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1477
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1479
1478
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")