27
27
WorkingTree.open(dir).
30
from __future__ import absolute_import
37
39
from .lazy_import import lazy_import
38
40
lazy_import(globals(), """
41
from bisect import bisect_left
42
46
from breezy import (
43
48
conflicts as _mod_conflicts,
44
51
filters as _mod_filters,
46
54
revision as _mod_revision,
51
from breezy.bzr import (
59
from .controldir import (
61
ControlComponentFormatRegistry,
62
ControlComponentFormat,
66
from .decorators import needs_read_lock, needs_write_lock
69
67
from .i18n import gettext
70
68
from . import mutabletree
71
from .symbol_versioning import deprecated_method, deprecated_in
69
from .mutabletree import needs_tree_write_lock
72
73
from .trace import mutter, note
75
class SettingFileIdUnsupported(errors.BzrError):
77
_fmt = "This format does not support setting file ids."
80
class ShelvingUnsupported(errors.BzrError):
82
_fmt = "This format does not support shelving changes."
85
class WorkingTree(mutabletree.MutableTree, ControlComponent):
76
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
79
class TreeEntry(object):
80
"""An entry that implements the minimum interface used by commands.
82
This needs further inspection, it may be better to have
83
InventoryEntries without ids - though that seems wrong. For now,
84
this is a parallel hierarchy to InventoryEntry, and needs to become
85
one of several things: decorates to that hierarchy, children of, or
87
Another note is that these objects are currently only used when there is
88
no InventoryEntry available - i.e. for unversioned objects.
89
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
92
def __eq__(self, other):
93
# yes, this us ugly, TODO: best practice __eq__ style.
94
return (isinstance(other, TreeEntry)
95
and other.__class__ == self.__class__)
97
def kind_character(self):
101
class TreeDirectory(TreeEntry):
102
"""See TreeEntry. This is a directory in a working tree."""
104
def __eq__(self, other):
105
return (isinstance(other, TreeDirectory)
106
and other.__class__ == self.__class__)
108
def kind_character(self):
112
class TreeFile(TreeEntry):
113
"""See TreeEntry. This is a regular file in a working tree."""
115
def __eq__(self, other):
116
return (isinstance(other, TreeFile)
117
and other.__class__ == self.__class__)
119
def kind_character(self):
123
class TreeLink(TreeEntry):
124
"""See TreeEntry. This is a symlink in a working tree."""
126
def __eq__(self, other):
127
return (isinstance(other, TreeLink)
128
and other.__class__ == self.__class__)
130
def kind_character(self):
134
class WorkingTree(mutabletree.MutableTree,
135
controldir.ControlComponent):
86
136
"""Working copy tree.
88
138
:ivar basedir: The root of the tree on disk. This is a unicode path object
350
408
def has_filename(self, filename):
351
409
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]
411
def get_file(self, file_id, path=None, filtered=True):
412
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
356
def get_file_with_stat(self, path, filtered=True,
414
def get_file_with_stat(self, file_id, path=None, filtered=True,
357
415
_fstat=osutils.fstat):
358
416
"""See Tree.get_file_with_stat."""
359
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)
418
path = self.id2path(file_id)
419
file_obj = self.get_file_byname(path, filtered=False)
366
420
stat_value = _fstat(file_obj.fileno())
367
421
if filtered and self.supports_content_filtering():
368
422
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)
423
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
424
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:
426
def get_file_text(self, file_id, path=None, filtered=True):
427
my_file = self.get_file(file_id, path=path, filtered=filtered)
378
429
return my_file.read()
380
def get_file_lines(self, path, filtered=True):
433
def get_file_byname(self, filename, filtered=True):
434
path = self.abspath(filename)
436
if filtered and self.supports_content_filtering():
437
filters = self._content_filter_stack(filename)
438
return _mod_filters.filtered_input_file(f, filters)
442
def get_file_lines(self, file_id, path=None, filtered=True):
381
443
"""See Tree.get_file_lines()"""
382
with self.get_file(path, filtered=filtered) as file:
444
file = self.get_file(file_id, path, filtered=filtered)
383
446
return file.readlines()
385
450
def get_parent_ids(self):
386
451
"""See Tree.get_parent_ids.
416
486
revision, and difference between the source trees last revision
417
487
and this one merged in.
419
with self.lock_read():
420
# assumes the target bzr dir format is compatible.
421
result = to_controldir.create_workingtree()
422
self.copy_content_into(result, revision_id)
489
# assumes the target bzr dir format is compatible.
490
result = to_controldir.create_workingtree()
491
self.copy_content_into(result, revision_id)
425
495
def copy_content_into(self, tree, revision_id=None):
426
496
"""Copy the current content and user files of this tree into tree."""
427
with self.lock_read():
428
tree.set_root_id(self.path2id(''))
429
if revision_id is None:
430
merge.transform_tree(tree, self)
497
tree.set_root_id(self.get_root_id())
498
if revision_id is None:
499
merge.transform_tree(tree, self)
501
# TODO now merge from tree.last_revision to revision (to preserve
502
# user local changes)
504
other_tree = self.revision_tree(revision_id)
505
except errors.NoSuchRevision:
506
other_tree = self.branch.repository.revision_tree(revision_id)
508
merge.transform_tree(tree, other_tree)
509
if revision_id == _mod_revision.NULL_REVISION:
432
# TODO now merge from tree.last_revision to revision (to
433
# preserve user local changes)
435
other_tree = self.revision_tree(revision_id)
436
except errors.NoSuchRevision:
437
other_tree = self.branch.repository.revision_tree(
440
merge.transform_tree(tree, other_tree)
441
if revision_id == _mod_revision.NULL_REVISION:
444
new_parents = [revision_id]
445
tree.set_parent_ids(new_parents)
447
def get_file_size(self, path):
512
new_parents = [revision_id]
513
tree.set_parent_ids(new_parents)
515
def id2abspath(self, file_id):
516
return self.abspath(self.id2path(file_id))
518
def get_file_size(self, file_id):
448
519
"""See Tree.get_file_size"""
449
520
# XXX: this returns the on-disk size; it should probably return the
452
return os.path.getsize(self.abspath(path))
523
return os.path.getsize(self.id2abspath(file_id))
453
524
except OSError as e:
454
525
if e.errno != errno.ENOENT:
530
@needs_tree_write_lock
459
531
def _gather_kinds(self, files, kinds):
460
532
"""See MutableTree._gather_kinds."""
461
with self.lock_tree_write():
462
for pos, f in enumerate(files):
463
if kinds[pos] is None:
464
fullpath = osutils.normpath(self.abspath(f))
466
kinds[pos] = osutils.file_kind(fullpath)
468
if e.errno == errno.ENOENT:
469
raise errors.NoSuchFile(fullpath)
533
for pos, f in enumerate(files):
534
if kinds[pos] is None:
535
fullpath = osutils.normpath(self.abspath(f))
537
kinds[pos] = osutils.file_kind(fullpath)
539
if e.errno == errno.ENOENT:
540
raise errors.NoSuchFile(fullpath)
471
543
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
472
544
"""Add revision_id as a parent.
497
569
If the revision_id is a ghost, pass None for the tree.
498
570
:param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
500
with self.lock_tree_write():
501
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
502
if len(parent_ids) > 1:
503
# the leftmost may have already been a ghost, preserve that if it
505
allow_leftmost_as_ghost = True
506
self.set_parent_ids(parent_ids,
507
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
572
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
573
if len(parent_ids) > 1:
574
# the leftmost may have already been a ghost, preserve that if it
576
allow_leftmost_as_ghost = True
577
self.set_parent_ids(parent_ids,
578
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
580
@needs_tree_write_lock
509
581
def add_pending_merge(self, *revision_ids):
510
with self.lock_tree_write():
511
# TODO: Perhaps should check at this point that the
512
# history of the revision is actually present?
513
parents = self.get_parent_ids()
515
for rev_id in revision_ids:
516
if rev_id in parents:
518
parents.append(rev_id)
521
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
582
# TODO: Perhaps should check at this point that the
583
# history of the revision is actually present?
584
parents = self.get_parent_ids()
586
for rev_id in revision_ids:
587
if rev_id in parents:
589
parents.append(rev_id)
592
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
523
594
def path_content_summary(self, path, _lstat=os.lstat,
524
_mapper=osutils.file_kind_from_stat_mode):
595
_mapper=osutils.file_kind_from_stat_mode):
525
596
"""See Tree.path_content_summary."""
526
597
abspath = self.abspath(path)
602
674
:param revision_ids: The revision_ids to set as the parent ids of this
603
675
working tree. Any of these may be ghosts.
605
with self.lock_tree_write():
606
self._check_parents_for_ghosts(revision_ids,
607
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
608
for revision_id in revision_ids:
609
_mod_revision.check_not_reserved_id(revision_id)
611
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
613
if len(revision_ids) > 0:
614
self.set_last_revision(revision_ids[0])
616
self.set_last_revision(_mod_revision.NULL_REVISION)
618
self._set_merges_from_parent_ids(revision_ids)
677
self._check_parents_for_ghosts(revision_ids,
678
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
679
for revision_id in revision_ids:
680
_mod_revision.check_not_reserved_id(revision_id)
682
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
684
if len(revision_ids) > 0:
685
self.set_last_revision(revision_ids[0])
687
self.set_last_revision(_mod_revision.NULL_REVISION)
689
self._set_merges_from_parent_ids(revision_ids)
691
@needs_tree_write_lock
620
692
def set_pending_merges(self, rev_list):
621
with self.lock_tree_write():
622
parents = self.get_parent_ids()
623
leftmost = parents[:1]
624
new_parents = leftmost + rev_list
625
self.set_parent_ids(new_parents)
693
parents = self.get_parent_ids()
694
leftmost = parents[:1]
695
new_parents = leftmost + rev_list
696
self.set_parent_ids(new_parents)
698
@needs_tree_write_lock
627
699
def set_merge_modified(self, modified_hashes):
628
700
"""Set the merge modified hashes."""
629
701
raise NotImplementedError(self.set_merge_modified)
649
722
branch.last_revision().
651
724
from .merge import Merger, Merge3Merger
652
with self.lock_write():
653
merger = Merger(self.branch, this_tree=self)
654
# check that there are no local alterations
655
if not force and self.has_changes():
656
raise errors.UncommittedChanges(self)
657
if to_revision is None:
658
to_revision = _mod_revision.ensure_null(branch.last_revision())
659
merger.other_rev_id = to_revision
660
if _mod_revision.is_null(merger.other_rev_id):
661
raise errors.NoCommits(branch)
662
self.branch.fetch(branch, stop_revision=merger.other_rev_id)
663
merger.other_basis = merger.other_rev_id
664
merger.other_tree = self.branch.repository.revision_tree(
666
merger.other_branch = branch
667
if from_revision is None:
670
merger.set_base_revision(from_revision, branch)
671
if merger.base_rev_id == merger.other_rev_id:
672
raise errors.PointlessMerge
673
merger.backup_files = False
674
if merge_type is None:
675
merger.merge_type = Merge3Merger
677
merger.merge_type = merge_type
678
merger.set_interesting_files(None)
679
merger.show_base = False
680
merger.reprocess = False
681
conflicts = merger.do_merge()
725
merger = Merger(self.branch, this_tree=self)
726
# check that there are no local alterations
727
if not force and self.has_changes():
728
raise errors.UncommittedChanges(self)
729
if to_revision is None:
730
to_revision = _mod_revision.ensure_null(branch.last_revision())
731
merger.other_rev_id = to_revision
732
if _mod_revision.is_null(merger.other_rev_id):
733
raise errors.NoCommits(branch)
734
self.branch.fetch(branch, last_revision=merger.other_rev_id)
735
merger.other_basis = merger.other_rev_id
736
merger.other_tree = self.branch.repository.revision_tree(
738
merger.other_branch = branch
739
if from_revision is None:
742
merger.set_base_revision(from_revision, branch)
743
if merger.base_rev_id == merger.other_rev_id:
744
raise errors.PointlessMerge
745
merger.backup_files = False
746
if merge_type is None:
747
merger.merge_type = Merge3Merger
749
merger.merge_type = merge_type
750
merger.set_interesting_files(None)
751
merger.show_base = False
752
merger.reprocess = False
753
conflicts = merger.do_merge()
685
757
def merge_modified(self):
686
758
"""Return a dictionary of files modified by a merge.
690
762
because of a merge.
692
764
This returns a map of file_id->sha1, containing only files which are
693
still in the working tree and have that text hash.
765
still in the working inventory and have that text hash.
695
767
raise NotImplementedError(self.merge_modified)
697
770
def mkdir(self, path, file_id=None):
698
771
"""See MutableTree.mkdir()."""
699
772
if file_id is None:
700
if self.supports_setting_file_ids():
701
file_id = generate_ids.gen_file_id(os.path.basename(path))
773
file_id = generate_ids.gen_file_id(os.path.basename(path))
774
os.mkdir(self.abspath(path))
775
self.add(path, file_id, 'directory')
778
def get_symlink_target(self, file_id, path=None):
780
abspath = self.abspath(path)
703
if not self.supports_setting_file_ids():
704
raise SettingFileIdUnsupported()
705
with self.lock_write():
706
os.mkdir(self.abspath(path))
707
self.add(path, file_id, 'directory')
710
def get_symlink_target(self, path):
711
abspath = self.abspath(path)
713
return osutils.readlink(abspath)
715
if getattr(e, 'errno', None) == errno.ENOENT:
716
raise errors.NoSuchFile(path)
782
abspath = self.id2abspath(file_id)
783
target = osutils.readlink(abspath)
719
786
def subsume(self, other_tree):
720
787
raise NotImplementedError(self.subsume)
722
def _directory_is_tree_reference(self, relpath):
723
raise NotImplementedError(self._directory_is_tree_reference)
725
def extract(self, path, format=None):
789
def _setup_directory_is_tree_reference(self):
790
if self._branch.repository._format.supports_tree_reference:
791
self._directory_is_tree_reference = \
792
self._directory_may_be_tree_reference
794
self._directory_is_tree_reference = \
795
self._directory_is_never_tree_reference
797
def _directory_is_never_tree_reference(self, relpath):
800
def _directory_may_be_tree_reference(self, relpath):
801
# as a special case, if a directory contains control files then
802
# it's a tree reference, except that the root of the tree is not
803
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
804
# TODO: We could ask all the control formats whether they
805
# recognize this directory, but at the moment there's no cheap api
806
# to do that. Since we probably can only nest bzr checkouts and
807
# they always use this name it's ok for now. -- mbp 20060306
809
# FIXME: There is an unhandled case here of a subdirectory
810
# containing .bzr but not a branch; that will probably blow up
811
# when you try to commit it. It might happen if there is a
812
# checkout in a subdirectory. This can be avoided by not adding
815
def extract(self, file_id, format=None):
726
816
"""Extract a subtree from this tree.
728
818
A new branch will be created, relative to the path for this tree.
787
876
raise NotImplementedError(self.move)
789
def copy_one(self, from_rel, to_rel):
790
"""Copy a file in the tree to a new location.
792
This default implementation just copies the file, then
795
:param from_rel: From location (relative to tree root)
796
:param to_rel: Target location (relative to tree root)
878
@needs_tree_write_lock
879
def rename_one(self, from_rel, to_rel, after=False):
882
This can change the directory or the filename or both.
884
rename_one has several 'modes' to work. First, it can rename a physical
885
file and change the file_id. That is the normal mode. Second, it can
886
only change the file_id without touching any physical file.
888
rename_one uses the second mode if 'after == True' and 'to_rel' is
889
either not versioned or newly added, and present in the working tree.
891
rename_one uses the second mode if 'after == False' and 'from_rel' is
892
versioned but no longer in the working tree, and 'to_rel' is not
893
versioned but present in the working tree.
895
rename_one uses the first mode if 'after == False' and 'from_rel' is
896
versioned and present in the working tree, and 'to_rel' is not
897
versioned and not present in the working tree.
899
Everything else results in an error.
798
shutil.copyfile(self.abspath(from_rel), self.abspath(to_rel))
901
raise NotImplementedError(self.rename_one)
801
904
def unknowns(self):
802
905
"""Return all unknown files.
804
907
These are files in the working directory that are not versioned or
805
908
control files or ignored.
807
with self.lock_read():
808
# force the extras method to be fully executed before returning, to
809
# prevent race conditions with the lock
811
[subp for subp in self.extras() if not self.is_ignored(subp)])
813
def unversion(self, paths):
814
"""Remove the path in pahs from the current versioned set.
816
When a path is unversioned, all of its children are automatically
910
# force the extras method to be fully executed before returning, to
911
# prevent race conditions with the lock
913
[subp for subp in self.extras() if not self.is_ignored(subp)])
915
def unversion(self, file_ids):
916
"""Remove the file ids in file_ids from the current versioned set.
918
When a file_id is unversioned, all of its children are automatically
819
:param paths: The paths to stop versioning.
820
:raises NoSuchFile: if any path is not currently versioned.
921
:param file_ids: The file ids to stop versioning.
922
:raises: NoSuchId if any fileid is not currently versioned.
822
924
raise NotImplementedError(self.unversion)
824
927
def pull(self, source, overwrite=False, stop_revision=None,
825
928
change_reporter=None, possible_transports=None, local=False,
826
show_base=False, tag_selector=None):
827
with self.lock_write(), source.lock_read():
828
932
old_revision_info = self.branch.last_revision_info()
829
933
basis_tree = self.basis_tree()
830
934
count = self.branch.pull(source, overwrite, stop_revision,
831
935
possible_transports=possible_transports,
832
local=local, tag_selector=tag_selector)
833
937
new_revision_info = self.branch.last_revision_info()
834
938
if new_revision_info != old_revision_info:
835
939
repository = self.branch.repository
866
973
merges = self.get_parent_ids()[1:]
867
974
parent_trees.extend([
868
975
(parent, repository.revision_tree(parent)) for
870
977
self.set_parent_trees(parent_trees)
873
def put_file_bytes_non_atomic(self, path, bytes):
983
def put_file_bytes_non_atomic(self, file_id, bytes):
874
984
"""See MutableTree.put_file_bytes_non_atomic."""
875
with self.lock_write(), open(self.abspath(path), 'wb') as stream:
985
stream = file(self.id2abspath(file_id), 'wb')
876
987
stream.write(bytes)
878
991
def extras(self):
879
992
"""Yield all unversioned files in this WorkingTree.
881
If there are any unversioned directories and the file format
882
supports versioning directories, then only the directory is returned,
883
not all its children. But if there are unversioned files under a
884
versioned subdirectory, they are returned.
994
If there are any unversioned directories then only the directory is
995
returned, not all its children. But if there are unversioned files
996
under a versioned subdirectory, they are returned.
886
998
Currently returned depth-first, sorted by name within directories.
887
999
This is the same order used by 'osutils.walkdirs'.
999
1118
:force: Delete files and directories, even if they are changed and
1000
1119
even if the directories are not empty.
1002
raise NotImplementedError(self.remove)
1121
if isinstance(files, (str, text_type)):
1126
all_files = set() # specified and nested files
1127
unknown_nested_files=set()
1129
to_file = sys.stdout
1131
files_to_backup = []
1133
def recurse_directory_to_add_files(directory):
1134
# Recurse directory and add all files
1135
# so we can check if they have changed.
1136
for parent_info, file_infos in self.walkdirs(directory):
1137
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1138
# Is it versioned or ignored?
1139
if self.path2id(relpath):
1140
# Add nested content for deletion.
1141
all_files.add(relpath)
1143
# Files which are not versioned
1144
# should be treated as unknown.
1145
files_to_backup.append(relpath)
1147
for filename in files:
1148
# Get file name into canonical form.
1149
abspath = self.abspath(filename)
1150
filename = self.relpath(abspath)
1151
if len(filename) > 0:
1152
all_files.add(filename)
1153
recurse_directory_to_add_files(filename)
1155
files = list(all_files)
1158
return # nothing to do
1160
# Sort needed to first handle directory content before the directory
1161
files.sort(reverse=True)
1163
# Bail out if we are going to delete files we shouldn't
1164
if not keep_files and not force:
1165
for (file_id, path, content_change, versioned, parent_id, name,
1166
kind, executable) in self.iter_changes(self.basis_tree(),
1167
include_unchanged=True, require_versioned=False,
1168
want_unversioned=True, specific_files=files):
1169
if versioned[0] == False:
1170
# The record is unknown or newly added
1171
files_to_backup.append(path[1])
1172
elif (content_change and (kind[1] is not None) and
1173
osutils.is_inside_any(files, path[1])):
1174
# Versioned and changed, but not deleted, and still
1175
# in one of the dirs to be deleted.
1176
files_to_backup.append(path[1])
1178
def backup(file_to_backup):
1179
backup_name = self.controldir._available_backup_name(file_to_backup)
1180
osutils.rename(abs_path, self.abspath(backup_name))
1181
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1184
# Build inv_delta and delete files where applicable,
1185
# do this before any modifications to meta data.
1187
fid = self.path2id(f)
1190
message = "%s is not versioned." % (f,)
1193
# having removed it, it must be either ignored or unknown
1194
if self.is_ignored(f):
1198
# XXX: Really should be a more abstract reporter interface
1199
kind_ch = osutils.kind_marker(self.kind(fid))
1200
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1202
inv_delta.append((f, None, fid, None))
1203
message = "removed %s" % (f,)
1206
abs_path = self.abspath(f)
1207
if osutils.lexists(abs_path):
1208
if (osutils.isdir(abs_path) and
1209
len(os.listdir(abs_path)) > 0):
1211
osutils.rmtree(abs_path)
1212
message = "deleted %s" % (f,)
1216
if f in files_to_backup:
1219
osutils.delete_any(abs_path)
1220
message = "deleted %s" % (f,)
1221
elif message is not None:
1222
# Only care if we haven't done anything yet.
1223
message = "%s does not exist." % (f,)
1225
# Print only one message (if any) per file.
1226
if message is not None:
1228
self.apply_inventory_delta(inv_delta)
1230
@needs_tree_write_lock
1004
1231
def revert(self, filenames=None, old_tree=None, backups=True,
1005
1232
pb=None, report_changes=False):
1006
1233
from .conflicts import resolve
1007
with contextlib.ExitStack() as exit_stack:
1008
exit_stack.enter_context(self.lock_tree_write())
1009
if old_tree is None:
1010
basis_tree = self.basis_tree()
1011
exit_stack.enter_context(basis_tree.lock_read())
1012
old_tree = basis_tree
1234
if old_tree is None:
1235
basis_tree = self.basis_tree()
1236
basis_tree.lock_read()
1237
old_tree = basis_tree
1015
1241
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1016
1242
report_changes)
1017
1243
if filenames is None and len(self.get_parent_ids()) > 1:
1020
1246
if last_revision != _mod_revision.NULL_REVISION:
1021
1247
if basis_tree is None:
1022
1248
basis_tree = self.basis_tree()
1023
exit_stack.enter_context(basis_tree.lock_read())
1249
basis_tree.lock_read()
1024
1250
parent_trees.append((last_revision, basis_tree))
1025
1251
self.set_parent_trees(parent_trees)
1028
1254
resolve(self, filenames, ignore_misses=True, recursive=True)
1256
if basis_tree is not None:
1031
1261
def store_uncommitted(self):
1032
1262
"""Store uncommitted changes from the tree in the branch."""
1033
raise NotImplementedError(self.store_uncommitted)
1263
target_tree = self.basis_tree()
1264
shelf_creator = shelf.ShelfCreator(self, target_tree)
1266
if not shelf_creator.shelve_all():
1268
self.branch.store_uncommitted(shelf_creator)
1269
shelf_creator.transform()
1271
shelf_creator.finalize()
1272
note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1035
1275
def restore_uncommitted(self):
1036
1276
"""Restore uncommitted changes from the branch into the tree."""
1037
raise NotImplementedError(self.restore_uncommitted)
1277
unshelver = self.branch.get_unshelver(self)
1278
if unshelver is None:
1281
merger = unshelver.make_merger()
1282
merger.ignore_zero = True
1284
self.branch.store_uncommitted(None)
1286
unshelver.finalize()
1039
1288
def revision_tree(self, revision_id):
1040
1289
"""See Tree.revision_tree.
1042
For trees that can be obtained from the working tree, this
1043
will do so. For other trees, it will fall back to the repository.
1291
WorkingTree can supply revision_trees for the basis revision only
1292
because there is only one cached inventory in the bzr directory.
1045
1294
raise NotImplementedError(self.revision_tree)
1296
@needs_tree_write_lock
1047
1297
def set_root_id(self, file_id):
1048
1298
"""Set the root id for this tree."""
1049
if not self.supports_setting_file_ids():
1050
raise SettingFileIdUnsupported()
1051
with self.lock_tree_write():
1055
'WorkingTree.set_root_id with fileid=None')
1056
self._set_root_id(file_id)
1302
'WorkingTree.set_root_id with fileid=None')
1303
file_id = osutils.safe_file_id(file_id)
1304
self._set_root_id(file_id)
1058
1306
def _set_root_id(self, file_id):
1059
1307
"""Set the root id for this tree, in a format specific manner.
1143
1392
# We MUST save it even if an error occurs, because otherwise the users
1144
1393
# local work is unreferenced and will appear to have been lost.
1146
with self.lock_tree_write():
1397
last_rev = self.get_parent_ids()[0]
1399
last_rev = _mod_revision.NULL_REVISION
1400
if revision is None:
1401
revision = self.branch.last_revision()
1403
old_tip = old_tip or _mod_revision.NULL_REVISION
1405
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1406
# the branch we are bound to was updated
1407
# merge those changes in first
1408
base_tree = self.basis_tree()
1409
other_tree = self.branch.repository.revision_tree(old_tip)
1410
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1411
base_tree, this_tree=self,
1412
change_reporter=change_reporter,
1413
show_base=show_base)
1415
self.add_parent_tree((old_tip, other_tree))
1416
note(gettext('Rerun update after fixing the conflicts.'))
1419
if last_rev != _mod_revision.ensure_null(revision):
1420
# the working tree is up to date with the branch
1421
# we can merge the specified revision from master
1422
to_tree = self.branch.repository.revision_tree(revision)
1423
to_root_id = to_tree.get_root_id()
1425
basis = self.basis_tree()
1149
last_rev = self.get_parent_ids()[0]
1151
last_rev = _mod_revision.NULL_REVISION
1152
if revision is None:
1153
revision = self.branch.last_revision()
1155
old_tip = old_tip or _mod_revision.NULL_REVISION
1157
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1158
# the branch we are bound to was updated
1159
# merge those changes in first
1160
base_tree = self.basis_tree()
1161
other_tree = self.branch.repository.revision_tree(old_tip)
1162
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1163
base_tree, this_tree=self,
1164
change_reporter=change_reporter,
1165
show_base=show_base)
1167
self.add_parent_tree((old_tip, other_tree))
1168
note(gettext('Rerun update after fixing the conflicts.'))
1171
if last_rev != _mod_revision.ensure_null(revision):
1172
# the working tree is up to date with the branch
1173
# we can merge the specified revision from master
1174
to_tree = self.branch.repository.revision_tree(revision)
1175
to_root_id = to_tree.path2id('')
1177
basis = self.basis_tree()
1178
with basis.lock_read():
1179
if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1180
self.set_root_id(to_root_id)
1183
# determine the branch point
1184
graph = self.branch.repository.get_graph()
1185
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1187
base_tree = self.branch.repository.revision_tree(base_rev_id)
1189
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1191
change_reporter=change_reporter,
1192
show_base=show_base)
1193
self.set_last_revision(revision)
1194
# TODO - dedup parents list with things merged by pull ?
1195
# reuse the tree we've updated to to set the basis:
1196
parent_trees = [(revision, to_tree)]
1197
merges = self.get_parent_ids()[1:]
1198
# Ideally we ask the tree for the trees here, that way the working
1199
# tree can decide whether to give us the entire tree or give us a
1200
# lazy initialised tree. dirstate for instance will have the trees
1201
# in ram already, whereas a last-revision + basis-inventory tree
1202
# will not, but also does not need them when setting parents.
1203
for parent in merges:
1204
parent_trees.append(
1205
(parent, self.branch.repository.revision_tree(parent)))
1206
if not _mod_revision.is_null(old_tip):
1207
parent_trees.append(
1208
(old_tip, self.branch.repository.revision_tree(old_tip)))
1209
self.set_parent_trees(parent_trees)
1210
last_rev = parent_trees[0][0]
1428
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1429
self.set_root_id(to_root_id)
1434
# determine the branch point
1435
graph = self.branch.repository.get_graph()
1436
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1438
base_tree = self.branch.repository.revision_tree(base_rev_id)
1440
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1442
change_reporter=change_reporter,
1443
show_base=show_base)
1444
self.set_last_revision(revision)
1445
# TODO - dedup parents list with things merged by pull ?
1446
# reuse the tree we've updated to to set the basis:
1447
parent_trees = [(revision, to_tree)]
1448
merges = self.get_parent_ids()[1:]
1449
# Ideally we ask the tree for the trees here, that way the working
1450
# tree can decide whether to give us the entire tree or give us a
1451
# lazy initialised tree. dirstate for instance will have the trees
1452
# in ram already, whereas a last-revision + basis-inventory tree
1453
# will not, but also does not need them when setting parents.
1454
for parent in merges:
1455
parent_trees.append(
1456
(parent, self.branch.repository.revision_tree(parent)))
1457
if not _mod_revision.is_null(old_tip):
1458
parent_trees.append(
1459
(old_tip, self.branch.repository.revision_tree(old_tip)))
1460
self.set_parent_trees(parent_trees)
1461
last_rev = parent_trees[0][0]
1213
1464
def set_conflicts(self, arg):
1214
1465
raise errors.UnsupportedOperation(self.set_conflicts, self)
1233
1484
If the tree is not locked, it may cause an error to be raised,
1234
1485
depending on the tree implementation.
1236
raise NotImplementedError(self.walkdirs)
1238
@deprecated_method(deprecated_in((3, 0, 1)))
1487
disk_top = self.abspath(prefix)
1488
if disk_top.endswith('/'):
1489
disk_top = disk_top[:-1]
1490
top_strip_len = len(disk_top) + 1
1491
inventory_iterator = self._walkdirs(prefix)
1492
disk_iterator = osutils.walkdirs(disk_top, prefix)
1494
current_disk = next(disk_iterator)
1495
disk_finished = False
1496
except OSError as e:
1497
if not (e.errno == errno.ENOENT or
1498
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1501
disk_finished = True
1503
current_inv = next(inventory_iterator)
1504
inv_finished = False
1505
except StopIteration:
1508
while not inv_finished or not disk_finished:
1510
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1511
cur_disk_dir_content) = current_disk
1513
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1514
cur_disk_dir_content) = ((None, None), None)
1515
if not disk_finished:
1516
# strip out .bzr dirs
1517
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1518
len(cur_disk_dir_content) > 0):
1519
# osutils.walkdirs can be made nicer -
1520
# yield the path-from-prefix rather than the pathjoined
1522
bzrdir_loc = bisect_left(cur_disk_dir_content,
1524
if (bzrdir_loc < len(cur_disk_dir_content)
1525
and self.controldir.is_control_filename(
1526
cur_disk_dir_content[bzrdir_loc][0])):
1527
# we dont yield the contents of, or, .bzr itself.
1528
del cur_disk_dir_content[bzrdir_loc]
1530
# everything is unknown
1533
# everything is missing
1536
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1538
# disk is before inventory - unknown
1539
dirblock = [(relpath, basename, kind, stat, None, None) for
1540
relpath, basename, kind, stat, top_path in
1541
cur_disk_dir_content]
1542
yield (cur_disk_dir_relpath, None), dirblock
1544
current_disk = next(disk_iterator)
1545
except StopIteration:
1546
disk_finished = True
1548
# inventory is before disk - missing.
1549
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1550
for relpath, basename, dkind, stat, fileid, kind in
1552
yield (current_inv[0][0], current_inv[0][1]), dirblock
1554
current_inv = next(inventory_iterator)
1555
except StopIteration:
1558
# versioned present directory
1559
# merge the inventory and disk data together
1561
for relpath, subiterator in itertools.groupby(sorted(
1562
current_inv[1] + cur_disk_dir_content,
1563
key=operator.itemgetter(0)), operator.itemgetter(1)):
1564
path_elements = list(subiterator)
1565
if len(path_elements) == 2:
1566
inv_row, disk_row = path_elements
1567
# versioned, present file
1568
dirblock.append((inv_row[0],
1569
inv_row[1], disk_row[2],
1570
disk_row[3], inv_row[4],
1572
elif len(path_elements[0]) == 5:
1574
dirblock.append((path_elements[0][0],
1575
path_elements[0][1], path_elements[0][2],
1576
path_elements[0][3], None, None))
1577
elif len(path_elements[0]) == 6:
1578
# versioned, absent file.
1579
dirblock.append((path_elements[0][0],
1580
path_elements[0][1], 'unknown', None,
1581
path_elements[0][4], path_elements[0][5]))
1583
raise NotImplementedError('unreachable code')
1584
yield current_inv[0], dirblock
1586
current_inv = next(inventory_iterator)
1587
except StopIteration:
1590
current_disk = next(disk_iterator)
1591
except StopIteration:
1592
disk_finished = True
1594
def _walkdirs(self, prefix=""):
1595
"""Walk the directories of this tree.
1597
:param prefix: is used as the directrory to start with.
1598
:returns: a generator which yields items in the form::
1600
((curren_directory_path, fileid),
1601
[(file1_path, file1_name, file1_kind, None, file1_id,
1604
raise NotImplementedError(self._walkdirs)
1606
@needs_tree_write_lock
1239
1607
def auto_resolve(self):
1240
1608
"""Automatically resolve text conflicts according to contents.
1244
1612
into files that have text conflicts. The corresponding .THIS .BASE and
1245
1613
.OTHER files are deleted, as per 'resolve'.
1247
:return: a tuple of lists: (un_resolved, resolved).
1615
:return: a tuple of ConflictLists: (un_resolved, resolved).
1249
with self.lock_tree_write():
1252
for conflict in self.conflicts():
1254
conflict.action_auto(self)
1255
except NotImplementedError:
1256
un_resolved.append(conflict)
1617
un_resolved = _mod_conflicts.ConflictList()
1618
resolved = _mod_conflicts.ConflictList()
1619
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1620
for conflict in self.conflicts():
1621
if (conflict.typestring != 'text conflict' or
1622
self.kind(conflict.file_id) != 'file'):
1623
un_resolved.append(conflict)
1625
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1627
for line in my_file:
1628
if conflict_re.search(line):
1629
un_resolved.append(conflict)
1258
conflict.cleanup(self)
1259
1632
resolved.append(conflict)
1260
self.set_conflicts(un_resolved)
1261
return un_resolved, resolved
1635
resolved.remove_files(self)
1636
self.set_conflicts(un_resolved)
1637
return un_resolved, resolved
1263
1639
def _validate(self):
1264
1640
"""Validate internal structures.
1287
1663
"""See Tree._get_rules_searcher."""
1288
1664
if self._rules_searcher is None:
1289
1665
self._rules_searcher = super(WorkingTree,
1290
self)._get_rules_searcher(default_searcher)
1666
self)._get_rules_searcher(default_searcher)
1291
1667
return self._rules_searcher
1293
1669
def get_shelf_manager(self):
1294
1670
"""Return the ShelfManager for this WorkingTree."""
1295
raise NotImplementedError(self.get_shelf_manager)
1297
def get_canonical_paths(self, paths):
1298
"""Like get_canonical_path() but works on multiple items.
1300
:param paths: A sequence of paths relative to the root of the tree.
1301
:return: A list of paths, with each item the corresponding input path
1302
adjusted to account for existing elements that match case
1305
with self.lock_read():
1309
def get_canonical_path(self, path):
1310
"""Returns the first item in the tree that matches a path.
1312
This is meant to allow case-insensitive path lookups on e.g.
1315
If a path matches exactly, it is returned. If no path matches exactly
1316
but more than one path matches according to the underlying file system,
1317
it is implementation defined which is returned.
1319
If no path matches according to the file system, the input path is
1320
returned, but with as many path entries that do exist changed to their
1323
If you need to resolve many names from the same tree, you should
1324
use get_canonical_paths() to avoid O(N) behaviour.
1326
:param path: A paths relative to the root of the tree.
1327
:return: The input path adjusted to account for existing elements
1328
that match case insensitively.
1330
with self.lock_read():
1331
return next(self.get_canonical_paths([path]))
1333
def reference_parent(self, path, branch=None, possible_transports=None):
1334
raise errors.UnsupportedOperation(self.reference_parent, self)
1336
def get_reference_info(self, path, branch=None):
1337
raise errors.UnsupportedOperation(self.get_reference_info, self)
1339
def set_reference_info(self, tree_path, branch_location):
1340
raise errors.UnsupportedOperation(self.set_reference_info, self)
1343
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
1671
from .shelf import ShelfManager
1672
return ShelfManager(self, self._transport)
1675
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1344
1676
"""Registry for working tree formats."""
1346
1678
def __init__(self, other_registry=None):
1466
1782
This is to support testing of working tree formats that can not exist
1467
1783
in the same control directory as a branch.
1469
return self._matchingcontroldir
1472
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1473
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1474
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1475
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1476
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1477
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1478
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1479
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1480
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")
1785
return self._matchingbzrdir
1788
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
1789
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1790
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
1791
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1792
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
1793
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1794
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
1795
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1796
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")