207
"""Construct a WorkingTree for basedir.
195
"""Construct a WorkingTree instance. This is not a public API.
209
If the branch is not supplied, it is opened automatically.
210
If the branch is supplied, it must be the branch for this basedir.
211
(branch.base is not cross checked, because for remote branches that
212
would be meaningless).
197
:param branch: A branch to override probing for the branch.
214
199
self._format = _format
215
200
self.bzrdir = _bzrdir
216
201
if not _internal:
217
# not created via open etc.
218
warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
219
"Please use bzrdir.open_workingtree or WorkingTree.open().",
222
wt = WorkingTree.open(basedir)
223
self._branch = wt.branch
224
self.basedir = wt.basedir
225
self._control_files = wt._control_files
226
self._hashcache = wt._hashcache
227
self._set_inventory(wt._inventory, dirty=False)
228
self._format = wt._format
229
self.bzrdir = wt.bzrdir
230
assert isinstance(basedir, basestring), \
231
"base directory %r is not a string" % basedir
202
raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
"WorkingTree.open() to obtain a WorkingTree.")
232
204
basedir = safe_unicode(basedir)
233
205
mutter("opening working tree %r", basedir)
234
206
if deprecated_passed(branch):
236
warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
237
" as of bzr 0.8. Please use bzrdir.open_workingtree() or"
238
" WorkingTree.open().",
242
207
self._branch = branch
244
209
self._branch = self.bzrdir.open_branch()
392
405
# at this point ?
394
407
return self.branch.repository.revision_tree(revision_id)
395
except errors.RevisionNotPresent:
408
except (errors.RevisionNotPresent, errors.NoSuchRevision):
396
409
# the basis tree *may* be a ghost or a low level error may have
397
410
# occured. If the revision is present, its a problem, if its not
399
412
if self.branch.repository.has_revision(revision_id):
401
414
# the basis tree is a ghost so return an empty tree.
402
return self.branch.repository.revision_tree(None)
405
@deprecated_method(zero_eight)
406
def create(branch, directory):
407
"""Create a workingtree for branch at directory.
409
If existing_directory already exists it must have a .bzr directory.
410
If it does not exist, it will be created.
412
This returns a new WorkingTree object for the new checkout.
414
TODO FIXME RBC 20060124 when we have checkout formats in place this
415
should accept an optional revisionid to checkout [and reject this if
416
checking out into the same dir as a pre-checkout-aware branch format.]
418
XXX: When BzrDir is present, these should be created through that
421
warnings.warn('delete WorkingTree.create', stacklevel=3)
422
transport = get_transport(directory)
423
if branch.bzrdir.root_transport.base == transport.base:
425
return branch.bzrdir.create_workingtree()
426
# different directory,
427
# create a branch reference
428
# and now a working tree.
429
raise NotImplementedError
432
@deprecated_method(zero_eight)
433
def create_standalone(directory):
434
"""Create a checkout and a branch and a repo at directory.
436
Directory must exist and be empty.
438
please use BzrDir.create_standalone_workingtree
440
return bzrdir.BzrDir.create_standalone_workingtree(directory)
415
return self.branch.repository.revision_tree(
416
_mod_revision.NULL_REVISION)
419
self._flush_ignore_list_cache()
442
421
def relpath(self, path):
443
422
"""Return the local path portion from a given path.
450
429
def has_filename(self, filename):
451
430
return osutils.lexists(self.abspath(filename))
453
def get_file(self, file_id):
454
file_id = osutils.safe_file_id(file_id)
455
return self.get_file_byname(self.id2path(file_id))
432
def get_file(self, file_id, path=None):
433
return self.get_file_with_stat(file_id, path)[0]
457
def get_file_text(self, file_id):
458
file_id = osutils.safe_file_id(file_id)
459
return self.get_file(file_id).read()
435
def get_file_with_stat(self, file_id, path=None, _fstat=os.fstat):
436
"""See MutableTree.get_file_with_stat."""
438
path = self.id2path(file_id)
439
file_obj = self.get_file_byname(path)
440
return (file_obj, _fstat(file_obj.fileno()))
461
442
def get_file_byname(self, filename):
462
443
return file(self.abspath(filename), 'rb')
445
def get_file_lines(self, file_id, path=None):
446
"""See Tree.get_file_lines()"""
447
file = self.get_file(file_id, path)
449
return file.readlines()
465
def annotate_iter(self, file_id):
454
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
466
455
"""See Tree.annotate_iter
468
457
This implementation will use the basis tree implementation if possible.
472
461
incorrectly attributed to CURRENT_REVISION (but after committing, the
473
462
attribution will be correct).
475
file_id = osutils.safe_file_id(file_id)
476
464
basis = self.basis_tree()
477
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
478
require_versioned=True).next()
479
changed_content, kind = changes[2], changes[6]
480
if not changed_content:
481
return basis.annotate_iter(file_id)
485
if kind[0] != 'file':
488
old_lines = list(basis.annotate_iter(file_id))
490
for tree in self.branch.repository.revision_trees(
491
self.get_parent_ids()[1:]):
492
if file_id not in tree:
494
old.append(list(tree.annotate_iter(file_id)))
495
return annotate.reannotate(old, self.get_file(file_id).readlines(),
467
changes = self.iter_changes(basis, True, [self.id2path(file_id)],
468
require_versioned=True).next()
469
changed_content, kind = changes[2], changes[6]
470
if not changed_content:
471
return basis.annotate_iter(file_id)
475
if kind[0] != 'file':
478
old_lines = list(basis.annotate_iter(file_id))
480
for tree in self.branch.repository.revision_trees(
481
self.get_parent_ids()[1:]):
482
if file_id not in tree:
484
old.append(list(tree.annotate_iter(file_id)))
485
return annotate.reannotate(old, self.get_file(file_id).readlines(),
490
def _get_ancestors(self, default_revision):
491
ancestors = set([default_revision])
492
for parent_id in self.get_parent_ids():
493
ancestors.update(self.branch.repository.get_ancestry(
494
parent_id, topo_sorted=False))
498
497
def get_parent_ids(self):
499
498
"""See Tree.get_parent_ids.
583
574
__contains__ = has_id
585
576
def get_file_size(self, file_id):
586
file_id = osutils.safe_file_id(file_id)
587
return os.path.getsize(self.id2abspath(file_id))
577
"""See Tree.get_file_size"""
579
return os.path.getsize(self.id2abspath(file_id))
581
if e.errno != errno.ENOENT:
590
587
def get_file_sha1(self, file_id, path=None, stat_value=None):
591
file_id = osutils.safe_file_id(file_id)
593
589
path = self._inventory.id2path(file_id)
594
590
return self._hashcache.get_sha1(path, stat_value)
596
592
def get_file_mtime(self, file_id, path=None):
597
file_id = osutils.safe_file_id(file_id)
599
594
path = self.inventory.id2path(file_id)
600
595
return os.lstat(self.abspath(path)).st_mtime
597
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
598
file_id = self.path2id(path)
599
return self._inventory[file_id].executable
601
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
602
mode = stat_result.st_mode
603
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
602
605
if not supports_executable():
603
606
def is_executable(self, file_id, path=None):
604
file_id = osutils.safe_file_id(file_id)
605
607
return self._inventory[file_id].executable
609
_is_executable_from_path_and_stat = \
610
_is_executable_from_path_and_stat_from_basis
607
612
def is_executable(self, file_id, path=None):
609
file_id = osutils.safe_file_id(file_id)
610
614
path = self.id2path(file_id)
611
615
mode = os.lstat(self.abspath(path)).st_mode
612
616
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
618
_is_executable_from_path_and_stat = \
619
_is_executable_from_path_and_stat_from_stat
621
@needs_tree_write_lock
615
622
def _add(self, files, ids, kinds):
616
623
"""See MutableTree._add."""
617
624
# TODO: Re-adding a file that is removed in the working copy
618
625
# should probably put it back with the previous ID.
619
626
# the read and write working inventory should not occur in this
620
627
# function - they should be part of lock_write and unlock.
621
inv = self.read_working_inventory()
622
629
for f, file_id, kind in zip(files, ids, kinds):
623
assert kind is not None
624
630
if file_id is None:
625
631
inv.add_path(f, kind=kind)
627
file_id = osutils.safe_file_id(file_id)
628
633
inv.add_path(f, kind=kind, file_id=file_id)
629
self._write_inventory(inv)
634
self._inventory_is_modified = True
631
636
@needs_tree_write_lock
632
637
def _gather_kinds(self, files, kinds):
693
698
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
695
@deprecated_method(zero_eleven)
697
def pending_merges(self):
698
"""Return a list of pending merges.
700
These are revisions that have been merged into the working
701
directory but not yet committed.
703
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
704
instead - which is available on all tree objects.
706
return self.get_parent_ids()[1:]
700
def path_content_summary(self, path, _lstat=os.lstat,
701
_mapper=osutils.file_kind_from_stat_mode):
702
"""See Tree.path_content_summary."""
703
abspath = self.abspath(path)
705
stat_result = _lstat(abspath)
707
if getattr(e, 'errno', None) == errno.ENOENT:
709
return ('missing', None, None, None)
710
# propagate other errors
712
kind = _mapper(stat_result.st_mode)
714
size = stat_result.st_size
715
# try for a stat cache lookup
716
executable = self._is_executable_from_path_and_stat(path, stat_result)
717
return (kind, size, executable, self._sha_from_stat(
719
elif kind == 'directory':
720
# perhaps it looks like a plain directory, but it's really a
722
if self._directory_is_tree_reference(path):
723
kind = 'tree-reference'
724
return kind, None, None, None
725
elif kind == 'symlink':
726
return ('symlink', None, None, os.readlink(abspath))
728
return (kind, None, None, None)
708
730
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
709
731
"""Common ghost checking functionality from set_parent_*.
734
776
:param revision_ids: The revision_ids to set as the parent ids of this
735
777
working tree. Any of these may be ghosts.
737
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
738
779
self._check_parents_for_ghosts(revision_ids,
739
780
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
781
for revision_id in revision_ids:
782
_mod_revision.check_not_reserved_id(revision_id)
784
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
741
786
if len(revision_ids) > 0:
742
787
self.set_last_revision(revision_ids[0])
744
self.set_last_revision(None)
789
self.set_last_revision(_mod_revision.NULL_REVISION)
746
791
self._set_merges_from_parent_ids(revision_ids)
748
793
@needs_tree_write_lock
749
794
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
750
795
"""See MutableTree.set_parent_trees."""
751
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
796
parent_ids = [rev for (rev, tree) in parents_list]
797
for revision_id in parent_ids:
798
_mod_revision.check_not_reserved_id(revision_id)
753
800
self._check_parents_for_ghosts(parent_ids,
754
801
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
803
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
756
805
if len(parent_ids) == 0:
757
leftmost_parent_id = None
806
leftmost_parent_id = _mod_revision.NULL_REVISION
758
807
leftmost_parent_tree = None
760
809
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
839
905
def merge_modified(self):
906
"""Return a dictionary of files modified by a merge.
908
The list is initialized by WorkingTree.set_merge_modified, which is
909
typically called after we make some automatic updates to the tree
912
This returns a map of file_id->sha1, containing only files which are
913
still in the working inventory and have that text hash.
841
hashfile = self._control_files.get('merge-hashes')
916
hashfile = self._transport.get('merge-hashes')
842
917
except errors.NoSuchFile:
846
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
922
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
923
raise errors.MergeModifiedFormatError()
924
except StopIteration:
847
925
raise errors.MergeModifiedFormatError()
848
except StopIteration:
849
raise errors.MergeModifiedFormatError()
850
for s in RioReader(hashfile):
851
file_id = s.get("file_id")
852
if file_id not in self.inventory:
855
if hash == self.get_file_sha1(file_id):
856
merge_hashes[file_id] = hash
926
for s in RioReader(hashfile):
927
# RioReader reads in Unicode, so convert file_ids back to utf8
928
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
929
if file_id not in self.inventory:
931
text_hash = s.get("hash")
932
if text_hash == self.get_file_sha1(file_id):
933
merge_hashes[file_id] = text_hash
859
938
@needs_write_lock
860
939
def mkdir(self, path, file_id=None):
868
947
def get_symlink_target(self, file_id):
869
948
return os.readlink(self.id2abspath(file_id))
871
def file_class(self, filename):
872
if self.path2id(filename):
874
elif self.is_ignored(filename):
951
def subsume(self, other_tree):
952
def add_children(inventory, entry):
953
for child_entry in entry.children.values():
954
inventory._byid[child_entry.file_id] = child_entry
955
if child_entry.kind == 'directory':
956
add_children(inventory, child_entry)
957
if other_tree.get_root_id() == self.get_root_id():
958
raise errors.BadSubsumeSource(self, other_tree,
959
'Trees have the same root')
961
other_tree_path = self.relpath(other_tree.basedir)
962
except errors.PathNotChild:
963
raise errors.BadSubsumeSource(self, other_tree,
964
'Tree is not contained by the other')
965
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
966
if new_root_parent is None:
967
raise errors.BadSubsumeSource(self, other_tree,
968
'Parent directory is not versioned.')
969
# We need to ensure that the result of a fetch will have a
970
# versionedfile for the other_tree root, and only fetching into
971
# RepositoryKnit2 guarantees that.
972
if not self.branch.repository.supports_rich_root():
973
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
974
other_tree.lock_tree_write()
976
new_parents = other_tree.get_parent_ids()
977
other_root = other_tree.inventory.root
978
other_root.parent_id = new_root_parent
979
other_root.name = osutils.basename(other_tree_path)
980
self.inventory.add(other_root)
981
add_children(self.inventory, other_root)
982
self._write_inventory(self.inventory)
983
# normally we don't want to fetch whole repositories, but i think
984
# here we really do want to consolidate the whole thing.
985
for parent_id in other_tree.get_parent_ids():
986
self.branch.fetch(other_tree.branch, parent_id)
987
self.add_parent_tree_id(parent_id)
990
other_tree.bzrdir.retire_bzrdir()
992
def _setup_directory_is_tree_reference(self):
993
if self._branch.repository._format.supports_tree_reference:
994
self._directory_is_tree_reference = \
995
self._directory_may_be_tree_reference
997
self._directory_is_tree_reference = \
998
self._directory_is_never_tree_reference
1000
def _directory_is_never_tree_reference(self, relpath):
1003
def _directory_may_be_tree_reference(self, relpath):
1004
# as a special case, if a directory contains control files then
1005
# it's a tree reference, except that the root of the tree is not
1006
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
1007
# TODO: We could ask all the control formats whether they
1008
# recognize this directory, but at the moment there's no cheap api
1009
# to do that. Since we probably can only nest bzr checkouts and
1010
# they always use this name it's ok for now. -- mbp 20060306
1012
# FIXME: There is an unhandled case here of a subdirectory
1013
# containing .bzr but not a branch; that will probably blow up
1014
# when you try to commit it. It might happen if there is a
1015
# checkout in a subdirectory. This can be avoided by not adding
1018
@needs_tree_write_lock
1019
def extract(self, file_id, format=None):
1020
"""Extract a subtree from this tree.
1022
A new branch will be created, relative to the path for this tree.
1026
segments = osutils.splitpath(path)
1027
transport = self.branch.bzrdir.root_transport
1028
for name in segments:
1029
transport = transport.clone(name)
1030
transport.ensure_base()
1033
sub_path = self.id2path(file_id)
1034
branch_transport = mkdirs(sub_path)
1036
format = self.bzrdir.cloning_metadir()
1037
branch_transport.ensure_base()
1038
branch_bzrdir = format.initialize_on_transport(branch_transport)
1040
repo = branch_bzrdir.find_repository()
1041
except errors.NoRepositoryPresent:
1042
repo = branch_bzrdir.create_repository()
1043
if not repo.supports_rich_root():
1044
raise errors.RootNotRich()
1045
new_branch = branch_bzrdir.create_branch()
1046
new_branch.pull(self.branch)
1047
for parent_id in self.get_parent_ids():
1048
new_branch.fetch(self.branch, parent_id)
1049
tree_transport = self.bzrdir.root_transport.clone(sub_path)
1050
if tree_transport.base != branch_transport.base:
1051
tree_bzrdir = format.initialize_on_transport(tree_transport)
1052
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1054
tree_bzrdir = branch_bzrdir
1055
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
1056
wt.set_parent_ids(self.get_parent_ids())
1057
my_inv = self.inventory
1058
child_inv = Inventory(root_id=None)
1059
new_root = my_inv[file_id]
1060
my_inv.remove_recursive_id(file_id)
1061
new_root.parent_id = None
1062
child_inv.add(new_root)
1063
self._write_inventory(my_inv)
1064
wt._write_inventory(child_inv)
1067
def _serialize(self, inventory, out_file):
1068
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1071
def _deserialize(selt, in_file):
1072
return xml5.serializer_v5.read_inventory(in_file)
879
1074
def flush(self):
880
1075
"""Write the in memory inventory to disk."""
1642
1856
if self._inventory_is_modified:
1643
1857
raise errors.InventoryModified(self)
1644
result = xml5.serializer_v5.read_inventory(
1645
self._control_files.get('inventory'))
1858
result = self._deserialize(self._transport.get('inventory'))
1646
1859
self._set_inventory(result, dirty=False)
1649
1862
@needs_tree_write_lock
1650
def remove(self, files, verbose=False, to_file=None):
1651
"""Remove nominated files from the working inventory..
1653
This does not remove their text. This does not run on XXX on what? RBC
1655
TODO: Refuse to remove modified files unless --force is given?
1657
TODO: Do something useful with directories.
1659
TODO: Should this remove the text or not? Tough call; not
1660
removing may be useful and the user can just use use rm, and
1661
is the opposite of add. Removing it is consistent with most
1662
other tools. Maybe an option.
1863
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1865
"""Remove nominated files from the working inventory.
1867
:files: File paths relative to the basedir.
1868
:keep_files: If true, the files will also be kept.
1869
:force: Delete files and directories, even if they are changed and
1870
even if the directories are not empty.
1664
## TODO: Normalize names
1665
## TODO: Remove nested loops; better scalability
1666
1872
if isinstance(files, basestring):
1667
1873
files = [files]
1669
inv = self.inventory
1671
# do this before any modifications
1878
unknown_nested_files=set()
1880
def recurse_directory_to_add_files(directory):
1881
# Recurse directory and add all files
1882
# so we can check if they have changed.
1883
for parent_info, file_infos in\
1884
self.walkdirs(directory):
1885
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1886
# Is it versioned or ignored?
1887
if self.path2id(relpath) or self.is_ignored(relpath):
1888
# Add nested content for deletion.
1889
new_files.add(relpath)
1891
# Files which are not versioned and not ignored
1892
# should be treated as unknown.
1893
unknown_nested_files.add((relpath, None, kind))
1895
for filename in files:
1896
# Get file name into canonical form.
1897
abspath = self.abspath(filename)
1898
filename = self.relpath(abspath)
1899
if len(filename) > 0:
1900
new_files.add(filename)
1901
recurse_directory_to_add_files(filename)
1903
files = list(new_files)
1906
return # nothing to do
1908
# Sort needed to first handle directory content before the directory
1909
files.sort(reverse=True)
1911
# Bail out if we are going to delete files we shouldn't
1912
if not keep_files and not force:
1913
has_changed_files = len(unknown_nested_files) > 0
1914
if not has_changed_files:
1915
for (file_id, path, content_change, versioned, parent_id, name,
1916
kind, executable) in self.iter_changes(self.basis_tree(),
1917
include_unchanged=True, require_versioned=False,
1918
want_unversioned=True, specific_files=files):
1919
if versioned == (False, False):
1920
# The record is unknown ...
1921
if not self.is_ignored(path[1]):
1922
# ... but not ignored
1923
has_changed_files = True
1925
elif content_change and (kind[1] is not None):
1926
# Versioned and changed, but not deleted
1927
has_changed_files = True
1930
if has_changed_files:
1931
# Make delta show ALL applicable changes in error message.
1932
tree_delta = self.changes_from(self.basis_tree(),
1933
require_versioned=False, want_unversioned=True,
1934
specific_files=files)
1935
for unknown_file in unknown_nested_files:
1936
if unknown_file not in tree_delta.unversioned:
1937
tree_delta.unversioned.extend((unknown_file,))
1938
raise errors.BzrRemoveChangedFilesError(tree_delta)
1940
# Build inv_delta and delete files where applicaple,
1941
# do this before any modifications to inventory.
1672
1942
for f in files:
1673
fid = inv.path2id(f)
1943
fid = self.path2id(f)
1675
note("%s is not versioned."%f)
1946
message = "%s is not versioned." % (f,)
1678
# having remove it, it must be either ignored or unknown
1949
# having removed it, it must be either ignored or unknown
1679
1950
if self.is_ignored(f):
1680
1951
new_status = 'I'
1682
1953
new_status = '?'
1683
textui.show_status(new_status, inv[fid].kind, f,
1954
textui.show_status(new_status, self.kind(fid), f,
1684
1955
to_file=to_file)
1687
self._write_inventory(inv)
1957
inv_delta.append((f, None, fid, None))
1958
message = "removed %s" % (f,)
1961
abs_path = self.abspath(f)
1962
if osutils.lexists(abs_path):
1963
if (osutils.isdir(abs_path) and
1964
len(os.listdir(abs_path)) > 0):
1966
osutils.rmtree(abs_path)
1968
message = "%s is not an empty directory "\
1969
"and won't be deleted." % (f,)
1971
osutils.delete_any(abs_path)
1972
message = "deleted %s" % (f,)
1973
elif message is not None:
1974
# Only care if we haven't done anything yet.
1975
message = "%s does not exist." % (f,)
1977
# Print only one message (if any) per file.
1978
if message is not None:
1980
self.apply_inventory_delta(inv_delta)
1689
1982
@needs_tree_write_lock
1690
def revert(self, filenames, old_tree=None, backups=True,
1983
def revert(self, filenames=None, old_tree=None, backups=True,
1691
1984
pb=DummyProgress(), report_changes=False):
1692
1985
from bzrlib.conflicts import resolve
1988
symbol_versioning.warn('Using [] to revert all files is deprecated'
1989
' as of bzr 0.91. Please use None (the default) instead.',
1990
DeprecationWarning, stacklevel=2)
1693
1991
if old_tree is None:
1694
old_tree = self.basis_tree()
1695
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1697
if not len(filenames):
1698
self.set_parent_ids(self.get_parent_ids()[:1])
1992
basis_tree = self.basis_tree()
1993
basis_tree.lock_read()
1994
old_tree = basis_tree
1701
resolve(self, filenames, ignore_misses=True)
1998
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2000
if filenames is None and len(self.get_parent_ids()) > 1:
2002
last_revision = self.last_revision()
2003
if last_revision != NULL_REVISION:
2004
if basis_tree is None:
2005
basis_tree = self.basis_tree()
2006
basis_tree.lock_read()
2007
parent_trees.append((last_revision, basis_tree))
2008
self.set_parent_trees(parent_trees)
2011
resolve(self, filenames, ignore_misses=True, recursive=True)
2013
if basis_tree is not None:
1702
2015
return conflicts
1704
2017
def revision_tree(self, revision_id):
2106
2440
# FIXME: stash the node in pending
2107
2441
entry = inv[top_id]
2108
for name, child in entry.sorted_children():
2109
dirblock.append((relroot + name, name, child.kind, None,
2110
child.file_id, child.kind
2442
if entry.kind == 'directory':
2443
for name, child in entry.sorted_children():
2444
dirblock.append((relroot + name, name, child.kind, None,
2445
child.file_id, child.kind
2112
2447
yield (currentdir[0], entry.file_id), dirblock
2113
2448
# push the user specified dirs from dirblock
2114
2449
for dir in reversed(dirblock):
2115
2450
if dir[2] == _directory:
2116
2451
pending.append(dir)
2453
@needs_tree_write_lock
2454
def auto_resolve(self):
2455
"""Automatically resolve text conflicts according to contents.
2457
Only text conflicts are auto_resolvable. Files with no conflict markers
2458
are considered 'resolved', because bzr always puts conflict markers
2459
into files that have text conflicts. The corresponding .THIS .BASE and
2460
.OTHER files are deleted, as per 'resolve'.
2461
:return: a tuple of ConflictLists: (un_resolved, resolved).
2463
un_resolved = _mod_conflicts.ConflictList()
2464
resolved = _mod_conflicts.ConflictList()
2465
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2466
for conflict in self.conflicts():
2467
if (conflict.typestring != 'text conflict' or
2468
self.kind(conflict.file_id) != 'file'):
2469
un_resolved.append(conflict)
2471
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2473
for line in my_file:
2474
if conflict_re.search(line):
2475
un_resolved.append(conflict)
2478
resolved.append(conflict)
2481
resolved.remove_files(self)
2482
self.set_conflicts(un_resolved)
2483
return un_resolved, resolved
2487
tree_basis = self.basis_tree()
2488
tree_basis.lock_read()
2490
repo_basis = self.branch.repository.revision_tree(
2491
self.last_revision())
2492
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2493
raise errors.BzrCheckError(
2494
"Mismatched basis inventory content.")
2499
def _validate(self):
2500
"""Validate internal structures.
2502
This is meant mostly for the test suite. To give it a chance to detect
2503
corruption after actions have occurred. The default implementation is a
2506
:return: None. An exception should be raised if there is an error.
2511
def _get_rules_searcher(self, default_searcher):
2512
"""See Tree._get_rules_searcher."""
2513
if self._rules_searcher is None:
2514
self._rules_searcher = super(WorkingTree,
2515
self)._get_rules_searcher(default_searcher)
2516
return self._rules_searcher
2518
def get_shelf_manager(self):
2519
"""Return the ShelfManager for this WorkingTree."""
2520
from bzrlib.shelf import ShelfManager
2521
return ShelfManager(self, self._transport)
2119
2524
class WorkingTree2(WorkingTree):
2120
2525
"""This is the Format 2 working tree.
2313
2740
def unregister_format(klass, format):
2314
assert klass._formats[format.get_format_string()] is format
2315
2741
del klass._formats[format.get_format_string()]
2319
2744
class WorkingTreeFormat2(WorkingTreeFormat):
2320
2745
"""The second working tree format.
2322
2747
This format modified the hash cache from the format 1 hash cache.
2750
upgrade_recommended = True
2325
2752
def get_format_description(self):
2326
2753
"""See WorkingTreeFormat.get_format_description()."""
2327
2754
return "Working tree format 2"
2329
def stub_initialize_remote(self, control_files):
2330
"""As a special workaround create critical control files for a remote working tree
2756
def _stub_initialize_on_transport(self, transport, file_mode):
2757
"""Workaround: create control files for a remote working tree.
2332
2759
This ensures that it can later be updated and dealt with locally,
2333
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2760
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2334
2761
no working tree. (See bug #43064).
2336
2763
sio = StringIO()
2337
2764
inv = Inventory()
2338
xml5.serializer_v5.write_inventory(inv, sio)
2765
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2340
control_files.put('inventory', sio)
2342
control_files.put_bytes('pending-merges', '')
2345
def initialize(self, a_bzrdir, revision_id=None):
2767
transport.put_file('inventory', sio, file_mode)
2768
transport.put_bytes('pending-merges', '', file_mode)
2770
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2771
accelerator_tree=None, hardlink=False):
2346
2772
"""See WorkingTreeFormat.initialize()."""
2347
2773
if not isinstance(a_bzrdir.transport, LocalTransport):
2348
2774
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2349
branch = a_bzrdir.open_branch()
2350
if revision_id is not None:
2351
revision_id = osutils.safe_revision_id(revision_id)
2354
revision_history = branch.revision_history()
2356
position = revision_history.index(revision_id)
2358
raise errors.NoSuchRevision(branch, revision_id)
2359
branch.set_revision_history(revision_history[:position + 1])
2362
revision = branch.last_revision()
2775
if from_branch is not None:
2776
branch = from_branch
2778
branch = a_bzrdir.open_branch()
2779
if revision_id is None:
2780
revision_id = _mod_revision.ensure_null(branch.last_revision())
2783
branch.generate_revision_history(revision_id)
2363
2786
inv = Inventory()
2364
2787
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2419
2848
_lock_file_name = 'lock'
2420
2849
_lock_class = LockDir
2851
_tree_class = WorkingTree3
2853
def __get_matchingbzrdir(self):
2854
return bzrdir.BzrDirMetaFormat1()
2856
_matchingbzrdir = property(__get_matchingbzrdir)
2422
2858
def _open_control_files(self, a_bzrdir):
2423
2859
transport = a_bzrdir.get_workingtree_transport(None)
2424
2860
return LockableFiles(transport, self._lock_file_name,
2425
2861
self._lock_class)
2427
def initialize(self, a_bzrdir, revision_id=None):
2863
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2864
accelerator_tree=None, hardlink=False):
2428
2865
"""See WorkingTreeFormat.initialize().
2430
revision_id allows creating a working tree at a different
2431
revision than the branch is at.
2867
:param revision_id: if supplied, create a working tree at a different
2868
revision than the branch is at.
2869
:param accelerator_tree: A tree which can be used for retrieving file
2870
contents more quickly than the revision tree, i.e. a workingtree.
2871
The revision tree will be used for cases where accelerator_tree's
2872
content is different.
2873
:param hardlink: If true, hard-link files from accelerator_tree,
2433
2876
if not isinstance(a_bzrdir.transport, LocalTransport):
2434
2877
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2496
2944
:param a_bzrdir: the dir for the tree.
2497
2945
:param control_files: the control files for the tree.
2499
return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2503
_control_files=control_files)
2947
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2951
_control_files=control_files)
2505
2953
def __str__(self):
2506
2954
return self.get_format_string()
2509
__default_format = WorkingTreeFormat3()
2957
__default_format = WorkingTreeFormat4()
2510
2958
WorkingTreeFormat.register_format(__default_format)
2511
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2959
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2960
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2512
2961
WorkingTreeFormat.set_default_format(__default_format)
2513
2962
# formats which have no format string are not discoverable
2514
2963
# and not independently creatable, so are not registered.
2515
2964
_legacy_formats = [WorkingTreeFormat2(),
2519
class WorkingTreeTestProviderAdapter(object):
2520
"""A tool to generate a suite testing multiple workingtree formats at once.
2522
This is done by copying the test once for each transport and injecting
2523
the transport_server, transport_readonly_server, and workingtree_format
2524
classes into each copy. Each copy is also given a new id() to make it
2528
def __init__(self, transport_server, transport_readonly_server, formats):
2529
self._transport_server = transport_server
2530
self._transport_readonly_server = transport_readonly_server
2531
self._formats = formats
2533
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2534
"""Clone test for adaption."""
2535
new_test = deepcopy(test)
2536
new_test.transport_server = self._transport_server
2537
new_test.transport_readonly_server = self._transport_readonly_server
2538
new_test.bzrdir_format = bzrdir_format
2539
new_test.workingtree_format = workingtree_format
2540
def make_new_test_id():
2541
new_id = "%s(%s)" % (test.id(), variation)
2542
return lambda: new_id
2543
new_test.id = make_new_test_id()
2546
def adapt(self, test):
2547
from bzrlib.tests import TestSuite
2548
result = TestSuite()
2549
for workingtree_format, bzrdir_format in self._formats:
2550
new_test = self._clone_test(
2553
workingtree_format, workingtree_format.__class__.__name__)
2554
result.addTest(new_test)