1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
90
85
from bzrlib import symbol_versioning
91
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
92
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
93
87
from bzrlib.lockable_files import LockableFiles
94
88
from bzrlib.lockdir import LockDir
95
89
import bzrlib.mutabletree
96
90
from bzrlib.mutabletree import needs_tree_write_lock
97
91
from bzrlib import osutils
98
92
from bzrlib.osutils import (
111
103
from bzrlib.trace import mutter, note
112
104
from bzrlib.transport.local import LocalTransport
113
105
from bzrlib.progress import DummyProgress, ProgressPhase
114
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
106
from bzrlib.revision import CURRENT_REVISION
115
107
from bzrlib.rio import RioReader, rio_file, Stanza
116
from bzrlib.symbol_versioning import (deprecated_passed,
119
DEPRECATED_PARAMETER,
108
from bzrlib.symbol_versioning import (
110
DEPRECATED_PARAMETER,
123
114
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
451
442
path = self.id2path(file_id)
452
443
file_obj = self.get_file_byname(path, filtered=False)
453
444
stat_value = _fstat(file_obj.fileno())
454
if self.supports_content_filtering() and filtered:
445
if filtered and self.supports_content_filtering():
455
446
filters = self._content_filter_stack(path)
456
447
file_obj = filtered_input_file(file_obj, filters)
457
448
return (file_obj, stat_value)
462
453
def get_file_byname(self, filename, filtered=True):
463
454
path = self.abspath(filename)
464
455
f = file(path, 'rb')
465
if self.supports_content_filtering() and filtered:
456
if filtered and self.supports_content_filtering():
466
457
filters = self._content_filter_stack(filename)
467
458
return filtered_input_file(f, filters)
487
478
incorrectly attributed to CURRENT_REVISION (but after committing, the
488
479
attribution will be correct).
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(),
481
maybe_file_parent_keys = []
482
for parent_id in self.get_parent_ids():
484
parent_tree = self.revision_tree(parent_id)
485
except errors.NoSuchRevisionInTree:
486
parent_tree = self.branch.repository.revision_tree(parent_id)
487
parent_tree.lock_read()
489
if file_id not in parent_tree:
491
ie = parent_tree.inventory[file_id]
492
if ie.kind != 'file':
493
# Note: this is slightly unnecessary, because symlinks and
494
# directories have a "text" which is the empty text, and we
495
# know that won't mess up annotations. But it seems cleaner
497
parent_text_key = (file_id, ie.revision)
498
if parent_text_key not in maybe_file_parent_keys:
499
maybe_file_parent_keys.append(parent_text_key)
502
graph = _mod_graph.Graph(self.branch.repository.texts)
503
heads = graph.heads(maybe_file_parent_keys)
504
file_parent_keys = []
505
for key in maybe_file_parent_keys:
507
file_parent_keys.append(key)
509
# Now we have the parents of this content
510
annotator = self.branch.repository.texts.get_annotator()
511
text = self.get_file(file_id).read()
512
this_key =(file_id, default_revision)
513
annotator.add_special_text(this_key, file_parent_keys, text)
514
annotations = [(key[-1], line)
515
for key, line in annotator.annotate_flat(this_key)]
516
518
def _get_ancestors(self, default_revision):
517
519
ancestors = set([default_revision])
889
891
branch.last_revision().
891
893
from bzrlib.merge import Merger, Merge3Merger
892
pb = bzrlib.ui.ui_factory.nested_progress_bar()
894
pb = ui.ui_factory.nested_progress_bar()
894
896
merger = Merger(self.branch, this_tree=self, pb=pb)
895
897
merger.pp = ProgressPhase("Merge phase", 5, pb)
1081
1083
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
1083
1085
tree_bzrdir = branch_bzrdir
1084
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
1086
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
1085
1087
wt.set_parent_ids(self.get_parent_ids())
1086
1088
my_inv = self.inventory
1087
child_inv = Inventory(root_id=None)
1089
child_inv = inventory.Inventory(root_id=None)
1088
1090
new_root = my_inv[file_id]
1089
1091
my_inv.remove_recursive_id(file_id)
1090
1092
new_root.parent_id = None
1115
1117
def _kind(self, relpath):
1116
1118
return osutils.file_kind(self.abspath(relpath))
1118
def list_files(self, include_root=False):
1119
"""Recursively list all files as (path, class, kind, id, entry).
1120
def list_files(self, include_root=False, from_dir=None, recursive=True):
1121
"""List all files as (path, class, kind, id, entry).
1121
1123
Lists, but does not descend into unversioned directories.
1123
1124
This does not include files that have been deleted in this
1125
tree. Skips the control directory.
1126
Skips the control directory.
1127
:param include_root: if True, do not return an entry for the root
1128
:param from_dir: start from this directory or None for the root
1129
:param recursive: whether to recurse into subdirectories or not
1128
1131
# list_files is an iterator, so @needs_read_lock doesn't work properly
1129
1132
# with it. So callers should be careful to always read_lock the tree.
1131
1134
raise errors.ObjectNotLocked(self)
1133
1136
inv = self.inventory
1134
if include_root is True:
1137
if from_dir is None and include_root is True:
1135
1138
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
1136
1139
# Convert these into local objects to save lookup times
1137
1140
pathjoin = osutils.pathjoin
1144
1147
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
1146
1149
# directory file_id, relative path, absolute path, reverse sorted children
1147
children = os.listdir(self.basedir)
1150
if from_dir is not None:
1151
from_dir_id = inv.path2id(from_dir)
1152
if from_dir_id is None:
1153
# Directory not versioned
1155
from_dir_abspath = pathjoin(self.basedir, from_dir)
1157
from_dir_id = inv.root.file_id
1158
from_dir_abspath = self.basedir
1159
children = os.listdir(from_dir_abspath)
1148
1160
children.sort()
1149
1161
# jam 20060527 The kernel sized tree seems equivalent whether we
1150
1162
# use a deque and popleft to keep them sorted, or if we use a plain
1151
1163
# list and just reverse() them.
1152
1164
children = collections.deque(children)
1153
stack = [(inv.root.file_id, u'', self.basedir, children)]
1165
stack = [(from_dir_id, u'', from_dir_abspath, children)]
1155
1167
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
1214
1226
if fk != 'directory':
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
1229
# But do this child first if recursing down
1231
new_children = os.listdir(fap)
1233
new_children = collections.deque(new_children)
1234
stack.append((f_ie.file_id, fp, fap, new_children))
1235
# Break out of inner loop,
1236
# so that we start outer loop with child
1226
1239
# if we finished all children, pop it off the stack
1405
1418
inv = self.inventory
1406
1419
for entry in moved:
1408
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1421
self._move_entry(WorkingTree._RenameEntry(
1422
entry.to_rel, entry.from_id,
1409
1423
entry.to_tail, entry.to_parent_id, entry.from_rel,
1410
1424
entry.from_tail, entry.from_parent_id,
1411
1425
entry.only_change_inv))
1462
1476
from_tail = splitpath(from_rel)[-1]
1463
1477
from_id = inv.path2id(from_rel)
1464
1478
if from_id is None:
1465
raise errors.BzrRenameFailedError(from_rel,to_rel,
1466
errors.NotVersionedError(path=str(from_rel)))
1467
from_entry = inv[from_id]
1479
# if file is missing in the inventory maybe it's in the basis_tree
1480
basis_tree = self.branch.basis_tree()
1481
from_id = basis_tree.path2id(from_rel)
1483
raise errors.BzrRenameFailedError(from_rel,to_rel,
1484
errors.NotVersionedError(path=str(from_rel)))
1485
# put entry back in the inventory so we can rename it
1486
from_entry = basis_tree.inventory[from_id].copy()
1489
from_entry = inv[from_id]
1468
1490
from_parent_id = from_entry.parent_id
1469
1491
to_dir, to_tail = os.path.split(to_rel)
1470
1492
to_dir_id = inv.path2id(to_dir)
1562
1584
@needs_write_lock
1563
1585
def pull(self, source, overwrite=False, stop_revision=None,
1564
1586
change_reporter=None, possible_transports=None, local=False):
1565
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1587
top_pb = ui.ui_factory.nested_progress_bar()
1566
1588
source.lock_read()
1568
1590
pp = ProgressPhase("Pull phase", 2, top_pb)
2624
2645
def _change_last_revision(self, revision_id):
2625
2646
"""See WorkingTree._change_last_revision."""
2626
if revision_id is None or revision_id == NULL_REVISION:
2647
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
2628
2649
self._transport.delete('last-revision')
2629
2650
except errors.NoSuchFile:
2938
2959
# only set an explicit root id if there is one to set.
2939
2960
if basis_tree.inventory.root is not None:
2940
2961
wt.set_root_id(basis_tree.get_root_id())
2941
if revision_id == NULL_REVISION:
2962
if revision_id == _mod_revision.NULL_REVISION:
2942
2963
wt.set_parent_trees([])
2944
2965
wt.set_parent_trees([(revision_id, basis_tree)])