195
"""Construct a WorkingTree instance. This is not a public API.
207
"""Construct a WorkingTree for basedir.
197
:param branch: A branch to override probing for the branch.
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).
199
214
self._format = _format
200
215
self.bzrdir = _bzrdir
201
216
if not _internal:
202
raise errors.BzrError("Please use bzrdir.open_workingtree or "
203
"WorkingTree.open() to obtain a WorkingTree.")
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
204
232
basedir = safe_unicode(basedir)
205
233
mutter("opening working tree %r", basedir)
206
234
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().",
207
242
self._branch = branch
209
244
self._branch = self.bzrdir.open_branch()
405
392
# at this point ?
407
394
return self.branch.repository.revision_tree(revision_id)
408
except (errors.RevisionNotPresent, errors.NoSuchRevision):
395
except errors.RevisionNotPresent:
409
396
# the basis tree *may* be a ghost or a low level error may have
410
397
# occured. If the revision is present, its a problem, if its not
412
399
if self.branch.repository.has_revision(revision_id):
414
401
# the basis tree is a ghost so return an empty tree.
415
return self.branch.repository.revision_tree(
416
_mod_revision.NULL_REVISION)
419
self._flush_ignore_list_cache()
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)
421
442
def relpath(self, path):
422
443
"""Return the local path portion from a given path.
429
450
def has_filename(self, filename):
430
451
return osutils.lexists(self.abspath(filename))
432
def get_file(self, file_id, path=None):
433
return self.get_file_with_stat(file_id, path)[0]
453
def get_file(self, file_id):
454
return self.get_file_byname(self.id2path(file_id))
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()))
456
def get_file_text(self, file_id):
457
return self.get_file(file_id).read()
442
459
def get_file_byname(self, filename):
443
460
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()
454
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
463
def annotate_iter(self, file_id):
455
464
"""See Tree.annotate_iter
457
466
This implementation will use the basis tree implementation if possible.
462
471
attribution will be correct).
464
473
basis = self.basis_tree()
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))
474
changes = self._iter_changes(basis, True, [file_id]).next()
475
changed_content, kind = changes[2], changes[6]
476
if not changed_content:
477
return basis.annotate_iter(file_id)
481
if kind[0] != 'file':
484
old_lines = list(basis.annotate_iter(file_id))
486
for tree in self.branch.repository.revision_trees(
487
self.get_parent_ids()[1:]):
488
if file_id not in tree:
490
old.append(list(tree.annotate_iter(file_id)))
491
return annotate.reannotate(old, self.get_file(file_id).readlines(),
497
494
def get_parent_ids(self):
498
495
"""See Tree.get_parent_ids.
698
678
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
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.encode(osutils._fs_enc)))
728
return (kind, None, None, None)
680
@deprecated_method(zero_eleven)
682
def pending_merges(self):
683
"""Return a list of pending merges.
685
These are revisions that have been merged into the working
686
directory but not yet committed.
688
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
689
instead - which is available on all tree objects.
691
return self.get_parent_ids()[1:]
730
693
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
731
694
"""Common ghost checking functionality from set_parent_*.
831
766
def set_merge_modified(self, modified_hashes):
832
767
def iter_stanzas():
833
768
for file_id, hash in modified_hashes.iteritems():
834
yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
769
yield Stanza(file_id=file_id, hash=hash)
835
770
self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
837
def _sha_from_stat(self, path, stat_result):
838
"""Get a sha digest from the tree's stat cache.
840
The default implementation assumes no stat cache is present.
842
:param path: The path.
843
:param stat_result: The stat result being looked up.
772
@needs_tree_write_lock
847
773
def _put_rio(self, filename, stanzas, header):
848
self._must_be_locked()
849
774
my_file = rio_file(stanzas, header)
850
self._transport.put_file(filename, my_file,
851
mode=self._control_files._file_mode)
775
self._control_files.put(filename, my_file)
853
777
@needs_write_lock # because merge pulls data into the branch.
854
def merge_from_branch(self, branch, to_revision=None, from_revision=None,
778
def merge_from_branch(self, branch, to_revision=None):
856
779
"""Merge from a branch into this working tree.
858
781
:param branch: The branch to merge from.
905
821
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.
916
hashfile = self._transport.get('merge-hashes')
823
hashfile = self._control_files.get('merge-hashes')
917
824
except errors.NoSuchFile:
922
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
923
raise errors.MergeModifiedFormatError()
924
except StopIteration:
828
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
925
829
raise errors.MergeModifiedFormatError()
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
830
except StopIteration:
831
raise errors.MergeModifiedFormatError()
832
for s in RioReader(hashfile):
833
file_id = s.get("file_id")
834
if file_id not in self.inventory:
837
if hash == self.get_file_sha1(file_id):
838
merge_hashes[file_id] = hash
938
841
@needs_write_lock
939
842
def mkdir(self, path, file_id=None):
947
850
def get_symlink_target(self, file_id):
948
return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc))
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)
851
return os.readlink(self.id2abspath(file_id))
853
def file_class(self, filename):
854
if self.path2id(filename):
856
elif self.is_ignored(filename):
1074
861
def flush(self):
1075
862
"""Write the in memory inventory to disk."""
1856
1615
if self._inventory_is_modified:
1857
1616
raise errors.InventoryModified(self)
1858
result = self._deserialize(self._transport.get('inventory'))
1617
result = xml5.serializer_v5.read_inventory(
1618
self._control_files.get('inventory'))
1859
1619
self._set_inventory(result, dirty=False)
1862
1622
@needs_tree_write_lock
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.
1623
def remove(self, files, verbose=False, to_file=None):
1624
"""Remove nominated files from the working inventory..
1626
This does not remove their text. This does not run on XXX on what? RBC
1628
TODO: Refuse to remove modified files unless --force is given?
1630
TODO: Do something useful with directories.
1632
TODO: Should this remove the text or not? Tough call; not
1633
removing may be useful and the user can just use use rm, and
1634
is the opposite of add. Removing it is consistent with most
1635
other tools. Maybe an option.
1637
## TODO: Normalize names
1638
## TODO: Remove nested loops; better scalability
1872
1639
if isinstance(files, basestring):
1873
1640
files = [files]
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.
1642
inv = self.inventory
1644
# do this before any modifications
1942
1645
for f in files:
1943
fid = self.path2id(f)
1646
fid = inv.path2id(f)
1946
message = "%s is not versioned." % (f,)
1949
# having removed it, it must be either ignored or unknown
1950
if self.is_ignored(f):
1954
textui.show_status(new_status, self.kind(fid), f,
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)
1648
# TODO: Perhaps make this just a warning, and continue?
1649
# This tends to happen when
1650
raise errors.NotVersionedError(path=f)
1652
# having remove it, it must be either ignored or unknown
1653
if self.is_ignored(f):
1657
textui.show_status(new_status, inv[fid].kind, f,
1661
self._write_inventory(inv)
1982
1663
@needs_tree_write_lock
1983
def revert(self, filenames=None, old_tree=None, backups=True,
1664
def revert(self, filenames, old_tree=None, backups=True,
1984
1665
pb=DummyProgress(), report_changes=False):
1985
1666
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)
1991
1667
if old_tree is None:
1992
basis_tree = self.basis_tree()
1993
basis_tree.lock_read()
1994
old_tree = basis_tree
1668
old_tree = self.basis_tree()
1669
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1671
if not len(filenames):
1672
self.set_parent_ids(self.get_parent_ids()[:1])
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:
1675
resolve(self, filenames, ignore_misses=True)
2015
1676
return conflicts
2017
1678
def revision_tree(self, revision_id):
2210
1871
# the working tree had the same last-revision as the master
2211
1872
# branch did. We may still have pivot local work from the local
2212
1873
# branch into old_tip:
2213
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
1874
if old_tip is not None:
2214
1875
self.add_parent_tree_id(old_tip)
2215
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2216
and old_tip != last_rev):
1876
if old_tip and old_tip != last_rev:
2217
1877
# our last revision was not the prior branch last revision
2218
1878
# and we have converted that last revision to a pending merge.
2219
1879
# base is somewhere between the branch tip now
2220
1880
# and the now pending merge
2222
# Since we just modified the working tree and inventory, flush out
2223
# the current state, before we modify it again.
2224
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2225
# requires it only because TreeTransform directly munges the
2226
# inventory and calls tree._write_inventory(). Ultimately we
2227
# should be able to remove this extra flush.
2229
graph = self.branch.repository.get_graph()
2230
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1881
from bzrlib.revision import common_ancestor
1883
base_rev_id = common_ancestor(self.branch.last_revision(),
1885
self.branch.repository)
1886
except errors.NoCommonAncestor:
2232
1888
base_tree = self.branch.repository.revision_tree(base_rev_id)
2233
1889
other_tree = self.branch.repository.revision_tree(old_tip)
2234
1890
result += merge.merge_inner(
2239
change_reporter=change_reporter)
2242
1897
def _write_hashcache_if_dirty(self):
2440
2070
# FIXME: stash the node in pending
2441
2071
entry = inv[top_id]
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
2072
for name, child in entry.sorted_children():
2073
dirblock.append((relroot + name, name, child.kind, None,
2074
child.file_id, child.kind
2447
2076
yield (currentdir[0], entry.file_id), dirblock
2448
2077
# push the user specified dirs from dirblock
2449
2078
for dir in reversed(dirblock):
2450
2079
if dir[2] == _directory:
2451
2080
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)
2524
2083
class WorkingTree2(WorkingTree):
2525
2084
"""This is the Format 2 working tree.
2740
2276
def unregister_format(klass, format):
2277
assert klass._formats[format.get_format_string()] is format
2741
2278
del klass._formats[format.get_format_string()]
2744
2282
class WorkingTreeFormat2(WorkingTreeFormat):
2745
2283
"""The second working tree format.
2747
2285
This format modified the hash cache from the format 1 hash cache.
2750
upgrade_recommended = True
2752
2288
def get_format_description(self):
2753
2289
"""See WorkingTreeFormat.get_format_description()."""
2754
2290
return "Working tree format 2"
2756
def _stub_initialize_on_transport(self, transport, file_mode):
2757
"""Workaround: create control files for a remote working tree.
2292
def stub_initialize_remote(self, control_files):
2293
"""As a special workaround create critical control files for a remote working tree
2759
2295
This ensures that it can later be updated and dealt with locally,
2760
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2296
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2761
2297
no working tree. (See bug #43064).
2763
2299
sio = StringIO()
2764
2300
inv = Inventory()
2765
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2301
xml5.serializer_v5.write_inventory(inv, sio)
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):
2303
control_files.put('inventory', sio)
2305
control_files.put_utf8('pending-merges', '')
2308
def initialize(self, a_bzrdir, revision_id=None):
2772
2309
"""See WorkingTreeFormat.initialize()."""
2773
2310
if not isinstance(a_bzrdir.transport, LocalTransport):
2774
2311
raise errors.NotLocalUrl(a_bzrdir.transport.base)
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)
2312
branch = a_bzrdir.open_branch()
2313
if revision_id is not None:
2316
revision_history = branch.revision_history()
2318
position = revision_history.index(revision_id)
2320
raise errors.NoSuchRevision(branch, revision_id)
2321
branch.set_revision_history(revision_history[:position + 1])
2324
revision = branch.last_revision()
2786
2325
inv = Inventory()
2787
2326
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2848
2381
_lock_file_name = 'lock'
2849
2382
_lock_class = LockDir
2851
_tree_class = WorkingTree3
2853
def __get_matchingbzrdir(self):
2854
return bzrdir.BzrDirMetaFormat1()
2856
_matchingbzrdir = property(__get_matchingbzrdir)
2858
2384
def _open_control_files(self, a_bzrdir):
2859
2385
transport = a_bzrdir.get_workingtree_transport(None)
2860
2386
return LockableFiles(transport, self._lock_file_name,
2861
2387
self._lock_class)
2863
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2864
accelerator_tree=None, hardlink=False):
2389
def initialize(self, a_bzrdir, revision_id=None):
2865
2390
"""See WorkingTreeFormat.initialize().
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,
2392
revision_id allows creating a working tree at a different
2393
revision than the branch is at.
2876
2395
if not isinstance(a_bzrdir.transport, LocalTransport):
2877
2396
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2944
2456
:param a_bzrdir: the dir for the tree.
2945
2457
:param control_files: the control files for the tree.
2947
return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
2951
_control_files=control_files)
2459
return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
2463
_control_files=control_files)
2953
2465
def __str__(self):
2954
2466
return self.get_format_string()
2957
__default_format = WorkingTreeFormat4()
2469
__default_format = WorkingTreeFormat3()
2958
2470
WorkingTreeFormat.register_format(__default_format)
2959
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2960
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2471
WorkingTreeFormat.register_format(WorkingTreeFormat4())
2961
2472
WorkingTreeFormat.set_default_format(__default_format)
2962
2473
# formats which have no format string are not discoverable
2963
2474
# and not independently creatable, so are not registered.
2964
2475
_legacy_formats = [WorkingTreeFormat2(),
2479
class WorkingTreeTestProviderAdapter(object):
2480
"""A tool to generate a suite testing multiple workingtree formats at once.
2482
This is done by copying the test once for each transport and injecting
2483
the transport_server, transport_readonly_server, and workingtree_format
2484
classes into each copy. Each copy is also given a new id() to make it
2488
def __init__(self, transport_server, transport_readonly_server, formats):
2489
self._transport_server = transport_server
2490
self._transport_readonly_server = transport_readonly_server
2491
self._formats = formats
2493
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2494
"""Clone test for adaption."""
2495
new_test = deepcopy(test)
2496
new_test.transport_server = self._transport_server
2497
new_test.transport_readonly_server = self._transport_readonly_server
2498
new_test.bzrdir_format = bzrdir_format
2499
new_test.workingtree_format = workingtree_format
2500
def make_new_test_id():
2501
new_id = "%s(%s)" % (test.id(), variation)
2502
return lambda: new_id
2503
new_test.id = make_new_test_id()
2506
def adapt(self, test):
2507
from bzrlib.tests import TestSuite
2508
result = TestSuite()
2509
for workingtree_format, bzrdir_format in self._formats:
2510
new_test = self._clone_test(
2513
workingtree_format, workingtree_format.__class__.__name__)
2514
result.addTest(new_test)