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."
154
151
"""See `Tree.has_versioned_directories`."""
155
152
return self._format.supports_versioned_directories
157
def supports_merge_modified(self):
158
"""Indicate whether this workingtree supports storing merge_modified.
160
return self._format.supports_merge_modified
162
154
def _supports_executable(self):
163
return osutils.supports_executable(self.basedir)
155
if sys.platform == 'win32':
157
# FIXME: Ideally this should check the file system
165
160
def break_lock(self):
166
161
"""Break a lock if one is present from another instance.
299
293
return WorkingTree.open(path, _unsupported=True)
296
def find_trees(location):
297
def list_current(transport):
298
return [d for d in transport.list_dir('')
299
if not controldir.is_control_filename(d)]
300
def evaluate(controldir):
302
tree = controldir.open_workingtree()
303
except errors.NoWorkingTree:
307
t = transport.get_transport(location)
308
iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
309
list_current=list_current)
310
return [tr for tr in iterator if tr is not None]
301
312
def __repr__(self):
302
313
return "<%s of %s>" % (self.__class__.__name__,
303
314
getattr(self, 'basedir', None))
319
330
# in the future this should return the tree for
320
331
# 'empty:' - the implicit root empty tree.
321
332
return self.branch.repository.revision_tree(
322
_mod_revision.NULL_REVISION)
333
_mod_revision.NULL_REVISION)
324
335
return self.revision_tree(revision_id)
325
336
except errors.NoSuchRevision:
350
361
def has_filename(self, filename):
351
362
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]
364
def get_file(self, path, file_id=None, filtered=True):
365
return self.get_file_with_stat(path, file_id, filtered=filtered)[0]
356
def get_file_with_stat(self, path, filtered=True,
367
def get_file_with_stat(self, path, file_id=None, filtered=True,
357
368
_fstat=osutils.fstat):
358
369
"""See Tree.get_file_with_stat."""
359
370
abspath = self.abspath(path)
361
file_obj = open(abspath, 'rb')
362
except EnvironmentError as e:
363
if e.errno == errno.ENOENT:
364
raise errors.NoSuchFile(path)
371
file_obj = file(abspath, 'rb')
366
372
stat_value = _fstat(file_obj.fileno())
367
373
if filtered and self.supports_content_filtering():
368
374
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)
375
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
376
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:
378
def get_file_text(self, path, file_id=None, filtered=True):
379
my_file = self.get_file(path, file_id, filtered=filtered)
378
381
return my_file.read()
380
def get_file_lines(self, path, filtered=True):
385
def get_file_lines(self, path, file_id=None, filtered=True):
381
386
"""See Tree.get_file_lines()"""
382
with self.get_file(path, filtered=filtered) as file:
387
file = self.get_file(path, file_id, filtered=filtered)
383
389
return file.readlines()
385
393
def get_parent_ids(self):
386
394
"""See Tree.get_parent_ids.
401
409
for l in osutils.split_lines(merges_bytes):
402
revision_id = l.rstrip(b'\n')
410
revision_id = l.rstrip('\n')
403
411
parents.append(revision_id)
414
def get_root_id(self):
415
"""Return the id of this trees root"""
416
raise NotImplementedError(self.get_root_id)
406
418
def clone(self, to_controldir, revision_id=None):
407
419
"""Duplicate this working tree into to_bzr, including all state.
444
456
new_parents = [revision_id]
445
457
tree.set_parent_ids(new_parents)
447
def get_file_size(self, path):
459
def id2abspath(self, file_id):
460
return self.abspath(self.id2path(file_id))
462
def get_file_size(self, path, file_id=None):
448
463
"""See Tree.get_file_size"""
449
464
# XXX: this returns the on-disk size; it should probably return the
482
497
with self.lock_write():
483
498
parents = self.get_parent_ids() + [revision_id]
484
499
self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
485
or allow_leftmost_as_ghost)
500
or allow_leftmost_as_ghost)
487
502
def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
488
503
"""Add revision_id, tree tuple as a parent.
505
520
allow_leftmost_as_ghost = True
506
521
self.set_parent_ids(parent_ids,
507
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
522
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
509
524
def add_pending_merge(self, *revision_ids):
510
525
with self.lock_tree_write():
521
536
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
523
538
def path_content_summary(self, path, _lstat=os.lstat,
524
_mapper=osutils.file_kind_from_stat_mode):
539
_mapper=osutils.file_kind_from_stat_mode):
525
540
"""See Tree.path_content_summary."""
526
541
abspath = self.abspath(path)
563
578
if len(revision_ids) > 0:
564
579
leftmost_id = revision_ids[0]
565
580
if (not allow_leftmost_as_ghost and not
566
self.branch.repository.has_revision(leftmost_id)):
581
self.branch.repository.has_revision(leftmost_id)):
567
582
raise errors.GhostRevisionUnusableHere(leftmost_id)
569
584
def _set_merges_from_parent_ids(self, parent_ids):
570
585
merges = parent_ids[1:]
571
self._transport.put_bytes('pending-merges', b'\n'.join(merges),
572
mode=self.controldir._get_file_mode())
586
self._transport.put_bytes('pending-merges', '\n'.join(merges),
587
mode=self.controldir._get_file_mode())
574
589
def _filter_parent_ids_by_ancestry(self, revision_ids):
575
590
"""Check that all merged revisions are proper 'heads'.
587
602
new_revision_ids.append(revision_id)
588
603
if new_revision_ids != revision_ids:
589
604
mutter('requested to set revision_ids = %s,'
590
' but filtered to %s', revision_ids, new_revision_ids)
605
' but filtered to %s', revision_ids, new_revision_ids)
591
606
return new_revision_ids
593
608
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
659
674
merger.other_rev_id = to_revision
660
675
if _mod_revision.is_null(merger.other_rev_id):
661
676
raise errors.NoCommits(branch)
662
self.branch.fetch(branch, stop_revision=merger.other_rev_id)
677
self.branch.fetch(branch, last_revision=merger.other_rev_id)
663
678
merger.other_basis = merger.other_rev_id
664
679
merger.other_tree = self.branch.repository.revision_tree(
665
680
merger.other_rev_id)
707
722
self.add(path, file_id, 'directory')
710
def get_symlink_target(self, path):
711
abspath = self.abspath(path)
725
def get_symlink_target(self, path, file_id=None):
727
abspath = self.abspath(path)
729
abspath = self.id2abspath(file_id)
712
730
target = osutils.readlink(abspath)
715
733
def subsume(self, other_tree):
716
734
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):
736
def _setup_directory_is_tree_reference(self):
737
if self._branch.repository._format.supports_tree_reference:
738
self._directory_is_tree_reference = \
739
self._directory_may_be_tree_reference
741
self._directory_is_tree_reference = \
742
self._directory_is_never_tree_reference
744
def _directory_is_never_tree_reference(self, relpath):
747
def _directory_may_be_tree_reference(self, relpath):
748
# as a special case, if a directory contains control files then
749
# it's a tree reference, except that the root of the tree is not
750
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
751
# TODO: We could ask all the control formats whether they
752
# recognize this directory, but at the moment there's no cheap api
753
# to do that. Since we probably can only nest bzr checkouts and
754
# they always use this name it's ok for now. -- mbp 20060306
756
# FIXME: There is an unhandled case here of a subdirectory
757
# containing .bzr but not a branch; that will probably blow up
758
# when you try to commit it. It might happen if there is a
759
# checkout in a subdirectory. This can be avoided by not adding
762
def extract(self, path, file_id=None, format=None):
722
763
"""Extract a subtree from this tree.
724
765
A new branch will be created, relative to the path for this tree.
729
770
"""Write the in memory meta data to disk."""
730
771
raise NotImplementedError(self.flush)
732
def kind(self, relpath):
773
def kind(self, relpath, file_id=None):
774
if file_id is not None:
775
return osutils.file_kind(self.id2abspath(file_id))
733
776
return osutils.file_kind(self.abspath(relpath))
735
def list_files(self, include_root=False, from_dir=None, recursive=True,
736
recurse_nested=False):
778
def list_files(self, include_root=False, from_dir=None, recursive=True):
737
779
"""List all files as (path, class, kind, id, entry).
739
781
Lists, but does not descend into unversioned directories.
820
862
def pull(self, source, overwrite=False, stop_revision=None,
821
863
change_reporter=None, possible_transports=None, local=False,
822
show_base=False, tag_selector=None):
823
865
with self.lock_write(), source.lock_read():
824
866
old_revision_info = self.branch.last_revision_info()
825
867
basis_tree = self.basis_tree()
826
868
count = self.branch.pull(source, overwrite, stop_revision,
827
869
possible_transports=possible_transports,
828
local=local, tag_selector=tag_selector)
829
871
new_revision_info = self.branch.last_revision_info()
830
872
if new_revision_info != old_revision_info:
831
873
repository = self.branch.repository
837
879
with basis_tree.lock_read():
838
880
new_basis_tree = self.branch.basis_tree()
839
881
merge.merge_inner(
844
change_reporter=change_reporter,
846
basis_root_id = basis_tree.path2id('')
847
new_root_id = new_basis_tree.path2id('')
886
change_reporter=change_reporter,
888
basis_root_id = basis_tree.get_root_id()
889
new_root_id = new_basis_tree.get_root_id()
848
890
if new_root_id is not None and basis_root_id != new_root_id:
849
891
self.set_root_id(new_root_id)
850
892
# TODO - dedup parents list with things merged by pull ?
862
904
merges = self.get_parent_ids()[1:]
863
905
parent_trees.extend([
864
906
(parent, repository.revision_tree(parent)) for
866
908
self.set_parent_trees(parent_trees)
869
def put_file_bytes_non_atomic(self, path, bytes):
911
def put_file_bytes_non_atomic(self, path, bytes, file_id=None):
870
912
"""See MutableTree.put_file_bytes_non_atomic."""
871
with self.lock_write(), open(self.abspath(path), 'wb') as stream:
913
with self.lock_write():
914
stream = file(self.abspath(path), 'wb')
874
920
def extras(self):
875
921
"""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.
923
If there are any unversioned directories then only the directory is
924
returned, not all its children. But if there are unversioned files
925
under a versioned subdirectory, they are returned.
882
927
Currently returned depth-first, sorted by name within directories.
883
928
This is the same order used by 'osutils.walkdirs'.
920
965
executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
921
966
return kind, executable, stat_value
968
def _file_size(self, entry, stat_value):
969
return stat_value.st_size
923
971
def last_revision(self):
924
972
"""Return the last revision of the branch for this tree.
1000
1048
def revert(self, filenames=None, old_tree=None, backups=True,
1001
1049
pb=None, report_changes=False):
1002
1050
from .conflicts import resolve
1003
with contextlib.ExitStack() as exit_stack:
1004
exit_stack.enter_context(self.lock_tree_write())
1051
with self.lock_tree_write():
1005
1052
if old_tree is None:
1006
1053
basis_tree = self.basis_tree()
1007
exit_stack.enter_context(basis_tree.lock_read())
1054
basis_tree.lock_read()
1008
1055
old_tree = basis_tree
1010
1057
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)
1059
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1061
if filenames is None and len(self.get_parent_ids()) > 1:
1063
last_revision = self.last_revision()
1064
if last_revision != _mod_revision.NULL_REVISION:
1065
if basis_tree is None:
1066
basis_tree = self.basis_tree()
1067
basis_tree.lock_read()
1068
parent_trees.append((last_revision, basis_tree))
1069
self.set_parent_trees(parent_trees)
1072
resolve(self, filenames, ignore_misses=True, recursive=True)
1074
if basis_tree is not None:
1025
1076
return conflicts
1027
1078
def store_uncommitted(self):
1045
1096
if not self.supports_setting_file_ids():
1046
1097
raise SettingFileIdUnsupported()
1047
1098
with self.lock_tree_write():
1049
1100
if file_id is None:
1050
1101
raise ValueError(
1051
1102
'WorkingTree.set_root_id with fileid=None')
1103
file_id = osutils.safe_file_id(file_id)
1052
1104
self._set_root_id(file_id)
1054
1106
def _set_root_id(self, file_id):
1153
1205
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1154
1206
# the branch we are bound to was updated
1155
1207
# merge those changes in first
1156
base_tree = self.basis_tree()
1208
base_tree = self.basis_tree()
1157
1209
other_tree = self.branch.repository.revision_tree(old_tip)
1158
1210
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1159
1211
base_tree, this_tree=self,
1168
1220
# the working tree is up to date with the branch
1169
1221
# we can merge the specified revision from master
1170
1222
to_tree = self.branch.repository.revision_tree(revision)
1171
to_root_id = to_tree.path2id('')
1223
to_root_id = to_tree.get_root_id()
1173
1225
basis = self.basis_tree()
1174
1226
with basis.lock_read():
1175
if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1227
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1176
1228
self.set_root_id(to_root_id)
1245
1296
with self.lock_tree_write():
1246
1297
un_resolved = _mod_conflicts.ConflictList()
1247
1298
resolved = _mod_conflicts.ConflictList()
1299
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1248
1300
for conflict in self.conflicts():
1250
conflict.action_auto(self)
1251
except NotImplementedError:
1301
if (conflict.typestring != 'text conflict' or
1302
self.kind(self.id2path(conflict.file_id), conflict.file_id) != 'file'):
1252
1303
un_resolved.append(conflict)
1254
conflict.cleanup(self)
1255
resolved.append(conflict)
1305
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1307
for line in my_file:
1308
if conflict_re.search(line):
1309
un_resolved.append(conflict)
1312
resolved.append(conflict)
1315
resolved.remove_files(self)
1256
1316
self.set_conflicts(un_resolved)
1257
1317
return un_resolved, resolved
1283
1343
"""See Tree._get_rules_searcher."""
1284
1344
if self._rules_searcher is None:
1285
1345
self._rules_searcher = super(WorkingTree,
1286
self)._get_rules_searcher(default_searcher)
1346
self)._get_rules_searcher(default_searcher)
1287
1347
return self._rules_searcher
1289
1349
def get_shelf_manager(self):
1290
1350
"""Return the ShelfManager for this WorkingTree."""
1291
1351
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):
1354
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1340
1355
"""Registry for working tree formats."""
1342
1357
def __init__(self, other_registry=None):
1408
1420
supports_leftmost_parent_id_as_ghost = True
1410
supports_righthand_parent_id_as_ghost = True
1412
ignore_filename = None
1413
"""Name of file with ignore patterns, if any. """
1415
1422
def initialize(self, controldir, revision_id=None, from_branch=None,
1416
1423
accelerator_tree=None, hardlink=False):
1417
1424
"""Initialize a new working tree in controldir.
1465
1472
return self._matchingcontroldir
1468
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1469
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1470
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1471
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1472
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1473
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1474
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1475
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1476
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")
1475
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
1476
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1477
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
1478
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1479
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
1480
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1481
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
1482
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1483
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")