19
19
A WorkingTree represents the editable working copy of a branch.
20
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files.
21
such as renaming or adding files.
23
23
At the moment every WorkingTree has its own branch. Remote
24
24
WorkingTrees aren't supported.
26
To get a WorkingTree, call controldir.open_workingtree() or
26
To get a WorkingTree, call bzrdir.open_workingtree() or
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
56
revision as _mod_revision,
51
from breezy.bzr import (
59
from .controldir import (
61
ControlComponentFormatRegistry,
62
ControlComponentFormat,
68
from .decorators import needs_read_lock, needs_write_lock
69
69
from .i18n import gettext
70
70
from . import mutabletree
71
from .symbol_versioning import deprecated_method, deprecated_in
71
from .mutabletree import needs_tree_write_lock
72
72
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):
75
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
78
class TreeEntry(object):
79
"""An entry that implements the minimum interface used by commands.
81
This needs further inspection, it may be better to have
82
InventoryEntries without ids - though that seems wrong. For now,
83
this is a parallel hierarchy to InventoryEntry, and needs to become
84
one of several things: decorates to that hierarchy, children of, or
86
Another note is that these objects are currently only used when there is
87
no InventoryEntry available - i.e. for unversioned objects.
88
Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
91
def __eq__(self, other):
92
# yes, this us ugly, TODO: best practice __eq__ style.
93
return (isinstance(other, TreeEntry)
94
and other.__class__ == self.__class__)
96
def kind_character(self):
100
class TreeDirectory(TreeEntry):
101
"""See TreeEntry. This is a directory in a working tree."""
103
def __eq__(self, other):
104
return (isinstance(other, TreeDirectory)
105
and other.__class__ == self.__class__)
107
def kind_character(self):
111
class TreeFile(TreeEntry):
112
"""See TreeEntry. This is a regular file in a working tree."""
114
def __eq__(self, other):
115
return (isinstance(other, TreeFile)
116
and other.__class__ == self.__class__)
118
def kind_character(self):
122
class TreeLink(TreeEntry):
123
"""See TreeEntry. This is a symlink in a working tree."""
125
def __eq__(self, other):
126
return (isinstance(other, TreeLink)
127
and other.__class__ == self.__class__)
129
def kind_character(self):
133
class WorkingTree(mutabletree.MutableTree,
134
controldir.ControlComponent):
86
135
"""Working copy tree.
88
137
:ivar basedir: The root of the tree on disk. This is a unicode path object
294
334
def open_downlevel(path=None):
295
335
"""Open an unsupported working tree.
297
Only intended for advanced situations like upgrading part of a controldir.
337
Only intended for advanced situations like upgrading part of a bzrdir.
299
339
return WorkingTree.open(path, _unsupported=True)
342
def find_trees(location):
343
def list_current(transport):
344
return [d for d in transport.list_dir('') if d != '.bzr']
345
def evaluate(bzrdir):
347
tree = bzrdir.open_workingtree()
348
except errors.NoWorkingTree:
352
t = transport.get_transport(location)
353
iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
354
list_current=list_current)
355
return [tr for tr in iterator if tr is not None]
301
357
def __repr__(self):
302
358
return "<%s of %s>" % (self.__class__.__name__,
303
359
getattr(self, 'basedir', None))
350
409
def has_filename(self, filename):
351
410
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]
412
def get_file(self, file_id, path=None, filtered=True):
413
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
356
def get_file_with_stat(self, path, filtered=True,
415
def get_file_with_stat(self, file_id, path=None, filtered=True,
357
416
_fstat=osutils.fstat):
358
417
"""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)
419
path = self.id2path(file_id)
420
file_obj = self.get_file_byname(path, filtered=False)
366
421
stat_value = _fstat(file_obj.fileno())
367
422
if filtered and self.supports_content_filtering():
368
423
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)
424
file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
425
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:
427
def get_file_text(self, file_id, path=None, filtered=True):
428
my_file = self.get_file(file_id, path=path, filtered=filtered)
378
430
return my_file.read()
380
def get_file_lines(self, path, filtered=True):
434
def get_file_byname(self, filename, filtered=True):
435
path = self.abspath(filename)
437
if filtered and self.supports_content_filtering():
438
filters = self._content_filter_stack(filename)
439
return _mod_filters.filtered_input_file(f, filters)
443
def get_file_lines(self, file_id, path=None, filtered=True):
381
444
"""See Tree.get_file_lines()"""
382
with self.get_file(path, filtered=filtered) as file:
445
file = self.get_file(file_id, path, filtered=filtered)
383
447
return file.readlines()
385
451
def get_parent_ids(self):
386
452
"""See Tree.get_parent_ids.
416
487
revision, and difference between the source trees last revision
417
488
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)
490
# assumes the target bzr dir format is compatible.
491
result = to_controldir.create_workingtree()
492
self.copy_content_into(result, revision_id)
425
496
def copy_content_into(self, tree, revision_id=None):
426
497
"""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)
498
tree.set_root_id(self.get_root_id())
499
if revision_id is None:
500
merge.transform_tree(tree, self)
502
# TODO now merge from tree.last_revision to revision (to preserve
503
# user local changes)
505
other_tree = self.revision_tree(revision_id)
506
except errors.NoSuchRevision:
507
other_tree = self.branch.repository.revision_tree(revision_id)
509
merge.transform_tree(tree, other_tree)
510
if revision_id == _mod_revision.NULL_REVISION:
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):
513
new_parents = [revision_id]
514
tree.set_parent_ids(new_parents)
516
def id2abspath(self, file_id):
517
return self.abspath(self.id2path(file_id))
519
def get_file_size(self, file_id):
448
520
"""See Tree.get_file_size"""
449
521
# XXX: this returns the on-disk size; it should probably return the
452
return os.path.getsize(self.abspath(path))
524
return os.path.getsize(self.id2abspath(file_id))
453
525
except OSError as e:
454
526
if e.errno != errno.ENOENT:
531
@needs_tree_write_lock
459
532
def _gather_kinds(self, files, kinds):
460
533
"""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)
534
for pos, f in enumerate(files):
535
if kinds[pos] is None:
536
fullpath = osutils.normpath(self.abspath(f))
538
kinds[pos] = osutils.file_kind(fullpath)
540
if e.errno == errno.ENOENT:
541
raise errors.NoSuchFile(fullpath)
471
544
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
472
545
"""Add revision_id as a parent.
497
570
If the revision_id is a ghost, pass None for the tree.
498
571
: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)
573
parent_ids = self.get_parent_ids() + [parent_tuple[0]]
574
if len(parent_ids) > 1:
575
# the leftmost may have already been a ghost, preserve that if it
577
allow_leftmost_as_ghost = True
578
self.set_parent_ids(parent_ids,
579
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
581
@needs_tree_write_lock
509
582
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)
583
# TODO: Perhaps should check at this point that the
584
# history of the revision is actually present?
585
parents = self.get_parent_ids()
587
for rev_id in revision_ids:
588
if rev_id in parents:
590
parents.append(rev_id)
593
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
523
595
def path_content_summary(self, path, _lstat=os.lstat,
524
_mapper=osutils.file_kind_from_stat_mode):
596
_mapper=osutils.file_kind_from_stat_mode):
525
597
"""See Tree.path_content_summary."""
526
598
abspath = self.abspath(path)
602
675
:param revision_ids: The revision_ids to set as the parent ids of this
603
676
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)
678
self._check_parents_for_ghosts(revision_ids,
679
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
680
for revision_id in revision_ids:
681
_mod_revision.check_not_reserved_id(revision_id)
683
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
685
if len(revision_ids) > 0:
686
self.set_last_revision(revision_ids[0])
688
self.set_last_revision(_mod_revision.NULL_REVISION)
690
self._set_merges_from_parent_ids(revision_ids)
692
@needs_tree_write_lock
620
693
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)
694
parents = self.get_parent_ids()
695
leftmost = parents[:1]
696
new_parents = leftmost + rev_list
697
self.set_parent_ids(new_parents)
699
@needs_tree_write_lock
627
700
def set_merge_modified(self, modified_hashes):
628
701
"""Set the merge modified hashes."""
629
702
raise NotImplementedError(self.set_merge_modified)
649
723
branch.last_revision().
651
725
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()
726
merger = Merger(self.branch, this_tree=self)
727
# check that there are no local alterations
728
if not force and self.has_changes():
729
raise errors.UncommittedChanges(self)
730
if to_revision is None:
731
to_revision = _mod_revision.ensure_null(branch.last_revision())
732
merger.other_rev_id = to_revision
733
if _mod_revision.is_null(merger.other_rev_id):
734
raise errors.NoCommits(branch)
735
self.branch.fetch(branch, last_revision=merger.other_rev_id)
736
merger.other_basis = merger.other_rev_id
737
merger.other_tree = self.branch.repository.revision_tree(
739
merger.other_branch = branch
740
if from_revision is None:
743
merger.set_base_revision(from_revision, branch)
744
if merger.base_rev_id == merger.other_rev_id:
745
raise errors.PointlessMerge
746
merger.backup_files = False
747
if merge_type is None:
748
merger.merge_type = Merge3Merger
750
merger.merge_type = merge_type
751
merger.set_interesting_files(None)
752
merger.show_base = False
753
merger.reprocess = False
754
conflicts = merger.do_merge()
685
758
def merge_modified(self):
686
759
"""Return a dictionary of files modified by a merge.
690
763
because of a merge.
692
765
This returns a map of file_id->sha1, containing only files which are
693
still in the working tree and have that text hash.
766
still in the working inventory and have that text hash.
695
768
raise NotImplementedError(self.merge_modified)
697
771
def mkdir(self, path, file_id=None):
698
772
"""See MutableTree.mkdir()."""
699
773
if file_id is None:
700
if self.supports_setting_file_ids():
701
file_id = generate_ids.gen_file_id(os.path.basename(path))
774
file_id = generate_ids.gen_file_id(os.path.basename(path))
775
os.mkdir(self.abspath(path))
776
self.add(path, file_id, 'directory')
779
def get_symlink_target(self, file_id, path=None):
781
abspath = self.abspath(path)
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)
783
abspath = self.id2abspath(file_id)
712
784
target = osutils.readlink(abspath)
715
787
def subsume(self, other_tree):
716
788
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):
790
def _setup_directory_is_tree_reference(self):
791
if self._branch.repository._format.supports_tree_reference:
792
self._directory_is_tree_reference = \
793
self._directory_may_be_tree_reference
795
self._directory_is_tree_reference = \
796
self._directory_is_never_tree_reference
798
def _directory_is_never_tree_reference(self, relpath):
801
def _directory_may_be_tree_reference(self, relpath):
802
# as a special case, if a directory contains control files then
803
# it's a tree reference, except that the root of the tree is not
804
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
805
# TODO: We could ask all the control formats whether they
806
# recognize this directory, but at the moment there's no cheap api
807
# to do that. Since we probably can only nest bzr checkouts and
808
# they always use this name it's ok for now. -- mbp 20060306
810
# FIXME: There is an unhandled case here of a subdirectory
811
# containing .bzr but not a branch; that will probably blow up
812
# when you try to commit it. It might happen if there is a
813
# checkout in a subdirectory. This can be avoided by not adding
816
def extract(self, file_id, format=None):
722
817
"""Extract a subtree from this tree.
724
819
A new branch will be created, relative to the path for this tree.
783
877
raise NotImplementedError(self.move)
785
def copy_one(self, from_rel, to_rel):
786
"""Copy a file in the tree to a new location.
788
This default implementation just copies the file, then
791
:param from_rel: From location (relative to tree root)
792
:param to_rel: Target location (relative to tree root)
879
@needs_tree_write_lock
880
def rename_one(self, from_rel, to_rel, after=False):
883
This can change the directory or the filename or both.
885
rename_one has several 'modes' to work. First, it can rename a physical
886
file and change the file_id. That is the normal mode. Second, it can
887
only change the file_id without touching any physical file.
889
rename_one uses the second mode if 'after == True' and 'to_rel' is
890
either not versioned or newly added, and present in the working tree.
892
rename_one uses the second mode if 'after == False' and 'from_rel' is
893
versioned but no longer in the working tree, and 'to_rel' is not
894
versioned but present in the working tree.
896
rename_one uses the first mode if 'after == False' and 'from_rel' is
897
versioned and present in the working tree, and 'to_rel' is not
898
versioned and not present in the working tree.
900
Everything else results in an error.
794
shutil.copyfile(self.abspath(from_rel), self.abspath(to_rel))
902
raise NotImplementedError(self.rename_one)
797
905
def unknowns(self):
798
906
"""Return all unknown files.
800
908
These are files in the working directory that are not versioned or
801
909
control files or ignored.
803
with self.lock_read():
804
# force the extras method to be fully executed before returning, to
805
# prevent race conditions with the lock
807
[subp for subp in self.extras() if not self.is_ignored(subp)])
809
def unversion(self, paths):
810
"""Remove the path in pahs from the current versioned set.
812
When a path is unversioned, all of its children are automatically
911
# force the extras method to be fully executed before returning, to
912
# prevent race conditions with the lock
914
[subp for subp in self.extras() if not self.is_ignored(subp)])
916
def unversion(self, file_ids):
917
"""Remove the file ids in file_ids from the current versioned set.
919
When a file_id is unversioned, all of its children are automatically
815
:param paths: The paths to stop versioning.
816
:raises NoSuchFile: if any path is not currently versioned.
922
:param file_ids: The file ids to stop versioning.
923
:raises: NoSuchId if any fileid is not currently versioned.
818
925
raise NotImplementedError(self.unversion)
820
928
def pull(self, source, overwrite=False, stop_revision=None,
821
929
change_reporter=None, possible_transports=None, local=False,
822
show_base=False, tag_selector=None):
823
with self.lock_write(), source.lock_read():
824
933
old_revision_info = self.branch.last_revision_info()
825
934
basis_tree = self.basis_tree()
826
935
count = self.branch.pull(source, overwrite, stop_revision,
827
936
possible_transports=possible_transports,
828
local=local, tag_selector=tag_selector)
829
938
new_revision_info = self.branch.last_revision_info()
830
939
if new_revision_info != old_revision_info:
831
940
repository = self.branch.repository
862
975
merges = self.get_parent_ids()[1:]
863
976
parent_trees.extend([
864
977
(parent, repository.revision_tree(parent)) for
866
979
self.set_parent_trees(parent_trees)
869
def put_file_bytes_non_atomic(self, path, bytes):
985
def put_file_bytes_non_atomic(self, file_id, bytes):
870
986
"""See MutableTree.put_file_bytes_non_atomic."""
871
with self.lock_write(), open(self.abspath(path), 'wb') as stream:
987
stream = file(self.id2abspath(file_id), 'wb')
872
989
stream.write(bytes)
874
993
def extras(self):
875
994
"""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.
996
If there are any unversioned directories then only the directory is
997
returned, not all its children. But if there are unversioned files
998
under a versioned subdirectory, they are returned.
882
1000
Currently returned depth-first, sorted by name within directories.
883
1001
This is the same order used by 'osutils.walkdirs'.
891
1009
if pat is not None:
1012
def get_ignore_list(self):
1013
"""Return list of ignore patterns.
1015
Cached in the Tree object after the first call.
1017
ignoreset = getattr(self, '_ignoreset', None)
1018
if ignoreset is not None:
1021
ignore_globs = set()
1022
ignore_globs.update(ignores.get_runtime_ignores())
1023
ignore_globs.update(ignores.get_user_ignores())
1024
if self.has_filename(breezy.IGNORE_FILENAME):
1025
f = self.get_file_byname(breezy.IGNORE_FILENAME)
1027
ignore_globs.update(ignores.parse_ignore_file(f))
1030
self._ignoreset = ignore_globs
1033
def _flush_ignore_list_cache(self):
1034
"""Resets the cached ignore list to force a cache rebuild."""
1035
self._ignoreset = None
1036
self._ignoreglobster = None
894
1038
def is_ignored(self, filename):
895
1039
r"""Check whether the filename matches an ignore pattern.
897
raise NotImplementedError(self.is_ignored)
899
def stored_kind(self, path):
1041
Patterns containing '/' or '\' need to match the whole path;
1042
others match against only the last component. Patterns starting
1043
with '!' are ignore exceptions. Exceptions take precedence
1044
over regular patterns and cause the filename to not be ignored.
1046
If the file is ignored, returns the pattern which caused it to
1047
be ignored, otherwise None. So this can simply be used as a
1048
boolean if desired."""
1049
if getattr(self, '_ignoreglobster', None) is None:
1050
self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1051
return self._ignoreglobster.match(filename)
1053
def kind(self, file_id):
1054
return osutils.file_kind(self.id2abspath(file_id))
1056
def stored_kind(self, file_id):
900
1057
"""See Tree.stored_kind"""
901
1058
raise NotImplementedError(self.stored_kind)
995
1156
:force: Delete files and directories, even if they are changed and
996
1157
even if the directories are not empty.
998
raise NotImplementedError(self.remove)
1159
if isinstance(files, basestring):
1164
all_files = set() # specified and nested files
1165
unknown_nested_files=set()
1167
to_file = sys.stdout
1169
files_to_backup = []
1171
def recurse_directory_to_add_files(directory):
1172
# Recurse directory and add all files
1173
# so we can check if they have changed.
1174
for parent_info, file_infos in self.walkdirs(directory):
1175
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1176
# Is it versioned or ignored?
1177
if self.path2id(relpath):
1178
# Add nested content for deletion.
1179
all_files.add(relpath)
1181
# Files which are not versioned
1182
# should be treated as unknown.
1183
files_to_backup.append(relpath)
1185
for filename in files:
1186
# Get file name into canonical form.
1187
abspath = self.abspath(filename)
1188
filename = self.relpath(abspath)
1189
if len(filename) > 0:
1190
all_files.add(filename)
1191
recurse_directory_to_add_files(filename)
1193
files = list(all_files)
1196
return # nothing to do
1198
# Sort needed to first handle directory content before the directory
1199
files.sort(reverse=True)
1201
# Bail out if we are going to delete files we shouldn't
1202
if not keep_files and not force:
1203
for (file_id, path, content_change, versioned, parent_id, name,
1204
kind, executable) in self.iter_changes(self.basis_tree(),
1205
include_unchanged=True, require_versioned=False,
1206
want_unversioned=True, specific_files=files):
1207
if versioned[0] == False:
1208
# The record is unknown or newly added
1209
files_to_backup.append(path[1])
1210
elif (content_change and (kind[1] is not None) and
1211
osutils.is_inside_any(files, path[1])):
1212
# Versioned and changed, but not deleted, and still
1213
# in one of the dirs to be deleted.
1214
files_to_backup.append(path[1])
1216
def backup(file_to_backup):
1217
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1218
osutils.rename(abs_path, self.abspath(backup_name))
1219
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1222
# Build inv_delta and delete files where applicable,
1223
# do this before any modifications to meta data.
1225
fid = self.path2id(f)
1228
message = "%s is not versioned." % (f,)
1231
# having removed it, it must be either ignored or unknown
1232
if self.is_ignored(f):
1236
# XXX: Really should be a more abstract reporter interface
1237
kind_ch = osutils.kind_marker(self.kind(fid))
1238
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1240
inv_delta.append((f, None, fid, None))
1241
message = "removed %s" % (f,)
1244
abs_path = self.abspath(f)
1245
if osutils.lexists(abs_path):
1246
if (osutils.isdir(abs_path) and
1247
len(os.listdir(abs_path)) > 0):
1249
osutils.rmtree(abs_path)
1250
message = "deleted %s" % (f,)
1254
if f in files_to_backup:
1257
osutils.delete_any(abs_path)
1258
message = "deleted %s" % (f,)
1259
elif message is not None:
1260
# Only care if we haven't done anything yet.
1261
message = "%s does not exist." % (f,)
1263
# Print only one message (if any) per file.
1264
if message is not None:
1266
self.apply_inventory_delta(inv_delta)
1268
@needs_tree_write_lock
1000
1269
def revert(self, filenames=None, old_tree=None, backups=True,
1001
1270
pb=None, report_changes=False):
1002
1271
from .conflicts import resolve
1003
with contextlib.ExitStack() as exit_stack:
1004
exit_stack.enter_context(self.lock_tree_write())
1005
if old_tree is None:
1006
basis_tree = self.basis_tree()
1007
exit_stack.enter_context(basis_tree.lock_read())
1008
old_tree = basis_tree
1272
if old_tree is None:
1273
basis_tree = self.basis_tree()
1274
basis_tree.lock_read()
1275
old_tree = basis_tree
1011
1279
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1012
1280
report_changes)
1013
1281
if filenames is None and len(self.get_parent_ids()) > 1:
1016
1284
if last_revision != _mod_revision.NULL_REVISION:
1017
1285
if basis_tree is None:
1018
1286
basis_tree = self.basis_tree()
1019
exit_stack.enter_context(basis_tree.lock_read())
1287
basis_tree.lock_read()
1020
1288
parent_trees.append((last_revision, basis_tree))
1021
1289
self.set_parent_trees(parent_trees)
1024
1292
resolve(self, filenames, ignore_misses=True, recursive=True)
1294
if basis_tree is not None:
1027
1299
def store_uncommitted(self):
1028
1300
"""Store uncommitted changes from the tree in the branch."""
1029
raise NotImplementedError(self.store_uncommitted)
1301
target_tree = self.basis_tree()
1302
shelf_creator = shelf.ShelfCreator(self, target_tree)
1304
if not shelf_creator.shelve_all():
1306
self.branch.store_uncommitted(shelf_creator)
1307
shelf_creator.transform()
1309
shelf_creator.finalize()
1310
note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1031
1313
def restore_uncommitted(self):
1032
1314
"""Restore uncommitted changes from the branch into the tree."""
1033
raise NotImplementedError(self.restore_uncommitted)
1315
unshelver = self.branch.get_unshelver(self)
1316
if unshelver is None:
1319
merger = unshelver.make_merger()
1320
merger.ignore_zero = True
1322
self.branch.store_uncommitted(None)
1324
unshelver.finalize()
1035
1326
def revision_tree(self, revision_id):
1036
1327
"""See Tree.revision_tree.
1038
For trees that can be obtained from the working tree, this
1039
will do so. For other trees, it will fall back to the repository.
1329
WorkingTree can supply revision_trees for the basis revision only
1330
because there is only one cached inventory in the bzr directory.
1041
1332
raise NotImplementedError(self.revision_tree)
1334
@needs_tree_write_lock
1043
1335
def set_root_id(self, file_id):
1044
1336
"""Set the root id for this tree."""
1045
if not self.supports_setting_file_ids():
1046
raise SettingFileIdUnsupported()
1047
with self.lock_tree_write():
1051
'WorkingTree.set_root_id with fileid=None')
1052
self._set_root_id(file_id)
1340
'WorkingTree.set_root_id with fileid=None')
1341
file_id = osutils.safe_file_id(file_id)
1342
self._set_root_id(file_id)
1054
1344
def _set_root_id(self, file_id):
1055
1345
"""Set the root id for this tree, in a format specific manner.
1139
1430
# We MUST save it even if an error occurs, because otherwise the users
1140
1431
# local work is unreferenced and will appear to have been lost.
1142
with self.lock_tree_write():
1435
last_rev = self.get_parent_ids()[0]
1437
last_rev = _mod_revision.NULL_REVISION
1438
if revision is None:
1439
revision = self.branch.last_revision()
1441
old_tip = old_tip or _mod_revision.NULL_REVISION
1443
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1444
# the branch we are bound to was updated
1445
# merge those changes in first
1446
base_tree = self.basis_tree()
1447
other_tree = self.branch.repository.revision_tree(old_tip)
1448
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1449
base_tree, this_tree=self,
1450
change_reporter=change_reporter,
1451
show_base=show_base)
1453
self.add_parent_tree((old_tip, other_tree))
1454
note(gettext('Rerun update after fixing the conflicts.'))
1457
if last_rev != _mod_revision.ensure_null(revision):
1458
# the working tree is up to date with the branch
1459
# we can merge the specified revision from master
1460
to_tree = self.branch.repository.revision_tree(revision)
1461
to_root_id = to_tree.get_root_id()
1463
basis = self.basis_tree()
1145
last_rev = self.get_parent_ids()[0]
1147
last_rev = _mod_revision.NULL_REVISION
1148
if revision is None:
1149
revision = self.branch.last_revision()
1151
old_tip = old_tip or _mod_revision.NULL_REVISION
1153
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1154
# the branch we are bound to was updated
1155
# merge those changes in first
1156
base_tree = self.basis_tree()
1157
other_tree = self.branch.repository.revision_tree(old_tip)
1158
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1159
base_tree, this_tree=self,
1160
change_reporter=change_reporter,
1161
show_base=show_base)
1163
self.add_parent_tree((old_tip, other_tree))
1164
note(gettext('Rerun update after fixing the conflicts.'))
1167
if last_rev != _mod_revision.ensure_null(revision):
1168
# the working tree is up to date with the branch
1169
# we can merge the specified revision from master
1170
to_tree = self.branch.repository.revision_tree(revision)
1171
to_root_id = to_tree.path2id('')
1173
basis = self.basis_tree()
1174
with basis.lock_read():
1175
if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1176
self.set_root_id(to_root_id)
1179
# determine the branch point
1180
graph = self.branch.repository.get_graph()
1181
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1183
base_tree = self.branch.repository.revision_tree(base_rev_id)
1185
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1187
change_reporter=change_reporter,
1188
show_base=show_base)
1189
self.set_last_revision(revision)
1190
# TODO - dedup parents list with things merged by pull ?
1191
# reuse the tree we've updated to to set the basis:
1192
parent_trees = [(revision, to_tree)]
1193
merges = self.get_parent_ids()[1:]
1194
# Ideally we ask the tree for the trees here, that way the working
1195
# tree can decide whether to give us the entire tree or give us a
1196
# lazy initialised tree. dirstate for instance will have the trees
1197
# in ram already, whereas a last-revision + basis-inventory tree
1198
# will not, but also does not need them when setting parents.
1199
for parent in merges:
1200
parent_trees.append(
1201
(parent, self.branch.repository.revision_tree(parent)))
1202
if not _mod_revision.is_null(old_tip):
1203
parent_trees.append(
1204
(old_tip, self.branch.repository.revision_tree(old_tip)))
1205
self.set_parent_trees(parent_trees)
1206
last_rev = parent_trees[0][0]
1466
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1467
self.set_root_id(to_root_id)
1472
# determine the branch point
1473
graph = self.branch.repository.get_graph()
1474
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1476
base_tree = self.branch.repository.revision_tree(base_rev_id)
1478
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1480
change_reporter=change_reporter,
1481
show_base=show_base)
1482
self.set_last_revision(revision)
1483
# TODO - dedup parents list with things merged by pull ?
1484
# reuse the tree we've updated to to set the basis:
1485
parent_trees = [(revision, to_tree)]
1486
merges = self.get_parent_ids()[1:]
1487
# Ideally we ask the tree for the trees here, that way the working
1488
# tree can decide whether to give us the entire tree or give us a
1489
# lazy initialised tree. dirstate for instance will have the trees
1490
# in ram already, whereas a last-revision + basis-inventory tree
1491
# will not, but also does not need them when setting parents.
1492
for parent in merges:
1493
parent_trees.append(
1494
(parent, self.branch.repository.revision_tree(parent)))
1495
if not _mod_revision.is_null(old_tip):
1496
parent_trees.append(
1497
(old_tip, self.branch.repository.revision_tree(old_tip)))
1498
self.set_parent_trees(parent_trees)
1499
last_rev = parent_trees[0][0]
1209
1502
def set_conflicts(self, arg):
1210
1503
raise errors.UnsupportedOperation(self.set_conflicts, self)
1229
1522
If the tree is not locked, it may cause an error to be raised,
1230
1523
depending on the tree implementation.
1232
raise NotImplementedError(self.walkdirs)
1234
@deprecated_method(deprecated_in((3, 0, 1)))
1525
disk_top = self.abspath(prefix)
1526
if disk_top.endswith('/'):
1527
disk_top = disk_top[:-1]
1528
top_strip_len = len(disk_top) + 1
1529
inventory_iterator = self._walkdirs(prefix)
1530
disk_iterator = osutils.walkdirs(disk_top, prefix)
1532
current_disk = next(disk_iterator)
1533
disk_finished = False
1534
except OSError as e:
1535
if not (e.errno == errno.ENOENT or
1536
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1539
disk_finished = True
1541
current_inv = next(inventory_iterator)
1542
inv_finished = False
1543
except StopIteration:
1546
while not inv_finished or not disk_finished:
1548
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1549
cur_disk_dir_content) = current_disk
1551
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1552
cur_disk_dir_content) = ((None, None), None)
1553
if not disk_finished:
1554
# strip out .bzr dirs
1555
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1556
len(cur_disk_dir_content) > 0):
1557
# osutils.walkdirs can be made nicer -
1558
# yield the path-from-prefix rather than the pathjoined
1560
bzrdir_loc = bisect_left(cur_disk_dir_content,
1562
if (bzrdir_loc < len(cur_disk_dir_content)
1563
and self.bzrdir.is_control_filename(
1564
cur_disk_dir_content[bzrdir_loc][0])):
1565
# we dont yield the contents of, or, .bzr itself.
1566
del cur_disk_dir_content[bzrdir_loc]
1568
# everything is unknown
1571
# everything is missing
1574
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1576
# disk is before inventory - unknown
1577
dirblock = [(relpath, basename, kind, stat, None, None) for
1578
relpath, basename, kind, stat, top_path in
1579
cur_disk_dir_content]
1580
yield (cur_disk_dir_relpath, None), dirblock
1582
current_disk = next(disk_iterator)
1583
except StopIteration:
1584
disk_finished = True
1586
# inventory is before disk - missing.
1587
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1588
for relpath, basename, dkind, stat, fileid, kind in
1590
yield (current_inv[0][0], current_inv[0][1]), dirblock
1592
current_inv = next(inventory_iterator)
1593
except StopIteration:
1596
# versioned present directory
1597
# merge the inventory and disk data together
1599
for relpath, subiterator in itertools.groupby(sorted(
1600
current_inv[1] + cur_disk_dir_content,
1601
key=operator.itemgetter(0)), operator.itemgetter(1)):
1602
path_elements = list(subiterator)
1603
if len(path_elements) == 2:
1604
inv_row, disk_row = path_elements
1605
# versioned, present file
1606
dirblock.append((inv_row[0],
1607
inv_row[1], disk_row[2],
1608
disk_row[3], inv_row[4],
1610
elif len(path_elements[0]) == 5:
1612
dirblock.append((path_elements[0][0],
1613
path_elements[0][1], path_elements[0][2],
1614
path_elements[0][3], None, None))
1615
elif len(path_elements[0]) == 6:
1616
# versioned, absent file.
1617
dirblock.append((path_elements[0][0],
1618
path_elements[0][1], 'unknown', None,
1619
path_elements[0][4], path_elements[0][5]))
1621
raise NotImplementedError('unreachable code')
1622
yield current_inv[0], dirblock
1624
current_inv = next(inventory_iterator)
1625
except StopIteration:
1628
current_disk = next(disk_iterator)
1629
except StopIteration:
1630
disk_finished = True
1632
def _walkdirs(self, prefix=""):
1633
"""Walk the directories of this tree.
1635
:param prefix: is used as the directrory to start with.
1636
:returns: a generator which yields items in the form::
1638
((curren_directory_path, fileid),
1639
[(file1_path, file1_name, file1_kind, None, file1_id,
1642
raise NotImplementedError(self._walkdirs)
1644
@needs_tree_write_lock
1235
1645
def auto_resolve(self):
1236
1646
"""Automatically resolve text conflicts according to contents.
1243
1653
:return: a tuple of ConflictLists: (un_resolved, resolved).
1245
with self.lock_tree_write():
1246
un_resolved = _mod_conflicts.ConflictList()
1247
resolved = _mod_conflicts.ConflictList()
1248
for conflict in self.conflicts():
1250
conflict.action_auto(self)
1251
except NotImplementedError:
1252
un_resolved.append(conflict)
1655
un_resolved = _mod_conflicts.ConflictList()
1656
resolved = _mod_conflicts.ConflictList()
1657
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1658
for conflict in self.conflicts():
1659
if (conflict.typestring != 'text conflict' or
1660
self.kind(conflict.file_id) != 'file'):
1661
un_resolved.append(conflict)
1663
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1665
for line in my_file:
1666
if conflict_re.search(line):
1667
un_resolved.append(conflict)
1254
conflict.cleanup(self)
1255
1670
resolved.append(conflict)
1256
self.set_conflicts(un_resolved)
1257
return un_resolved, resolved
1673
resolved.remove_files(self)
1674
self.set_conflicts(un_resolved)
1675
return un_resolved, resolved
1259
1677
def _validate(self):
1260
1678
"""Validate internal structures.
1283
1701
"""See Tree._get_rules_searcher."""
1284
1702
if self._rules_searcher is None:
1285
1703
self._rules_searcher = super(WorkingTree,
1286
self)._get_rules_searcher(default_searcher)
1704
self)._get_rules_searcher(default_searcher)
1287
1705
return self._rules_searcher
1289
1707
def get_shelf_manager(self):
1290
1708
"""Return the ShelfManager for this WorkingTree."""
1291
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):
1709
from .shelf import ShelfManager
1710
return ShelfManager(self, self._transport)
1713
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1340
1714
"""Registry for working tree formats."""
1342
1716
def __init__(self, other_registry=None):
1462
1820
This is to support testing of working tree formats that can not exist
1463
1821
in the same control directory as a branch.
1465
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")
1823
return self._matchingbzrdir
1826
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
1827
"breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1828
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
1829
"breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1830
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
1831
"breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1832
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
1833
"breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1834
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")