13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""WorkingTree object and friends.
19
19
A WorkingTree represents the editable working copy of a branch.
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files. The WorkingTree has an inventory
22
which is updated by these operations. A commit produces a
20
Operations which represent the WorkingTree are also done here,
21
such as renaming or adding files. The WorkingTree has an inventory
22
which is updated by these operations. A commit produces a
23
23
new revision based on the workingtree and its inventory.
25
25
At the moment every WorkingTree has its own branch. Remote
59
57
conflicts as _mod_conflicts,
67
66
revision as _mod_revision,
79
75
import bzrlib.branch
80
76
from bzrlib.transport import get_transport
82
from bzrlib.workingtree_4 import WorkingTreeFormat4
77
from bzrlib.workingtree_4 import (
85
84
from bzrlib import symbol_versioning
86
85
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
88
from bzrlib.lockable_files import LockableFiles, TransportLock
86
from bzrlib.lockable_files import LockableFiles
89
87
from bzrlib.lockdir import LockDir
90
88
import bzrlib.mutabletree
91
89
from bzrlib.mutabletree import needs_tree_write_lock
92
90
from bzrlib import osutils
93
91
from bzrlib.osutils import (
103
99
supports_executable,
101
from bzrlib.filters import filtered_input_file
105
102
from bzrlib.trace import mutter, note
106
103
from bzrlib.transport.local import LocalTransport
107
from bzrlib.progress import DummyProgress, ProgressPhase
108
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
104
from bzrlib.progress import ProgressPhase
105
from bzrlib.revision import CURRENT_REVISION
109
106
from bzrlib.rio import RioReader, rio_file, Stanza
110
from bzrlib.symbol_versioning import (deprecated_passed,
113
DEPRECATED_PARAMETER,
107
from bzrlib.symbol_versioning import (
109
DEPRECATED_PARAMETER,
120
113
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
114
# TODO: Modifying the conflict objects or their type is currently nearly
115
# impossible as there is no clear relationship between the working tree format
116
# and the conflict list file format.
121
117
CONFLICT_HEADER_1 = "BZR conflict list format 1"
123
119
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
126
@deprecated_function(zero_thirteen)
127
def gen_file_id(name):
128
"""Return new file id for the basename 'name'.
130
Use bzrlib.generate_ids.gen_file_id() instead
132
return generate_ids.gen_file_id(name)
135
@deprecated_function(zero_thirteen)
137
"""Return a new tree-root file id.
139
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
141
return generate_ids.gen_root_id()
144
122
class TreeEntry(object):
145
123
"""An entry that implements the minimum interface used by commands.
147
This needs further inspection, it may be better to have
125
This needs further inspection, it may be better to have
148
126
InventoryEntries without ids - though that seems wrong. For now,
149
127
this is a parallel hierarchy to InventoryEntry, and needs to become
150
128
one of several things: decorates to that hierarchy, children of, or
290
283
self._control_files.break_lock()
291
284
self.branch.break_lock()
286
def _get_check_refs(self):
287
"""Return the references needed to perform a check of this tree.
289
The default implementation returns no refs, and is only suitable for
290
trees that have no local caching and can commit on ghosts at any time.
292
:seealso: bzrlib.check for details about check_refs.
293
296
def requires_rich_root(self):
294
297
return self._format.requires_rich_root
296
299
def supports_tree_reference(self):
302
def supports_content_filtering(self):
303
return self._format.supports_content_filtering()
305
def supports_views(self):
306
return self.views.supports_views()
299
308
def _set_inventory(self, inv, dirty):
300
309
"""Set the internal cached inventory.
390
420
# at this point ?
392
422
return self.branch.repository.revision_tree(revision_id)
393
except errors.RevisionNotPresent:
423
except (errors.RevisionNotPresent, errors.NoSuchRevision):
394
424
# the basis tree *may* be a ghost or a low level error may have
395
# occured. If the revision is present, its a problem, if its not
425
# occurred. If the revision is present, its a problem, if its not
397
427
if self.branch.repository.has_revision(revision_id):
399
429
# the basis tree is a ghost so return an empty tree.
400
return self.branch.repository.revision_tree(None)
430
return self.branch.repository.revision_tree(
431
_mod_revision.NULL_REVISION)
402
433
def _cleanup(self):
403
434
self._flush_ignore_list_cache()
406
@deprecated_method(zero_eight)
407
def create(branch, directory):
408
"""Create a workingtree for branch at directory.
410
If existing_directory already exists it must have a .bzr directory.
411
If it does not exist, it will be created.
413
This returns a new WorkingTree object for the new checkout.
415
TODO FIXME RBC 20060124 when we have checkout formats in place this
416
should accept an optional revisionid to checkout [and reject this if
417
checking out into the same dir as a pre-checkout-aware branch format.]
419
XXX: When BzrDir is present, these should be created through that
422
warnings.warn('delete WorkingTree.create', stacklevel=3)
423
transport = get_transport(directory)
424
if branch.bzrdir.root_transport.base == transport.base:
426
return branch.bzrdir.create_workingtree()
427
# different directory,
428
# create a branch reference
429
# and now a working tree.
430
raise NotImplementedError
433
@deprecated_method(zero_eight)
434
def create_standalone(directory):
435
"""Create a checkout and a branch and a repo at directory.
437
Directory must exist and be empty.
439
please use BzrDir.create_standalone_workingtree
441
return bzrdir.BzrDir.create_standalone_workingtree(directory)
443
436
def relpath(self, path):
444
437
"""Return the local path portion from a given path.
446
The path may be absolute or relative. If its a relative path it is
439
The path may be absolute or relative. If its a relative path it is
447
440
interpreted relative to the python current working directory.
449
442
return osutils.relpath(self.basedir, path)
451
444
def has_filename(self, filename):
452
445
return osutils.lexists(self.abspath(filename))
454
def get_file(self, file_id, path=None):
447
def get_file(self, file_id, path=None, filtered=True):
448
return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
450
def get_file_with_stat(self, file_id, path=None, filtered=True,
452
"""See Tree.get_file_with_stat."""
456
454
path = self.id2path(file_id)
457
return self.get_file_byname(path)
459
def get_file_text(self, file_id):
460
return self.get_file(file_id).read()
462
def get_file_byname(self, filename):
463
return file(self.abspath(filename), 'rb')
455
file_obj = self.get_file_byname(path, filtered=False)
456
stat_value = _fstat(file_obj.fileno())
457
if filtered and self.supports_content_filtering():
458
filters = self._content_filter_stack(path)
459
file_obj = filtered_input_file(file_obj, filters)
460
return (file_obj, stat_value)
462
def get_file_text(self, file_id, path=None, filtered=True):
463
return self.get_file(file_id, path=path, filtered=filtered).read()
465
def get_file_byname(self, filename, filtered=True):
466
path = self.abspath(filename)
468
if filtered and self.supports_content_filtering():
469
filters = self._content_filter_stack(filename)
470
return filtered_input_file(f, filters)
474
def get_file_lines(self, file_id, path=None, filtered=True):
475
"""See Tree.get_file_lines()"""
476
file = self.get_file(file_id, path, filtered=filtered)
478
return file.readlines()
466
483
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
473
490
incorrectly attributed to CURRENT_REVISION (but after committing, the
474
491
attribution will be correct).
476
basis = self.basis_tree()
479
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
480
require_versioned=True).next()
481
changed_content, kind = changes[2], changes[6]
482
if not changed_content:
483
return basis.annotate_iter(file_id)
487
if kind[0] != 'file':
490
old_lines = list(basis.annotate_iter(file_id))
492
for tree in self.branch.repository.revision_trees(
493
self.get_parent_ids()[1:]):
494
if file_id not in tree:
496
old.append(list(tree.annotate_iter(file_id)))
497
return annotate.reannotate(old, self.get_file(file_id).readlines(),
493
maybe_file_parent_keys = []
494
for parent_id in self.get_parent_ids():
496
parent_tree = self.revision_tree(parent_id)
497
except errors.NoSuchRevisionInTree:
498
parent_tree = self.branch.repository.revision_tree(parent_id)
499
parent_tree.lock_read()
501
if file_id not in parent_tree:
503
ie = parent_tree.inventory[file_id]
504
if ie.kind != 'file':
505
# Note: this is slightly unnecessary, because symlinks and
506
# directories have a "text" which is the empty text, and we
507
# know that won't mess up annotations. But it seems cleaner
509
parent_text_key = (file_id, ie.revision)
510
if parent_text_key not in maybe_file_parent_keys:
511
maybe_file_parent_keys.append(parent_text_key)
514
graph = _mod_graph.Graph(self.branch.repository.texts)
515
heads = graph.heads(maybe_file_parent_keys)
516
file_parent_keys = []
517
for key in maybe_file_parent_keys:
519
file_parent_keys.append(key)
521
# Now we have the parents of this content
522
annotator = self.branch.repository.texts.get_annotator()
523
text = self.get_file(file_id).read()
524
this_key =(file_id, default_revision)
525
annotator.add_special_text(this_key, file_parent_keys, text)
526
annotations = [(key[-1], line)
527
for key, line in annotator.annotate_flat(this_key)]
502
530
def _get_ancestors(self, default_revision):
503
531
ancestors = set([default_revision])
540
568
def clone(self, to_bzrdir, revision_id=None):
541
569
"""Duplicate this working tree into to_bzr, including all state.
543
571
Specifically modified files are kept as modified, but
544
572
ignored and unknown files are discarded.
546
574
If you want to make a new line of development, see bzrdir.sprout()
549
If not None, the cloned tree will have its last revision set to
550
revision, and and difference between the source trees last revision
577
If not None, the cloned tree will have its last revision set to
578
revision, and difference between the source trees last revision
551
579
and this one merged in.
553
581
# assumes the target bzr dir format is compatible.
554
result = self._format.initialize(to_bzrdir)
582
result = to_bzrdir.create_workingtree()
555
583
self.copy_content_into(result, revision_id)
729
765
kind = 'tree-reference'
730
766
return kind, None, None, None
731
767
elif kind == 'symlink':
732
return ('symlink', None, None, os.readlink(abspath))
768
target = osutils.readlink(abspath)
769
return ('symlink', None, None, target)
734
771
return (kind, None, None, None)
736
@deprecated_method(zero_eleven)
738
def pending_merges(self):
739
"""Return a list of pending merges.
741
These are revisions that have been merged into the working
742
directory but not yet committed.
744
As of 0.11 this is deprecated. Please see WorkingTree.get_parent_ids()
745
instead - which is available on all tree objects.
747
return self.get_parent_ids()[1:]
773
def _file_content_summary(self, path, stat_result):
774
size = stat_result.st_size
775
executable = self._is_executable_from_path_and_stat(path, stat_result)
776
# try for a stat cache lookup
777
return ('file', size, executable, self._sha_from_stat(
749
780
def _check_parents_for_ghosts(self, revision_ids, allow_leftmost_as_ghost):
750
781
"""Common ghost checking functionality from set_parent_*.
761
792
def _set_merges_from_parent_ids(self, parent_ids):
762
793
merges = parent_ids[1:]
763
self._control_files.put_bytes('pending-merges', '\n'.join(merges))
794
self._transport.put_bytes('pending-merges', '\n'.join(merges),
795
mode=self.bzrdir._get_file_mode())
797
def _filter_parent_ids_by_ancestry(self, revision_ids):
798
"""Check that all merged revisions are proper 'heads'.
800
This will always return the first revision_id, and any merged revisions
803
if len(revision_ids) == 0:
805
graph = self.branch.repository.get_graph()
806
heads = graph.heads(revision_ids)
807
new_revision_ids = revision_ids[:1]
808
for revision_id in revision_ids[1:]:
809
if revision_id in heads and revision_id not in new_revision_ids:
810
new_revision_ids.append(revision_id)
811
if new_revision_ids != revision_ids:
812
trace.mutter('requested to set revision_ids = %s,'
813
' but filtered to %s', revision_ids, new_revision_ids)
814
return new_revision_ids
765
816
@needs_tree_write_lock
766
817
def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
767
818
"""Set the parent ids to revision_ids.
769
820
See also set_parent_trees. This api will try to retrieve the tree data
770
821
for each element of revision_ids from the trees repository. If you have
771
822
tree data already available, it is more efficient to use
856
912
branch.last_revision().
858
914
from bzrlib.merge import Merger, Merge3Merger
859
pb = bzrlib.ui.ui_factory.nested_progress_bar()
861
merger = Merger(self.branch, this_tree=self, pb=pb)
862
merger.pp = ProgressPhase("Merge phase", 5, pb)
863
merger.pp.next_phase()
864
# check that there are no
866
merger.check_basis(check_clean=True, require_commits=False)
867
if to_revision is None:
868
to_revision = _mod_revision.ensure_null(branch.last_revision())
869
merger.other_rev_id = to_revision
870
if _mod_revision.is_null(merger.other_rev_id):
871
raise errors.NoCommits(branch)
872
self.branch.fetch(branch, last_revision=merger.other_rev_id)
873
merger.other_basis = merger.other_rev_id
874
merger.other_tree = self.branch.repository.revision_tree(
876
merger.other_branch = branch
877
merger.pp.next_phase()
878
if from_revision is None:
881
merger.set_base_revision(from_revision, branch)
882
if merger.base_rev_id == merger.other_rev_id:
883
raise errors.PointlessMerge
884
merger.backup_files = False
885
if merge_type is None:
886
merger.merge_type = Merge3Merger
888
merger.merge_type = merge_type
889
merger.set_interesting_files(None)
890
merger.show_base = False
891
merger.reprocess = False
892
conflicts = merger.do_merge()
915
merger = Merger(self.branch, this_tree=self)
916
# check that there are no local alterations
917
if not force and self.has_changes():
918
raise errors.UncommittedChanges(self)
919
if to_revision is None:
920
to_revision = _mod_revision.ensure_null(branch.last_revision())
921
merger.other_rev_id = to_revision
922
if _mod_revision.is_null(merger.other_rev_id):
923
raise errors.NoCommits(branch)
924
self.branch.fetch(branch, last_revision=merger.other_rev_id)
925
merger.other_basis = merger.other_rev_id
926
merger.other_tree = self.branch.repository.revision_tree(
928
merger.other_branch = branch
929
if from_revision is None:
932
merger.set_base_revision(from_revision, branch)
933
if merger.base_rev_id == merger.other_rev_id:
934
raise errors.PointlessMerge
935
merger.backup_files = False
936
if merge_type is None:
937
merger.merge_type = Merge3Merger
939
merger.merge_type = merge_type
940
merger.set_interesting_files(None)
941
merger.show_base = False
942
merger.reprocess = False
943
conflicts = merger.do_merge()
899
948
def merge_modified(self):
900
949
"""Return a dictionary of files modified by a merge.
902
The list is initialized by WorkingTree.set_merge_modified, which is
951
The list is initialized by WorkingTree.set_merge_modified, which is
903
952
typically called after we make some automatic updates to the tree
904
953
because of a merge.
907
956
still in the working inventory and have that text hash.
910
hashfile = self._control_files.get('merge-hashes')
959
hashfile = self._transport.get('merge-hashes')
911
960
except errors.NoSuchFile:
915
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
965
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
966
raise errors.MergeModifiedFormatError()
967
except StopIteration:
916
968
raise errors.MergeModifiedFormatError()
917
except StopIteration:
918
raise errors.MergeModifiedFormatError()
919
for s in RioReader(hashfile):
920
# RioReader reads in Unicode, so convert file_ids back to utf8
921
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
922
if file_id not in self.inventory:
924
text_hash = s.get("hash")
925
if text_hash == self.get_file_sha1(file_id):
926
merge_hashes[file_id] = text_hash
969
for s in RioReader(hashfile):
970
# RioReader reads in Unicode, so convert file_ids back to utf8
971
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
972
if file_id not in self.inventory:
974
text_hash = s.get("hash")
975
if text_hash == self.get_file_sha1(file_id):
976
merge_hashes[file_id] = text_hash
929
981
@needs_write_lock
930
982
def mkdir(self, path, file_id=None):
980
1034
other_tree.unlock()
981
1035
other_tree.bzrdir.retire_bzrdir()
983
def _directory_is_tree_reference(self, relpath):
984
# as a special case, if a directory contains control files then
1037
def _setup_directory_is_tree_reference(self):
1038
if self._branch.repository._format.supports_tree_reference:
1039
self._directory_is_tree_reference = \
1040
self._directory_may_be_tree_reference
1042
self._directory_is_tree_reference = \
1043
self._directory_is_never_tree_reference
1045
def _directory_is_never_tree_reference(self, relpath):
1048
def _directory_may_be_tree_reference(self, relpath):
1049
# as a special case, if a directory contains control files then
985
1050
# it's a tree reference, except that the root of the tree is not
986
1051
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
987
1052
# TODO: We could ask all the control formats whether they
1009
1074
transport = transport.clone(name)
1010
1075
transport.ensure_base()
1011
1076
return transport
1013
1078
sub_path = self.id2path(file_id)
1014
1079
branch_transport = mkdirs(sub_path)
1015
1080
if format is None:
1016
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
1081
format = self.bzrdir.cloning_metadir()
1017
1082
branch_transport.ensure_base()
1018
1083
branch_bzrdir = format.initialize_on_transport(branch_transport)
1020
1085
repo = branch_bzrdir.find_repository()
1021
1086
except errors.NoRepositoryPresent:
1022
1087
repo = branch_bzrdir.create_repository()
1023
assert repo.supports_rich_root()
1025
if not repo.supports_rich_root():
1026
raise errors.RootNotRich()
1088
if not repo.supports_rich_root():
1089
raise errors.RootNotRich()
1027
1090
new_branch = branch_bzrdir.create_branch()
1028
1091
new_branch.pull(self.branch)
1029
1092
for parent_id in self.get_parent_ids():
1061
1124
sio = StringIO()
1062
1125
self._serialize(self._inventory, sio)
1064
self._control_files.put('inventory', sio)
1127
self._transport.put_file('inventory', sio,
1128
mode=self.bzrdir._get_file_mode())
1065
1129
self._inventory_is_modified = False
1067
1131
def _kind(self, relpath):
1068
1132
return osutils.file_kind(self.abspath(relpath))
1070
def list_files(self, include_root=False):
1071
"""Recursively list all files as (path, class, kind, id, entry).
1134
def list_files(self, include_root=False, from_dir=None, recursive=True):
1135
"""List all files as (path, class, kind, id, entry).
1073
1137
Lists, but does not descend into unversioned directories.
1075
1138
This does not include files that have been deleted in this
1139
tree. Skips the control directory.
1078
Skips the control directory.
1141
:param include_root: if True, do not return an entry for the root
1142
:param from_dir: start from this directory or None for the root
1143
:param recursive: whether to recurse into subdirectories or not
1080
1145
# list_files is an iterator, so @needs_read_lock doesn't work properly
1081
1146
# with it. So callers should be careful to always read_lock the tree.
1096
1161
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1098
1163
# directory file_id, relative path, absolute path, reverse sorted children
1099
children = os.listdir(self.basedir)
1164
if from_dir is not None:
1165
from_dir_id = inv.path2id(from_dir)
1166
if from_dir_id is None:
1167
# Directory not versioned
1169
from_dir_abspath = pathjoin(self.basedir, from_dir)
1171
from_dir_id = inv.root.file_id
1172
from_dir_abspath = self.basedir
1173
children = os.listdir(from_dir_abspath)
1100
1174
children.sort()
1101
# jam 20060527 The kernel sized tree seems equivalent whether we
1175
# jam 20060527 The kernel sized tree seems equivalent whether we
1102
1176
# use a deque and popleft to keep them sorted, or if we use a plain
1103
1177
# list and just reverse() them.
1104
1178
children = collections.deque(children)
1105
stack = [(inv.root.file_id, u'', self.basedir, children)]
1179
stack = [(from_dir_id, u'', from_dir_abspath, children)]
1107
1181
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1162
1236
except KeyError:
1163
1237
yield fp[1:], c, fk, None, TreeEntry()
1166
1240
if fk != 'directory':
1169
# But do this child first
1170
new_children = os.listdir(fap)
1172
new_children = collections.deque(new_children)
1173
stack.append((f_ie.file_id, fp, fap, new_children))
1174
# Break out of inner loop,
1175
# so that we start outer loop with child
1243
# But do this child first if recursing down
1245
new_children = os.listdir(fap)
1247
new_children = collections.deque(new_children)
1248
stack.append((f_ie.file_id, fp, fap, new_children))
1249
# Break out of inner loop,
1250
# so that we start outer loop with child
1178
1253
# if we finished all children, pop it off the stack
1410
1490
from_tail = splitpath(from_rel)[-1]
1411
1491
from_id = inv.path2id(from_rel)
1412
1492
if from_id is None:
1413
raise errors.BzrRenameFailedError(from_rel,to_rel,
1414
errors.NotVersionedError(path=str(from_rel)))
1415
from_entry = inv[from_id]
1493
# if file is missing in the inventory maybe it's in the basis_tree
1494
basis_tree = self.branch.basis_tree()
1495
from_id = basis_tree.path2id(from_rel)
1497
raise errors.BzrRenameFailedError(from_rel,to_rel,
1498
errors.NotVersionedError(path=str(from_rel)))
1499
# put entry back in the inventory so we can rename it
1500
from_entry = basis_tree.inventory[from_id].copy()
1503
from_entry = inv[from_id]
1416
1504
from_parent_id = from_entry.parent_id
1417
1505
to_dir, to_tail = os.path.split(to_rel)
1418
1506
to_dir_id = inv.path2id(to_dir)
1480
1568
:raises: NoSuchId if any fileid is not currently versioned.
1482
1570
for file_id in file_ids:
1571
if file_id not in self._inventory:
1572
raise errors.NoSuchId(self, file_id)
1573
for file_id in file_ids:
1483
1574
if self._inventory.has_id(file_id):
1484
1575
self._inventory.remove_recursive_id(file_id)
1486
raise errors.NoSuchId(self, file_id)
1487
1576
if len(file_ids):
1488
# in the future this should just set a dirty bit to wait for the
1577
# in the future this should just set a dirty bit to wait for the
1489
1578
# final unlock. However, until all methods of workingtree start
1490
# with the current in -memory inventory rather than triggering
1579
# with the current in -memory inventory rather than triggering
1491
1580
# a read, it is more complex - we need to teach read_inventory
1492
1581
# to know when to read, and when to not read first... and possibly
1493
1582
# to save first when the in memory one may be corrupted.
1494
1583
# so for now, we just only write it if it is indeed dirty.
1495
1584
# - RBC 20060907
1496
1585
self._write_inventory(self._inventory)
1498
@deprecated_method(zero_eight)
1499
def iter_conflicts(self):
1500
"""List all files in the tree that have text or content conflicts.
1501
DEPRECATED. Use conflicts instead."""
1502
return self._iter_conflicts()
1504
1587
def _iter_conflicts(self):
1505
1588
conflicted = set()
1515
1598
@needs_write_lock
1516
1599
def pull(self, source, overwrite=False, stop_revision=None,
1517
change_reporter=None, possible_transports=None):
1518
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1600
change_reporter=None, possible_transports=None, local=False):
1519
1601
source.lock_read()
1521
pp = ProgressPhase("Pull phase", 2, top_pb)
1523
1603
old_revision_info = self.branch.last_revision_info()
1524
1604
basis_tree = self.basis_tree()
1525
1605
count = self.branch.pull(source, overwrite, stop_revision,
1526
possible_transports=possible_transports)
1606
possible_transports=possible_transports,
1527
1608
new_revision_info = self.branch.last_revision_info()
1528
1609
if new_revision_info != old_revision_info:
1530
1610
repository = self.branch.repository
1531
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1532
1611
basis_tree.lock_read()
1534
1613
new_basis_tree = self.branch.basis_tree()
1537
1616
new_basis_tree,
1539
1618
this_tree=self,
1541
1620
change_reporter=change_reporter)
1542
if (basis_tree.inventory.root is None and
1543
new_basis_tree.inventory.root is not None):
1544
self.set_root_id(new_basis_tree.inventory.root.file_id)
1621
basis_root_id = basis_tree.get_root_id()
1622
new_root_id = new_basis_tree.get_root_id()
1623
if basis_root_id != new_root_id:
1624
self.set_root_id(new_root_id)
1547
1626
basis_tree.unlock()
1548
1627
# TODO - dedup parents list with things merged by pull ?
1549
1628
# reuse the revisiontree we merged against to set the new
1551
1630
parent_trees = [(self.branch.last_revision(), new_basis_tree)]
1552
# we have to pull the merge trees out again, because
1553
# merge_inner has set the ids. - this corner is not yet
1631
# we have to pull the merge trees out again, because
1632
# merge_inner has set the ids. - this corner is not yet
1554
1633
# layered well enough to prevent double handling.
1555
1634
# XXX TODO: Fix the double handling: telling the tree about
1556
1635
# the already known parent data is wasteful.
1646
1731
r"""Check whether the filename matches an ignore pattern.
1648
1733
Patterns containing '/' or '\' need to match the whole path;
1649
others match against only the last component.
1734
others match against only the last component. Patterns starting
1735
with '!' are ignore exceptions. Exceptions take precedence
1736
over regular patterns and cause the filename to not be ignored.
1651
1738
If the file is ignored, returns the pattern which caused it to
1652
1739
be ignored, otherwise None. So this can simply be used as a
1653
1740
boolean if desired."""
1654
1741
if getattr(self, '_ignoreglobster', None) is None:
1655
self._ignoreglobster = globbing.Globster(self.get_ignore_list())
1742
self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
1656
1743
return self._ignoreglobster.match(filename)
1658
1745
def kind(self, file_id):
1659
1746
return file_kind(self.id2abspath(file_id))
1748
def stored_kind(self, file_id):
1749
"""See Tree.stored_kind"""
1750
return self.inventory[file_id].kind
1661
1752
def _comparison_data(self, entry, path):
1662
1753
abspath = self.abspath(path)
1788
1883
# as commit already has that ready-to-use [while the format is the
1789
1884
# same, that is].
1791
# this double handles the inventory - unpack and repack -
1886
# this double handles the inventory - unpack and repack -
1792
1887
# but is easier to understand. We can/should put a conditional
1793
1888
# in here based on whether the inventory is in the latest format
1794
1889
# - perhaps we should repack all inventories on a repository
1796
1891
# the fast path is to copy the raw xml from the repository. If the
1797
# xml contains 'revision_id="', then we assume the right
1892
# xml contains 'revision_id="', then we assume the right
1798
1893
# revision_id is set. We must check for this full string, because a
1799
1894
# root node id can legitimately look like 'revision_id' but cannot
1800
1895
# contain a '"'.
1801
xml = self.branch.repository.get_inventory_xml(new_revision)
1896
xml = self.branch.repository._get_inventory_xml(new_revision)
1802
1897
firstline = xml.split('\n', 1)[0]
1803
if (not 'revision_id="' in firstline or
1898
if (not 'revision_id="' in firstline or
1804
1899
'format="7"' not in firstline):
1805
inv = self.branch.repository.deserialise_inventory(
1900
inv = self.branch.repository._serializer.read_inventory_from_string(
1807
1902
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1808
1903
self._write_basis_inventory(xml)
1809
1904
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1812
1907
def read_basis_inventory(self):
1813
1908
"""Read the cached basis inventory."""
1814
1909
path = self._basis_inventory_name()
1815
return self._control_files.get(path).read()
1910
return self._transport.get_bytes(path)
1817
1912
@needs_read_lock
1818
1913
def read_working_inventory(self):
1819
1914
"""Read the working inventory.
1821
1916
:raises errors.InventoryModified: read_working_inventory will fail
1822
1917
when the current in memory inventory has been modified.
1824
# conceptually this should be an implementation detail of the tree.
1919
# conceptually this should be an implementation detail of the tree.
1825
1920
# XXX: Deprecate this.
1826
1921
# ElementTree does its own conversion from UTF-8, so open in
1828
1923
if self._inventory_is_modified:
1829
1924
raise errors.InventoryModified(self)
1830
result = self._deserialize(self._control_files.get('inventory'))
1925
f = self._transport.get('inventory')
1927
result = self._deserialize(f)
1831
1930
self._set_inventory(result, dirty=False)
1849
1948
new_files=set()
1850
1949
unknown_nested_files=set()
1951
to_file = sys.stdout
1852
1953
def recurse_directory_to_add_files(directory):
1853
1954
# Recurse directory and add all files
1854
1955
# so we can check if they have changed.
1855
1956
for parent_info, file_infos in\
1856
osutils.walkdirs(self.abspath(directory),
1858
for relpath, basename, kind, lstat, abspath in file_infos:
1957
self.walkdirs(directory):
1958
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1859
1959
# Is it versioned or ignored?
1860
1960
if self.path2id(relpath) or self.is_ignored(relpath):
1861
1961
# Add nested content for deletion.
1887
1986
has_changed_files = len(unknown_nested_files) > 0
1888
1987
if not has_changed_files:
1889
1988
for (file_id, path, content_change, versioned, parent_id, name,
1890
kind, executable) in self._iter_changes(self.basis_tree(),
1989
kind, executable) in self.iter_changes(self.basis_tree(),
1891
1990
include_unchanged=True, require_versioned=False,
1892
1991
want_unversioned=True, specific_files=files):
1893
# Check if it's an unknown (but not ignored) OR
1894
# changed (but not deleted) :
1895
if not self.is_ignored(path[1]) and (
1896
versioned == (False, False) or
1897
content_change and kind[1] != None):
1992
if versioned == (False, False):
1993
# The record is unknown ...
1994
if not self.is_ignored(path[1]):
1995
# ... but not ignored
1996
has_changed_files = True
1998
elif content_change and (kind[1] is not None):
1999
# Versioned and changed, but not deleted
1898
2000
has_changed_files = True
2037
2139
@needs_tree_write_lock
2038
2140
def set_root_id(self, file_id):
2039
2141
"""Set the root id for this tree."""
2041
2143
if file_id is None:
2042
symbol_versioning.warn(symbol_versioning.zero_twelve
2043
% 'WorkingTree.set_root_id with fileid=None',
2048
file_id = osutils.safe_file_id(file_id)
2145
'WorkingTree.set_root_id with fileid=None')
2146
file_id = osutils.safe_file_id(file_id)
2049
2147
self._set_root_id(file_id)
2051
2149
def _set_root_id(self, file_id):
2052
2150
"""Set the root id for this tree, in a format specific manner.
2054
:param file_id: The file id to assign to the root. It must not be
2152
:param file_id: The file id to assign to the root. It must not be
2055
2153
present in the current inventory or an error will occur. It must
2056
2154
not be None, but rather a valid file id.
2077
2175
def unlock(self):
2078
2176
"""See Branch.unlock.
2080
2178
WorkingTree locking just uses the Branch locking facilities.
2081
2179
This is current because all working trees have an embedded branch
2082
2180
within them. IF in the future, we were to make branch data shareable
2083
between multiple working trees, i.e. via shared storage, then we
2181
between multiple working trees, i.e. via shared storage, then we
2084
2182
would probably want to lock both the local tree, and the branch.
2086
2184
raise NotImplementedError(self.unlock)
2088
def update(self, change_reporter=None, possible_transports=None):
2188
def update(self, change_reporter=None, possible_transports=None,
2189
revision=None, old_tip=_marker):
2089
2190
"""Update a working tree along its branch.
2091
2192
This will update the branch if its bound too, which means we have
2138
2246
# cant set that until we update the working trees last revision to be
2139
2247
# one from the new branch, because it will just get absorbed by the
2140
2248
# parent de-duplication logic.
2142
2250
# We MUST save it even if an error occurs, because otherwise the users
2143
2251
# local work is unreferenced and will appear to have been lost.
2147
2255
last_rev = self.get_parent_ids()[0]
2148
2256
except IndexError:
2149
2257
last_rev = _mod_revision.NULL_REVISION
2150
if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
2151
# merge tree state up to new branch tip.
2258
if revision is None:
2259
revision = self.branch.last_revision()
2261
if revision not in self.branch.revision_history():
2262
raise errors.NoSuchRevision(self.branch, revision)
2264
old_tip = old_tip or _mod_revision.NULL_REVISION
2266
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
2267
# the branch we are bound to was updated
2268
# merge those changes in first
2269
base_tree = self.basis_tree()
2270
other_tree = self.branch.repository.revision_tree(old_tip)
2271
nb_conflicts = merge.merge_inner(self.branch, other_tree,
2272
base_tree, this_tree=self,
2273
change_reporter=change_reporter)
2275
self.add_parent_tree((old_tip, other_tree))
2276
trace.note('Rerun update after fixing the conflicts.')
2279
if last_rev != _mod_revision.ensure_null(revision):
2280
# the working tree is up to date with the branch
2281
# we can merge the specified revision from master
2282
to_tree = self.branch.repository.revision_tree(revision)
2283
to_root_id = to_tree.get_root_id()
2152
2285
basis = self.basis_tree()
2153
2286
basis.lock_read()
2155
to_tree = self.branch.basis_tree()
2156
if basis.inventory.root is None:
2157
self.set_root_id(to_tree.inventory.root.file_id)
2288
if (basis.inventory.root is None
2289
or basis.inventory.root.file_id != to_root_id):
2290
self.set_root_id(to_root_id)
2159
result += merge.merge_inner(
2164
change_reporter=change_reporter)
2295
# determine the branch point
2296
graph = self.branch.repository.get_graph()
2297
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2299
base_tree = self.branch.repository.revision_tree(base_rev_id)
2301
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2303
change_reporter=change_reporter)
2304
self.set_last_revision(revision)
2167
2305
# TODO - dedup parents list with things merged by pull ?
2168
2306
# reuse the tree we've updated to to set the basis:
2169
parent_trees = [(self.branch.last_revision(), to_tree)]
2307
parent_trees = [(revision, to_tree)]
2170
2308
merges = self.get_parent_ids()[1:]
2171
2309
# Ideally we ask the tree for the trees here, that way the working
2172
# tree can decide whether to give us teh entire tree or give us a
2310
# tree can decide whether to give us the entire tree or give us a
2173
2311
# lazy initialised tree. dirstate for instance will have the trees
2174
2312
# in ram already, whereas a last-revision + basis-inventory tree
2175
2313
# will not, but also does not need them when setting parents.
2176
2314
for parent in merges:
2177
2315
parent_trees.append(
2178
2316
(parent, self.branch.repository.revision_tree(parent)))
2179
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2317
if not _mod_revision.is_null(old_tip):
2180
2318
parent_trees.append(
2181
2319
(old_tip, self.branch.repository.revision_tree(old_tip)))
2182
2320
self.set_parent_trees(parent_trees)
2183
2321
last_rev = parent_trees[0][0]
2185
# the working tree had the same last-revision as the master
2186
# branch did. We may still have pivot local work from the local
2187
# branch into old_tip:
2188
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
2189
self.add_parent_tree_id(old_tip)
2190
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2191
and old_tip != last_rev):
2192
# our last revision was not the prior branch last revision
2193
# and we have converted that last revision to a pending merge.
2194
# base is somewhere between the branch tip now
2195
# and the now pending merge
2197
# Since we just modified the working tree and inventory, flush out
2198
# the current state, before we modify it again.
2199
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2200
# requires it only because TreeTransform directly munges the
2201
# inventory and calls tree._write_inventory(). Ultimately we
2202
# should be able to remove this extra flush.
2204
graph = self.branch.repository.get_graph()
2205
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
2207
base_tree = self.branch.repository.revision_tree(base_rev_id)
2208
other_tree = self.branch.repository.revision_tree(old_tip)
2209
result += merge.merge_inner(
2214
change_reporter=change_reporter)
2217
2324
def _write_hashcache_if_dirty(self):
2218
2325
"""Write out the hashcache if it is dirty."""
2302
2409
current_inv = None
2303
2410
inv_finished = True
2304
2411
while not inv_finished or not disk_finished:
2413
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2414
cur_disk_dir_content) = current_disk
2416
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
2417
cur_disk_dir_content) = ((None, None), None)
2305
2418
if not disk_finished:
2306
2419
# strip out .bzr dirs
2307
if current_disk[0][1][top_strip_len:] == '':
2308
# osutils.walkdirs can be made nicer -
2420
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
2421
len(cur_disk_dir_content) > 0):
2422
# osutils.walkdirs can be made nicer -
2309
2423
# yield the path-from-prefix rather than the pathjoined
2311
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2312
if current_disk[1][bzrdir_loc][0] == '.bzr':
2425
bzrdir_loc = bisect_left(cur_disk_dir_content,
2427
if (bzrdir_loc < len(cur_disk_dir_content)
2428
and self.bzrdir.is_control_filename(
2429
cur_disk_dir_content[bzrdir_loc][0])):
2313
2430
# we dont yield the contents of, or, .bzr itself.
2314
del current_disk[1][bzrdir_loc]
2431
del cur_disk_dir_content[bzrdir_loc]
2315
2432
if inv_finished:
2316
2433
# everything is unknown
2319
2436
# everything is missing
2322
direction = cmp(current_inv[0][0], current_disk[0][0])
2439
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
2323
2440
if direction > 0:
2324
2441
# disk is before inventory - unknown
2325
2442
dirblock = [(relpath, basename, kind, stat, None, None) for
2326
relpath, basename, kind, stat, top_path in current_disk[1]]
2327
yield (current_disk[0][0], None), dirblock
2443
relpath, basename, kind, stat, top_path in
2444
cur_disk_dir_content]
2445
yield (cur_disk_dir_relpath, None), dirblock
2329
2447
current_disk = disk_iterator.next()
2330
2448
except StopIteration:
2404
2523
# FIXME: stash the node in pending
2405
2524
entry = inv[top_id]
2406
for name, child in entry.sorted_children():
2407
dirblock.append((relroot + name, name, child.kind, None,
2408
child.file_id, child.kind
2525
if entry.kind == 'directory':
2526
for name, child in entry.sorted_children():
2527
dirblock.append((relroot + name, name, child.kind, None,
2528
child.file_id, child.kind
2410
2530
yield (currentdir[0], entry.file_id), dirblock
2411
2531
# push the user specified dirs from dirblock
2412
2532
for dir in reversed(dirblock):
2598
def _get_rules_searcher(self, default_searcher):
2599
"""See Tree._get_rules_searcher."""
2600
if self._rules_searcher is None:
2601
self._rules_searcher = super(WorkingTree,
2602
self)._get_rules_searcher(default_searcher)
2603
return self._rules_searcher
2605
def get_shelf_manager(self):
2606
"""Return the ShelfManager for this WorkingTree."""
2607
from bzrlib.shelf import ShelfManager
2608
return ShelfManager(self, self._transport)
2460
2611
class WorkingTree2(WorkingTree):
2461
2612
"""This is the Format 2 working tree.
2463
This was the first weave based working tree.
2614
This was the first weave based working tree.
2464
2615
- uses os locks for locking.
2465
2616
- uses the branch last-revision.
2521
2676
def _last_revision(self):
2522
2677
"""See Mutable.last_revision."""
2524
return self._control_files.get('last-revision').read()
2679
return self._transport.get_bytes('last-revision')
2525
2680
except errors.NoSuchFile:
2526
2681
return _mod_revision.NULL_REVISION
2528
2683
def _change_last_revision(self, revision_id):
2529
2684
"""See WorkingTree._change_last_revision."""
2530
if revision_id is None or revision_id == NULL_REVISION:
2685
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2532
self._control_files._transport.delete('last-revision')
2687
self._transport.delete('last-revision')
2533
2688
except errors.NoSuchFile:
2537
self._control_files.put_bytes('last-revision', revision_id)
2692
self._transport.put_bytes('last-revision', revision_id,
2693
mode=self.bzrdir._get_file_mode())
2696
def _get_check_refs(self):
2697
"""Return the references needed to perform a check of this tree."""
2698
return [('trees', self.last_revision())]
2540
2700
@needs_tree_write_lock
2541
2701
def set_conflicts(self, conflicts):
2542
self._put_rio('conflicts', conflicts.to_stanzas(),
2702
self._put_rio('conflicts', conflicts.to_stanzas(),
2543
2703
CONFLICT_HEADER_1)
2545
2705
@needs_tree_write_lock
2693
2848
"""See WorkingTreeFormat.get_format_description()."""
2694
2849
return "Working tree format 2"
2696
def stub_initialize_remote(self, control_files):
2697
"""As a special workaround create critical control files for a remote working tree
2851
def _stub_initialize_on_transport(self, transport, file_mode):
2852
"""Workaround: create control files for a remote working tree.
2699
2854
This ensures that it can later be updated and dealt with locally,
2700
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2855
since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
2701
2856
no working tree. (See bug #43064).
2703
2858
sio = StringIO()
2859
inv = inventory.Inventory()
2705
2860
xml5.serializer_v5.write_inventory(inv, sio, working=True)
2707
control_files.put('inventory', sio)
2709
control_files.put_bytes('pending-merges', '')
2712
def initialize(self, a_bzrdir, revision_id=None):
2862
transport.put_file('inventory', sio, file_mode)
2863
transport.put_bytes('pending-merges', '', file_mode)
2865
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2866
accelerator_tree=None, hardlink=False):
2713
2867
"""See WorkingTreeFormat.initialize()."""
2714
2868
if not isinstance(a_bzrdir.transport, LocalTransport):
2715
2869
raise errors.NotLocalUrl(a_bzrdir.transport.base)
2716
branch = a_bzrdir.open_branch()
2870
if from_branch is not None:
2871
branch = from_branch
2873
branch = a_bzrdir.open_branch()
2717
2874
if revision_id is None:
2718
2875
revision_id = _mod_revision.ensure_null(branch.last_revision())
2719
2876
branch.lock_write()
2796
2953
def _open_control_files(self, a_bzrdir):
2797
2954
transport = a_bzrdir.get_workingtree_transport(None)
2798
return LockableFiles(transport, self._lock_file_name,
2955
return LockableFiles(transport, self._lock_file_name,
2799
2956
self._lock_class)
2801
def initialize(self, a_bzrdir, revision_id=None):
2958
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2959
accelerator_tree=None, hardlink=False):
2802
2960
"""See WorkingTreeFormat.initialize().
2804
revision_id allows creating a working tree at a different
2805
revision than the branch is at.
2962
:param revision_id: if supplied, create a working tree at a different
2963
revision than the branch is at.
2964
:param accelerator_tree: A tree which can be used for retrieving file
2965
contents more quickly than the revision tree, i.e. a workingtree.
2966
The revision tree will be used for cases where accelerator_tree's
2967
content is different.
2968
:param hardlink: If true, hard-link files from accelerator_tree,
2807
2971
if not isinstance(a_bzrdir.transport, LocalTransport):
2808
2972
raise errors.NotLocalUrl(a_bzrdir.transport.base)