57
59
conflicts as _mod_conflicts,
66
revision as _mod_revision,
76
77
import bzrlib.branch
77
78
from bzrlib.transport import get_transport
78
from bzrlib.workingtree_4 import (
80
from bzrlib.workingtree_4 import WorkingTreeFormat4
85
83
from bzrlib import symbol_versioning
86
84
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
from bzrlib.lockable_files import LockableFiles
85
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
86
from bzrlib.lockable_files import LockableFiles, TransportLock
88
87
from bzrlib.lockdir import LockDir
89
88
import bzrlib.mutabletree
90
89
from bzrlib.mutabletree import needs_tree_write_lock
91
from bzrlib import osutils
92
90
from bzrlib.osutils import (
100
100
supports_executable,
102
from bzrlib.filters import filtered_input_file
103
102
from bzrlib.trace import mutter, note
104
103
from bzrlib.transport.local import LocalTransport
105
104
from bzrlib.progress import DummyProgress, ProgressPhase
106
from bzrlib.revision import CURRENT_REVISION
105
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
107
106
from bzrlib.rio import RioReader, rio_file, Stanza
108
from bzrlib.symbol_versioning import (
110
DEPRECATED_PARAMETER,
107
from bzrlib.symbol_versioning import (deprecated_passed,
110
DEPRECATED_PARAMETER,
114
117
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
115
118
CONFLICT_HEADER_1 = "BZR conflict list format 1"
117
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
121
@deprecated_function(zero_thirteen)
122
def gen_file_id(name):
123
"""Return new file id for the basename 'name'.
125
Use bzrlib.generate_ids.gen_file_id() instead
127
return generate_ids.gen_file_id(name)
130
@deprecated_function(zero_thirteen)
132
"""Return a new tree-root file id.
134
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
136
return generate_ids.gen_root_id()
120
139
class TreeEntry(object):
121
140
"""An entry that implements the minimum interface used by commands.
123
This needs further inspection, it may be better to have
142
This needs further inspection, it may be better to have
124
143
InventoryEntries without ids - though that seems wrong. For now,
125
144
this is a parallel hierarchy to InventoryEntry, and needs to become
126
145
one of several things: decorates to that hierarchy, children of, or
196
"""Construct a WorkingTree instance. This is not a public API.
211
"""Construct a WorkingTree for basedir.
198
:param branch: A branch to override probing for the branch.
213
If the branch is not supplied, it is opened automatically.
214
If the branch is supplied, it must be the branch for this basedir.
215
(branch.base is not cross checked, because for remote branches that
216
would be meaningless).
200
218
self._format = _format
201
219
self.bzrdir = _bzrdir
202
220
if not _internal:
203
raise errors.BzrError("Please use bzrdir.open_workingtree or "
204
"WorkingTree.open() to obtain a WorkingTree.")
221
# not created via open etc.
222
warnings.warn("WorkingTree() is deprecated as of bzr version 0.8. "
223
"Please use bzrdir.open_workingtree or WorkingTree.open().",
226
wt = WorkingTree.open(basedir)
227
self._branch = wt.branch
228
self.basedir = wt.basedir
229
self._control_files = wt._control_files
230
self._hashcache = wt._hashcache
231
self._set_inventory(wt._inventory, dirty=False)
232
self._format = wt._format
233
self.bzrdir = wt.bzrdir
234
assert isinstance(basedir, basestring), \
235
"base directory %r is not a string" % basedir
205
236
basedir = safe_unicode(basedir)
206
237
mutter("opening working tree %r", basedir)
207
238
if deprecated_passed(branch):
240
warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
241
" as of bzr 0.8. Please use bzrdir.open_workingtree() or"
242
" WorkingTree.open().",
208
246
self._branch = branch
210
248
self._branch = self.bzrdir.open_branch()
418
405
# at this point ?
420
407
return self.branch.repository.revision_tree(revision_id)
421
except (errors.RevisionNotPresent, errors.NoSuchRevision):
408
except errors.RevisionNotPresent:
422
409
# the basis tree *may* be a ghost or a low level error may have
423
# occurred. If the revision is present, its a problem, if its not
410
# occured. If the revision is present, its a problem, if its not
425
412
if self.branch.repository.has_revision(revision_id):
427
414
# the basis tree is a ghost so return an empty tree.
428
return self.branch.repository.revision_tree(
429
_mod_revision.NULL_REVISION)
432
self._flush_ignore_list_cache()
415
return self.branch.repository.revision_tree(None)
418
@deprecated_method(zero_eight)
419
def create(branch, directory):
420
"""Create a workingtree for branch at directory.
422
If existing_directory already exists it must have a .bzr directory.
423
If it does not exist, it will be created.
425
This returns a new WorkingTree object for the new checkout.
427
TODO FIXME RBC 20060124 when we have checkout formats in place this
428
should accept an optional revisionid to checkout [and reject this if
429
checking out into the same dir as a pre-checkout-aware branch format.]
431
XXX: When BzrDir is present, these should be created through that
434
warnings.warn('delete WorkingTree.create', stacklevel=3)
435
transport = get_transport(directory)
436
if branch.bzrdir.root_transport.base == transport.base:
438
return branch.bzrdir.create_workingtree()
439
# different directory,
440
# create a branch reference
441
# and now a working tree.
442
raise NotImplementedError
445
@deprecated_method(zero_eight)
446
def create_standalone(directory):
447
"""Create a checkout and a branch and a repo at directory.
449
Directory must exist and be empty.
451
please use BzrDir.create_standalone_workingtree
453
return bzrdir.BzrDir.create_standalone_workingtree(directory)
434
455
def relpath(self, path):
435
456
"""Return the local path portion from a given path.
437
The path may be absolute or relative. If its a relative path it is
458
The path may be absolute or relative. If its a relative path it is
438
459
interpreted relative to the python current working directory.
440
461
return osutils.relpath(self.basedir, path)
442
463
def has_filename(self, filename):
443
464
return osutils.lexists(self.abspath(filename))
445
def get_file(self, file_id, path=None, filtered=True):
446
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
448
def get_file_with_stat(self, file_id, path=None, filtered=True,
450
"""See Tree.get_file_with_stat."""
452
path = self.id2path(file_id)
453
file_obj = self.get_file_byname(path, filtered=False)
454
stat_value = _fstat(file_obj.fileno())
455
if filtered and self.supports_content_filtering():
456
filters = self._content_filter_stack(path)
457
file_obj = filtered_input_file(file_obj, filters)
458
return (file_obj, stat_value)
460
def get_file_text(self, file_id, path=None, filtered=True):
461
return self.get_file(file_id, path=path, filtered=filtered).read()
463
def get_file_byname(self, filename, filtered=True):
464
path = self.abspath(filename)
466
if filtered and self.supports_content_filtering():
467
filters = self._content_filter_stack(filename)
468
return filtered_input_file(f, filters)
472
def get_file_lines(self, file_id, path=None, filtered=True):
473
"""See Tree.get_file_lines()"""
474
file = self.get_file(file_id, path, filtered=filtered)
476
return file.readlines()
466
def get_file(self, file_id):
467
file_id = osutils.safe_file_id(file_id)
468
return self.get_file_byname(self.id2path(file_id))
470
def get_file_text(self, file_id):
471
file_id = osutils.safe_file_id(file_id)
472
return self.get_file(file_id).read()
474
def get_file_byname(self, filename):
475
return file(self.abspath(filename), 'rb')
481
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
478
def annotate_iter(self, file_id):
482
479
"""See Tree.annotate_iter
484
481
This implementation will use the basis tree implementation if possible.
488
485
incorrectly attributed to CURRENT_REVISION (but after committing, the
489
486
attribution will be correct).
491
maybe_file_parent_keys = []
492
for parent_id in self.get_parent_ids():
494
parent_tree = self.revision_tree(parent_id)
495
except errors.NoSuchRevisionInTree:
496
parent_tree = self.branch.repository.revision_tree(parent_id)
497
parent_tree.lock_read()
499
if file_id not in parent_tree:
501
ie = parent_tree.inventory[file_id]
502
if ie.kind != 'file':
503
# Note: this is slightly unnecessary, because symlinks and
504
# directories have a "text" which is the empty text, and we
505
# know that won't mess up annotations. But it seems cleaner
507
parent_text_key = (file_id, ie.revision)
508
if parent_text_key not in maybe_file_parent_keys:
509
maybe_file_parent_keys.append(parent_text_key)
512
graph = _mod_graph.Graph(self.branch.repository.texts)
513
heads = graph.heads(maybe_file_parent_keys)
514
file_parent_keys = []
515
for key in maybe_file_parent_keys:
517
file_parent_keys.append(key)
519
# Now we have the parents of this content
520
annotator = self.branch.repository.texts.get_annotator()
521
text = self.get_file(file_id).read()
522
this_key =(file_id, default_revision)
523
annotator.add_special_text(this_key, file_parent_keys, text)
524
annotations = [(key[-1], line)
525
for key, line in annotator.annotate_flat(this_key)]
528
def _get_ancestors(self, default_revision):
529
ancestors = set([default_revision])
530
for parent_id in self.get_parent_ids():
531
ancestors.update(self.branch.repository.get_ancestry(
532
parent_id, topo_sorted=False))
488
file_id = osutils.safe_file_id(file_id)
489
basis = self.basis_tree()
492
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
493
require_versioned=True).next()
494
changed_content, kind = changes[2], changes[6]
495
if not changed_content:
496
return basis.annotate_iter(file_id)
500
if kind[0] != 'file':
503
old_lines = list(basis.annotate_iter(file_id))
505
for tree in self.branch.repository.revision_trees(
506
self.get_parent_ids()[1:]):
507
if file_id not in tree:
509
old.append(list(tree.annotate_iter(file_id)))
510
return annotate.reannotate(old, self.get_file(file_id).readlines(),
535
515
def get_parent_ids(self):
536
516
"""See Tree.get_parent_ids.
538
518
This implementation reads the pending merges list and last_revision
539
519
value and uses that to decide what the parents list should be.
541
last_rev = _mod_revision.ensure_null(self._last_revision())
542
if _mod_revision.NULL_REVISION == last_rev:
521
last_rev = self._last_revision()
545
525
parents = [last_rev]
547
merges_file = self._transport.get('pending-merges')
527
merges_file = self._control_files.get('pending-merges')
548
528
except errors.NoSuchFile:
551
531
for l in merges_file.readlines():
552
revision_id = l.rstrip('\n')
532
revision_id = osutils.safe_revision_id(l.rstrip('\n'))
553
533
parents.append(revision_id)
557
537
def get_root_id(self):
558
538
"""Return the id of this trees root"""
559
539
return self._inventory.root.file_id
561
541
def _get_store_filename(self, file_id):
562
542
## XXX: badly named; this is not in the store at all
543
file_id = osutils.safe_file_id(file_id)
563
544
return self.abspath(self.id2path(file_id))
566
def clone(self, to_bzrdir, revision_id=None):
547
def clone(self, to_bzrdir, revision_id=None, basis=None):
567
548
"""Duplicate this working tree into to_bzr, including all state.
569
550
Specifically modified files are kept as modified, but
570
551
ignored and unknown files are discarded.
572
553
If you want to make a new line of development, see bzrdir.sprout()
575
If not None, the cloned tree will have its last revision set to
576
revision, and difference between the source trees last revision
556
If not None, the cloned tree will have its last revision set to
557
revision, and and difference between the source trees last revision
577
558
and this one merged in.
561
If not None, a closer copy of a tree which may have some files in
562
common, and which file content should be preferentially copied from.
579
564
# assumes the target bzr dir format is compatible.
580
result = to_bzrdir.create_workingtree()
565
result = self._format.initialize(to_bzrdir)
581
566
self.copy_content_into(result, revision_id)
612
600
__contains__ = has_id
614
602
def get_file_size(self, file_id):
615
"""See Tree.get_file_size"""
617
return os.path.getsize(self.id2abspath(file_id))
619
if e.errno != errno.ENOENT:
603
file_id = osutils.safe_file_id(file_id)
604
return os.path.getsize(self.id2abspath(file_id))
625
607
def get_file_sha1(self, file_id, path=None, stat_value=None):
608
file_id = osutils.safe_file_id(file_id)
627
610
path = self._inventory.id2path(file_id)
628
611
return self._hashcache.get_sha1(path, stat_value)
630
613
def get_file_mtime(self, file_id, path=None):
614
file_id = osutils.safe_file_id(file_id)
632
616
path = self.inventory.id2path(file_id)
633
617
return os.lstat(self.abspath(path)).st_mtime
635
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
636
file_id = self.path2id(path)
637
return self._inventory[file_id].executable
639
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
640
mode = stat_result.st_mode
641
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
643
619
if not supports_executable():
644
620
def is_executable(self, file_id, path=None):
621
file_id = osutils.safe_file_id(file_id)
645
622
return self._inventory[file_id].executable
647
_is_executable_from_path_and_stat = \
648
_is_executable_from_path_and_stat_from_basis
650
624
def is_executable(self, file_id, path=None):
626
file_id = osutils.safe_file_id(file_id)
652
627
path = self.id2path(file_id)
653
628
mode = os.lstat(self.abspath(path)).st_mode
654
629
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
656
_is_executable_from_path_and_stat = \
657
_is_executable_from_path_and_stat_from_stat
659
631
@needs_tree_write_lock
660
632
def _add(self, files, ids, kinds):
661
633
"""See MutableTree._add."""
662
634
# TODO: Re-adding a file that is removed in the working copy
663
635
# should probably put it back with the previous ID.
664
# the read and write working inventory should not occur in this
636
# the read and write working inventory should not occur in this
665
637
# function - they should be part of lock_write and unlock.
638
inv = self.read_working_inventory()
667
639
for f, file_id, kind in zip(files, ids, kinds):
640
assert kind is not None
668
641
if file_id is None:
669
642
inv.add_path(f, kind=kind)
644
file_id = osutils.safe_file_id(file_id)
671
645
inv.add_path(f, kind=kind, file_id=file_id)
672
self._inventory_is_modified = True
646
self._write_inventory(inv)
674
648
@needs_tree_write_lock
675
649
def _gather_kinds(self, files, kinds):
736
710
self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
738
def path_content_summary(self, path, _lstat=os.lstat,
739
_mapper=osutils.file_kind_from_stat_mode):
740
"""See Tree.path_content_summary."""
741
abspath = self.abspath(path)
743
stat_result = _lstat(abspath)
745
if getattr(e, 'errno', None) == errno.ENOENT:
747
return ('missing', None, None, None)
748
# propagate other errors
750
kind = _mapper(stat_result.st_mode)
752
size = stat_result.st_size
753
# try for a stat cache lookup
754
executable = self._is_executable_from_path_and_stat(path, stat_result)
755
return (kind, size, executable, self._sha_from_stat(
757
elif kind == 'directory':
758
# perhaps it looks like a plain directory, but it's really a
760
if self._directory_is_tree_reference(path):
761
kind = 'tree-reference'
762
return kind, None, None, None
763
elif kind == 'symlink':
764
target = osutils.readlink(abspath)
765
return ('symlink', None, None, target)
767
return (kind, None, None, None)
712
@deprecated_method(zero_eleven)
714
def pending_merges(self):
715
"""Return a list of pending merges.
717
These are revisions that have been merged into the working
718
directory but not yet committed.
720
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
721
instead - which is available on all tree objects.
723
return self.get_parent_ids()[1:]
769
725
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
770
726
"""Common ghost checking functionality from set_parent_*.
781
737
def _set_merges_from_parent_ids(self, parent_ids):
782
738
merges = parent_ids[1:]
783
self._transport.put_bytes('pending-merges', '\n'.join(merges),
784
mode=self.bzrdir._get_file_mode())
786
def _filter_parent_ids_by_ancestry(self, revision_ids):
787
"""Check that all merged revisions are proper 'heads'.
789
This will always return the first revision_id, and any merged revisions
792
if len(revision_ids) == 0:
794
graph = self.branch.repository.get_graph()
795
heads = graph.heads(revision_ids)
796
new_revision_ids = revision_ids[:1]
797
for revision_id in revision_ids[1:]:
798
if revision_id in heads and revision_id not in new_revision_ids:
799
new_revision_ids.append(revision_id)
800
if new_revision_ids != revision_ids:
801
trace.mutter('requested to set revision_ids = %s,'
802
' but filtered to %s', revision_ids, new_revision_ids)
803
return new_revision_ids
739
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
805
741
@needs_tree_write_lock
806
742
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
807
743
"""Set the parent ids to revision_ids.
809
745
See also set_parent_trees. This api will try to retrieve the tree data
810
746
for each element of revision_ids from the trees repository. If you have
811
747
tree data already available, it is more efficient to use
815
751
:param revision_ids: The revision_ids to set as the parent ids of this
816
752
working tree. Any of these may be ghosts.
754
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
818
755
self._check_parents_for_ghosts(revision_ids,
819
756
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
820
for revision_id in revision_ids:
821
_mod_revision.check_not_reserved_id(revision_id)
823
revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
825
758
if len(revision_ids) > 0:
826
759
self.set_last_revision(revision_ids[0])
828
self.set_last_revision(_mod_revision.NULL_REVISION)
761
self.set_last_revision(None)
830
763
self._set_merges_from_parent_ids(revision_ids)
832
765
@needs_tree_write_lock
833
766
def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
834
767
"""See MutableTree.set_parent_trees."""
835
parent_ids = [rev for (rev, tree) in parents_list]
836
for revision_id in parent_ids:
837
_mod_revision.check_not_reserved_id(revision_id)
768
parent_ids = [osutils.safe_revision_id(rev) for (rev, tree) in parents_list]
839
770
self._check_parents_for_ghosts(parent_ids,
840
771
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
842
parent_ids = self._filter_parent_ids_by_ancestry(parent_ids)
844
773
if len(parent_ids) == 0:
845
leftmost_parent_id = _mod_revision.NULL_REVISION
774
leftmost_parent_id = None
846
775
leftmost_parent_tree = None
848
777
leftmost_parent_id, leftmost_parent_tree = parents_list[0]
1120
1010
sio = StringIO()
1121
1011
self._serialize(self._inventory, sio)
1123
self._transport.put_file('inventory', sio,
1124
mode=self.bzrdir._get_file_mode())
1013
self._control_files.put('inventory', sio)
1125
1014
self._inventory_is_modified = False
1127
def _kind(self, relpath):
1128
return osutils.file_kind(self.abspath(relpath))
1130
def list_files(self, include_root=False, from_dir=None, recursive=True):
1131
"""List all files as (path, class, kind, id, entry).
1016
def list_files(self, include_root=False):
1017
"""Recursively list all files as (path, class, kind, id, entry).
1133
1019
Lists, but does not descend into unversioned directories.
1134
1021
This does not include files that have been deleted in this
1135
tree. Skips the control directory.
1137
:param include_root: if True, do not return an entry for the root
1138
:param from_dir: start from this directory or None for the root
1139
:param recursive: whether to recurse into subdirectories or not
1024
Skips the control directory.
1141
1026
# list_files is an iterator, so @needs_read_lock doesn't work properly
1142
1027
# with it. So callers should be careful to always read_lock the tree.
1903
1769
def read_basis_inventory(self):
1904
1770
"""Read the cached basis inventory."""
1905
1771
path = self._basis_inventory_name()
1906
return self._transport.get_bytes(path)
1772
return self._control_files.get(path).read()
1908
1774
@needs_read_lock
1909
1775
def read_working_inventory(self):
1910
1776
"""Read the working inventory.
1912
1778
:raises errors.InventoryModified: read_working_inventory will fail
1913
1779
when the current in memory inventory has been modified.
1915
# conceptually this should be an implementation detail of the tree.
1781
# conceptually this should be an implementation detail of the tree.
1916
1782
# XXX: Deprecate this.
1917
1783
# ElementTree does its own conversion from UTF-8, so open in
1919
1785
if self._inventory_is_modified:
1920
1786
raise errors.InventoryModified(self)
1921
result = self._deserialize(self._transport.get('inventory'))
1787
result = self._deserialize(self._control_files.get('inventory'))
1922
1788
self._set_inventory(result, dirty=False)
1925
1791
@needs_tree_write_lock
1926
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1928
"""Remove nominated files from the working inventory.
1930
:files: File paths relative to the basedir.
1931
:keep_files: If true, the files will also be kept.
1932
:force: Delete files and directories, even if they are changed and
1933
even if the directories are not empty.
1792
def remove(self, files, verbose=False, to_file=None):
1793
"""Remove nominated files from the working inventory..
1795
This does not remove their text. This does not run on XXX on what? RBC
1797
TODO: Refuse to remove modified files unless --force is given?
1799
TODO: Do something useful with directories.
1801
TODO: Should this remove the text or not? Tough call; not
1802
removing may be useful and the user can just use use rm, and
1803
is the opposite of add. Removing it is consistent with most
1804
other tools. Maybe an option.
1806
## TODO: Normalize names
1807
## TODO: Remove nested loops; better scalability
1935
1808
if isinstance(files, basestring):
1936
1809
files = [files]
1941
unknown_nested_files=set()
1943
def recurse_directory_to_add_files(directory):
1944
# Recurse directory and add all files
1945
# so we can check if they have changed.
1946
for parent_info, file_infos in\
1947
self.walkdirs(directory):
1948
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1949
# Is it versioned or ignored?
1950
if self.path2id(relpath) or self.is_ignored(relpath):
1951
# Add nested content for deletion.
1952
new_files.add(relpath)
1954
# Files which are not versioned and not ignored
1955
# should be treated as unknown.
1956
unknown_nested_files.add((relpath, None, kind))
1958
for filename in files:
1959
# Get file name into canonical form.
1960
abspath = self.abspath(filename)
1961
filename = self.relpath(abspath)
1962
if len(filename) > 0:
1963
new_files.add(filename)
1964
recurse_directory_to_add_files(filename)
1966
files = list(new_files)
1969
return # nothing to do
1971
# Sort needed to first handle directory content before the directory
1972
files.sort(reverse=True)
1974
# Bail out if we are going to delete files we shouldn't
1975
if not keep_files and not force:
1976
has_changed_files = len(unknown_nested_files) > 0
1977
if not has_changed_files:
1978
for (file_id, path, content_change, versioned, parent_id, name,
1979
kind, executable) in self.iter_changes(self.basis_tree(),
1980
include_unchanged=True, require_versioned=False,
1981
want_unversioned=True, specific_files=files):
1982
if versioned == (False, False):
1983
# The record is unknown ...
1984
if not self.is_ignored(path[1]):
1985
# ... but not ignored
1986
has_changed_files = True
1988
elif content_change and (kind[1] is not None):
1989
# Versioned and changed, but not deleted
1990
has_changed_files = True
1993
if has_changed_files:
1994
# Make delta show ALL applicable changes in error message.
1995
tree_delta = self.changes_from(self.basis_tree(),
1996
require_versioned=False, want_unversioned=True,
1997
specific_files=files)
1998
for unknown_file in unknown_nested_files:
1999
if unknown_file not in tree_delta.unversioned:
2000
tree_delta.unversioned.extend((unknown_file,))
2001
raise errors.BzrRemoveChangedFilesError(tree_delta)
2003
# Build inv_delta and delete files where applicable,
2004
# do this before any modifications to inventory.
1811
inv = self.inventory
1813
# do this before any modifications
2005
1814
for f in files:
2006
fid = self.path2id(f)
1815
fid = inv.path2id(f)
2009
message = "%s is not versioned." % (f,)
1817
note("%s is not versioned."%f)
2012
# having removed it, it must be either ignored or unknown
1820
# having remove it, it must be either ignored or unknown
2013
1821
if self.is_ignored(f):
2014
1822
new_status = 'I'
2016
1824
new_status = '?'
2017
textui.show_status(new_status, self.kind(fid), f,
1825
textui.show_status(new_status, inv[fid].kind, f,
2018
1826
to_file=to_file)
2020
inv_delta.append((f, None, fid, None))
2021
message = "removed %s" % (f,)
2024
abs_path = self.abspath(f)
2025
if osutils.lexists(abs_path):
2026
if (osutils.isdir(abs_path) and
2027
len(os.listdir(abs_path)) > 0):
2029
osutils.rmtree(abs_path)
2031
message = "%s is not an empty directory "\
2032
"and won't be deleted." % (f,)
2034
osutils.delete_any(abs_path)
2035
message = "deleted %s" % (f,)
2036
elif message is not None:
2037
# Only care if we haven't done anything yet.
2038
message = "%s does not exist." % (f,)
2040
# Print only one message (if any) per file.
2041
if message is not None:
2043
self.apply_inventory_delta(inv_delta)
1829
self._write_inventory(inv)
2045
1831
@needs_tree_write_lock
2046
def revert(self, filenames=None, old_tree=None, backups=True,
1832
def revert(self, filenames, old_tree=None, backups=True,
2047
1833
pb=DummyProgress(), report_changes=False):
2048
1834
from bzrlib.conflicts import resolve
2051
symbol_versioning.warn('Using [] to revert all files is deprecated'
2052
' as of bzr 0.91. Please use None (the default) instead.',
2053
DeprecationWarning, stacklevel=2)
2054
1835
if old_tree is None:
2055
basis_tree = self.basis_tree()
2056
basis_tree.lock_read()
2057
old_tree = basis_tree
1836
old_tree = self.basis_tree()
1837
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1839
if not len(filenames):
1840
self.set_parent_ids(self.get_parent_ids()[:1])
2061
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
2063
if filenames is None and len(self.get_parent_ids()) > 1:
2065
last_revision = self.last_revision()
2066
if last_revision != _mod_revision.NULL_REVISION:
2067
if basis_tree is None:
2068
basis_tree = self.basis_tree()
2069
basis_tree.lock_read()
2070
parent_trees.append((last_revision, basis_tree))
2071
self.set_parent_trees(parent_trees)
2074
resolve(self, filenames, ignore_misses=True, recursive=True)
2076
if basis_tree is not None:
1843
resolve(self, filenames, ignore_misses=True)
2078
1844
return conflicts
2080
1846
def revision_tree(self, revision_id):
2545
2290
self.set_conflicts(un_resolved)
2546
2291
return un_resolved, resolved
2549
def _check(self, references):
2550
"""Check the tree for consistency.
2552
:param references: A dict with keys matching the items returned by
2553
self._get_check_refs(), and values from looking those keys up in
2556
tree_basis = self.basis_tree()
2557
tree_basis.lock_read()
2559
repo_basis = references[('trees', self.last_revision())]
2560
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2561
raise errors.BzrCheckError(
2562
"Mismatched basis inventory content.")
2567
def _validate(self):
2568
"""Validate internal structures.
2570
This is meant mostly for the test suite. To give it a chance to detect
2571
corruption after actions have occurred. The default implementation is a
2574
:return: None. An exception should be raised if there is an error.
2579
def _get_rules_searcher(self, default_searcher):
2580
"""See Tree._get_rules_searcher."""
2581
if self._rules_searcher is None:
2582
self._rules_searcher = super(WorkingTree,
2583
self)._get_rules_searcher(default_searcher)
2584
return self._rules_searcher
2586
def get_shelf_manager(self):
2587
"""Return the ShelfManager for this WorkingTree."""
2588
from bzrlib.shelf import ShelfManager
2589
return ShelfManager(self, self._transport)
2592
2294
class WorkingTree2(WorkingTree):
2593
2295
"""This is the Format 2 working tree.
2595
This was the first weave based working tree.
2297
This was the first weave based working tree.
2596
2298
- uses os locks for locking.
2597
2299
- uses the branch last-revision.
2600
def __init__(self, *args, **kwargs):
2601
super(WorkingTree2, self).__init__(*args, **kwargs)
2602
# WorkingTree2 has more of a constraint that self._inventory must
2603
# exist. Because this is an older format, we don't mind the overhead
2604
# caused by the extra computation here.
2606
# Newer WorkingTree's should only have self._inventory set when they
2608
if self._inventory is None:
2609
self.read_working_inventory()
2611
def _get_check_refs(self):
2612
"""Return the references needed to perform a check of this tree."""
2613
return [('trees', self.last_revision())]
2615
2302
def lock_tree_write(self):
2616
2303
"""See WorkingTree.lock_tree_write().
2816
2496
def unregister_format(klass, format):
2497
assert klass._formats[format.get_format_string()] is format
2817
2498
del klass._formats[format.get_format_string()]
2820
2502
class WorkingTreeFormat2(WorkingTreeFormat):
2821
"""The second working tree format.
2503
"""The second working tree format.
2823
2505
This format modified the hash cache from the format 1 hash cache.
2826
upgrade_recommended = True
2828
2508
def get_format_description(self):
2829
2509
"""See WorkingTreeFormat.get_format_description()."""
2830
2510
return "Working tree format 2"
2832
def _stub_initialize_on_transport(self, transport, file_mode):
2833
"""Workaround: create control files for a remote working tree.
2512
def stub_initialize_remote(self, control_files):
2513
"""As a special workaround create critical control files for a remote working tree
2835
2515
This ensures that it can later be updated and dealt with locally,
2836
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2516
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2837
2517
no working tree. (See bug #43064).
2839
2519
sio = StringIO()
2840
inv = inventory.Inventory()
2841
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2521
xml5.serializer_v5.write_inventory(inv, sio)
2843
transport.put_file('inventory', sio, file_mode)
2844
transport.put_bytes('pending-merges', '', file_mode)
2846
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2847
accelerator_tree=None, hardlink=False):
2523
control_files.put('inventory', sio)
2525
control_files.put_bytes('pending-merges', '')
2528
def initialize(self, a_bzrdir, revision_id=None):
2848
2529
"""See WorkingTreeFormat.initialize()."""
2849
2530
if not isinstance(a_bzrdir.transport, LocalTransport):
2850
2531
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2851
if from_branch is not None:
2852
branch = from_branch
2854
branch = a_bzrdir.open_branch()
2855
if revision_id is None:
2856
revision_id = _mod_revision.ensure_null(branch.last_revision())
2859
branch.generate_revision_history(revision_id)
2862
inv = inventory.Inventory()
2532
branch = a_bzrdir.open_branch()
2533
if revision_id is not None:
2534
revision_id = osutils.safe_revision_id(revision_id)
2537
revision_history = branch.revision_history()
2539
position = revision_history.index(revision_id)
2541
raise errors.NoSuchRevision(branch, revision_id)
2542
branch.set_revision_history(revision_history[:position + 1])
2545
revision = branch.last_revision()
2863
2547
wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
2866
2550
_internal=True,
2868
2552
_bzrdir=a_bzrdir)
2869
basis_tree = branch.repository.revision_tree(revision_id)
2553
basis_tree = branch.repository.revision_tree(revision)
2870
2554
if basis_tree.inventory.root is not None:
2871
wt.set_root_id(basis_tree.get_root_id())
2555
wt.set_root_id(basis_tree.inventory.root.file_id)
2872
2556
# set the parent list and cache the basis tree.
2873
if _mod_revision.is_null(revision_id):
2876
parent_trees = [(revision_id, basis_tree)]
2877
wt.set_parent_trees(parent_trees)
2557
wt.set_parent_trees([(revision, basis_tree)])
2878
2558
transform.build_tree(basis_tree, wt)
3030
2698
return self.get_format_string()
3033
__default_format = WorkingTreeFormat6()
2701
__default_format = WorkingTreeFormat4()
3034
2702
WorkingTreeFormat.register_format(__default_format)
3035
WorkingTreeFormat.register_format(WorkingTreeFormat5())
3036
WorkingTreeFormat.register_format(WorkingTreeFormat4())
3037
2703
WorkingTreeFormat.register_format(WorkingTreeFormat3())
3038
2704
WorkingTreeFormat.set_default_format(__default_format)
3039
2705
# formats which have no format string are not discoverable
3040
2706
# and not independently creatable, so are not registered.
3041
2707
_legacy_formats = [WorkingTreeFormat2(),
2711
class WorkingTreeTestProviderAdapter(object):
2712
"""A tool to generate a suite testing multiple workingtree formats at once.
2714
This is done by copying the test once for each transport and injecting
2715
the transport_server, transport_readonly_server, and workingtree_format
2716
classes into each copy. Each copy is also given a new id() to make it
2720
def __init__(self, transport_server, transport_readonly_server, formats):
2721
self._transport_server = transport_server
2722
self._transport_readonly_server = transport_readonly_server
2723
self._formats = formats
2725
def _clone_test(self, test, bzrdir_format, workingtree_format, variation):
2726
"""Clone test for adaption."""
2727
new_test = deepcopy(test)
2728
new_test.transport_server = self._transport_server
2729
new_test.transport_readonly_server = self._transport_readonly_server
2730
new_test.bzrdir_format = bzrdir_format
2731
new_test.workingtree_format = workingtree_format
2732
def make_new_test_id():
2733
new_id = "%s(%s)" % (test.id(), variation)
2734
return lambda: new_id
2735
new_test.id = make_new_test_id()
2738
def adapt(self, test):
2739
from bzrlib.tests import TestSuite
2740
result = TestSuite()
2741
for workingtree_format, bzrdir_format in self._formats:
2742
new_test = self._clone_test(
2745
workingtree_format, workingtree_format.__class__.__name__)
2746
result.addTest(new_test)