1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
85
90
from bzrlib import symbol_versioning
86
91
from bzrlib.decorators import needs_read_lock, needs_write_lock
92
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
87
93
from bzrlib.lockable_files import LockableFiles
88
94
from bzrlib.lockdir import LockDir
89
95
import bzrlib.mutabletree
90
96
from bzrlib.mutabletree import needs_tree_write_lock
91
97
from bzrlib import osutils
92
98
from bzrlib.osutils import (
103
111
from bzrlib.trace import mutter, note
104
112
from bzrlib.transport.local import LocalTransport
105
113
from bzrlib.progress import DummyProgress, ProgressPhase
106
from bzrlib.revision import CURRENT_REVISION
114
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
107
115
from bzrlib.rio import RioReader, rio_file, Stanza
108
from bzrlib.symbol_versioning import (
110
DEPRECATED_PARAMETER,
116
from bzrlib.symbol_versioning import (deprecated_passed,
119
DEPRECATED_PARAMETER,
114
123
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
281
290
self._control_files.break_lock()
282
291
self.branch.break_lock()
284
def _get_check_refs(self):
285
"""Return the references needed to perform a check of this tree.
287
The default implementation returns no refs, and is only suitable for
288
trees that have no local caching and can commit on ghosts at any time.
290
:seealso: bzrlib.check for details about check_refs.
294
293
def requires_rich_root(self):
295
294
return self._format.requires_rich_root
448
447
def get_file_with_stat(self, file_id, path=None, filtered=True,
449
448
_fstat=os.fstat):
450
"""See Tree.get_file_with_stat."""
449
"""See MutableTree.get_file_with_stat."""
452
451
path = self.id2path(file_id)
453
452
file_obj = self.get_file_byname(path, filtered=False)
454
453
stat_value = _fstat(file_obj.fileno())
455
if filtered and self.supports_content_filtering():
454
if self.supports_content_filtering() and filtered:
456
455
filters = self._content_filter_stack(path)
457
456
file_obj = filtered_input_file(file_obj, filters)
458
457
return (file_obj, stat_value)
463
462
def get_file_byname(self, filename, filtered=True):
464
463
path = self.abspath(filename)
465
464
f = file(path, 'rb')
466
if filtered and self.supports_content_filtering():
465
if self.supports_content_filtering() and filtered:
467
466
filters = self._content_filter_stack(filename)
468
467
return filtered_input_file(f, filters)
488
487
incorrectly attributed to CURRENT_REVISION (but after committing, the
489
488
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)]
490
basis = self.basis_tree()
493
changes = self.iter_changes(basis, True, [self.id2path(file_id)],
494
require_versioned=True).next()
495
changed_content, kind = changes[2], changes[6]
496
if not changed_content:
497
return basis.annotate_iter(file_id)
501
if kind[0] != 'file':
504
old_lines = list(basis.annotate_iter(file_id))
506
for tree in self.branch.repository.revision_trees(
507
self.get_parent_ids()[1:]):
508
if file_id not in tree:
510
old.append(list(tree.annotate_iter(file_id)))
511
return annotate.reannotate(old, self.get_file(file_id).readlines(),
528
516
def _get_ancestors(self, default_revision):
529
517
ancestors = set([default_revision])
761
749
kind = 'tree-reference'
762
750
return kind, None, None, None
763
751
elif kind == 'symlink':
764
target = osutils.readlink(abspath)
765
return ('symlink', None, None, target)
752
return ('symlink', None, None,
753
os.readlink(abspath.encode(osutils._fs_enc)
754
).decode(osutils._fs_enc))
767
756
return (kind, None, None, None)
901
890
branch.last_revision().
903
892
from bzrlib.merge import Merger, Merge3Merger
904
pb = ui.ui_factory.nested_progress_bar()
893
pb = bzrlib.ui.ui_factory.nested_progress_bar()
906
895
merger = Merger(self.branch, this_tree=self, pb=pb)
907
896
merger.pp = ProgressPhase("Merge phase", 5, pb)
986
975
def get_symlink_target(self, file_id):
987
abspath = self.id2abspath(file_id)
988
target = osutils.readlink(abspath)
976
return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc)
977
).decode(osutils._fs_enc)
991
979
@needs_write_lock
992
980
def subsume(self, other_tree):
1093
1081
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1095
1083
tree_bzrdir = branch_bzrdir
1096
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1084
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
1097
1085
wt.set_parent_ids(self.get_parent_ids())
1098
1086
my_inv = self.inventory
1099
child_inv = inventory.Inventory(root_id=None)
1087
child_inv = Inventory(root_id=None)
1100
1088
new_root = my_inv[file_id]
1101
1089
my_inv.remove_recursive_id(file_id)
1102
1090
new_root.parent_id = None
1127
1115
def _kind(self, relpath):
1128
1116
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).
1118
def list_files(self, include_root=False):
1119
"""Recursively list all files as (path, class, kind, id, entry).
1133
1121
Lists, but does not descend into unversioned directories.
1134
1123
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
1126
Skips the control directory.
1141
1128
# list_files is an iterator, so @needs_read_lock doesn't work properly
1142
1129
# with it. So callers should be careful to always read_lock the tree.
1144
1131
raise errors.ObjectNotLocked(self)
1146
1133
inv = self.inventory
1147
if from_dir is None and include_root is True:
1134
if include_root is True:
1148
1135
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1149
1136
# Convert these into local objects to save lookup times
1150
1137
pathjoin = osutils.pathjoin
1157
1144
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1159
1146
# directory file_id, relative path, absolute path, reverse sorted children
1160
if from_dir is not None:
1161
from_dir_id = inv.path2id(from_dir)
1162
if from_dir_id is None:
1163
# Directory not versioned
1165
from_dir_abspath = pathjoin(self.basedir, from_dir)
1167
from_dir_id = inv.root.file_id
1168
from_dir_abspath = self.basedir
1169
children = os.listdir(from_dir_abspath)
1147
children = os.listdir(self.basedir)
1170
1148
children.sort()
1171
1149
# jam 20060527 The kernel sized tree seems equivalent whether we
1172
1150
# use a deque and popleft to keep them sorted, or if we use a plain
1173
1151
# list and just reverse() them.
1174
1152
children = collections.deque(children)
1175
stack = [(from_dir_id, u'', from_dir_abspath, children)]
1153
stack = [(inv.root.file_id, u'', self.basedir, children)]
1177
1155
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1236
1214
if fk != 'directory':
1239
# But do this child first if recursing down
1241
new_children = os.listdir(fap)
1243
new_children = collections.deque(new_children)
1244
stack.append((f_ie.file_id, fp, fap, new_children))
1245
# Break out of inner loop,
1246
# so that we start outer loop with child
1217
# But do this child first
1218
new_children = os.listdir(fap)
1220
new_children = collections.deque(new_children)
1221
stack.append((f_ie.file_id, fp, fap, new_children))
1222
# Break out of inner loop,
1223
# so that we start outer loop with child
1249
1226
# if we finished all children, pop it off the stack
1428
1405
inv = self.inventory
1429
1406
for entry in moved:
1431
self._move_entry(WorkingTree._RenameEntry(
1432
entry.to_rel, entry.from_id,
1408
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1433
1409
entry.to_tail, entry.to_parent_id, entry.from_rel,
1434
1410
entry.from_tail, entry.from_parent_id,
1435
1411
entry.only_change_inv))
1486
1462
from_tail = splitpath(from_rel)[-1]
1487
1463
from_id = inv.path2id(from_rel)
1488
1464
if from_id is None:
1489
# if file is missing in the inventory maybe it's in the basis_tree
1490
basis_tree = self.branch.basis_tree()
1491
from_id = basis_tree.path2id(from_rel)
1493
raise errors.BzrRenameFailedError(from_rel,to_rel,
1494
errors.NotVersionedError(path=str(from_rel)))
1495
# put entry back in the inventory so we can rename it
1496
from_entry = basis_tree.inventory[from_id].copy()
1499
from_entry = inv[from_id]
1465
raise errors.BzrRenameFailedError(from_rel,to_rel,
1466
errors.NotVersionedError(path=str(from_rel)))
1467
from_entry = inv[from_id]
1500
1468
from_parent_id = from_entry.parent_id
1501
1469
to_dir, to_tail = os.path.split(to_rel)
1502
1470
to_dir_id = inv.path2id(to_dir)
1594
1562
@needs_write_lock
1595
1563
def pull(self, source, overwrite=False, stop_revision=None,
1596
change_reporter=None, possible_transports=None, local=False):
1597
top_pb = ui.ui_factory.nested_progress_bar()
1564
change_reporter=None, possible_transports=None):
1565
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1598
1566
source.lock_read()
1600
1568
pp = ProgressPhase("Pull phase", 2, top_pb)
1602
1570
old_revision_info = self.branch.last_revision_info()
1603
1571
basis_tree = self.basis_tree()
1604
1572
count = self.branch.pull(source, overwrite, stop_revision,
1605
possible_transports=possible_transports,
1573
possible_transports=possible_transports)
1607
1574
new_revision_info = self.branch.last_revision_info()
1608
1575
if new_revision_info != old_revision_info:
1609
1576
pp.next_phase()
1610
1577
repository = self.branch.repository
1611
pb = ui.ui_factory.nested_progress_bar()
1578
pb = bzrlib.ui.ui_factory.nested_progress_bar()
1612
1579
basis_tree.lock_read()
1614
1581
new_basis_tree = self.branch.basis_tree()
2405
2373
bzrdir_loc = bisect_left(cur_disk_dir_content,
2406
2374
('.bzr', '.bzr'))
2407
2375
if (bzrdir_loc < len(cur_disk_dir_content)
2408
and self.bzrdir.is_control_filename(
2409
cur_disk_dir_content[bzrdir_loc][0])):
2376
and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
2410
2377
# we dont yield the contents of, or, .bzr itself.
2411
2378
del cur_disk_dir_content[bzrdir_loc]
2412
2379
if inv_finished:
2546
2513
return un_resolved, resolved
2548
2515
@needs_read_lock
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
2517
tree_basis = self.basis_tree()
2557
2518
tree_basis.lock_read()
2559
repo_basis = references[('trees', self.last_revision())]
2520
repo_basis = self.branch.repository.revision_tree(
2521
self.last_revision())
2560
2522
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2561
2523
raise errors.BzrCheckError(
2562
2524
"Mismatched basis inventory content.")
2608
2570
if self._inventory is None:
2609
2571
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
2573
def lock_tree_write(self):
2616
2574
"""See WorkingTree.lock_tree_write().
2664
2622
def _change_last_revision(self, revision_id):
2665
2623
"""See WorkingTree._change_last_revision."""
2666
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2624
if revision_id is None or revision_id == NULL_REVISION:
2668
2626
self._transport.delete('last-revision')
2669
2627
except errors.NoSuchFile:
2674
2632
mode=self.bzrdir._get_file_mode())
2677
def _get_check_refs(self):
2678
"""Return the references needed to perform a check of this tree."""
2679
return [('trees', self.last_revision())]
2681
2635
@needs_tree_write_lock
2682
2636
def set_conflicts(self, conflicts):
2683
2637
self._put_rio('conflicts', conflicts.to_stanzas(),
2982
2936
# only set an explicit root id if there is one to set.
2983
2937
if basis_tree.inventory.root is not None:
2984
2938
wt.set_root_id(basis_tree.get_root_id())
2985
if revision_id == _mod_revision.NULL_REVISION:
2939
if revision_id == NULL_REVISION:
2986
2940
wt.set_parent_trees([])
2988
2942
wt.set_parent_trees([(revision_id, basis_tree)])